I’ve been experimenting a bit with the new
in python 3.5.
From the documentation:
This module provides tools to manage the creation of zip files containing Python code, which can be executed directly by the Python interpreter. The module provides both a Command-Line Interface and a Python API.
So say we have written a little CLI tool. We can now package this up as a single file and execute it. Neat!
Here is how it works.
First thing to know: If you place a
__main__.py, you can execute the directory (package) with
Our directory looks like this
app ├── __main__.py ├── otherfile.py └── requirements.txt
__main__.py like this
We can now run python with the
app directory as argument:
$> python app hello world
Another neat feature is that it works even if you zip the app directory.
$> pushd app ~/src/demo/app ~/src/demo $> zip -r ../app.pyz * adding: __main__.py (stored 0%) adding: requirements.txt (stored 0%) $> popd ~/src/demo # Now let's run the archive $> python app.pyz hello world
We can even add a shebang to the start of the file:
$> echo '#!/usr/bin/env python' > app.cmd $> cat app.pyz >> app.cmd $> chmod +x app.cmd $> ./app.cmd hello world
And this is how we produce a single-file python executable.
You’ve been able to execute a zipfile with python since before time (it is present in 2.6, so it is oooold). So you should be able to use this just about anywhere you have a somewhat updated python version.
In Python 3.5
New in Python 3.5 is the
zipapp module, which wraps all
this functionality in a nice little module.
So instead of all the above, we can now just do:
$> python3.5 -m zipapp --python '/usr/bin/env python' --output app.pyz app $> ./app.pyz hello world
Which does pretty much what we did above.
Of course this only bundles our own code, what if we have dependencies? Sorry, you’ll have to bundle them yourself. Luckily, this is super easy!
Simply install your dependencies inside your application directory and
add it to your
You could do it like:
$> python -m pip install --prefix ./app click Collecting click Using cached click-6.2-py2.py3-none-any.whl Installing collected packages: click Successfully installed click
Resulting in a directory structure like this:
app ├── lib │ └── python2.7 │ └── site-packages │ ├── click │ └── click-6.2.dist-info ├── __main__.py └── requirements.txt
(note that i used
and in your
__main__.py, add it to your
import sys import os sys.path.append( os.path.join( os.path.dirname(__file__), 'lib', 'python%d.%d' % (sys.version_info.major, sys.version_info.minor), 'site-packages' ) )
And you should now be able to load click.
$> python app <module 'click' from '/home/tbug/src/pybundle/app/lib/python2.7/site-packages/click/__init__.pyc'>
Like always, path hacking feels a little dirty, but we’ll manage.
I wrapped the above examples in a neat little shell-script that does all of this for you.
It also contains a hello-world
Now, is this useful? Where should we use it?
I’m not sure. But it is really cool! :)