Recipe 16.12. Binding Main Script and Modules into One Executable on Unix
Credit: Joerg Raedler
Problem
You have a Python
application composed of a main script and some additional modules.
You want to bind the script and modules into one executable file, so
that no installation procedure is necessary.
Solution
Prepare the following mixed sh/Python script and
save it as file zipheader.unix:
#!/bin/shMake sure you have the Python bytecode files for the main script of
PYTHON=$(which python 2>/dev/null)
if [ x ! -x "x$PYTHON" ] ; then
echo "python executable not found - cannot continue!"
exit 1
fi
exec $PYTHON - $0 $@ << END_OF_PYTHON_CODE
import sys
version = sys.version_info[:2]
if version < (2, 3):
print 'Sorry, need Python 2.3 or better; %s.%s is too old!' % version
sys.path.insert(0, sys.argv[1])
del sys.argv[0:2]
import main
main.main( )
END_OF_PYTHON_CODE
your application (file main.pyc, containing a
function named main, which starts the application
when called without arguments) and any additional modules your
application needs (e.g., files spam.pyc and
eggs.pyc). Make a zip file out of them all:
$ zip myapp.zip main.pyc spam.pyc eggs.pyc(If you prefer, you can build the zip file with an auxiliary Python
program, of course.) Next, concatenate the
"header" and the zip file, and make
the resulting file executable:
$ cat zipheader.unix myapp.zip > myappThat's all! Your application is now contained in
$ chmod +x myapp
this executable file myapp. When
myapp runs, the shell /bin/sh
sets things up and replaces itself with the Python interpreter. The
Python interpreter reopens the file as a zip file, skipping the
"header" text, and finds all needed
modules in the zip file itself.
Discussion
On Windows machines, you would normally use py2exe
for similar tasks, as shown previously in Recipe 16.11; on Mac OS X, you would
normally use py2app (although this recipe works
just as well on Mac OS X as it does on any other Unix).This recipe is particularly useful for Linux and other Unix variants
that come with Python installed. By following the steps outlined in
this recipe's Solution, you can distribute a Python
application as a single, self-contained standalone executable file,
which runs on any version of Unix, on any hardware platformas
long as your Python application does not need any C-coded extension
modules beyond the ones that come with Python itself. When you do
need more, you can use Python's own
distutil package to perform more complicated
packaging tasks. But for many simple Python applications and quite a
few that aren't all that simple, this recipe can be
very useful, since it results in a file that can just be run
as is, without needing any kind of
"installation" step!The key idea of this recipe is to exploit Python's
ability to import modules from a zip file, while skipping leading
text that may precede the zip file itself. Here, as leading text, we
use a small shell script that turns itself into a Python script, and
within the same file continues with the zip file from which
everything gets imported. The concept of importing from a zip file is
described in Recipe 2.9.In the zip file, you may, if you wish, place Python source files
(with extension .py), as well as compiled
bytecode files (with extension .pyc); the latter
option is often preferable because if you zip up source files, Python
compiles them every time you run the application, slowing your
application's startup. On the other hand, if you zip
up compiled bytecode files, your application may be unable to run
with versions of Python that are newer than the one you used to
prepare the bytecode files, since binary compatibility of bytecode
files is not guaranteed across Python releases. The best approach may
be to place both sources and bytecodes in the zip file.You may also choose to zip up optimized bytecode
files (with extension .pyo)if you do so,
you need to add the flag -O right after the
$PYTHON in the shell script in this
recipe's Solution. Execution speed
doesn't generally change much, but optimized
execution skips assert statements, which may be
important to you. Also, if you prepare the .pyo
files by running Python with option -OO, all
docstrings are eliminated, which may slightly reduce your
application's size on disk (although docstrings tend
to compress well, so that size advantage may be minor).If you need help in finding all the modules that you need to place in
the zip file, see the modulefinder module in the
Python Standard Library. Unfortunately, no real documentation about
it is available at the time of this writing, but just running (in
version 2.3) something like:
$ python /usr/lib/python2.3/modulefinder.py main.pyshould help (you may have to change the change the path to the
modulefinder.py script, depending on your Python
installation). With Python 2.4, you can just use the handy new
-m switch:
$ python -mmodulefinder main.pyPython 2.4's -m switch lets you
run as the main script any module that's on
Python's sys.patha very
convenient little feature!
See Also
Recipe 16.11; Recipe 2.9; the sources of modules
modulefinder and zipimport
(which are not yet documented in the Library
Reference at the time of writing).