Python has had the ability to execute directories or ZIP-formatarchives as scripts since version 2.6[1]. When invoked with a zipfile or directory as its first argument the interpreter adds thatdirectory to sys.path and executes the__main__ module. Thesearchives provide a great way to publish software that needs to bedistributed as a single file script but is complex enough to need tobe written as a collection of modules.
This feature is not as popular as it should be mainly because it wasnot promoted as part of Python 2.6[2], so that it is relativelyunknown, but also because the Windows installer does not register afile extension (other than.py) for this format of file, to associatewith the launcher.
This PEP proposes to fix these problems by re-publicising the feature,defining the.pyz and.pyzw extensions as “Python ZIP Applications”and “Windowed Python ZIP Applications”, and providing some simpletooling to manage the format.
The terminology “Python Zip Application” will be the formal term usedfor a zip-format archive that contains Python code in a form that canbe directly executed by Python (specifically, it must have a__main__.py file in the root directory of the archive). Theextension.pyz will be formally associated with such files.
The Python 3.5 installer will associate.pyz and.pyzw “PythonZip Applications” with the platform launcher so they can be executed.A.pyz archive is a console application and a.pyzw archive is awindowed application, indicating whether the console should appearwhen running the app.
On Unix, it would be ideal if the.pyz extension and the name“Python Zip Application” were registered (in the mime types database?).However, such an association is out of scope for this PEP.
Python Zip applications can be prefixed with a#! linepointing to the correct Python interpreter and an optionalexplanation:
#!/usr/bin/env python3# Python application packed with zipapp module(binarycontentsofarchive)
On Unix, this allows the OS to run the file with the correctinterpreter, via the standard “shebang” support. On Windows, thePython launcher implements shebang support.
However, it is always possible to execute a.pyz application bysupplying the filename to the Python interpreter directly.
As background, ZIP archives are defined with a footer containingrelative offsets from the end of the file. They remain valid whenconcatenated to the end of any other file. This feature is completelystandard and is how self-extracting ZIP archives and the bdist_wininstinstaller format work.
This PEP also proposes including a module for working with thesearchives. The module will contain functions for working with Pythonzip application archives, and a command line interface (viapython-mzipapp) for their creation and manipulation.
More complete tools for managing Python Zip Applications areencouraged as 3rd party applications on PyPI. Currently, pyzzer[5]and pex[6] are two such tools.
The zipapp module will provide the following functions:
create_archive(source,target=None,interpreter=None,main=None)Create an application archive fromsource. The source can be anyof the following:
.pyz or.pyzw extension, if required.Thetarget argument determines where the resulting archive will bewritten:
.pyz extension added.Theinterpreter argument specifies the name of the Pythoninterpreter with which the archive will be executed. It is written asa “shebang” line at the start of the archive. On Unix, this will beinterpreted by the OS, and on Windows it will be handled by the Pythonlauncher. Omitting theinterpreter results in no shebang line beingwritten. If an interpreter is specified, and the target is afilename, the executable bit of the target file will be set.
Themain argument specifies the name of a callable which will beused as the main program for the archive. It can only be specified ifthe source is a directory, and the source does not already contain a__main__.py file. Themain argument should take the form“pkg.module:callable” and the archive will be run by importing“pkg.module” and executing the given callable with no arguments. Itis an error to omitmain if the source is a directory and does notcontain a__main__.py file, as otherwise the resulting archivewould not be executable.
If a file object is specified forsource ortarget, it is thecaller’s responsibility to close it after calling create_archive.
When copying an existing archive, file objects supplied only needread andreadline, orwrite methods. When creating anarchive from a directory, if the target is a file object it will bepassed to thezipfile.ZipFile class, and must supply the methodsneeded by that class.
get_interpreter(archive)Returns the interpreter specified in the shebang line of thearchive. If there is no shebang, the function returnsNone.Thearchive argument can be a filename or a file-like object openfor reading in bytes mode.
The zipapp module can be run with the python-m flag. The commandline interface is as follows:
python-mzipappdirectory[options]Createanarchivefromthegivendirectory.Anarchivewillbecreatedfromthecontentsofthatdirectory.Thearchivewillhavethesamenameasthesourcedirectorywitha.pyzextension.Thefollowingoptionscanbespecified:-oarchive/--outputarchiveThedestinationarchivewillhavethespecifiedname.Thegivennamewillbeusedaswritten,soshouldincludethe".pyz"or".pyzw"extension.-pinterpreter/--pythoninterpreterThegiveninterpreterwillbewrittentotheshebanglineofthearchive.Ifthisoptionisnotgiven,thearchivewillhavenoshebangline.-mpkg.mod:fn/--mainpkg.mod:fnThesourcedirectorymustnothavea__main__.pyfile.Thearchiverwillwritea__main__.pyfileintothetargetwhichcallsfnfromthemodulepkg.mod.
The behaviour of the command line interface matches that ofzipapp.create_archive().
In addition, it is possible to use the command line interface to workwith an existing archive:
python-mzipappapp.pyz--showDisplaystheshebanglineofanarchive.OutputisoftheformInterpreter:/usr/bin/envorInterpreter:<none>andisintendedfordiagnosticuse,notforscripts.python-mzipappapp.pyz-onewapp.pyz[-pinterpreter]Copyapp.pyztonewapp.pyz,modifyingtheshebanglinebasedonthe-poption(asforcreatinganarchive,no-poptionmeansremovetheshebangline).Specifyingadestinationismandatory.In-placemodificationofanarchiveis*not*supported,astheriskofdamagingarchivesistoogreatforasimpletool.
As noted, the archives are standard zip files, and so can be unpackedusing any standard ZIP utility or Python’s zipfile module. For thisreason, no interfaces to list the contents of an archive, or unpackthem, are provided or needed.
#! at the beginning?.zip or.py extension?.zip file to be opened with an archive tool, andexpect a.py file to contain readable text. Both would beconfusing for this use case.Is it worth having “convenience” forms for any of the commoninterpreter values? For example,-p3 meaning the same as-p"/usr/bin/envpython3". It would save a lot of typing for thecommon cases, as well as giving cross-platform options for people whodon’t want or need to understand the intricacies of shebang handlingon “other” platforms.
Downsides are that it’s not obvious how to translate theabbreviations. For example, should “3” mean “/usr/bin/env python3”,“/usr/bin/python3”, “python3”, or something else? Also, there is noobvious short form for the key case of “/usr/bin/env python” (anyavailable version of Python), which could easily result in scriptsbeing written with overly-restrictive shebang lines.
Overall, this seems like there are more problems than benefits, and asa result has been dropped from consideration.
.pyz as a Media TypeIt was suggested[3] that the.pyz extension should be registeredin the Unix database of extensions. While it makes sense to do thisas an equivalent of the Windows installer registering the extension,the.py extension is not listed in the media types database[4].It doesn’t seem reasonable to register.pyz without.py, sothis idea has been omitted from this PEP. An interested party couldarrange forboth.py and.pyz to be registered at a futuredate.
The initial draft of this PEP proposed using/usr/bin/envpythonas the default interpreter. Unix users have problems with thisbehaviour, as the default for the python command on many distributionsis Python 2, and it is felt that this PEP should prefer Python 3 bydefault. However, using a command ofpython3 can result inunexpected behaviour for Windows users, where the default behaviour ofthe launcher for the commandpython is commonly customised by users,but the behaviour ofpython3 may not be modified to match.
As a result, the principle “in the face of ambiguity, refuse to guess”has been invoked, and archives have no shebang line unless explicitlyrequested. On Windows, the archives will still be run (with thedefault Python) by the launcher, and on Unix, the archives can be runby explicitly invoking the desired Python interpreter.
It is conceivable that users would want to modify the shebang line foran existing archive, or even just display the current shebang line.This is tricky to do so with existing tools (zip programs typicallyignore prepended data totally, and text editors can have troubleediting files containing binary data).
The zipapp module provides functions to handle the shebang line, butdoes not include a command line interface to that functionality. Thisis because it is not clear how to provide one without the resultinginterface being over-complex and potentially confusing. Changing theshebang line is expected to be an uncommon requirement.
A reference implementation is athttp://bugs.python.org/issue23491.
The discussion of this PEP took place on the python-dev mailing list,in the thread starting athttps://mail.python.org/pipermail/python-dev/2015-February/138277.html
This document has been placed into the public domain.
Source:https://github.com/python/peps/blob/main/peps/pep-0441.rst
Last modified:2025-02-01 08:55:40 GMT