zipapp — Verwalten von ausführbaren Python-Zip-Archiven

Hinzugefügt in Version 3.5.

Quellcode: Lib/zipapp.py


Dieses Modul stellt Werkzeuge zur Verfügung, um die Erstellung von Zip-Dateien mit Python-Code zu verwalten, die direkt vom Python-Interpreter ausgeführt werden können. Das Modul bietet sowohl eine Kommandozeilenschnittstelle als auch eine Python-API.

Grundlegendes Beispiel

Das folgende Beispiel zeigt, wie die Kommandozeilenschnittstelle verwendet werden kann, um ein ausführbares Archiv aus einem Verzeichnis mit Python-Code zu erstellen. Beim Ausführen wird das Archiv die Funktion main aus dem Modul myapp im Archiv ausführen.

$ python -m zipapp myapp -m "myapp:main"
$ python myapp.pyz
<output from myapp>

Befehlszeilenschnittstelle

Wenn als Programm von der Kommandozeile aufgerufen, wird die folgende Form verwendet

$ python -m zipapp source [options]

Wenn source ein Verzeichnis ist, wird ein Archiv aus dem Inhalt von source erstellt. Wenn source eine Datei ist, sollte es sich um ein Archiv handeln, und es wird zum Zielarchiv kopiert (oder der Inhalt seiner Shebang-Zeile wird angezeigt, wenn die Option –info angegeben ist).

Die folgenden Optionen werden verstanden

-o <output>, --output=<output>

Schreibt die Ausgabe in eine Datei namens output. Wenn diese Option nicht angegeben wird, ist der Ausgabedateiname derselbe wie die Eingabe source, wobei die Erweiterung .pyz hinzugefügt wird. Wenn ein expliziter Dateiname angegeben wird, wird dieser unverändert verwendet (so dass eine .pyz-Erweiterung bei Bedarf enthalten sein sollte).

Ein Ausgabedateiname muss angegeben werden, wenn die source ein Archiv ist (und in diesem Fall darf output nicht dasselbe sein wie source).

-p <interpreter>, --python=<interpreter>

Fügt dem Archiv eine #!-Zeile hinzu, die interpreter als auszuführenden Befehl angibt. Außerdem wird auf POSIX das Archiv ausführbar gemacht. Standardmäßig wird keine #!-Zeile geschrieben und die Datei wird nicht ausführbar gemacht.

-m <mainfn>, --main=<mainfn>

Schreibt eine __main__.py-Datei in das Archiv, die mainfn ausführt. Das Argument mainfn sollte die Form "pkg.mod:fn" haben, wobei "pkg.mod" ein Paket/Modul im Archiv ist und "fn" ein aufrufbares Objekt im angegebenen Modul ist. Die Datei __main__.py wird dieses aufrufbare Objekt ausführen.

--main kann nicht angegeben werden, wenn ein Archiv kopiert wird.

-c, --compress

Komprimiert Dateien mit der Deflate-Methode und reduziert so die Größe der Ausgabedatei. Standardmäßig werden Dateien unkomprimiert im Archiv gespeichert.

--compress hat keine Auswirkung beim Kopieren eines Archivs.

Hinzugefügt in Version 3.7.

--info

Zeigt den im Archiv eingebetteten Interpreter zu Diagnosezwecken an. In diesem Fall werden alle anderen Optionen ignoriert und SOURCE muss ein Archiv und kein Verzeichnis sein.

-h, --help

Gibt eine kurze Nutzungsmeldung aus und wird beendet.

Python API

Das Modul definiert zwei Hilfsfunktionen

zipapp.create_archive(source, target=None, interpreter=None, main=None, filter=None, compressed=False)

Erstellt ein Anwendungsarchiv aus source. Die Quelle kann eine der folgenden sein

  • Der Name eines Verzeichnisses oder ein pfadähnliches Objekt, das auf ein Verzeichnis verweist, in diesem Fall wird ein neues Anwendungsarchiv aus dem Inhalt dieses Verzeichnisses erstellt.

  • Der Name einer vorhandenen Anwendungsarchivdatei oder ein pfadähnliches Objekt, das auf eine solche Datei verweist. In diesem Fall wird die Datei zum Ziel kopiert (und modifiziert, um den für das Argument interpreter angegebenen Wert widerzuspiegeln). Der Dateiname sollte die Erweiterung .pyz enthalten, falls erforderlich.

  • Ein Dateiobjekt, das im Bytes-Modus zum Lesen geöffnet ist. Der Inhalt der Datei sollte ein Anwendungsarchiv sein, und das Dateiobjekt wird am Anfang des Archivs positioniert angenommen.

