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

`+

  1. 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

`+

  1. 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

`+

  1. 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

`+

  1. 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

``