Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 441 – Verbesserung der Python ZIP-Anwendungsunterstützung

Autor:
Daniel Holth <dholth at gmail.com>, Paul Moore <p.f.moore at gmail.com>
Discussions-To:
Python-Dev Nachricht
Status:
Final
Typ:
Standards Track
Erstellt:
30. März 2013
Python-Version:
3.5
Post-History:
30. März 2013, 01. April 2013, 16. Februar 2015
Resolution:
Python-Dev Nachricht

Inhaltsverzeichnis

Improving Python ZIP Application Support

Python hat seit Version 2.6 die Möglichkeit, Verzeichnisse oder ZIP-Format-Archive als Skripte auszuführen [1]. Wenn der Interpreter mit einer Zip-Datei oder einem Verzeichnis als erstem Argument aufgerufen wird, fügt er dieses Verzeichnis zu sys.path hinzu und führt das __main__ Modul aus. Diese Archive bieten eine großartige Möglichkeit, Software zu verteilen, die als einzelnes Skript verteilt werden muss, aber komplex genug ist, um als Sammlung von Modulen geschrieben zu werden.

Dieses Feature ist nicht so populär, wie es sein sollte, hauptsächlich weil es nicht als Teil von Python 2.6 beworben wurde [2], so dass es relativ unbekannt ist, aber auch, weil der Windows-Installer keine Dateiendung (außer .py) für dieses Dateiformat registriert, um es mit dem Launcher zu verknüpfen.

Dieses PEP schlägt vor, diese Probleme zu beheben, indem das Feature neu beworben wird, die Endungen .pyz und .pyzw als "Python ZIP Applications" und "Windowed Python ZIP Applications" definiert werden und einige einfache Werkzeuge zur Verwaltung des Formats bereitgestellt werden.

Eine neue Python ZIP-Anwendungs-Erweiterung

Die Terminologie "Python Zip Application" ist der formale Begriff für ein ZIP-Format-Archiv, das Python-Code in einer Form enthält, die direkt von Python ausgeführt werden kann (insbesondere muss es eine Datei __main__.py im Stammverzeichnis des Archivs haben). Die Dateiendung .pyz wird formell mit solchen Dateien verknüpft.

Der Python 3.5 Installer wird die Erweiterungen .pyz und .pyzw "Python ZIP Applications" mit dem plattformspezifischen Launcher verknüpfen, damit sie ausgeführt werden können. Ein .pyz Archiv ist eine Konsolenanwendung und ein .pyzw Archiv ist eine grafische Anwendung, die angibt, ob die Konsole beim Ausführen der App angezeigt werden soll.

Unter Unix wäre es ideal, wenn die Erweiterung .pyz und der Name "Python Zip Application" registriert würden (in der Datenbank der MIME-Typen?). Eine solche Verknüpfung liegt jedoch außerhalb des Umfangs dieses PEP.

Python Zip-Anwendungen können mit einer #!-Zeile, die auf den richtigen Python-Interpreter verweist, und einer optionalen Erklärung vorangestellt werden.

#!/usr/bin/env python3
#  Python application packed with zipapp module
(binary contents of archive)

Unter Unix ermöglicht dies dem Betriebssystem, die Datei mit dem richtigen Interpreter über die Standardunterstützung für "Shebangs" auszuführen. Unter Windows implementiert der Python-Launcher die Shebang-Unterstützung.

Es ist jedoch immer möglich, eine .pyz-Anwendung auszuführen, indem der Dateiname direkt an den Python-Interpreter übergeben wird.

Zur Information: ZIP-Archive werden mit einem Footer definiert, der relative Offsets vom Dateiende enthält. Sie bleiben gültig, wenn sie an das Ende einer anderen Datei angehängt werden. Dieses Feature ist völlig Standard und die Art und Weise, wie selbstextrahierende ZIP-Archive und das bdist_wininst-Installerformat funktionieren.

Minimale Werkzeuge: Das zipapp-Modul

Dieses PEP schlägt außerdem die Aufnahme eines Moduls zur Arbeit mit diesen Archiven vor. Das Modul wird Funktionen zur Arbeit mit Python Zip-Anwendungsarchiven und eine Befehlszeilenschnittstelle (über python -m zipapp) für deren Erstellung und Manipulation enthalten.

Umfangreichere Tools zur Verwaltung von Python Zip-Anwendungen werden als Drittanbieter-Anwendungen auf PyPI gefördert. Derzeit sind pyzzer [5] und pex [6] zwei solche Tools.

Modulschnittstelle

Das zipapp-Modul bietet die folgenden Funktionen:

