diff --git a/pep-0441.txt b/pep-0441.txt index afa5a1532..772f9be47 100644 --- a/pep-0441.txt +++ b/pep-0441.txt @@ -73,6 +73,7 @@ concatenated to the end of any other file. This feature is completely standard and is how self-extracting ZIP archives and the bdist_wininst installer format work. + Minimal Tooling: The zipapp Module ================================== @@ -82,47 +83,75 @@ zip application archives, and a command line interface (via ``python -m zipapp``) for their creation and manipulation. More complete tools for managing Python Zip Applications are -encouraged as 3rd party applications on PyPI. Currently, pyyzer [5]_ -and pex [6]_ are two tools known to exist. +encouraged as 3rd party applications on PyPI. Currently, pyzzer [5]_ +and pex [6]_ are two such tools. Module Interface ---------------- The zipapp module will provide the following functions: -``pack(directory, target=None, interpreter=None, main=None)`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +``create_archive(source, target=None, interpreter=None, main=None)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Writes an application archive called *target*, containing the contents -of *directory*. The *target* can be a filename, or a file-like object -(which must be open for writing in bytes mode), or None (which means -use the name of *directory* with ``.pyz`` appended). If *interpreter* -is specified, it will be written to the start of the archive as a -shebang line and the file will be made executable (if no interpreter -is specified, the shebang line will be omitted). If the directory -contains no ``__main__.py`` file, the function will construct a -``__main__.py`` which calls the function specified in the *main* -argument (which should be in the form ``'pkg.mod:fn'``). +Create an application archive from *source*. The source can be any +of the following: -It is an error to specify *main* if the directory contains a -``__main__.py``, or to omit *main* when there is no ``__main__.py`` -(as that will result in an archive which has no main function and so -cannot be executed). +* The name of a directory, in which case a new application archive + will be created from the content of that directory. +* The name of an existing application archive file, in which case the + file is copied to the target. The file name should include the + ``.pyz`` extension, if required. +* A file object open for reading in bytes mode. The content of the + file should be an application archive, and the file object is + assumed to be positioned at the start of the archive. + +The *target* argument determines where the resulting archive will be +written: + +* If it is the name of a file, the archive will be written to that + file. +* If it is an open file object, the archive will be written to that + file object, which must be open for writing in bytes mode. +* If the target is omitted (or None), the source must be a directory + and the target will be a file with the same name as the source, with + a ``.pyz`` extension added. + +The *interpreter* argument specifies the name of the Python +interpreter with which the archive will be executed. It is written as +a "shebang" line at the start of the archive. On Unix, this will be +interpreted by the OS, and on Windows it will be handled by the Python +launcher. Omitting the *interpreter* results in no shebang line being +written. If an interpreter is specified, and the target is a +filename, the executable bit of the target file will be set. + +The *main* argument specifies the name of a callable which will be +used as the main program for the archive. It can only be specified if +the source is a directory, and the source does not already contain a +``__main__.py`` file. The *main* 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. It +is an error to omit *main* if the source is a directory and does not +contain a ``__main__.py`` file, as otherwise the resulting archive +would not be executable. + +If a file object is specified for *source* or *target*, it is the +caller's responsibility to close it after calling create_archive. + +When copying an existing archive, file objects supplied only need +``read`` and ``readline``, or ``write`` methods. When creating an +archive from a directory, if the target is a file object it will be +passed to the ``zipfile.ZipFile`` class, and must supply the methods +needed by that class. ``get_interpreter(archive)`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Returns the interpreter specified in the shebang line of the *archive*. -If there is no shebang, the function returns ``None``. +Returns the interpreter specified in the shebang line of the +*archive*. If there is no shebang, the function returns ``None``. +The *archive* argument can be a filename or a file-like object open +for reading in bytes mode. -``set_interpreter(archive, new_archive, interpreter=None)`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Modifies the *archive*'s shebang line to contain the specified -interpreter, and writes the updated archive to *new_archive*. If the -*interpreter* is ``None``, removes the shebang line. The -*new_archive* argument can be a filename, or a file-like object open -for writing in byte mode. Command Line Usage ------------------ @@ -130,22 +159,25 @@ Command Line Usage The zipapp module can be run with the python ``-m`` flag. The command line interface is as follows:: - python -m zipapp [options] directory + python -m zipapp directory [options] - Create an archive from the contents of the given directory. By - default, an archive will be created with the same name as the - source directory, with a .pyz extension. + 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 destination archive will have the specified name. The + given name will be used as written, so should include the + ".pyz" 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 + of the archive. If this option is not given, the archive will have no shebang line. -m pkg.mod:fn / --main pkg.mod:fn @@ -155,10 +187,36 @@ line interface is as follows:: which calls fn from the module pkg.mod. The behaviour of the command line interface matches that of -``zipapp.pack()``. +``zipapp.create_archive()``. + +In addition, it is possible to use the command line interface to work +with an existing archive:: + + python -m zipapp app.pyz --show + + Displays the shebang line of an archive. Output is of the + form + + Interpreter: /usr/bin/env + or + Interpreter: + + 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. As noted, the archives are standard zip files, and so can be unpacked -using any standard ZIP utility or Python's zipfile module. +using any standard ZIP utility or Python's zipfile module. For this +reason, no interfaces to list the contents of an archive, or unpack +them, are provided or needed. FAQ ---