Welcome, guest | Sign In | My Account | Store | Cart

Some python applications contain a main skript and some additional modules. By using zipimport from python-2.3 and some help of the shell one may bind all needed files (except the python interpreter and its standard modules) into one executable. This eliminates the need for an expensive installation.

Python, 15 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/bin/sh
PYTHON23=$(which python2.3 2>/dev/null)
if [ ! -x "$PYTHON23" ] ; then
    echo "Python-2.3 executable not found - can't continue!"
    exit 1
fi
exec $PYTHON23 - $0 $@ << END_OF_PYTHON_CODE

import sys
sys.path.insert(0, sys.argv[1])
del sys.argv[0:2]
import main
main.main()

END_OF_PYTHON_CODE

This solution was tested with Linux, but will work with other UNIX-like systems. It needs Python-2.3 final (2.3beta will not work!).

Let's assume you have a python application with some modules (spam.py and eggs.py). The main function is called main() in the file main.py.

  • First you need to zip all files: $ zip main.zip spam.py eggs.py main.py (you may use the zipfile module instead)
  • Save the code above to the file zipheader.unix
  • Then concatenate both files: $ cat zipheader.unix main.zip > standalone
  • Last step is to make standalone executable: $ chmod +x standalone

Done! All of your application code is contained in the file "standalone". When started, the shell replaces itself by the Python interpreter. The interpreter will reopen the file as a zipfile (skipping the 'leading garbage') and find all needed modules inside the zipfile.

I tried to implement the same for a batchfile on windows, but without success so far. ;-(

4 comments

Josiah Carlson 20 years, 7 months ago  # | flag

Very Nifty... In windows there is a useful tool called py2exe, it uses the distutils package to deal with all the required modules.

I am pretty sure there is something of a similar nature for linux that produces ELF binaries, handles all the required modules, etc.

Noah Spurrier 18 years, 4 months ago  # | flag

Side effect: closes stdin. This appears to close stdin. The herefile in the shell script redirects stdin before python gets a chance to start. This disable raw_input() and anything else that reads sys.stdin.

Noah Spurrier 18 years, 4 months ago  # | flag

fix to stdin close problem. But all is not lost. The "zipheader.unix" script can be rewritten to use the '-c' option of python instead of stdin and a herefile. This fix also handles the case where Python may be even less than version 2.0. This works the same, "cat zipheader.unix main.zip > main" then run "main".

---- save as zipheader.unix ----
#!/bin/sh
# This is a self-extracting executable.
# Execute this like any normal executable.
# You may need to "chmod a+x" this file.
# This is a binary ZIP file with a Python loader header.
#
# Bourne shell loader:
PYTHON=$(which python 2>/dev/null)
if [ ! -x "$PYTHON" ] ; then
    echo "Python not found!"
    exit 1
fi
exec $PYTHON -c "
# Python loader:
import sys, os
if int(sys.version[0])&lt;2:
    print 'Python version 2.3 final or greater is required.'
    print 'Your version is', sys.version
    os._exit(1)
major = sys.version_info[0]
minor = sys.version_info[1]
releaselevel = sys.version_info[3]
if (major==2 and minor&lt;3) or (major==2 and minor==3 and releaselevel!='final'):
    print 'Python version 2.3 final or greater is required.'
    print 'Your version is', sys.version
    os._exit(1)
sys.path.insert(0, sys.argv[1])
del sys.argv[0:1]
print sys.argv[1]
import main
main.main()
" $0 $@

# Zip file:
---- end of zipheader.unix ----

Yours, Noah

Hallvard Furuseth 18 years, 2 months ago  # | flag

Quoting and testing. First, these programs fail if the command line arguments or file name contains spaces etc. Use "$0" and "$@". (Or ${1+"$@"} for the latter to avoid an old Bourne shell bug, but that breaks on Zsh 3/4, which is used on Mac OS X.)

Next, what's the deal with all this testing?

The exec shell command can find Python without help from $(which) - which is an unportable shell construct anyway, it should have been which. Though we might append an exit statement in case it's some shell which doesn't exit if exec fails.

Thus, the first program could use

#! /bin/sh
exec python2.3 - "$0" "$@" &lt;&lt; END_OF_PYTHON_CODE || exit 1
...

The second program rejects e.g. 2.3.4 beta, which I suppose was not indented. But I don't suppose there is any need to test for 2.3 beta anymore, and since it tests sys.version anyway, it can just as well stick to that. Also, assert seems good enough for this purpose:

exec python -c '
import sys
assert sys.version &gt;= "2.3", sys.argv[1] + ": Python 2.3 or greater required"
sys.path.insert(0, sys.argv[1])
del sys.argv[0]
import main
main.main()
' "$0" "$@"
exit 1
Created by Joerg Raedler on Mon, 11 Aug 2003 (PSF)
Python recipes (4591)
Joerg Raedler's recipes (1)
Python Cookbook Edition 2 (117)

Required Modules

  • (none specified)

Other Information and Tasks