• zipapp —- Manage executable Python zip archives
    • Basic Example
    • 命令行界面
    • Python API
    • 示例
    • Specifying the Interpreter
    • Creating Standalone Applications with zipapp
      • Making a Windows executable
      • Caveats
    • The Python Zip Application Archive Format

    zipapp —- Manage executable Python zip archives

    3.5 新版功能.

    Source code:Lib/zipapp.py


    This module provides tools to manage the creation of zip files containingPython code, which can be executed directly by the Python interpreter. The module provides both a命令行界面 and a Python API.

    Basic Example

    The following example shows how the 命令行界面can be used to create an executable archive from a directory containingPython code. When run, the archive will execute the main function fromthe module myapp in the archive.

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

    命令行界面

    When called as a program from the command line, the following form is used:

    1. $ python -m zipapp source [options]

    If source is a directory, this will create an archive from the contents ofsource. If source is a file, it should be an archive, and it will becopied to the target archive (or the contents of its shebang line will bedisplayed if the —info option is specified).

    The following options are understood:

    • -o <output>, —output=<output>
    • Write the output to a file named output. If this option is not specified,the output filename will be the same as the input source, with theextension .pyz added. If an explicit filename is given, it is used asis (so a .pyz extension should be included if required).

    An output filename must be specified if the source is an archive (and inthat case, output must not be the same as source).

    • -p <interpreter>, —python=<interpreter>
    • Add a #! line to the archive specifying interpreter as the commandto run. Also, on POSIX, make the archive executable. The default is towrite no #! line, and not make the file executable.

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

    • Write a main.py file to the archive that executes mainfn. Themainfn argument should have the form "pkg.mod:fn", where "pkg.mod" is apackage/module in the archive, and "fn" is a callable in the given module.The main.py file will execute that callable.

    —main cannot be specified when copying an archive.

    • -c, —compress
    • Compress files with the deflate method, reducing the size of the outputfile. By default, files are stored uncompressed in the archive.

    —compress has no effect when copying an archive.

    3.7 新版功能.

    • —info
    • Display the interpreter embedded in the archive, for diagnostic purposes. Inthis case, any other options are ignored and SOURCE must be an archive, not adirectory.

    • -h, —help

    • Print a short usage message and exit.

    Python API

    The module defines two convenience functions:

    • zipapp.createarchive(_source, target=None, interpreter=None, main=None, filter=None, compressed=False)
    • Create an application archive from source. The source can be anyof the following:

      • The name of a directory, or a path-like object referringto a directory, in which case a new application archive will becreated from the content of that directory.

      • The name of an existing application archive file, or a path-like objectreferring to such a file, in which case the file is copied tothe target (modifying it to reflect the value given for the _interpreter_argument). The file name should include the .pyz extension, if required.

      • A file object open for reading in bytes mode. The content of thefile should be an application archive, and the file object isassumed to be positioned at the start of the archive.

    The target argument determines where the resulting archive will bewritten:

    • If it is the name of a file, or a path-like object,the archive will be written to that file.

    • If it is an open file object, the archive will be written to thatfile object, which must be open for writing in bytes mode.

    • If the target is omitted (or None), the source must be a directoryand the target will be a file with the same name as the source, witha .pyz extension added.

    The interpreter 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 POSIX, this will beinterpreted by the OS, and on Windows it will be handled by the Pythonlauncher. Omitting the interpreter 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.

    The main 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 amain.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. Itis an error to omit main if the source is a directory and does notcontain a main.py file, as otherwise the resulting archivewould not be executable.

    The optional filter argument specifies a callback function thatis passed a Path object representing the path to the file being added(relative to the source directory). It should return True if thefile is to be added.

    The optional compressed argument determines whether files arecompressed. If set to True, files in the archive are compressedwith the deflate method; otherwise, files are stored uncompressed.This argument has no effect when copying an existing archive.

    If a file object is specified for source or target, it is thecaller's responsibility to close it after calling create_archive.

    When copying an existing archive, file objects supplied only needread and readline, or write methods. When creating anarchive from a directory, if the target is a file object it will bepassed to the zipfile.ZipFile class, and must supply the methodsneeded by that class.

    3.7 新版功能: Added the filter and compressed arguments.

    • zipapp.getinterpreter(_archive)
    • Return the interpreter specified in the #! line at the start of thearchive. If there is no #! line, return None.The archive argument can be a filename or a file-like object openfor reading in bytes mode. It is assumed to be at the start of the archive.

    示例

    Pack up a directory into an archive, and run it.

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

    The same can be done using the create_archive() function:

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

    To make the application directly executable on POSIX, specify an interpreterto use.

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

    To replace the shebang line on an existing archive, create a modified archiveusing the create_archive() function:

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

    To update the file in place, do the replacement in memory using a BytesIOobject, and then overwrite the source afterwards. Note that there is a riskwhen overwriting a file in place that an error will result in the loss ofthe original file. This code does not protect against such errors, butproduction code should do so. Also, this method will only work if the archivefits in memory:

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

    Specifying the Interpreter

    Note that if you specify an interpreter and then distribute your applicationarchive, you need to ensure that the interpreter used is portable. The Pythonlauncher for Windows supports most common forms of POSIX #! line, but thereare other issues to consider:

    • If you use "/usr/bin/env python" (or other forms of the "python" command,such as "/usr/bin/python"), you need to consider that your users may haveeither Python 2 or Python 3 as their default, and write your code to workunder both versions.

    • If you use an explicit version, for example "/usr/bin/env python3" yourapplication will not work for users who do not have that version. (Thismay be what you want if you have not made your code Python 2 compatible).

    • There is no way to say "python X.Y or later", so be careful of using anexact version like "/usr/bin/env python3.4" as you will need to change yourshebang line for users of Python 3.5, for example.

    Typically, you should use an "/usr/bin/env python2" or "/usr/bin/env python3",depending on whether your code is written for Python 2 or 3.

    Creating Standalone Applications with zipapp

    Using the zipapp module, it is possible to create self-contained Pythonprograms, which can be distributed to end users who only need to have asuitable version of Python installed on their system. The key to doing thisis to bundle all of the application's dependencies into the archive, alongwith the application code.

    The steps to create a standalone archive are as follows:

    • Create your application in a directory as normal, so you have a myappdirectory containing a main.py file, and any supporting applicationcode.

    • Install all of your application's dependencies into the myapp directory,using pip:

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

    (this assumes you have your project requirements in a requirements.txtfile - if not, you can just list the dependencies manually on the pip commandline).

    • Optionally, delete the .dist-info directories created by pip in themyapp directory. These hold metadata for pip to manage the packages, andas you won't be making any further use of pip they aren't required -although it won't do any harm if you leave them.

    • Package the application using:

    1. $ python -m zipapp -p "interpreter" myapp

    This will produce a standalone executable, which can be run on any machine withthe appropriate interpreter available. See Specifying the Interpreterfor details. It can be shipped to users as a single file.

    On Unix, the myapp.pyz file is executable as it stands. You can rename thefile to remove the .pyz extension if you prefer a "plain" command name. OnWindows, the myapp.pyz[w] file is executable by virtue of the fact thatthe Python interpreter registers the .pyz and .pyzw file extensionswhen installed.

    Making a Windows executable

    On Windows, registration of the .pyz extension is optional, andfurthermore, there are certain places that don't recognise registeredextensions "transparently" (the simplest example is thatsubprocess.run(['myapp']) won't find your application - you need toexplicitly specify the extension).

    On Windows, therefore, it is often preferable to create an executable from thezipapp. This is relatively easy, although it does require a C compiler. Thebasic approach relies on the fact that zipfiles can have arbitrary dataprepended, and Windows exe files can have arbitrary data appended. So bycreating a suitable launcher and tacking the .pyz file onto the end of it,you end up with a single-file executable that runs your application.

    A suitable launcher can be as simple as the following:

    1. #define Py_LIMITED_API 1
    2. #include "Python.h"
    3.  
    4. #define WIN32_LEAN_AND_MEAN
    5. #include <windows.h>
    6.  
    7. #ifdef WINDOWS
    8. int WINAPI wWinMain(
    9. HINSTANCE hInstance, /* handle to current instance */
    10. HINSTANCE hPrevInstance, /* handle to previous instance */
    11. LPWSTR lpCmdLine, /* pointer to command line */
    12. int nCmdShow /* show state of window */
    13. )
    14. #else
    15. int wmain()
    16. #endif
    17. {
    18. wchar_t **myargv = _alloca((__argc + 1) * sizeof(wchar_t*));
    19. myargv[0] = __wargv[0];
    20. memcpy(myargv + 1, __wargv, __argc * sizeof(wchar_t *));
    21. return Py_Main(__argc+1, myargv);
    22. }

    If you define the WINDOWS preprocessor symbol, this will generate aGUI executable, and without it, a console executable.

    To compile the executable, you can either just use the standard MSVCcommand line tools, or you can take advantage of the fact that distutilsknows how to compile Python source:

    1. >>> from distutils.ccompiler import new_compiler
    2. >>> import distutils.sysconfig
    3. >>> import sys
    4. >>> import os
    5. >>> from pathlib import Path
    6.  
    7. >>> def compile(src):
    8. >>> src = Path(src)
    9. >>> cc = new_compiler()
    10. >>> exe = src.stem
    11. >>> cc.add_include_dir(distutils.sysconfig.get_python_inc())
    12. >>> cc.add_library_dir(os.path.join(sys.base_exec_prefix, 'libs'))
    13. >>> # First the CLI executable
    14. >>> objs = cc.compile([str(src)])
    15. >>> cc.link_executable(objs, exe)
    16. >>> # Now the GUI executable
    17. >>> cc.define_macro('WINDOWS')
    18. >>> objs = cc.compile([str(src)])
    19. >>> cc.link_executable(objs, exe + 'w')
    20.  
    21. >>> if __name__ == "__main__":
    22. >>> compile("zastub.c")

    The resulting launcher uses the "Limited ABI", so it will run unchanged withany version of Python 3.x. All it needs is for Python (python3.dll) to beon the user's PATH.

    For a fully standalone distribution, you can distribute the launcher with yourapplication appended, bundled with the Python "embedded" distribution. Thiswill run on any PC with the appropriate architecture (32 bit or 64 bit).

    Caveats

    There are some limitations to the process of bundling your application intoa single file. In most, if not all, cases they can be addressed withoutneeding major changes to your application.

    • If your application depends on a package that includes a C extension, thatpackage cannot be run from a zip file (this is an OS limitation, as executablecode must be present in the filesystem for the OS loader to load it). In thiscase, you can exclude that dependency from the zipfile, and either requireyour users to have it installed, or ship it alongside your zipfile and add codeto your main.py to include the directory containing the unzippedmodule in sys.path. In this case, you will need to make sure to shipappropriate binaries for your target architecture(s) (and potentially pick thecorrect version to add to sys.path at runtime, based on the user's machine).

    • If you are shipping a Windows executable as described above, you either need toensure that your users have python3.dll on their PATH (which is not thedefault behaviour of the installer) or you should bundle your application withthe embedded distribution.

    • The suggested launcher above uses the Python embedding API. This means that inyour application, sys.executable will be your application, and not aconventional Python interpreter. Your code and its dependencies need to beprepared for this possibility. For example, if your application uses themultiprocessing module, it will need to callmultiprocessing.set_executable() to let the module know where to find thestandard Python interpreter.

    The Python Zip Application Archive Format

    Python has been able to execute zip files which contain a main.py filesince version 2.6. In order to be executed by Python, an application archivesimply has to be a standard zip file containing a main.py file whichwill be run as the entry point for the application. As usual for any Pythonscript, the parent of the script (in this case the zip file) will be placed onsys.path and thus further modules can be imported from the zip file.

    The zip file format allows arbitrary data to be prepended to a zip file. Thezip application format uses this ability to prepend a standard POSIX "shebang"line to the file (#!/path/to/interpreter).

    Formally, the Python zip application format is therefore:

    • An optional shebang line, containing the characters b'#!' followed by aninterpreter name, and then a newline (b'\n') character. The interpretername can be anything acceptable to the OS "shebang" processing, or the Pythonlauncher on Windows. The interpreter should be encoded in UTF-8 on Windows,and in sys.getfilesystemencoding() on POSIX.

    • Standard zipfile data, as generated by the zipfile module. Thezipfile content must include a file called main.py (which must bein the "root" of the zipfile - i.e., it cannot be in a subdirectory). Thezipfile data can be compressed or uncompressed.

    If an application archive has a shebang line, it may have the executable bit seton POSIX systems, to allow it to be executed directly.

    There is no requirement that the tools in this module are used to createapplication archives - the module is a convenience, but archives in the aboveformat created by any means are acceptable to Python.