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

This function will package a python script and additional python modules, in either source or compiled form. Either are directly executable by Python 2.7/3.1 or newer.

Uses make-like logic to only rebuild if something is newer than the previous build.

Python, 65 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#####################################
def makeCheck( target, depend ):  # version 2010/02/11
    try:
        targmod = os.stat( target ).st_mtime
        if depend is not None:
            for ix in depend:
                if os.stat( ix ).st_mtime > targmod:
                    if debug:
                        print("makeCheck:",
                              target, targmod,
                              ix, os.stat( ix ).st_mtime
                              )
                        return True
        return False
    except:
        return True
#####################################
def zipScript( dest, main, progs = None, helps = None, compile = False
               ):  # version 2010/02/11
    """main gets renamed to __main__.py in the zip file
progs are a list of programs or modules to be included, source or compiled
helps is a list of helper modules to be included, source or compiled
"""
    tozip = []
    toremove = []
    funnymain = "__main__.py"
    
    if makeCheck( dest, (main,)) \
            or makeCheck( dest, progs ) \
            or makeCheck( dest, helps ):        
        # only if it is out of date

        import zipfile
        zf = zipfile.ZipFile( dest, "w", zipfile.ZIP_DEFLATED )

        if compile:
            import py_compile
            ixc = funnymain + "c"
            toremove.append( ixc )
            tozip.append( ixc )
            py_compile.compile( main, ixc, doraise = True )
            for ix in progs:
                ixc = ix + "c"
                toremove.append( ixc )
                tozip.append( ixc )
                py_compile.compile( ix, ixc, doraise = True )
            for ix in helps:
                ixc = ix + "c"
                toremove.append( ixc )
                tozip.append( ixc )
                py_compile.compile( ix, ixc, doraise = True )
        else:
            for ix in progs:
                tozip.append( ix )
            for ix in helps:
                tozip.append( ix )
            zf.write( ix, funnymain )

        for ix in tozip:
            zf.write( ix )
        zf.close()

        for ix in toremove:
            os.remove( ix )
#####################################

Invoke the code with something like:

zipScript("a.py", "src.py", ("depend1.py", "depend2.py"))

This will package source files as a zip, with src.py becoming __main__.py within the zip.

zipScript("a.py", "src.py", ("depend1.py", "depend2.py"), compile=True )

The compile parameter set to True will compile all the packages before placing them in the target zip file. But that adds a dependency on some particular versions of Python that can execute that form of compiled code. If distributing your application, you might need to build multiple version of your zipScript package with multiple versions of Python to reach all customers.

For older versions of Python, see http://code.activestate.com/recipes/497000/

3 comments

Marcus Britanicus 13 years, 7 months ago  # | flag

This code :

 >>> import sys, PyZipScript
 >>> PyZipScript.zipScript( "Trial.py", sys.argv[0], (), (), False )

crashes with the following output :

Traceback (most recent call last):
  File "PyZipScript.py", line 69, in <module>
    zipScript( "Trial.py", sys.argv[0], (), () )
  File "PyZipScript.py", line 57, in zipScript
    zf.write( ix, funnymain )
UnboundLocalError: local variable 'ix' referenced before assignment
Marcus Britanicus 13 years, 7 months ago  # | flag

Changing line 57 from

zf.write( ix, funnymain )

to

zf.write( main, funnymain )

does the trick. Also I think its better to write

def zipScript( dest, main, progs = (), helps = (), compile = False ) :

than

def zipScript( dest, main, progs = None, helps = None, compile = False ) :

because the second script raises TypeError :

>>> import sys
>>> import zipScript from PyZipScript
>>> zipScript( "Trial.py", sys.argv[0] )
Traceback (most recent call last):
  File "<stdin>", line 73, in <module>
    zipScript( "Trial.py", sys.argv[0] )
  File "<stdin>", line 55, in zipScript
    for ix in progs:
TypeError: 'NoneType' object is not iterable
Glenn (author) 11 years, 11 months ago  # | flag

Thanks, Marcus, for the suggestions/corrections. Somehow they never got emailed to me, but I happened back to look at this recipe, when I created another just now, and found them.

Created by Glenn on Thu, 11 Feb 2010 (MIT)
Python recipes (4591)
Glenn's recipes (2)

Required Modules

Other Information and Tasks