So schreiben Sie Ihre eigenen Python-Pakete

Überblick

Python ist eine wunderbare Programmiersprache und vieles mehr. Einer der Schwachpunkte ist das Verpacken. Dies ist eine bekannte Tatsache in der Gemeinschaft. Das Installieren, Importieren, Verwenden und Erstellen von Paketen hat sich im Laufe der Jahre verbessert, aber es ist immer noch nicht mit neueren Sprachen wie Go und Rust vergleichbar, die aus den Kämpfen von Python und anderen reifen Sprachen viel lernen könnten. 

In diesem Lernprogramm erfahren Sie alles Wissenswerte zum Erstellen und Freigeben Ihrer eigenen Pakete. Allgemeine Informationen zu Python-Paketen finden Sie unter So verwenden Sie Python-Pakete.

Ein Projekt verpacken

Das Packen eines Projekts ist der Prozess, bei dem Sie einen hoffentlich zusammenhängenden Satz von Python-Modulen und möglicherweise anderen Dateien verwenden und diese in einer Struktur ablegen, die problemlos verwendet werden kann. Es gibt verschiedene Dinge, die Sie berücksichtigen müssen, z. B. Abhängigkeiten von anderen Paketen, interne Struktur (Unterpakete), Versionierung, Zielgruppe und Form des Pakets (Quelle und / oder Binärdatei)..

Beispiel

Beginnen wir mit einem kurzen Beispiel. Das Conman-Paket ist ein Paket zum Verwalten der Konfiguration. Es unterstützt verschiedene Dateiformate sowie die verteilte Konfiguration mit etcd.

Der Inhalt eines Pakets wird normalerweise in einem einzigen Verzeichnis gespeichert (obwohl es üblich ist, Unterpakete in mehrere Verzeichnisse aufzuteilen) und manchmal, wie in diesem Fall, in einem eigenen Git-Repository. 

Das Stammverzeichnis enthält verschiedene Konfigurationsdateien (setup.py ist obligatorisch und das wichtigste), und der Paketcode selbst befindet sich normalerweise in einem Unterverzeichnis, dessen Name der Name des Pakets und idealerweise ein Testverzeichnis ist. So sieht es für "conman" aus:

> Baum. ├── LIZENZ ├── MANIFEST.in ├── README.md ├── conman in __init__.py __pycache__ ├── man conman_base.py │ man conman_etcd.py │ └── conman_file.py ├── Anforderungen.txt ├── setup.cfg ├── setup.py ├── test-Anforderungen.txt ├── tests __pycache__ ├── man conman_etcd_test.py ├── man conman_file_test .py └── └── etcd_test_util.py └── tox.ini

Werfen wir einen kurzen Blick auf die setup.py Datei. Es importiert zwei Funktionen aus dem Setuptools-Paket: Konfiguration() und find_packages (). Dann nennt es das Konfiguration() Funktion und Verwendung find_packages () für einen der Parameter.

von setuptools import setup, find_packages setup (name = 'conman', version = "0.3"), url = "https://github.com/the-gigi/conman", license = "MIT", author = "Gigi Sayfan" , author_email = "[email protected]", description = "Konfigurationsdateien verwalten", packages = find_packages (exclude = ['tests']), long_description = open ('README.md'). read (), zip_safe = False, setup_requires = ['nose> = 1.0'], test_suite = "nose.collector") 

Das ist ziemlich normal. Während setup.py Datei ist eine reguläre Python-Datei, und Sie können das tun, was Sie wollen Konfiguration() funktionieren mit den entsprechenden Parametern, da sie bei der Installation Ihres Pakets standardmäßig von verschiedenen Tools aufgerufen werden. Ich werde die Details im nächsten Abschnitt besprechen.

Die Konfigurationsdateien

Zusätzlich zu setup.py, Es gibt einige andere optionale Konfigurationsdateien, die hier angezeigt werden können und verschiedenen Zwecken dienen.

Setup.py

Das Konfiguration() function benötigt eine große Anzahl benannter Argumente, um viele Aspekte der Paketinstallation zu steuern und verschiedene Befehle auszuführen. Viele Argumente geben Metadaten an, die zum Suchen und Filtern beim Hochladen Ihres Pakets in ein Repository verwendet werden.

  • name: der Name Ihres Pakets (und wie es auf PYPI aufgeführt wird)
  • Version: Dies ist wichtig für die Aufrechterhaltung des richtigen Abhängigkeitsmanagements
  • url: die URL Ihres Pakets, normalerweise GitHub oder möglicherweise die readthedocs-URL
  • Pakete: Liste der Unterpakete, die aufgenommen werden müssen; find_packages () hilft hier
  • setup_requires: Hier legen Sie Abhängigkeiten fest
  • test_suite: welches Werkzeug zur Testzeit ausgeführt werden soll

