How to prepare a Windows system for a good PYZ experience.
Zipapps are a way to distribute Python applications and all of their dependencies in a single binary file.This is comparable to statically linked golang apps or Java's ‘executable JARs’. Their main advantage is that distributing and installing them is quite simple.
Running Python code directly from ZIP archives is nothing new, PEP 273 made its debut in 2001, as part of Python 2.3 in the form of the
zipimport module. PEP 441 builds on this and describes mechanisms to bundle full applications into a single ZIP file that can be made executable. It was approved in 2015 and a first implementation appeared in Python 3.5 via the
See the PEP for details on how making a ZIP into an executable file works, but basically on POSIX systems the Python interpreter is called in a ‘bang path’ that is followed by the ZIP archive. The interpreter recognizes the ‘script’ is a whole application archive and acts accordingly. On Windows, zipapps MUST carry the
.pyz extension which is bound to the
py dispatcher command, which in turn looks at the bang path and calls a matching Python interpreter from the installed set.
To display the bang path of a zipapp, use this command:
python3 -m zipapp --info foo.pyz
If you want to change the requested Python version to one that is actually installed or that you prefer, change the bang path as part of the installation process:
python3 -m zipapp -p '/usr/bin/env python3.8' -o ~/bin/foo foo.pyz
This can also be done on an ad-hoc basis, by explicitly calling the desired interpreter:
python3.8 foo.pyz … # POSIX py -3.8 foo.pyz … # Windows
On Windows, because there is no ‘+x’ flag, things are a bit more complicated than on POSIX. Zipapps MUST have a
.pyz extension, for which the
py launcher is registered as the default application.The net effect is that such files become executable and are handed over to the launcher if you add a few environment settings to your machine.
In the user-specific environment settings, add a new
PATHEXT variable (or extend an existing one), with the value
%PATHEXT%;.PYZ. Also edit the
PATH one and add a new
%LOCALAPPDATA%\bin entry. Save everything (click “OK”), open a new command window, and verify the changes with
echo %PATHEXT% & echo %PATH%
Create the new bin directory by calling
md %LOCALAPPDATA%\bin. Now you can place a zipapp file like
foo.pyz in that directory, and it is immediately callable as
To get such a test subject, you can build shiv with itself:
git clone https://github.com/linkedin/shiv.git cd shiv py -3 -m venv --prompt shiv venv venv\Scripts\activate.bat python -m pip install -e . shiv -e shiv.cli:main -o %LOCALAPPDATA%\bin\shiv.pyz . deactivate shiv --version
If that makes more sense to you, you can change the system-wide variables instead of the user-specific ones, and choose paths that are global for all users (like
C:\usr\bin or similar).
To make zipapps available network-wide, you can use
%APPDATA% to store the zipapps, so you only have to maintain them once in case you regularly work on several machines in the same network. Just make sure the same version of Python is used everywhere then.