Das Argument target bestimmt, wohin das resultierende Archiv geschrieben wird

  • Wenn es der Name einer Datei oder ein pfadähnliches Objekt ist, wird das Archiv in diese Datei geschrieben.

  • Wenn es sich um ein geöffnetes Dateiobjekt handelt, wird das Archiv in dieses Dateiobjekt geschrieben, das im Bytes-Modus zum Schreiben geöffnet sein muss.

  • Wenn das Ziel weggelassen wird (oder None ist), muss die Quelle ein Verzeichnis sein und das Ziel ist eine Datei mit demselben Namen wie die Quelle, wobei die Erweiterung .pyz hinzugefügt wird.

Das Argument interpreter gibt den Namen des Python-Interpreters an, mit dem das Archiv ausgeführt wird. Es wird als "Shebang"-Zeile am Anfang des Archivs geschrieben. Unter POSIX wird dies vom Betriebssystem interpretiert, unter Windows vom Python Launcher. Das Weglassen des interpreters führt dazu, dass keine Shebang-Zeile geschrieben wird. Wenn ein Interpreter angegeben ist und das Ziel ein Dateiname ist, wird das Ausführungsbit der Zieldatei gesetzt.

Das Argument main gibt den Namen eines aufrufbaren Objekts an, das als Hauptprogramm für das Archiv verwendet wird. Es kann nur angegeben werden, wenn die Quelle ein Verzeichnis ist und die Quelle noch keine __main__.py-Datei enthält. Das Argument main sollte die Form "pkg.module:callable" haben und das Archiv wird ausgeführt, indem "pkg.module" importiert und das angegebene aufrufbare Objekt ohne Argumente ausgeführt wird. Es ist ein Fehler, main wegzulassen, wenn die Quelle ein Verzeichnis ist und keine __main__.py-Datei enthält, da das resultierende Archiv sonst nicht ausführbar wäre.

Das optionale Argument filter gibt eine Callback-Funktion an, der ein Path-Objekt übergeben wird, das den Pfad zur hinzuzufügenden Datei darstellt (relativ zum Quellverzeichnis). Es sollte True zurückgeben, wenn die Datei hinzugefügt werden soll.

Das optionale Argument compressed bestimmt, ob Dateien komprimiert werden. Wenn es auf True gesetzt ist, werden Dateien im Archiv mit der Deflate-Methode komprimiert; andernfalls werden Dateien unkomprimiert gespeichert. Dieses Argument hat keine Auswirkung beim Kopieren eines vorhandenen Archivs.

Wenn für source oder target ein Dateiobjekt angegeben wird, ist der Aufrufer dafür verantwortlich, es nach dem Aufruf von create_archive zu schließen.

Beim Kopieren eines vorhandenen Archivs benötigen Dateiobjekte nur read und readline oder write-Methoden. Beim Erstellen eines Archivs aus einem Verzeichnis, wenn das Ziel ein Dateiobjekt ist, wird es an die Klasse zipfile.ZipFile übergeben und muss die von dieser Klasse benötigten Methoden bereitstellen.

Geändert in Version 3.7: Die Parameter filter und compressed hinzugefügt.

zipapp.get_interpreter(archive)

Gibt den Interpreter zurück, der in der #!-Zeile am Anfang des Archivs angegeben ist. Wenn keine #!-Zeile vorhanden ist, wird None zurückgegeben. Das Argument archive kann ein Dateiname oder ein für das Lesen im Bytes-Modus geöffnetes dateiähnliches Objekt sein. Es wird angenommen, dass es sich am Anfang des Archivs befindet.

Beispiele

Packt ein Verzeichnis in ein Archiv und führt es aus.

$ python -m zipapp myapp
$ python myapp.pyz
<output from myapp>

