bpo-28247: Document Windows executable creation in zipapp (GH-6158) · python/cpython@4be79f2 (original) (raw)
`@@ -229,6 +229,12 @@ fits in memory::
`
229
229
` >>> with open('myapp.pyz', 'wb') as f:
`
230
230
` >>> f.write(temp.getvalue())
`
231
231
``
``
232
+
``
233
`+
.. _zipapp-specifying-the-interpreter:
`
``
234
+
``
235
`+
Specifying the Interpreter
`
``
236
`+
`
``
237
+
232
238
`Note that if you specify an interpreter and then distribute your application
`
233
239
`archive, you need to ensure that the interpreter used is portable. The Python
`
234
240
``` launcher for Windows supports most common forms of POSIX #!
line, but there
`@@ -245,6 +251,169 @@ are other issues to consider:
`
`245`
`251`
` exact version like "/usr/bin/env python3.4" as you will need to change your
`
`246`
`252`
` shebang line for users of Python 3.5, for example.
`
`247`
`253`
``
``
`254`
`+
Typically, you should use an "/usr/bin/env python2" or "/usr/bin/env python3",
`
``
`255`
`+
depending on whether your code is written for Python 2 or 3.
`
``
`256`
`+`
``
`257`
`+`
``
`258`
`+
Creating Standalone Applications with zipapp
`
``
`259`
`+
--------------------------------------------
`
``
`260`
`+`
``
`261`
`` +
Using the :mod:`zipapp` module, it is possible to create self-contained Python
``
``
`262`
`+
programs, which can be distributed to end users who only need to have a
`
``
`263`
`+
suitable version of Python installed on their system. The key to doing this
`
``
`264`
`+
is to bundle all of the application's dependencies into the archive, along
`
``
`265`
`+
with the application code.
`
``
`266`
`+`
``
`267`
`+
The steps to create a standalone archive are as follows:
`
``
`268`
`+`
``
`269`
``` +
1. Create your application in a directory as normal, so you have a ``myapp``
``
270
directory containing a ``__main__.py`` file, and any supporting application
``
271
`+
code.
`
``
272
+
``
273
2. Install all of your application's dependencies into the ``myapp`` directory,
``
274
`+
using pip:
`
``
275
+
``
276
`+
.. code-block:: sh
`
``
277
+
``
278
`+
$ python -m pip install -r requirements.txt --target myapp
`
``
279
+
``
280
(this assumes you have your project requirements in a ``requirements.txt``
``
281
`+
file - if not, you can just list the dependencies manually on the pip command
`
``
282
`+
line).
`
``
283
+
``
284
3. Optionally, delete the ``.dist-info`` directories created by pip in the
``
285
``myapp`` directory. These hold metadata for pip to manage the packages, and
``
286
`+
as you won't be making any further use of pip they aren't required -
`
``
287
`+
although it won't do any harm if you leave them.
`
``
288
+
``
289
`+
- Package the application using:
`
``
290
+
``
291
`+
.. code-block:: sh
`
``
292
+
``
293
`+
$ python -m zipapp -p "interpreter" myapp
`
``
294
+
``
295
`+
This will produce a standalone executable, which can be run on any machine with
`
``
296
`` +
the appropriate interpreter available. See :ref:zipapp-specifying-the-interpreter
``
``
297
`+
for details. It can be shipped to users as a single file.
`
``
298
+
``
299
On Unix, the ``myapp.pyz`` file is executable as it stands. You can rename the
``
300
file to remove the ``.pyz`` extension if you prefer a "plain" command name. On
``
301
Windows, the ``myapp.pyz[w]`` file is executable by virtue of the fact that
``
302
the Python interpreter registers the ``.pyz`` and ``.pyzw`` file extensions
``
303
`+
when installed.
`
``
304
+
``
305
+
``
306
`+
Making a Windows executable
`
``
307
`+
`
``
`308`
`+`
``
`309`
``` +
On Windows, registration of the ``.pyz`` extension is optional, and
```
``
`310`
`+
furthermore, there are certain places that don't recognise registered
`
``
`311`
`+
extensions "transparently" (the simplest example is that
`
``
`312`
``` +
``subprocess.run(['myapp'])`` won't find your application - you need to
```
``
`313`
`+
explicitly specify the extension).
`
``
`314`
`+`
``
`315`
`+
On Windows, therefore, it is often preferable to create an executable from the
`
``
`316`
`+
zipapp. This is relatively easy, although it does require a C compiler. The
`
``
`317`
`+
basic approach relies on the fact that zipfiles can have arbitrary data
`
``
`318`
`+
prepended, and Windows exe files can have arbitrary data appended. So by
`
``
`319`
``` +
creating a suitable launcher and tacking the ``.pyz`` file onto the end of it,
```
``
`320`
`+
you end up with a single-file executable that runs your application.
`
``
`321`
`+`
``
`322`
`+
A suitable launcher can be as simple as the following::
`
``
`323`
`+`
``
`324`
`+
#define Py_LIMITED_API 1
`
``
`325`
`+
#include "Python.h"
`
``
`326`
`+`
``
`327`
`+
#define WIN32_LEAN_AND_MEAN
`
``
`328`
`+
#include <windows.h>
`
``
`329`
`+`
``
`330`
`+
#ifdef WINDOWS
`
``
`331`
`+
int WINAPI wWinMain(
`
``
`332`
`+
HINSTANCE hInstance, /* handle to current instance */
`
``
`333`
`+
HINSTANCE hPrevInstance, /* handle to previous instance */
`
``
`334`
`+
LPWSTR lpCmdLine, /* pointer to command line */
`
``
`335`
`+
int nCmdShow /* show state of window */
`
``
`336`
`+
)
`
``
`337`
`+
#else
`
``
`338`
`+
int wmain()
`
``
`339`
`+
#endif
`
``
`340`
`+
{
`
``
`341`
`+
wchar_t **myargv = _alloca((__argc + 1) * sizeof(wchar_t*));
`
``
`342`
`+
myargv[0] = __wargv[0];
`
``
`343`
`+
memcpy(myargv + 1, __wargv, __argc * sizeof(wchar_t *));
`
``
`344`
`+
return Py_Main(__argc+1, myargv);
`
``
`345`
`+
}
`
``
`346`
`+`
``
`347`
``` +
If you define the ``WINDOWS`` preprocessor symbol, this will generate a
```
``
`348`
`+
GUI executable, and without it, a console executable.
`
``
`349`
`+`
``
`350`
`+
To compile the executable, you can either just use the standard MSVC
`
``
`351`
`+
command line tools, or you can take advantage of the fact that distutils
`
``
`352`
`+
knows how to compile Python source::
`
``
`353`
`+`
``
`354`
`+
>>> from distutils.ccompiler import new_compiler
`
``
`355`
`+
>>> import distutils.sysconfig
`
``
`356`
`+
>>> import sys
`
``
`357`
`+
>>> import os
`
``
`358`
`+
>>> from pathlib import Path
`
``
`359`
`+`
``
`360`
`+
>>> def compile(src):
`
``
`361`
`+
>>> src = Path(src)
`
``
`362`
`+
>>> cc = new_compiler()
`
``
`363`
`+
>>> exe = src.stem
`
``
`364`
`+
>>> cc.add_include_dir(distutils.sysconfig.get_python_inc())
`
``
`365`
`+
>>> cc.add_library_dir(os.path.join(sys.base_exec_prefix, 'libs'))
`
``
`366`
`+
>>> # First the CLI executable
`
``
`367`
`+
>>> objs = cc.compile([str(src)])
`
``
`368`
`+
>>> cc.link_executable(objs, exe)
`
``
`369`
`+
>>> # Now the GUI executable
`
``
`370`
`+
>>> cc.define_macro('WINDOWS')
`
``
`371`
`+
>>> objs = cc.compile([str(src)])
`
``
`372`
`+
>>> cc.link_executable(objs, exe + 'w')
`
``
`373`
`+`
``
`374`
`+
>>> if __name__ == "__main__":
`
``
`375`
`+
>>> compile("zastub.c")
`
``
`376`
`+`
``
`377`
`+
The resulting launcher uses the "Limited ABI", so it will run unchanged with
`
``
`378`
``` +
any version of Python 3.x. All it needs is for Python (``python3.dll``) to be
```
``
`379`
``` +
on the user's ``PATH``.
```
``
`380`
`+`
``
`381`
`+
For a fully standalone distribution, you can distribute the launcher with your
`
``
`382`
`+
application appended, bundled with the Python "embedded" distribution. This
`
``
`383`
`+
will run on any PC with the appropriate architecture (32 bit or 64 bit).
`
``
`384`
`+`
``
`385`
`+`
``
`386`
`+
Caveats
`
``
`387`
`+
`
``
388
+
``
389
`+
There are some limitations to the process of bundling your application into
`
``
390
`+
a single file. In most, if not all, cases they can be addressed without
`
``
391
`+
needing major changes to your application.
`
``
392
+
``
393
`+
- If your application depends on a package that includes a C extension, that
`
``
394
`+
package cannot be run from a zip file (this is an OS limitation, as executable
`
``
395
`+
code must be present in the filesystem for the OS loader to load it). In this
`
``
396
`+
case, you can exclude that dependency from the zipfile, and either require
`
``
397
`+
your users to have it installed, or ship it alongside your zipfile and add code
`
``
398
to your ``__main__.py`` to include the directory containing the unzipped
``
399
module in ``sys.path``. In this case, you will need to make sure to ship
``
400
`+
appropriate binaries for your target architecture(s) (and potentially pick the
`
``
401
correct version to add to ``sys.path`` at runtime, based on the user's machine).
``
402
+
``
403
`+
- If you are shipping a Windows executable as described above, you either need to
`
``
404
ensure that your users have ``python3.dll`` on their PATH (which is not the
``
405
`+
default behaviour of the installer) or you should bundle your application with
`
``
406
`+
the embedded distribution.
`
``
407
+
``
408
`+
- The suggested launcher above uses the Python embedding API. This means that in
`
``
409
your application, ``sys.executable`` will be your application, and *not* a
``
410
`+
conventional Python interpreter. Your code and its dependencies need to be
`
``
411
`+
prepared for this possibility. For example, if your application uses the
`
``
412
`` +
:mod:multiprocessing
module, it will need to call
``
``
413
`` +
:func:multiprocessing.set_executable
to let the module know where to find the
``
``
414
`+
standard Python interpreter.
`
``
415
+
``
416
+
248
417
`The Python Zip Application Archive Format
`
249
418
`-----------------------------------------
`
250
419
``