Das Lange Beschreibung Hier wird auf den Inhalt des gesetzt README.md Datei, eine bewährte Methode, um eine einzige Quelle der Wahrheit zu haben.

Setup.cfg

Die Datei setup.py dient auch als Befehlszeilenschnittstelle, um verschiedene Befehle auszuführen. Um beispielsweise die Komponententests auszuführen, können Sie Folgendes eingeben: python setup.py test

running test running egg_info schreibt conman.egg-info / PKG-INFO schreibt Top-Level-Namen in conman.egg-info / top_level.txt und schreibt abhängige_links in conman.egg-info / dependency_links.txt, die Manifestdatei 'conman.egg-info' lesen Manuelles Lesen der Manifest-Vorlage 'MANIFEST.in' beim Schreiben der Manifest-Datei 'conman.egg-info / SOURCES.txt' läuft build_ext test_add_bad_key (conman_etcd_test.ConManEtcdTest) ... ok test_add_good_key (conman_etcd_test.ConMan.con) ) ... ok test_initialization (conman_etcd_test.ConManEtcdTest) ... ok test_refresh (conman_etcd_test.ConManEtcdTest) ... ok test_add_config_file_from_env_var (conman_file_test.ConmanFileTest) ... ok test_add_config_file_simple_guess_file_type (conman_file_test.ConmanFileTest) ... ok test_add_config_file_simple_unknown_wrong_file_type (conman_file_test.ConmanFileTest) ... ok test_add_config_file_simple_with_file_type (conman_file_test.ConmanFileTest) ... ok test_add_config_file_simple_w rong_file_type (conman_file_test.ConmanFileTest) ... ok test_add_config_file_with_base_dir (conman_file_test.ConmanFileTest) ... ok test_dictionary_access (conman_file_test.ConmanFileTest) ... ok test_guess_file_type (conman_file_test.ConmanFileTest) ... ok test_init_no_files (conman_file_test.ConmanFileTest) ... ok test_init_some_bad_files (conman_file_test.ConmanFileTest) ... ok test_init_some_good_files ( conman_file_test.ConmanFileTest)… ok ---------------------------------------- -------------------------- 16 Tests in 0,160 Sekunden durchgeführt OK 

Die Datei setup.cfg ist eine Datei im Ini-Format, die Standardeinstellungen für Befehle enthalten kann, an die Sie übergeben setup.py. Hier enthält setup.cfg einige Optionen für Nasentests (unser Testläufer):

[nosetests] verbose = 1 nocapture = 1 

MANIFEST.in

Diese Datei enthält Dateien, die nicht Teil des internen Paketverzeichnisses sind, aber dennoch eingefügt werden sollen. Das sind normalerweise die Readme Datei, die Lizenzdatei und ähnliches. Eine wichtige Datei ist die anforderungen.txt. Diese Datei wird von pip verwendet, um andere erforderliche Pakete zu installieren.

Hier ist Conmans MANIFEST.in Datei:

include LICENSE include README.md include needs.txt

Abhängigkeiten

Sie können beide Abhängigkeiten angeben install_requires Abschnitt von setup.py und in einem anforderungen.txt Datei. Pip installiert automatisch Abhängigkeiten von install_requires, aber nicht aus dem anforderungen.txt Datei. Um diese Anforderungen zu installieren, müssen Sie sie explizit angeben, wenn Sie pip ausführen: pip install -r Requirements.txt.

Das install_requires Mit dieser Option werden minimale und abstraktere Anforderungen auf der Hauptversionsebene festgelegt. Die Anforderungen.txt-Datei ist für konkretere Anforderungen gedacht, oft mit festgefahrenen Nebenversionen.

Hier ist die Anforderungsdatei von Conman. Sie können sehen, dass alle Versionen fixiert sind. Dies kann sich negativ auswirken, wenn eines dieser Pakete aktualisiert wird und eine Änderung eintritt, die Conman bricht.

PyYAML == 3.11 Python-etcd == 0.4.3 urllib3 == 1,7 pyOpenSSL == 0,15,1 psutil == 4,0,0 sechs == 1,7.3

Pinning gibt Ihnen Vorhersagbarkeit und Sicherheit. Dies ist besonders wichtig, wenn viele Personen Ihr Paket zu unterschiedlichen Zeitpunkten installieren. Ohne Fixieren erhält jede Person eine andere Mischung von Abhängigkeitsversionen, je nachdem, wann sie installiert wurde. Der Nachteil von Pinning ist, dass Sie, wenn Sie nicht mit der Entwicklung Ihrer Abhängigkeiten mithalten, an einer alten, schlecht funktionierenden und sogar anfälligen Version einer Abhängigkeit hängen bleiben können.