create_archive(source, target=None, interpreter=None, main=None)

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

  • Der Name eines Verzeichnisses, in diesem Fall wird ein neues Anwendungsarchiv aus dem Inhalt dieses Verzeichnisses erstellt.
  • Der Name einer bestehenden Anwendungsarchivdatei, in diesem Fall wird die Datei in das Ziel kopiert. Der Dateiname sollte die Erweiterung .pyz oder .pyzw enthalten, falls erforderlich.
  • Ein für das Lesen im Binärmodus geöffnetes Dateiobjekt. 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 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 zum Schreiben im Binärmodus 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, der 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 Unix wird dies vom Betriebssystem interpretiert, und unter Windows wird es vom Python-Launcher gehandhabt. Das Weglassen des interpreter führt dazu, dass keine Shebang-Zeile geschrieben wird. Wenn ein Interpreter angegeben wird und das Ziel ein Dateiname ist, wird das Ausführungsbit der Zieldatei gesetzt.

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

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

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

get_interpreter(archive)

Gibt den im Shebang-Header des archive angegebenen Interpreter zurück. Wenn kein Shebang vorhanden ist, gibt die Funktion None zurück. Das Argument archive kann ein Dateiname oder ein für das Lesen im Binärmodus geöffnetes dateiähnliches Objekt sein.

Befehlszeilen-Nutzung

Das zipapp-Modul kann mit dem Flag python -m ausgeführt werden. Die Befehlszeilenschnittstelle lautet wie folgt:

python -m zipapp directory [options]

    Create an archive from the given directory.  An archive will
    be created from the contents of that directory.  The archive
    will have the same name as the source directory with a .pyz
    extension.

    The following options can be specified:

    -o archive / --output archive

        The destination archive will have the specified name.  The
        given name will be used as written, so should include the
        ".pyz" or ".pyzw" extension.

    -p interpreter / --python interpreter

        The given interpreter will be written to the shebang line
        of the archive.  If this option is not given, the archive
        will have no shebang line.

    -m pkg.mod:fn / --main pkg.mod:fn

        The source directory must not have a __main__.py file. The
        archiver will write a __main__.py file into the target
        which calls fn from the module pkg.mod.

Das Verhalten der Befehlszeilenschnittstelle entspricht dem von zipapp.create_archive().

Darüber hinaus ist es möglich, die Befehlszeilenschnittstelle zur Bearbeitung eines bestehenden Archivs zu verwenden:

python -m zipapp app.pyz --show

    Displays the shebang line of an archive.  Output is of the
    form

        Interpreter: /usr/bin/env
    or
        Interpreter: <none>

    and is intended for diagnostic use, not for scripts.

python -m zipapp app.pyz -o newapp.pyz [-p interpreter]

    Copy app.pyz to newapp.pyz, modifying the shebang line based
    on the -p option (as for creating an archive, no -p option
    means remove the shebang line).  Specifying a destination is
    mandatory.

    In-place modification of an archive is *not* supported, as the
    risk of damaging archives is too great for a simple tool.

Wie erwähnt, sind die Archive Standard-Zip-Dateien und können daher mit jedem Standard-ZIP-Dienstprogramm oder dem zipfile-Modul von Python entpackt werden. Aus diesem Grund werden keine Schnittstellen zum Auflisten des Inhalts eines Archivs oder zum Entpacken bereitgestellt oder benötigt.

FAQ

Sind Sie sicher, dass ein Standard-ZIP-Dienstprogramm #! am Anfang verarbeiten kann?
Absolut. Die ZIP-Datei-Spezifikation erlaubt es, beliebige Daten vor einer ZIP-Datei einzufügen. Dieses Feature wird häufig von "selbstextrahierenden ZIP"-Programmen verwendet. Wenn Ihr Archivprogramm dies nicht verarbeiten kann, ist das ein Fehler in Ihrem Archivprogramm.
Ist zipapp nicht nur ein sehr dünner Wrapper um das zipfile-Modul?
Ja. Wenn Sie es vorziehen, Ihre eigenen Python ZIP-Anwendungsarchive mit anderen Werkzeugen zu erstellen, werden diese genauso gut funktionieren. Das zipapp-Modul ist eine Bequemlichkeit, nichts weiter.
Warum nicht einfach eine .zip- oder .py-Erweiterung verwenden?
Benutzer erwarten, dass eine .zip-Datei mit einem Archiv-Tool geöffnet wird, und erwarten, dass eine .py-Datei lesbaren Text enthält. Beides wäre für diesen Anwendungsfall verwirrend.
Wie verhält sich dies im Vergleich zu bestehenden Paketformaten?
Die Formate sdist, bdist und wheel sind für die Verpackung von Modulen konzipiert, die in einer bestehenden Python-Installation installiert werden sollen. Sie sind nicht dafür gedacht, ohne Installation verwendet zu werden. Das ausführbare ZIP-Format ist speziell für die eigenständige Verwendung konzipiert, ohne dass eine Installation erforderlich ist. Sie sind im Grunde eine Mehrfachdatei-Version eines eigenständigen Python-Skripts.

