cpython: d1e9f337fea1 (original) (raw)

Mercurial > cpython

changeset 94979:d1e9f337fea1

Issue #23491: Implement PEP 441: Improving Python Zip Application Support Thanks to Paul Moore for the PEP and implementation. [#23491]

Brett Cannon brett@python.org
date Fri, 13 Mar 2015 10:40:49 -0400
parents 211e29335e72
children 65a2b468fb3c
files Doc/library/distribution.rst Doc/library/zipapp.rst Doc/whatsnew/3.5.rst Lib/test/test_zipapp.py Lib/zipapp.py Tools/msi/launcher/launcher_en-US.wxl Tools/msi/launcher/launcher_reg.wxs
diffstat 7 files changed, 720 insertions(+), 4 deletions(-)[+] [-] Doc/library/distribution.rst 1 Doc/library/zipapp.rst 257 Doc/whatsnew/3.5.rst 21 Lib/test/test_zipapp.py 250 Lib/zipapp.py 179 Tools/msi/launcher/launcher_en-US.wxl 2 Tools/msi/launcher/launcher_reg.wxs 14

line wrap: on

line diff

--- a/Doc/library/distribution.rst +++ b/Doc/library/distribution.rst @@ -12,3 +12,4 @@ with a local index server, or without an distutils.rst ensurepip.rst venv.rst

new file mode 100644 --- /dev/null +++ b/Doc/library/zipapp.rst @@ -0,0 +1,257 @@ +:mod:zipapp --- Manage executable python zip archives +======================================================= + +.. module:: zipapp

+ +.. index::

+.. versionadded:: 3.5 + +Source code: :source:Lib/zipapp.py + +-------------- + +This module provides tools to manage the creation of zip files containing +Python code, which can be :ref:executed directly by the Python interpreter[](#l2.22) +<using-on-interface-options>. The module provides both a +:ref:zipapp-command-line-interface and a :ref:zipapp-python-api. + + +Basic Example +------------- + +The following example shows how the :ref:command-line-interface +can be used to create an executable archive from a directory containing +Python code. When run, the archive will execute the main function from +the module myapp in the archive. + +.. code-block:: sh +

+ +.. _zipapp-command-line-interface: + +Command-Line Interface +---------------------- + +When called as a program from the command line, the following form is used: + +.. code-block:: sh +

+If source is a directory, this will create an archive from the contents of +source. If source is a file, it should be an archive, and it will be +copied to the target archive (or the contents of its shebang line will be +displayed if the --info option is specified). + +The following options are understood: + +.. program:: zipapp + +.. cmdoption:: -o , --output= +

+.. cmdoption:: -p , --python= +

+.. cmdoption:: -m , --main= +

+.. cmdoption:: --info +

+.. cmdoption:: -h, --help +

+ +.. _zipapp-python-api: + +Python API +---------- + +The module defines two convenience functions: + + +.. function:: create_archive(source, target=None, interpreter=None, main=None) +

+.. function:: get_interpreter(archive) +

+ +.. _zipapp-examples: + +Examples +-------- + +Pack up a directory into an archive, and run it. + +.. code-block:: sh +

+The same can be done using the :func:create_archive functon:: +

+To make the application directly executable on POSIX, specify an interpreter +to use. + +.. code-block:: sh +

+To replace the shebang line on an existing archive, create a modified archive +using the :func:create_archive function:: +

+To update the file in place, do the replacement in memory using a :class:BytesIO +object, and then overwrite the source afterwards. Note that there is a risk +when overwriting a file in place that an error will result in the loss of +the original file. This code does not protect against such errors, but +production code should do so. Also, this method will only work if the archive +fits in memory:: +

+Note that if you specify an interpreter and then distribute your application +archive, you need to ensure that the interpreter used is portable. The Python +launcher for Windows supports most common forms of POSIX #! line, but there +are other issues to consider: + +* If you use "/usr/bin/env python" (or other forms of the "python" command,

+If an application archive has a shebang line, it may have the executable bit set +on POSIX systems, to allow it to be executed directly. + +There is no requirement that the tools in this module are used to create +application archives - the module is a convenience, but archives in the above +format created by any means are acceptable to Python.

--- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -71,7 +71,8 @@ New syntax features: New library modules: -* None yet. +* :mod:zipapp: :ref:`Improving Python ZIP Application Support

Improved Modules

new file mode 100644 --- /dev/null +++ b/Lib/test/test_zipapp.py @@ -0,0 +1,250 @@ +"""Test harness for the zipapp module.""" + +import io +import pathlib +import stat +import sys +import tempfile +import unittest +import zipapp +import zipfile + + +class ZipAppTest(unittest.TestCase): +

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+

+ + +if name == "main":

new file mode 100644 --- /dev/null +++ b/Lib/zipapp.py @@ -0,0 +1,179 @@ +import contextlib +import os +import pathlib +import shutil +import stat +import sys +import zipfile + +all = ['ZipAppError', 'create_archive', 'get_interpreter'] + + +# The main.py used if the users specifies "-m module:fn". +# Note that this will always be written as UTF-8 (module and +# function names can be non-ASCII in Python 3). +# We add a coding cookie even though UTF-8 is the default in Python 3 +# because the resulting archive may be intended to be run under Python 2. +MAIN_TEMPLATE = """[](#l5.21) +# -- coding: utf-8 -- +import {module} +{module}.{fn}() +""" + + +# The Windows launcher defaults to UTF-8 when parsing shebang lines if the +# file has no BOM. So use UTF-8 on Windows. +# On Unix, use the filesystem encoding. +if sys.platform.startswith('win'):

+else:

+ + +class ZipAppError(ValueError):

+ + +@contextlib.contextmanager +def _maybe_open(archive, mode):

+ + +def _write_file_prefix(f, interpreter):

+ + +def _copy_archive(archive, new_archive, interpreter=None):

+

+

+ + +def create_archive(source, target=None, interpreter=None, main=None):

+

+

+

+

+

+

+

+

+ + +def get_interpreter(archive):

+ + +def main():

+

+

+

+

+

+ + +if name == 'main':

--- a/Tools/msi/launcher/launcher_en-US.wxl +++ b/Tools/msi/launcher/launcher_en-US.wxl @@ -5,4 +5,6 @@ Python File Python File (no console) Compiled Python File

--- a/Tools/msi/launcher/launcher_reg.wxs +++ b/Tools/msi/launcher/launcher_reg.wxs @@ -26,6 +26,20 @@ +