Distribution Package erstellen

Distribution Packages sind Archive, die in einen Paket-Index hochgeladen und mit Pip installiert werden können.

Bemerkung

Beachtet bitte, dass es immer noch viele Anleitungen gibt, die einen Schritt zum Aufruf der setup.py enthalten, z.B. python setup.py sdist. Dies wird jedoch heutzutage von Teilen der Python Packaging Authority (PyPA) als Anti-Pattern angesehen.

setup.py

Eine minimale und dennoch funktionale setup.py findet ihr z.B. im attrs-Paket: setup.py. Daran könnt ihr erkennen, dass das meiste Boilerplate ist und lediglich die Zeilen 10–37 Metadaten für diese spezielle Paket sind. Die meisten anderen Metadaten sind in der __init__ gespeichert und werden mit regulären Ausdrücken erschlossen. Alternativ können diese Daten auch in ein separates Modul ausgelagert und mit Python analysiert werden, wie dies z.B. in cryptography erfolgt.

In beiden Fällen werden doppelte Metadaten in Paket und Code vermieden.

src-Package

Das packages-Feld verwendet setuptools’s find_packages() um darunterliegende Pakete zu finden und das package_dir-Feld beschreibt, wo das Root-Verzeichnis ist.

Bemerkung

find_packages() ohne src/-Verzeichnis würde alle Verzeichnisse mit einer __init__.py-Datei paketieren, also auch tests/-Verzeichnisse.

version

Für version gibt es verschiedene Möglichkeiten, die in PEP 0440 beschrieben sind.

classifiers

classifiers haben eine nützliche Zusatzfunktion: PyPI lehnt unbekannte Classifiers ab, sodass damit auch ein versehentlicher Upload vermieden werden kann.

Abhängigkeiten

Versionsnummern von Abhängigkeiten sollten üblicherweise nicht in der setup.py festgeschrieben werden sondern in der requirements.txt-Datei.

Andere Dateien

MANIFEST.in

Die Datei enthält alle Dateien und Verzeichnisse, die nicht bereits mit packages oder py_module erfasst werden. Sie kann z.B. so aussehen: attrs/MANIFEST.in.

Weitere Anweisungen in Manifest.in findet ihr in Creating a source distribution.

Bemerkung

Häufig wird die Aktualisierung der Manifest.in-Datei vergessen. Um dies zu vermeiden, könnt ihr check-manifest in einem pre-commit-Hook verwenden.

Bemerkung

Wenn ihr Dateien und Verzeichnisse aus MANIFEST.in auch installiert werden sollen, z.B. wenn es sich um laufzeitrelevante Daten handelt, könnt ihr dies mit include_package_data=True in eurem setup()-Aufruf angeben.

setup.cfg

Diese Datei wird nicht mehr benötigt, zumindest nicht für die Paketierung. wheel sammelt heutzutage alle erforderlichen Lizenzdateien automatisch und setuptools kann mit dem options-Keyword-Argument universelle whell-Pakete bauen, z.B. attrs-19.3.0-py2.py3-none-any.whl.

pyproject.toml

PEP 517 und PEP 518 brachten Plugable Build-Backends, isolierte Builds und pyproject.toml. Da wir setuptools verwenden, sollte die Datei so oder so ähnlich aussehen:

[build-system]
requires = ["setuptools>=40.6.0", "wheel"]
build-backend = "setuptools.build_meta"

LICENSE

Einen Überblick über freie und Open-Source-Software-Lizenzen erhaltet ihr in Comparison of free and open-source software licenses.

Wenn ihr z.B. eine möglichst große Verbreitung eures Pakets erreichen wollt, sind MIT- oder die BSD-Varianten eine gute Wahl. Die Apache-Lizenz schützt euch besser vor Patentverletzungen ist jedoch nicht kompatibel mit der GPL v2. Daher solltet ihr schauen, welche Lizenzen diejenigen Pakete haben, von denen ihr abhängt und zu denen ihr kompatibel sein solltet. Zur Analyse von Lizenzen könnt ihr licensechecker, verwenden, ein Kommandozeilenwerkzeug, das Installationsverzeichnisse nach Lizenzen durchsucht.

Darüberhinaus kann es auch sinnvoll sein, ein Package unter mehreren Lizenzen zu veröffentlichen. Ein Beispiel hierfür ist cryptography/LICENSE.

README.rst

Diese Datei teilt potentiellen Nutzern mit, worauf sie bei der Verwendung des Pakets achten müssen. Schreibt das Dokument in ReStructuredText (ReST), sodass ihr es später problemlos mit .. include:: ../../README.rst in die Sphinx-Dokumentation übernehmen könnt.

CHANGELOG.rst

Build

Wechselt in das Verzeichnis, in dem sich die setup.py-Datei befindet.

$ rm -rf build dist
$ pipenv run python3 -m pep517.build .

Die erste Zeile stellt sicher, dass ein sauberes Build ohne Artefakte früherer Builds erstellt wird. Die zweite Zeile baut ein sdist-Archiv unter Linux/Mac als gezippte Tar-Datei (.tar.gz) und unter Windows eine ZIP-Datei sowie ein bdist_wheel-Archiv mit .whl im dist-Verzeichnis.

Dieser Befehl sollte also die folgenden beiden Dateien erzeugen:

dist/
  example-0.0.1-py3-none-any.whl
  example-0.0.1.tar.gz
py3

Python-Version, mit der das Paket gebaut wurde

none

nicht OS-spezifisch

any

geeignet für jede Prozessorarchitektur

Die Referenz für die Dateinamen findet ihr in File name convention.

Siehe auch

Weitere Infos zu sdist erhaltet ihr in Creating a Source Distribution. und PEP 376.

Bemerkung

Die Verwendung von pep517.build zum Erstellen von Paketen ist aktuell (Oktober 2019) noch etwas umstritten. Es scheint Konsens zu sein, dass diese Funktionalität entweder in Pip oder in Twine zusammengeführt werden sollte. Derzeit scheint der oben genannte Weg jedoch der sauberste zu sein, ein Paket zu erstellen. Ich werde diesen Artikel aktualisieren, sobald sich eine andere Lösung durchsetzt.

Testen

$ pipenv --rm
$ pipenv install dist/attrs-19.3.0.tar.gz

Successfully built attrs
Installing collected packages: attrs
Successfully installed attrs-19.3.0
$ pipenv run python

>>> import attr; attr.__version__
'19.3.0'

oder

$ pipenv --rm
$ pipenv install dist/attrs-19.3.0-py2.py3-none-any.whl

Successfully built attrs
Installing collected packages: attrs
Successfully installed attrs-19.3.0
$ pipenv run python

>>> import attr; attr.__version__
'19.3.0'