Dasselbe kann mit der Funktion create_archive() erfolgen

>>> import zipapp
>>> zipapp.create_archive('myapp', 'myapp.pyz')

Um die Anwendung unter POSIX direkt ausführbar zu machen, geben Sie einen zu verwendenden Interpreter an.

$ python -m zipapp myapp -p "/usr/bin/env python"
$ ./myapp.pyz
<output from myapp>

Um die Shebang-Zeile in einem vorhandenen Archiv zu ersetzen, erstellen Sie ein modifiziertes Archiv mit der Funktion create_archive()

>>> import zipapp
>>> zipapp.create_archive('old_archive.pyz', 'new_archive.pyz', '/usr/bin/python3')

Um die Datei direkt zu aktualisieren, führen Sie die Ersetzung im Speicher mit einem BytesIO-Objekt durch und überschreiben Sie anschließend die Quelle. Beachten Sie, dass beim Überschreiben einer Datei an Ort und Stelle das Risiko besteht, dass ein Fehler zum Verlust der Originaldatei führt. Dieser Code schützt nicht vor solchen Fehlern, aber Produktionscode sollte dies tun. Außerdem funktioniert diese Methode nur, wenn das Archiv in den Speicher passt

>>> import zipapp
>>> import io
>>> temp = io.BytesIO()
>>> zipapp.create_archive('myapp.pyz', temp, '/usr/bin/python2')
>>> with open('myapp.pyz', 'wb') as f:
>>>     f.write(temp.getvalue())

Festlegen des Interpreters

Beachten Sie, dass Sie bei Angabe eines Interpreters und anschließender Verteilung Ihres Anwendungsarchivs sicherstellen müssen, dass der verwendete Interpreter portabel ist. Der Python Launcher für Windows unterstützt die meisten gängigen Formen von POSIX #!-Zeilen, aber es gibt andere zu berücksichtigende Probleme

  • Wenn Sie "/usr/bin/env python" (oder andere Formen des Befehls "python", wie "/usr/bin/python") verwenden, müssen Sie berücksichtigen, dass Ihre Benutzer möglicherweise entweder Python 2 oder Python 3 als Standard haben, und Ihren Code so schreiben, dass er unter beiden Versionen funktioniert.

  • Wenn Sie eine explizite Version verwenden, z. B. "/usr/bin/env python3", funktioniert Ihre Anwendung nicht für Benutzer, die diese Version nicht haben. (Dies kann gewollt sein, wenn Sie Ihren Code nicht Python 2-kompatibel gemacht haben).

  • Es gibt keine Möglichkeit zu sagen "Python X.Y oder später", also seien Sie vorsichtig bei der Verwendung einer genauen Version wie "/usr/bin/env python3.4", da Sie Ihre Shebang-Zeile für Benutzer von Python 3.5 beispielsweise ändern müssen.

Typischerweise sollten Sie "/usr/bin/env python2" oder "/usr/bin/env python3" verwenden, je nachdem, ob Ihr Code für Python 2 oder 3 geschrieben ist.

Erstellen von eigenständigen Anwendungen mit zipapp

Mithilfe des Moduls zipapp ist es möglich, in sich geschlossene Python-Programme zu erstellen, die an Endbenutzer verteilt werden können, die lediglich eine geeignete Python-Version auf ihrem System installiert haben müssen. Der Schlüssel dazu ist, alle Abhängigkeiten der Anwendung zusammen mit dem Anwendungscode in das Archiv zu bündeln.

Die Schritte zur Erstellung eines eigenständigen Archivs sind wie folgt

  1. Erstellen Sie Ihre Anwendung wie gewohnt in einem Verzeichnis, sodass Sie ein Verzeichnis myapp mit einer Datei __main__.py und jeglichem unterstützenden Anwendungscode haben.

  2. Installieren Sie alle Abhängigkeiten Ihrer Anwendung mithilfe von pip in das Verzeichnis myapp

    $ python -m pip install -r requirements.txt --target myapp
    

    (Dies setzt voraus, dass Ihre Projektanforderungen in einer Datei requirements.txt aufgeführt sind - falls nicht, können Sie die Abhängigkeiten auch manuell auf der Pip-Kommandozeile angeben).

  3. Paketieren Sie die Anwendung mit

    $ python -m zipapp -p "interpreter" myapp
    