Abgelehnte Vorschläge

Bequeme Werte für Shebang-Zeilen

Lohnt es sich, "Bequemlichkeits"-Formen für gängige Interpreterwerte zu haben? Zum Beispiel, -p 3 bedeutet dasselbe wie -p "/usr/bin/env python3". Dies würde viel Tipparbeit für die häufigsten Fälle sparen und auch plattformübergreifende Optionen für Leute bieten, die die Feinheiten der Shebang-Handhabung auf "anderen" Plattformen nicht verstehen wollen oder müssen.

Nachteile sind, dass nicht klar ist, wie die Abkürzungen übersetzt werden sollen. Soll zum Beispiel "3" bedeuten "/usr/bin/env python3", "/usr/bin/python3", "python3" oder etwas anderes? Außerdem gibt es keine offensichtliche Kurzform für den wichtigsten Fall von "/usr/bin/env python" (jede verfügbare Python-Version), was leicht dazu führen könnte, dass Skripte mit zu restriktiven Shebang-Zeilen geschrieben werden.

Insgesamt scheint es hier mehr Probleme als Vorteile zu geben, und deshalb wurde dies aus der Überlegung gestrichen.

Registrierung von .pyz als Medientyp

Es wurde vorgeschlagen [3], dass die Erweiterung .pyz in der Unix-Datenbank der Erweiterungen registriert werden sollte. Obwohl es sinnvoll wäre, dies als Äquivalent zur Registrierung der Erweiterung durch den Windows-Installer zu tun, ist die Erweiterung .py nicht in der Medientypen-Datenbank aufgeführt [4]. Es scheint nicht ratsam, .pyz ohne .py zu registrieren, daher wurde diese Idee aus diesem PEP weggelassen. Eine interessierte Partei könnte dafür sorgen, dass zu einem späteren Zeitpunkt *sowohl* .py als auch .pyz registriert werden.

Standard-Interpreter

Der erste Entwurf dieses PEP schlug die Verwendung von /usr/bin/env python als Standard-Interpreter vor. Unix-Benutzer haben Probleme mit diesem Verhalten, da der Standard für den Befehl python auf vielen Distributionen Python 2 ist, und es wird davon ausgegangen, dass dieses PEP standardmäßig Python 3 bevorzugen sollte. Die Verwendung des Befehls python3 kann jedoch zu unerwartetem Verhalten für Windows-Benutzer führen, wo das Standardverhalten des Launchers für den Befehl python häufig von Benutzern angepasst wird, das Verhalten von python3 jedoch möglicherweise nicht entsprechend geändert wird.

Infolgedessen wurde das Prinzip "Im Angesicht von Mehrdeutigkeit, weigere dich zu raten" angewendet, und Archive haben keine Shebang-Zeile, es sei denn, sie wird explizit angefordert. Unter Windows werden die Archive weiterhin vom Launcher ausgeführt (mit dem Standard-Python), und unter Unix können die Archive ausgeführt werden, indem der gewünschte Python-Interpreter explizit aufgerufen wird.

Befehlszeilentool zur Verwaltung von Shebang-Zeilen

Es ist denkbar, dass Benutzer die Shebang-Zeile für ein bestehendes Archiv ändern möchten oder sogar nur die aktuelle Shebang-Zeile anzeigen möchten. Dies ist mit vorhandenen Werkzeugen schwierig zu bewerkstelligen (ZIP-Programme ignorieren vorangestellte Daten normalerweise vollständig, und Texteditoren können Schwierigkeiten haben, Dateien mit Binärdaten zu bearbeiten).

Das zipapp-Modul bietet Funktionen zur Handhabung der Shebang-Zeile, enthält jedoch keine Befehlszeilenschnittstelle für diese Funktionalität. Dies liegt daran, dass unklar ist, wie eine solche bereitgestellt werden kann, ohne dass die resultierende Schnittstelle zu komplex und potenziell verwirrend wird. Das Ändern der Shebang-Zeile wird als ungewöhnliche Anforderung angesehen.

Referenzimplementierung

Eine Referenzimplementierung finden Sie unter http://bugs.python.org/issue23491.

Referenzen

Die Diskussion zu diesem PEP fand auf der Mailingliste python-dev statt, im Thread beginnend bei https://mail.python.org/pipermail/python-dev/2015-February/138277.html


Quelle: https://github.com/python/peps/blob/main/peps/pep-0441.rst

Zuletzt geändert: 2025-02-01 08:55:40 GMT