intro
py2exe tool converts Python scripts to standalone .exe distributives. It isolates all required Python modules together with Python interpreter itself and wraps them into single library.zip file. This way application is not affected by Python modules that may be already installed on user system. Unfortunately, this also means that you can't add new modules to your standalone Python program.
For example, you need to add
Hg-Git extension to
Mercurial installed as standalone program. You can specify path to extension in
Mercurial.ini, but Hg-Git depends on
Dulwich module, which is not present in
library.zip Attempt to use this extension will fail. The same problem is with converting Bazaar repositories using
convert extension that is present in library.zip, but additionally requires installed
bzr module.
solution
The script below helps to add Python modules to library.zip file made with py2exe. Latest version should be available at
this bitbucket repository. The following command unpacks
library.zip in current directory and makes
library_unpacked.zip that you can edit with your favorite archiver:
python relibzip.py unpack
After you've finished, issue:
python relibzip.py pack
and script will create library_packed.zip from extracted resources. Copy this file over library.zip and you all set.
Source code (ugly, but works):
"""
pack/unpack library.zip created by py2exe to standard .zip archive
library.zip created with py2exe can not always be processed by standard
archivers. this script removes extra chunks added by py2exe and puts
them back when requested
MIT license, by techtonik // gmail.com
"""
import sys
import os
from optparse import OptionParser
import struct
PYTHONDLL = "<pythondll>"
PDNAME = "pythondll"
ZLIBPYD = "<zlib.pyd>"
ZDNAME = "zlibpyd"
UNPACKED = "library_unpacked.zip"
PACKED = "library_packed.zip"
def unpack(filename):
f = open(filename, "rb")
# looking for PYTHONDLL name
pdname = f.read(len(PYTHONDLL))
if pdname != PYTHONDLL:
if pdname[:2] == "PK":
sys.exit("Seems to be normal .zip archive, not unpacking")
else:
sys.exit("Unknown archive format")
def save_section(secname, fname):
print "Extracting %s section to %s" % (secname, fname)
fsize = struct.unpack("i", f.read(4))[0]
fpd = open(fname, "wb")
fpd.write(f.read(fsize))
fpd.close()
save_section(PYTHONDLL, PDNAME)
buf = ""
zdname = f.read(len(ZLIBPYD))
if zdname != ZLIBPYD:
if zdname[:2] == "PK":
print "No zlib.pyd section"
buf = zdname
else:
sys.exit("Unknown archive format")
else:
save_section(ZLIBPYD, ZDNAME)
flib = open(UNPACKED, "wb")
flib.write(buf)
flib.write(f.read())
flib.close()
f.close()
print "Done. Unpacked .zip contents is available at %s" % UNPACKED
sys.exit(0)
def pack(tofname):
if not os.path.exists(PDNAME):
sys.exit("%s section file %s is not found. Exiting" % (PYTHONDLL, PDNAME))
if not os.path.exists(UNPACKED):
sys.exit("Unpacked version %s is not found. Exiting" % UNPACKED)
f = open(tofname, "wb")
def write_section(secname, fname):
print "Writing %s section from %s" % (secname,fname)
f.write(PYTHONDLL)
fsize = os.stat(PDNAME).st_size
f.write(struct.pack("i", fsize))
fpd = open(fname, "rb")
f.write(fpd.read())
fpd.close()
print "Removing section file %s" % fname
os.remove(fname)
write_section(PYTHONDLL, PDNAME)
# check for optional ZLIBPYD section
if not os.path.exists(ZDNAME):
print "No %s section file %s. Skipping" % (ZLIBPYD, ZDNAME)
else:
write_section(ZLIBPYD, ZDNAME)
fzip = open(UNPACKED, "rb")
f.write(fzip.read())
fzip.close()
f.close()
print "Done. Packed .zip contents is available at %s" % tofname
sys.exit(0)
parser = OptionParser(usage="usage: %prog ",
description="update library.zip created by py2exe utility")
opt,arg = parser.parse_args()
if arg and arg[0] == 'unpack':
unpack("library.zip")
elif arg and arg[0] == 'pack':
pack(PACKED)
else:
sys.exit(parser.format_help())