Dies erzeugt eine eigenständige ausführbare Datei, die auf jedem Computer mit dem entsprechenden Interpreter ausgeführt werden kann. Weitere Einzelheiten finden Sie unter Festlegen des Interpreters. Sie kann als einzelne Datei an Benutzer versendet werden.

Unter Unix ist die Datei myapp.pyz im Wesentlichen ausführbar. Sie können die Datei umbenennen, um die Erweiterung .pyz zu entfernen, wenn Sie einen "einfachen" Befehlsnamen bevorzugen. Unter Windows ist die Datei myapp.pyz[w] ausführbar, da der Python-Interpreter die Dateierweiterungen .pyz und .pyzw bei der Installation registriert.

Einschränkungen

Wenn Ihre Anwendung von einem Paket abhängt, das eine C-Erweiterung enthält, kann dieses Paket nicht aus einer Zip-Datei ausgeführt werden (dies ist eine Betriebssystembeschränkung, da ausführbarer Code auf dem Dateisystem vorhanden sein muss, damit der Betriebssystem-Loader ihn laden kann). In diesem Fall können Sie diese Abhängigkeit aus der Zip-Datei ausschließen und entweder von Ihren Benutzern verlangen, dass sie diese installiert haben, oder sie zusammen mit Ihrer Zip-Datei versenden und Code zu Ihrer __main__.py hinzufügen, um das Verzeichnis, das das entpackte Modul enthält, in sys.path aufzunehmen. In diesem Fall müssen Sie sicherstellen, dass Sie die entsprechenden Binärdateien für Ihre Zielarchitektur(en) versenden (und möglicherweise zur Laufzeit die richtige Version zum Hinzufügen zu sys.path basierend auf dem Rechner des Benutzers auswählen).

Das Python Zip Application Archive Format

Python kann seit Version 2.6 Zip-Dateien ausführen, die eine Datei __main__.py enthalten. Um von Python ausgeführt zu werden, muss ein Anwendungsarchiv einfach eine Standard-Zip-Datei sein, die eine Datei __main__.py enthält, die als Einstiegspunkt der Anwendung ausgeführt wird. Wie bei jedem Python-Skript wird das übergeordnete Verzeichnis des Skripts (in diesem Fall die Zip-Datei) auf sys.path gelegt, und weitere Module können aus der Zip-Datei importiert werden.

Das Zip-Dateiformat erlaubt es, beliebige Daten einer Zip-Datei voranzustellen. Das Zip-Anwendungsformat nutzt diese Fähigkeit, um eine Standard-POSIX-"Shebang"-Zeile an die Datei voranzustellen (#!/path/to/interpreter).

Formal ist das Python Zip Application Format daher

  1. Eine optionale Shebang-Zeile, die die Zeichen b'#!' gefolgt von einem Interpreter-Namen und dann einem Zeilenumbruchzeichen (b'\n') enthält. Der Interpreter-Name kann alles sein, was für die "Shebang"-Verarbeitung des Betriebssystems oder den Python Launcher unter Windows akzeptabel ist. Der Interpreter sollte unter Windows in UTF-8 und unter POSIX in sys.getfilesystemencoding() kodiert sein.

  2. Standard-Zipfile-Daten, wie sie vom Modul zipfile generiert werden. Der Zipfile-Inhalt **muss** eine Datei namens __main__.py enthalten (die sich im "Stammverzeichnis" der Zip-Datei befinden muss, d. h. sie kann nicht in einem Unterverzeichnis liegen). Die Zipfile-Daten können komprimiert oder unkomprimiert sein.

Wenn ein Anwendungsarchiv eine Shebang-Zeile hat, kann das Ausführungsbit auf POSIX-Systemen gesetzt sein, um es direkt ausführbar zu machen.

Es gibt keine Anforderung, dass die Werkzeuge in diesem Modul zum Erstellen von Anwendungsarchiven verwendet werden - das Modul ist eine Erleichterung, aber Archive im obigen Format, die mit beliebigen Mitteln erstellt wurden, sind für Python akzeptabel.