Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6b767ac

Browse files
committed
Lawrence Hudson, SF #401702: Modify co_filename in frozen programs
This patch was developed primarily to reduce the size of the frozen binary. It is particularly useful when freezing for 'small' platforms, such as Palm OS, where you really want to save that last miserable byte. A limitation of this patch is that it does not provide any feedback about the replacements being made. As the path matching is case-sensitive this may lead to unexpected behaviour for DOS and Windows people, eg > freeze.py -r C:\Python\Lib\=py\ goats.py should probably be: > freeze.py -r c:\python\lib\=py\ goats.py
1 parent b845cb0 commit 6b767ac

2 files changed

Lines changed: 42 additions & 3 deletions

File tree

Tools/freeze/freeze.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@
5757
(For debugging only -- on a win32 platform, win32 behavior
5858
is automatic.)
5959
60+
-r prefix=f: Replace path prefix.
61+
Replace prefix with f in the source path references
62+
contained in the resulting binary.
63+
6064
Arguments:
6165
6266
script: The Python script to be executed by the resulting binary.
@@ -109,6 +113,7 @@ def main():
109113
debug = 1
110114
odir = ''
111115
win = sys.platform[:3] == 'win'
116+
replace_paths = [] # settable with -r option
112117

113118
# default the exclude list for each platform
114119
if win: exclude = exclude + [
@@ -139,7 +144,7 @@ def main():
139144

140145
# Now parse the command line with the extras inserted.
141146
try:
142-
opts, args = getopt.getopt(sys.argv[1:], 'a:de:hmo:p:P:qs:wx:l:')
147+
opts, args = getopt.getopt(sys.argv[1:], 'r:a:de:hmo:p:P:qs:wx:l:')
143148
except getopt.error, msg:
144149
usage('getopt error: ' + str(msg))
145150

@@ -174,6 +179,9 @@ def main():
174179
addn_link.append(a)
175180
if o == '-a':
176181
apply(modulefinder.AddPackagePath, tuple(string.split(a,"=", 2)))
182+
if o == '-r':
183+
f,r = string.split(a,"=", 2)
184+
replace_paths.append( (f,r) )
177185

178186
# default prefix and exec_prefix
179187
if not exec_prefix:
@@ -310,7 +318,7 @@ def main():
310318
# collect all modules of the program
311319
dir = os.path.dirname(scriptfile)
312320
path[0] = dir
313-
mf = modulefinder.ModuleFinder(path, debug, exclude)
321+
mf = modulefinder.ModuleFinder(path, debug, exclude, replace_paths)
314322

315323
if win and subsystem=='service':
316324
# If a Windows service, then add the "built-in" module.

Tools/freeze/modulefinder.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import re
88
import string
99
import sys
10+
import new
1011

1112
IMPORT_NAME = dis.opname.index('IMPORT_NAME')
1213
IMPORT_FROM = dis.opname.index('IMPORT_FROM')
@@ -49,7 +50,7 @@ def __repr__(self):
4950

5051
class ModuleFinder:
5152

52-
def __init__(self, path=None, debug=0, excludes = []):
53+
def __init__(self, path=None, debug=0, excludes = [], replace_paths = []):
5354
if path is None:
5455
path = sys.path
5556
self.path = path
@@ -58,6 +59,8 @@ def __init__(self, path=None, debug=0, excludes = []):
5859
self.debug = debug
5960
self.indent = 0
6061
self.excludes = excludes
62+
self.replace_paths = replace_paths
63+
self.processed_paths = [] # Used in debugging only
6164

6265
def msg(self, level, str, *args):
6366
if level <= self.debug:
@@ -250,6 +253,8 @@ def load_module(self, fqname, fp, pathname, (suffix, mode, type)):
250253
m = self.add_module(fqname)
251254
m.__file__ = pathname
252255
if co:
256+
if self.replace_paths:
257+
co = self.replace_paths_in_code(co)
253258
m.__code__ = co
254259
self.scan_code(co, m)
255260
self.msgout(2, "load_module ->", m)
@@ -369,6 +374,32 @@ def report(self):
369374
mods.sort()
370375
print "?", key, "from", string.join(mods, ', ')
371376

377+
def replace_paths_in_code(self, co):
378+
new_filename = original_filename = os.path.normpath(co.co_filename)
379+
for f,r in self.replace_paths:
380+
if original_filename.startswith(f):
381+
new_filename = r+original_filename[len(f):]
382+
break
383+
384+
if self.debug and original_filename not in self.processed_paths:
385+
if new_filename!=original_filename:
386+
self.msgout(2, "co_filename %r changed to %r" \
387+
% (original_filename,new_filename,))
388+
else:
389+
self.msgout(2, "co_filename %r remains unchanged" \
390+
% (original_filename,))
391+
self.processed_paths.append(original_filename)
392+
393+
consts = list(co.co_consts)
394+
for i in range(len(consts)):
395+
if isinstance(consts[i], type(co)):
396+
consts[i] = self.replace_paths_in_code(consts[i])
397+
398+
return new.code(co.co_argcount, co.co_nlocals, co.co_stacksize,
399+
co.co_flags, co.co_code, tuple(consts), co.co_names,
400+
co.co_varnames, new_filename, co.co_name,
401+
co.co_firstlineno, co.co_lnotab)
402+
372403

373404
def test():
374405
# Parse command line

0 commit comments

Comments
 (0)