Ich habe Conman ursprünglich im Jahr 2014 geschrieben und habe dem nicht viel Aufmerksamkeit geschenkt. Nun, für dieses Tutorial habe ich alles aufgerüstet und es gab generell einige große Verbesserungen für fast jede Abhängigkeit.

Ausschüttungen

Sie können eine Quelldistribution oder eine Binärdistribution erstellen. Ich werde beide behandeln.

Quellenverteilung

Sie erstellen eine Quelldistribution mit dem Befehl: python setup.py sdist. Hier ist die Ausgabe für Conman:

> python setup.py sdist läuft sdist läuft egg_info schreibt conman.egg-info / PKG-INFO und schreibt Top-Level-Namen in conman.egg-info / top_level.txt und schreibt abhängige_Links zu conman.egg-info / dependency_links.txt, die Manifestdatei lesen 'conman.egg-info / SOURCES.txt' Lese-Manifest-Vorlage 'MANIFEST.in' Schreibmanifest-Datei 'conman.egg-info / SOURCES.txt' Warnung: sdist: Standarddatei nicht gefunden: README, README muss vorhanden sein. Zuerst wird README.txt ausgeführt, um zu prüfen, ob Conman-0.3 erstellt wird. Erstellen von Conman-0.3 / Conman. Erstellen von Conman-0.3 / Conman.egg-Info. Erstellen von harten Links in Conman-0.3… Festes Verknüpfen von LIZENZ -> Conman-0.3 conman-0.3 Hard Linking README.md -> Conman-0.3 Hard Linking Anforderungen.txt -> Conman-0.3 Hard Linking setup.cfg -> Conman-0.3 Hard Linking setup.py -> Conman-0.3 Hard Linking Conman / __ init__.py -> conman-0.3 / conman fest verknüpft conman / conman_base.py -> conman-0.3 / conman fest verlinkend conman / conman_etcd.py -> conman-0.3 / conman fest verlinkend conman / conman_fil e.py -> conman-0.3 / conman-Hardlink-Verknüpfung conman.egg-info / PKG-INFO -> conman-0.3 / conman.egg-info-Hardlink-Verknüpfung conman.egg-info / SOURCES.txt -> conman-0.3 / conman .egg-info Festes Verlinken conman.egg-info / dependency_links.txt -> conman-0.3 / conman.egg-info Festes Verlinken conman.egg-info / not-zip-safe -> conman-0.3 / conman.egg-info Hard Linking conman.egg-info / top_level.txt -> conman-0.3 / conman.egg-info Kopieren von setup.cfg -> conman-0.3 Erstellen von conman-0.3 / setup.cfg Erstellen von Teer-Archiv Entfernen von 'conman-0.3' (und alles darunter) 

Wie Sie sehen, habe ich eine Warnung erhalten, dass eine README-Datei mit einem der Standardpräfixe fehlt, da ich Markdown mag. Ich habe stattdessen eine "README.md". Ansonsten waren alle Paketquelldateien und die zusätzlichen Dateien enthalten. Dann wurde eine Reihe von Metadaten in der erstellt conman.egg-info Verzeichnis. Zum Schluss wird ein komprimiertes Tar-Archiv aufgerufen conman-0,3.tar.gz wird erstellt und in eine dist Unterverzeichnis.

Die Installation dieses Pakets erfordert einen Erstellungsschritt (auch wenn es sich um reines Python handelt). Sie können es normalerweise mit pip installieren, indem Sie einfach den Pfad an das Paket übergeben. Zum Beispiel:

pip install dist / conman-0.3.tar.gz Verarbeitung ./dist/conman-0.3.tar.gz Installieren der gesammelten Pakete: conman Ausführen von setup.py install für conman ... done Konman-0.3 erfolgreich installiert

Conman wurde in Site-Packages installiert und kann wie jedes andere Package importiert werden:

importieren Sie conman conman .__ file__ '/Users/gigi/.virtualenvs/conman/lib/python2.7/site-packages/conman/__init__.pyc'

Räder

Räder sind eine relativ neue Möglichkeit, Python-Code und optional C-Erweiterungen zu packen. Sie ersetzen das Eiformat. Es gibt verschiedene Arten von Rädern: reine Python-Räder, Plattformräder und Universalräder. Die reinen Python-Räder sind Pakete wie Conman, die keinen C-Erweiterungscode haben. 

Die Plattformräder haben einen C-Erweiterungscode. Bei den Universalrädern handelt es sich um reine Python-Räder, die mit Python 2 und Python 3 mit derselben Codebasis kompatibel sind (sie benötigen nicht einmal 2to3). Wenn Sie ein reines Python-Paket haben und möchten, dass Ihr Paket sowohl Python 2 als auch Python 3 unterstützt (was immer wichtiger wird), können Sie anstelle eines Rads für Python 2 ein einzelnes Universal-Build und für Python 3 ein Rad erstellen. 

Wenn Ihr Paket über C-Erweiterungscode verfügt, müssen Sie für jede Plattform ein Plattformrad erstellen. Der große Vorteil von Rädern, insbesondere für Pakete mit C-Erweiterungen, besteht darin, dass auf dem Zielcomputer keine Compiler- und unterstützenden Bibliotheken verfügbar sind. Das Rad enthält bereits ein eingebautes Paket. Sie wissen also, dass die Erstellung nicht fehlschlägt und die Installation viel schneller ist, da es sich buchstäblich nur um eine Kopie handelt. Benutzer, die wissenschaftliche Bibliotheken wie Numpy und Pandas verwenden, können dies wirklich schätzen, da die Installation solcher Pakete sehr lange dauerte und möglicherweise fehlgeschlagen ist, wenn eine Bibliothek fehlte oder der Compiler nicht ordnungsgemäß konfiguriert wurde.

Der Befehl, reine Räder oder Plattformräder zu bauen, lautet: python setup.py bdist_wheel.

Setuptools - die Engine, die das bereitstellt Konfiguration() Funktion-erkennt automatisch, ob ein reines oder Plattformrad benötigt wird.

laufendes bdist_wheel laufendes build laufendes build_py erstellen build erstellen build / lib erstellen build / lib / conman kopieren conman / __ init__.py -> build / lib / conman kopieren conman / conman_base.py -> build / lib / conman kopieren conman / conman_etcd.py -> build / lib / conman kopiert conman / conman_file.py -> build / lib / conman beim Installieren von build / bdist.macosx-10.9-x86_64 / laufendem Lauf Installieren von install_lib Erstellen von build / bdist.macosx-10.9-x86_64 Erstellen von build / bdist.macosx-10.9-x86_64 / wheel erstellt build / bdist.macosx-10.9-x86_64 / wheel / conman kopiert build / lib / conman / __ init__.py -> build / bdist.macosx-10.9-x86_64 / wheel / conman kopiert build /lib/conman/conman_base.py -> build / bdist.macosx-10.9-x86_64 / wheel / conman kopiert build / lib / conman / conman_etcd.py -> build / bdist.macosx-10.9-x86_64 / wheel / conman wird kopiert /lib/conman/conman_file.py -> build / bdist.macosx-10.9-x86_64 / wheel / conman läuft install_egg_info und führt egg_info aus, wobei conman.egg-info erstellt wird. conman.egg-info / PKG-INFO schreibt Namen der obersten Ebene es an conman.egg-info / top_level.txt. Schreiben von abhängigen_links an conman.egg-info / dependency_links.txt. Schreiben der Manifestdatei 'conman.egg-info / SOURCES.txt'. Lesen der Manifestdatei 'conman.egg-info / SOURCES.txt 'Manifest-Vorlage lesen' MANIFEST.in 'Manifestdatei schreiben' conman.egg-info / SOURCES.txt 'Conman.egg-info nach build / bdist.macosx-10.9-x86_64 / wheel / conman-0.3-py2.7 kopieren. egg-info, das install_scripts ausführt, um build / bdist.macosx-10.9-x86_64 / wheel / conman-0.3.dist-info / WHEEL zu erstellen

Überprüfen der dist Verzeichnis, können Sie sehen, dass ein reines Python-Rad erstellt wurde:

ls -la dist dist / total 32 -rw-r - r-- 1 Gigi-Mitarbeiter 5.5K 29. Februar 07:57 conman-0.3-py2-none-any.whl -rw-r - r-- 1 Gigi-Mitarbeiter 4.4K 28. Februar 23:33 conman-0.3.tar.gz

Der Name "conman-0.3-py2-none-any.whl" besteht aus mehreren Komponenten: Paketname, Paketversion, Python-Version, Plattformversion und schließlich die Erweiterung "whl".

Um universelle Pakete zu erstellen, fügen Sie sie einfach hinzu --Universal-, wie in python setup.py bdist_wheel --universal.

Das resultierende Rad wird als "conman-0.3-py2.py3-none-any.whl" bezeichnet..

Beachten Sie, dass Sie selbst dafür sorgen müssen, dass Ihr Code unter Python 2 und Python 3 tatsächlich funktioniert, wenn Sie ein universelles Paket erstellen.

Fazit

Um eigene Python-Pakete zu erstellen, müssen Sie mit vielen Tools arbeiten, viele Metadaten angeben und sorgfältig über Ihre Abhängigkeiten und Ihre Zielgruppe nachdenken. Aber die Belohnung ist großartig. 

Wenn Sie nützlichen Code schreiben und ordnungsgemäß verpacken, können die Benutzer ihn leicht installieren und davon profitieren.