better support for pyc's

This commit is contained in:
David Rose 2009-05-02 03:39:51 +00:00
parent 4456d45f62
commit cce0129c7b
2 changed files with 70 additions and 27 deletions

View File

@ -23,11 +23,22 @@ Options:
(this is preferable to having the module start itself immediately (this is preferable to having the module start itself immediately
upon importing). upon importing).
-c [py,pyc,pyo]
Specifies the compilation mode of python files. 'py' means to
leave them as source files, 'pyc' and 'pyo' are equivalent, and
mean to compile to byte code. pyc files will be written if the
interpreter is running in normal debug mode, while pyo files will
be written if it is running in optimize mode (-O or -OO).
""" """
import sys import sys
import getopt import getopt
import imp
import marshal
import direct import direct
from direct.stdpy.file import open
from pandac.PandaModules import * from pandac.PandaModules import *
vfs = VirtualFileSystem.getGlobalPtr() vfs = VirtualFileSystem.getGlobalPtr()
@ -51,6 +62,9 @@ class AppPacker:
# without compression. # without compression.
uncompressible_extensions = [ 'mp3' ] uncompressible_extensions = [ 'mp3' ]
# Specifies how or if python files are compiled.
compilation_mode = 'pyc'
def __init__(self, multifile_name): def __init__(self, multifile_name):
# Make sure any pre-existing file is removed. # Make sure any pre-existing file is removed.
Filename(multifile_name).unlink() Filename(multifile_name).unlink()
@ -67,6 +81,12 @@ class AppPacker:
self.image_extensions += type.getExtensions() self.image_extensions += type.getExtensions()
def scan(self, root, main): def scan(self, root, main):
if self.compilation_mode != 'py':
if __debug__:
self.compilation_mode = 'pyc'
else:
self.compilation_mode = 'pyo'
self.root = Filename(root) self.root = Filename(root)
self.root.makeAbsolute(vfs.getCwd()) self.root.makeAbsolute(vfs.getCwd())
@ -131,15 +151,30 @@ class AppPacker:
self.addUncompressibleFile(filename) self.addUncompressibleFile(filename)
def addPyFile(self, filename): def addPyFile(self, filename):
# For now, just add it as an ordinary file. Later we'll targetFilename = self.makeRelFilename(filename)
# precompile these to .pyo's.
if filename == self.main: if filename == self.main:
# This one is the "main.py"; the starter file. # This one is the "main.py"; the starter file.
self.multifile.addSubfile('main.py', filename, self.compression_level) targetFilename = Filename('main.py')
else:
# Any other Python file; add it normally.
self.addTextFile(filename)
if self.compilation_mode == 'py':
# Add python files as source files.
self.multifile.addSubfile(targetFilename.cStr(), filename, self.compression_level)
elif self.compilation_mode == 'pyc' or self.compilation_mode == 'pyo':
# Compile it to bytecode.
targetFilename.setExtension(self.compilation_mode)
source = open(filename, 'r').read()
if source and source[-1] != '\n':
source = source + '\n'
code = compile(source, targetFilename.cStr(), 'exec')
data = imp.get_magic() + '\0\0\0\0' + marshal.dumps(code)
stream = StringStream(data)
self.multifile.addSubfile(targetFilename.cStr(), stream, self.compression_level)
self.multifile.flush()
else:
raise StandardError, 'Unsupported compilation mode %s' % (self.compilation_mode)
def addEggFile(self, filename, outFilename): def addEggFile(self, filename, outFilename):
# Precompile egg files to bam's. # Precompile egg files to bam's.
node = loadEggFile(filename) node = loadEggFile(filename)
@ -268,15 +303,18 @@ class AppPacker:
def makePackedApp(args): def makePackedApp(args):
opts, args = getopt.getopt(args, 'r:m:h') opts, args = getopt.getopt(args, 'r:m:c:h')
root = '.' root = '.'
main = None main = None
compilation_mode = AppPacker.compilation_mode
for option, value in opts: for option, value in opts:
if option == '-r': if option == '-r':
root = value root = value
elif option == '-m': elif option == '-m':
main = value main = value
elif option == '-c':
compilation_mode = value
elif option == '-h': elif option == '-h':
print __doc__ print __doc__
sys.exit(1) sys.exit(1)
@ -289,6 +327,7 @@ def makePackedApp(args):
raise ArgumentError, "Too many arguments." raise ArgumentError, "Too many arguments."
p = AppPacker(multifile_name) p = AppPacker(multifile_name)
p.compilation_mode = compilation_mode
p.scan(root = root, main = main) p.scan(root = root, main = main)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -17,11 +17,11 @@ FTPythonSource = 0
FTPythonCompiled = 1 FTPythonCompiled = 1
FTCompiledModule = 2 FTCompiledModule = 2
pycExtension = 'pyc' compiledExtensions = [ 'pyc', 'pyo' ]
if not __debug__: if not __debug__:
# In optimized mode, we actually operate on .pyo files, not .pyc # In optimized mode, we prefer loading .pyo files over .pyc files.
# files. # We implement that by reversing the extension names.
pycExtension = 'pyo' compiledExtensions = [ 'pyo', 'pyc' ]
class VFSImporter: class VFSImporter:
""" This class serves as a Python importer to support loading """ This class serves as a Python importer to support loading
@ -45,11 +45,12 @@ class VFSImporter:
# If there's no .py file, but there's a .pyc file, load that # If there's no .py file, but there's a .pyc file, load that
# anyway. # anyway.
filename = Filename(path) for ext in compiledExtensions:
filename.setExtension(pycExtension) filename = Filename(path)
vfile = vfs.getFile(filename, True) filename.setExtension(ext)
if vfile: vfile = vfs.getFile(filename, True)
return VFSLoader(self, vfile, filename, FTPythonCompiled) if vfile:
return VFSLoader(self, vfile, filename, FTPythonCompiled)
# Look for a compiled C/C++ module. # Look for a compiled C/C++ module.
for desc in imp.get_suffixes(): for desc in imp.get_suffixes():
@ -71,11 +72,12 @@ class VFSImporter:
if vfile: if vfile:
return VFSLoader(self, vfile, filename, FTPythonSource, return VFSLoader(self, vfile, filename, FTPythonSource,
packagePath = path) packagePath = path)
filename = Filename(path, '__init__.' + pycExtension) for ext in compiledExtensions:
vfile = vfs.getFile(filename, True) filename = Filename(path, '__init__.' + ext)
if vfile: vfile = vfs.getFile(filename, True)
return VFSLoader(self, vfile, filename, FTPythonCompiled, if vfile:
packagePath = path) return VFSLoader(self, vfile, filename, FTPythonCompiled,
packagePath = path)
return None return None
@ -192,12 +194,14 @@ class VFSLoader:
# It's a .py file (or an __init__.py file; same thing). Read # It's a .py file (or an __init__.py file; same thing). Read
# the .pyc file if it is available and current; otherwise read # the .pyc file if it is available and current; otherwise read
# the .py file and compile it. # the .py file and compile it.
pycFilename = Filename(self.filename)
pycFilename.setExtension(pycExtension)
pycVfile = vfs.getFile(pycFilename, False)
t_pyc = None t_pyc = None
if pycVfile: for ext in compiledExtensions:
t_pyc = pycVfile.getTimestamp() pycFilename = Filename(self.filename)
pycFilename.setExtension(ext)
pycVfile = vfs.getFile(pycFilename, False)
if pycVfile:
t_pyc = pycVfile.getTimestamp()
break
code = None code = None
if t_pyc and t_pyc >= self.timestamp: if t_pyc and t_pyc >= self.timestamp:
@ -233,7 +237,7 @@ class VFSLoader:
# try to cache the compiled code # try to cache the compiled code
pycFilename = Filename(filename) pycFilename = Filename(filename)
pycFilename.setExtension(pycExtension) pycFilename.setExtension(compiledExtensions[0])
try: try:
f = open(pycFilename, 'wb') f = open(pycFilename, 'wb')
except IOError: except IOError: