diff --git a/direct/src/pyinst/archive.py b/direct/src/pyinst/archive.py index 0affaa245a..3cfdf73192 100644 --- a/direct/src/pyinst/archive.py +++ b/direct/src/pyinst/archive.py @@ -1,246 +1,246 @@ -# -# Gordon McMillan (as inspired and influenced by Greg Stein) -# - -# subclasses may not need marshal or struct, but since they're -# builtin, importing is safe. -# -# While an Archive is really an abstraction for any "filesystem -# within a file", it is tuned for use with imputil.FuncImporter. -# This assumes it contains python code objects, indexed by the -# the internal name (ie, no '.py'). -# See carchive.py for a more general archive (contains anything) -# that can be understood by a C program. - -import marshal -import struct - -class Archive: - """ A base class for a repository of python code objects. - - The get_code method is used by imputil.FuntionImporter - to get code objects by name. - Archives are flat namespaces, so conflict between module - names in different packages are possible. Use a different - Archive for each package. - """ - MAGIC = 'PYL\0' - HDRLEN = 12 # default is MAGIC followed by python's magic, int pos of toc - TOCPOS = 8 - TRLLEN = 0 # default - no trailer - TOCTMPLT = {} # - os = None - def __init__(self, path=None, start=0): - """ - Initialize an Archive. If path is omitted, it will be an empty Archive. - start is the seek position within path where the Archive starts.""" - self.toc = None - self.path = path - self.start = start - import imp - self.pymagic = imp.get_magic() - if path is not None: - self.lib = open(self.path, 'rb') - self.checkmagic() - self.loadtoc() - - ####### Sub-methods of __init__ - override as needed ############# - def checkmagic(self): - """Verify version and validity of file. - - Overridable. - Check to see if the file object self.lib actually has a file - we understand. - """ - self.lib.seek(self.start) #default - magic is at start of file - if self.lib.read(len(self.MAGIC)) != self.MAGIC: - raise RuntimeError, "%s is not a valid %s archive file" \ - % (self.path, self.__class__.__name__) - if self.lib.read(len(self.pymagic)) != self.pymagic: - raise RuntimeError, "%s has version mismatch to dll" % (self.path) - - def loadtoc(self): - """Load the table of contents. - - Overridable. - Default: After magic comes an int (4 byte native) giving the - position of the TOC within self.lib. - Default: The TOC is a marshal-able string. - """ - self.lib.seek(self.start + self.TOCPOS) - (offset,) = struct.unpack('=i', self.lib.read(4)) - self.lib.seek(self.start + offset) - self.toc = marshal.load(self.lib) - - ######## This is what is called by FuncImporter ####### - ## Since an Archive is flat, we ignore parent and modname. - - def get_code(self, parent, modname, fqname): - """The import hook. - - Called by imputil.FunctionImporter. - Override extract to tune getting code from the Archive.""" - rslt = self.extract(fqname) # None if not found, (ispkg, code) otherwise - if rslt is None: - return None - ispkg, code = rslt - if ispkg: - return ispkg, code, {'__path__' : []} - return rslt - - ####### Core method - Override as needed ######### - def extract(self, name): - """ Get the object corresponding to name, or None. - - NAME is the name as specified in an 'import name'. - 'import a.b' will become: - extract('a') (return None because 'a' is not a code object) - extract('a.__init__') (return a code object) - extract('a.b') (return a code object) - Default implementation: - self.toc is a dict - self.toc[name] is pos - self.lib has the code object marshal-ed at pos - """ - ispkg, pos = self.toc.get(name, (0,None)) - if pos is None: - return None - self.lib.seek(self.start + pos) - return ispkg, marshal.load(self.lib) - - ######################################################################## - # Informational methods - - def contents(self): - """Return a list of the contents. - - Default implementation assumes self.toc is a dict like object. - """ - return self.toc.keys() - - ######################################################################## - # Building - - ####### Top level method - shouldn't need overriding ####### - def build(self, path, lTOC): - """Create an archive file of name PATH from LTOC. - - lTOC is a 'logical TOC' - a list of (name, path, ...) - where name is the internal (import) name, - and path is a file to get the object from, eg './a.pyc'. - """ - self.path = path - self.lib = open(path, 'wb') - #reserve space for the header - if self.HDRLEN: - self.lib.write('\0'*self.HDRLEN) - - #create an empty toc - - if type(self.TOCTMPLT) == type({}): - self.toc = {} - else: # assume callable - self.toc = self.TOCTMPLT() - - for tocentry in lTOC: - self.add(tocentry) # the guts of the archive - - tocpos = self.lib.tell() - self.save_toc(tocpos) - if self.TRLLEN: - self.save_trailer(tocpos) - if self.HDRLEN: - self.update_headers(tocpos) - self.lib.close() - - - ####### manages keeping the internal TOC and the guts in sync ####### - def add(self, entry): - """Add an entry to the archive. - - Override this to influence the mechanics of the Archive. - Assumes entry is a seq beginning with (nm, pth, ...) where - nm is the key by which we'll be asked for the object. - pth is the name of where we find the object. - """ - if self.os is None: - import os - self.os = os - nm = entry[0] - pth = entry[1] - ispkg = self.os.path.splitext(self.os.path.basename(pth))[0] == '__init__' - self.toc[nm] = (ispkg, self.lib.tell()) - f = open(entry[1], 'rb') - f.seek(8) #skip magic and timestamp - self.lib.write(f.read()) - - def save_toc(self, tocpos): - """Save the table of contents. - - Default - toc is a dict - Gets marshaled to self.lib - """ - marshal.dump(self.toc, self.lib) - - def save_trailer(self, tocpos): - """Placeholder for Archives with trailers.""" - pass - - def update_headers(self, tocpos): - """Update any header data. - - Default header is MAGIC + Python's magic + tocpos""" - self.lib.seek(self.start) - self.lib.write(self.MAGIC) - self.lib.write(self.pymagic) - self.lib.write(struct.pack('=i', tocpos)) - -############################################################## -# -# ZlibArchive - an archive with compressed entries -# - -class ZlibArchive(Archive): - """A subclass of Archive that compresses entries with zlib - and uses a (marshalled) dict as a table of contents""" - MAGIC = 'PYZ\0' - TOCPOS = 8 - HDRLEN = 12 - TRLLEN = 0 - TOCTMPLT = {} - LEVEL = 9 - - def __init__(self, path=None, offset=0): - Archive.__init__(self, path, offset) - # dynamic import so not imported if not needed - global zlib - import zlib - - def extract(self, name): - """Get the code object for NAME. - - Return None if name is not in the table of contents. - Otherwise, return a tuple (ispkg, code)""" - (ispkg, pos, lngth) = self.toc.get(name, (0, None, 0)) - if pos is None: - return None - self.lib.seek(self.start + pos) - return ispkg, marshal.loads(zlib.decompress(self.lib.read(lngth))) - - def add(self, entry): - """Add an entry. - - ENTRY is a sequence where entry[0] is name and entry[1] is full path name. - zlib compress the code object, and build a toc entry""" - if self.os is None: - import os - self.os = os - nm = entry[0] - pth = entry[1] - ispkg = self.os.path.splitext(self.os.path.basename(pth))[0] == '__init__' - f = open(pth, 'rb') - f.seek(8) #skip magic and timestamp - obj = zlib.compress(f.read(), self.LEVEL) - self.toc[nm] = (ispkg, self.lib.tell(), len(obj)) - self.lib.write(obj) - +# +# Gordon McMillan (as inspired and influenced by Greg Stein) +# + +# subclasses may not need marshal or struct, but since they're +# builtin, importing is safe. +# +# While an Archive is really an abstraction for any "filesystem +# within a file", it is tuned for use with imputil.FuncImporter. +# This assumes it contains python code objects, indexed by the +# the internal name (ie, no '.py'). +# See carchive.py for a more general archive (contains anything) +# that can be understood by a C program. + +import marshal +import struct + +class Archive: + """ A base class for a repository of python code objects. + + The get_code method is used by imputil.FuntionImporter + to get code objects by name. + Archives are flat namespaces, so conflict between module + names in different packages are possible. Use a different + Archive for each package. + """ + MAGIC = 'PYL\0' + HDRLEN = 12 # default is MAGIC followed by python's magic, int pos of toc + TOCPOS = 8 + TRLLEN = 0 # default - no trailer + TOCTMPLT = {} # + os = None + def __init__(self, path=None, start=0): + """ + Initialize an Archive. If path is omitted, it will be an empty Archive. + start is the seek position within path where the Archive starts.""" + self.toc = None + self.path = path + self.start = start + import imp + self.pymagic = imp.get_magic() + if path is not None: + self.lib = open(self.path, 'rb') + self.checkmagic() + self.loadtoc() + + ####### Sub-methods of __init__ - override as needed ############# + def checkmagic(self): + """Verify version and validity of file. + + Overridable. + Check to see if the file object self.lib actually has a file + we understand. + """ + self.lib.seek(self.start) #default - magic is at start of file + if self.lib.read(len(self.MAGIC)) != self.MAGIC: + raise RuntimeError, "%s is not a valid %s archive file" \ + % (self.path, self.__class__.__name__) + if self.lib.read(len(self.pymagic)) != self.pymagic: + raise RuntimeError, "%s has version mismatch to dll" % (self.path) + + def loadtoc(self): + """Load the table of contents. + + Overridable. + Default: After magic comes an int (4 byte native) giving the + position of the TOC within self.lib. + Default: The TOC is a marshal-able string. + """ + self.lib.seek(self.start + self.TOCPOS) + (offset,) = struct.unpack('=i', self.lib.read(4)) + self.lib.seek(self.start + offset) + self.toc = marshal.load(self.lib) + + ######## This is what is called by FuncImporter ####### + ## Since an Archive is flat, we ignore parent and modname. + + def get_code(self, parent, modname, fqname): + """The import hook. + + Called by imputil.FunctionImporter. + Override extract to tune getting code from the Archive.""" + rslt = self.extract(fqname) # None if not found, (ispkg, code) otherwise + if rslt is None: + return None + ispkg, code = rslt + if ispkg: + return ispkg, code, {'__path__' : []} + return rslt + + ####### Core method - Override as needed ######### + def extract(self, name): + """ Get the object corresponding to name, or None. + + NAME is the name as specified in an 'import name'. + 'import a.b' will become: + extract('a') (return None because 'a' is not a code object) + extract('a.__init__') (return a code object) + extract('a.b') (return a code object) + Default implementation: + self.toc is a dict + self.toc[name] is pos + self.lib has the code object marshal-ed at pos + """ + ispkg, pos = self.toc.get(name, (0,None)) + if pos is None: + return None + self.lib.seek(self.start + pos) + return ispkg, marshal.load(self.lib) + + ######################################################################## + # Informational methods + + def contents(self): + """Return a list of the contents. + + Default implementation assumes self.toc is a dict like object. + """ + return self.toc.keys() + + ######################################################################## + # Building + + ####### Top level method - shouldn't need overriding ####### + def build(self, path, lTOC): + """Create an archive file of name PATH from LTOC. + + lTOC is a 'logical TOC' - a list of (name, path, ...) + where name is the internal (import) name, + and path is a file to get the object from, eg './a.pyc'. + """ + self.path = path + self.lib = open(path, 'wb') + #reserve space for the header + if self.HDRLEN: + self.lib.write('\0'*self.HDRLEN) + + #create an empty toc + + if type(self.TOCTMPLT) == type({}): + self.toc = {} + else: # assume callable + self.toc = self.TOCTMPLT() + + for tocentry in lTOC: + self.add(tocentry) # the guts of the archive + + tocpos = self.lib.tell() + self.save_toc(tocpos) + if self.TRLLEN: + self.save_trailer(tocpos) + if self.HDRLEN: + self.update_headers(tocpos) + self.lib.close() + + + ####### manages keeping the internal TOC and the guts in sync ####### + def add(self, entry): + """Add an entry to the archive. + + Override this to influence the mechanics of the Archive. + Assumes entry is a seq beginning with (nm, pth, ...) where + nm is the key by which we'll be asked for the object. + pth is the name of where we find the object. + """ + if self.os is None: + import os + self.os = os + nm = entry[0] + pth = entry[1] + ispkg = self.os.path.splitext(self.os.path.basename(pth))[0] == '__init__' + self.toc[nm] = (ispkg, self.lib.tell()) + f = open(entry[1], 'rb') + f.seek(8) #skip magic and timestamp + self.lib.write(f.read()) + + def save_toc(self, tocpos): + """Save the table of contents. + + Default - toc is a dict + Gets marshaled to self.lib + """ + marshal.dump(self.toc, self.lib) + + def save_trailer(self, tocpos): + """Placeholder for Archives with trailers.""" + pass + + def update_headers(self, tocpos): + """Update any header data. + + Default header is MAGIC + Python's magic + tocpos""" + self.lib.seek(self.start) + self.lib.write(self.MAGIC) + self.lib.write(self.pymagic) + self.lib.write(struct.pack('=i', tocpos)) + +############################################################## +# +# ZlibArchive - an archive with compressed entries +# + +class ZlibArchive(Archive): + """A subclass of Archive that compresses entries with zlib + and uses a (marshalled) dict as a table of contents""" + MAGIC = 'PYZ\0' + TOCPOS = 8 + HDRLEN = 12 + TRLLEN = 0 + TOCTMPLT = {} + LEVEL = 9 + + def __init__(self, path=None, offset=0): + Archive.__init__(self, path, offset) + # dynamic import so not imported if not needed + global zlib + import zlib + + def extract(self, name): + """Get the code object for NAME. + + Return None if name is not in the table of contents. + Otherwise, return a tuple (ispkg, code)""" + (ispkg, pos, lngth) = self.toc.get(name, (0, None, 0)) + if pos is None: + return None + self.lib.seek(self.start + pos) + return ispkg, marshal.loads(zlib.decompress(self.lib.read(lngth))) + + def add(self, entry): + """Add an entry. + + ENTRY is a sequence where entry[0] is name and entry[1] is full path name. + zlib compress the code object, and build a toc entry""" + if self.os is None: + import os + self.os = os + nm = entry[0] + pth = entry[1] + ispkg = self.os.path.splitext(self.os.path.basename(pth))[0] == '__init__' + f = open(pth, 'rb') + f.seek(8) #skip magic and timestamp + obj = zlib.compress(f.read(), self.LEVEL) + self.toc[nm] = (ispkg, self.lib.tell(), len(obj)) + self.lib.write(obj) + diff --git a/direct/src/pyinst/archive_rt.py b/direct/src/pyinst/archive_rt.py index baefccf726..2027e158c5 100644 --- a/direct/src/pyinst/archive_rt.py +++ b/direct/src/pyinst/archive_rt.py @@ -1,226 +1,226 @@ -# -# Gordon McMillan (as inspired and influenced by Greg Stein) -# - -# subclasses may not need marshal or struct, but since they're -# builtin, importing is safe. -# -# While an Archive is really an abstraction for any "filesystem -# within a file", it is tuned for use with imputil.FuncImporter. -# This assumes it contains python code objects, indexed by the -# the internal name (ie, no '.py'). -# See carchive.py for a more general archive (contains anything) -# that can be understood by a C program. - -#archive_rt is a stripped down version of MEInc.Dist.archive. -#It has had all building logic removed. -#It's purpose is to bootstrap the Python installation. - -import marshal -import struct - -class Archive: - """ A base class for a repository of python code objects. - The extract method is used by imputil.ArchiveImporter - to get code objects by name (fully qualified name), so - an enduser "import a.b" would become - extract('a.__init__') - extract('a.b') - """ - MAGIC = 'PYL\0' - HDRLEN = 12 # default is MAGIC followed by python's magic, int pos of toc - TOCPOS = 8 - TRLLEN = 0 # default - no trailer - TOCTMPLT = {} # - os = None - def __init__(self, path=None, start=0): - "Initialize an Archive. If path is omitted, it will be an empty Archive." - self.toc = None - self.path = path - self.start = start - import imp - self.pymagic = imp.get_magic() - if path is not None: - self.lib = open(self.path, 'rb') - self.checkmagic() - self.loadtoc() - - ####### Sub-methods of __init__ - override as needed ############# - def checkmagic(self): - """ Overridable. - Check to see if the file object self.lib actually has a file - we understand. - """ - self.lib.seek(self.start) #default - magic is at start of file - if self.lib.read(len(self.MAGIC)) != self.MAGIC: - raise RuntimeError, "%s is not a valid %s archive file" \ - % (self.path, self.__class__.__name__) - if self.lib.read(len(self.pymagic)) != self.pymagic: - raise RuntimeError, "%s has version mismatch to dll" % (self.path) - - def loadtoc(self): - """ Overridable. - Default: After magic comes an int (4 byte native) giving the - position of the TOC within self.lib. - Default: The TOC is a marshal-able string. - """ - self.lib.seek(self.start + self.TOCPOS) - (offset,) = struct.unpack('=i', self.lib.read(4)) - self.lib.seek(self.start + offset) - self.toc = marshal.load(self.lib) - - ######## This is what is called by FuncImporter ####### - ## Since an Archive is flat, we ignore parent and modname. - - def get_code(self, parent, modname, fqname): - print "parent: ", parent - print "modname: ", modname - print "fqname: ", fqname - return self.extract(fqname) # None if not found, (ispkg, code) otherwise - if rslt is None: - return None - ispkg, code = rslt - if ispkg: - return ispkg, code, {'__path__' : []} - return rslt - - ####### Core method - Override as needed ######### - def extract(self, name): - """ Get the object corresponding to name, or None. - For use with imputil ArchiveImporter, object is a python code object. - 'name' is the name as specified in an 'import name'. - 'import a.b' will become: - extract('a') (return None because 'a' is not a code object) - extract('a.__init__') (return a code object) - extract('a.b') (return a code object) - Default implementation: - self.toc is a dict - self.toc[name] is pos - self.lib has the code object marshal-ed at pos - """ - ispkg, pos = self.toc.get(name, (0,None)) - if pos is None: - return None - self.lib.seek(self.start + pos) - return ispkg, marshal.load(self.lib) - - ######################################################################## - # Informational methods - - def contents(self): - """Return a list of the contents - Default implementation assumes self.toc is a dict like object. - Not required by ArchiveImporter. - """ - return self.toc.keys() - - ######################################################################## - # Building - - ####### Top level method - shouldn't need overriding ####### -## def build(self, path, lTOC): -## """Create an archive file of name 'path'. -## lTOC is a 'logical TOC' - a list of (name, path, ...) -## where name is the internal name, eg 'a' -## and path is a file to get the object from, eg './a.pyc'. -## """ -## self.path = path -## self.lib = open(path, 'wb') -## #reserve space for the header -## if self.HDRLEN: -## self.lib.write('\0'*self.HDRLEN) -## -## #create an empty toc -## -## if type(self.TOCTMPLT) == type({}): -## self.toc = {} -## else: # assume callable -## self.toc = self.TOCTMPLT() -## -## for tocentry in lTOC: -## self.add(tocentry) # the guts of the archive -## -## tocpos = self.lib.tell() -## self.save_toc(tocpos) -## if self.TRLLEN: -## self.save_trailer(tocpos) -## if self.HDRLEN: -## self.update_headers(tocpos) -## self.lib.close() -## -## -## ####### manages keeping the internal TOC and the guts in sync ####### -## def add(self, entry): -## """Override this to influence the mechanics of the Archive. -## Assumes entry is a seq beginning with (nm, pth, ...) where -## nm is the key by which we'll be asked for the object. -## pth is the name of where we find the object. Overrides of -## get_obj_from can make use of further elements in entry. -## """ -## if self.os is None: -## import os -## self.os = os -## nm = entry[0] -## pth = entry[1] -## ispkg = self.os.path.splitext(self.os.path.basename(pth))[0] == '__init__' -## self.toc[nm] = (ispkg, self.lib.tell()) -## f = open(entry[1], 'rb') -## f.seek(8) #skip magic and timestamp -## self.lib.write(f.read()) -## -## def save_toc(self, tocpos): -## """Default - toc is a dict -## Gets marshaled to self.lib -## """ -## marshal.dump(self.toc, self.lib) -## -## def save_trailer(self, tocpos): -## """Default - not used""" -## pass -## -## def update_headers(self, tocpos): -## """Default - MAGIC + Python's magic + tocpos""" -## self.lib.seek(self.start) -## self.lib.write(self.MAGIC) -## self.lib.write(self.pymagic) -## self.lib.write(struct.pack('=i', tocpos)) - -############################################################## -# -# ZlibArchive - an archive with compressed entries -# - -class ZlibArchive(Archive): - MAGIC = 'PYZ\0' - TOCPOS = 8 - HDRLEN = 12 - TRLLEN = 0 - TOCTMPLT = {} - LEVEL = 9 - - def __init__(self, path=None, offset=0): - Archive.__init__(self, path, offset) - # dynamic import so not imported if not needed - global zlib - import zlib - - def extract(self, name): - (ispkg, pos, lngth) = self.toc.get(name, (0, None, 0)) - if pos is None: - return None - self.lib.seek(self.start + pos) - return ispkg, marshal.loads(zlib.decompress(self.lib.read(lngth))) - -## def add(self, entry): -## if self.os is None: -## import os -## self.os = os -## nm = entry[0] -## pth = entry[1] -## ispkg = self.os.path.splitext(self.os.path.basename(pth))[0] == '__init__' -## f = open(pth, 'rb') -## f.seek(8) #skip magic and timestamp -## obj = zlib.compress(f.read(), self.LEVEL) -## self.toc[nm] = (ispkg, self.lib.tell(), len(obj)) -## self.lib.write(obj) -## +# +# Gordon McMillan (as inspired and influenced by Greg Stein) +# + +# subclasses may not need marshal or struct, but since they're +# builtin, importing is safe. +# +# While an Archive is really an abstraction for any "filesystem +# within a file", it is tuned for use with imputil.FuncImporter. +# This assumes it contains python code objects, indexed by the +# the internal name (ie, no '.py'). +# See carchive.py for a more general archive (contains anything) +# that can be understood by a C program. + +#archive_rt is a stripped down version of MEInc.Dist.archive. +#It has had all building logic removed. +#It's purpose is to bootstrap the Python installation. + +import marshal +import struct + +class Archive: + """ A base class for a repository of python code objects. + The extract method is used by imputil.ArchiveImporter + to get code objects by name (fully qualified name), so + an enduser "import a.b" would become + extract('a.__init__') + extract('a.b') + """ + MAGIC = 'PYL\0' + HDRLEN = 12 # default is MAGIC followed by python's magic, int pos of toc + TOCPOS = 8 + TRLLEN = 0 # default - no trailer + TOCTMPLT = {} # + os = None + def __init__(self, path=None, start=0): + "Initialize an Archive. If path is omitted, it will be an empty Archive." + self.toc = None + self.path = path + self.start = start + import imp + self.pymagic = imp.get_magic() + if path is not None: + self.lib = open(self.path, 'rb') + self.checkmagic() + self.loadtoc() + + ####### Sub-methods of __init__ - override as needed ############# + def checkmagic(self): + """ Overridable. + Check to see if the file object self.lib actually has a file + we understand. + """ + self.lib.seek(self.start) #default - magic is at start of file + if self.lib.read(len(self.MAGIC)) != self.MAGIC: + raise RuntimeError, "%s is not a valid %s archive file" \ + % (self.path, self.__class__.__name__) + if self.lib.read(len(self.pymagic)) != self.pymagic: + raise RuntimeError, "%s has version mismatch to dll" % (self.path) + + def loadtoc(self): + """ Overridable. + Default: After magic comes an int (4 byte native) giving the + position of the TOC within self.lib. + Default: The TOC is a marshal-able string. + """ + self.lib.seek(self.start + self.TOCPOS) + (offset,) = struct.unpack('=i', self.lib.read(4)) + self.lib.seek(self.start + offset) + self.toc = marshal.load(self.lib) + + ######## This is what is called by FuncImporter ####### + ## Since an Archive is flat, we ignore parent and modname. + + def get_code(self, parent, modname, fqname): + print "parent: ", parent + print "modname: ", modname + print "fqname: ", fqname + return self.extract(fqname) # None if not found, (ispkg, code) otherwise + if rslt is None: + return None + ispkg, code = rslt + if ispkg: + return ispkg, code, {'__path__' : []} + return rslt + + ####### Core method - Override as needed ######### + def extract(self, name): + """ Get the object corresponding to name, or None. + For use with imputil ArchiveImporter, object is a python code object. + 'name' is the name as specified in an 'import name'. + 'import a.b' will become: + extract('a') (return None because 'a' is not a code object) + extract('a.__init__') (return a code object) + extract('a.b') (return a code object) + Default implementation: + self.toc is a dict + self.toc[name] is pos + self.lib has the code object marshal-ed at pos + """ + ispkg, pos = self.toc.get(name, (0,None)) + if pos is None: + return None + self.lib.seek(self.start + pos) + return ispkg, marshal.load(self.lib) + + ######################################################################## + # Informational methods + + def contents(self): + """Return a list of the contents + Default implementation assumes self.toc is a dict like object. + Not required by ArchiveImporter. + """ + return self.toc.keys() + + ######################################################################## + # Building + + ####### Top level method - shouldn't need overriding ####### +## def build(self, path, lTOC): +## """Create an archive file of name 'path'. +## lTOC is a 'logical TOC' - a list of (name, path, ...) +## where name is the internal name, eg 'a' +## and path is a file to get the object from, eg './a.pyc'. +## """ +## self.path = path +## self.lib = open(path, 'wb') +## #reserve space for the header +## if self.HDRLEN: +## self.lib.write('\0'*self.HDRLEN) +## +## #create an empty toc +## +## if type(self.TOCTMPLT) == type({}): +## self.toc = {} +## else: # assume callable +## self.toc = self.TOCTMPLT() +## +## for tocentry in lTOC: +## self.add(tocentry) # the guts of the archive +## +## tocpos = self.lib.tell() +## self.save_toc(tocpos) +## if self.TRLLEN: +## self.save_trailer(tocpos) +## if self.HDRLEN: +## self.update_headers(tocpos) +## self.lib.close() +## +## +## ####### manages keeping the internal TOC and the guts in sync ####### +## def add(self, entry): +## """Override this to influence the mechanics of the Archive. +## Assumes entry is a seq beginning with (nm, pth, ...) where +## nm is the key by which we'll be asked for the object. +## pth is the name of where we find the object. Overrides of +## get_obj_from can make use of further elements in entry. +## """ +## if self.os is None: +## import os +## self.os = os +## nm = entry[0] +## pth = entry[1] +## ispkg = self.os.path.splitext(self.os.path.basename(pth))[0] == '__init__' +## self.toc[nm] = (ispkg, self.lib.tell()) +## f = open(entry[1], 'rb') +## f.seek(8) #skip magic and timestamp +## self.lib.write(f.read()) +## +## def save_toc(self, tocpos): +## """Default - toc is a dict +## Gets marshaled to self.lib +## """ +## marshal.dump(self.toc, self.lib) +## +## def save_trailer(self, tocpos): +## """Default - not used""" +## pass +## +## def update_headers(self, tocpos): +## """Default - MAGIC + Python's magic + tocpos""" +## self.lib.seek(self.start) +## self.lib.write(self.MAGIC) +## self.lib.write(self.pymagic) +## self.lib.write(struct.pack('=i', tocpos)) + +############################################################## +# +# ZlibArchive - an archive with compressed entries +# + +class ZlibArchive(Archive): + MAGIC = 'PYZ\0' + TOCPOS = 8 + HDRLEN = 12 + TRLLEN = 0 + TOCTMPLT = {} + LEVEL = 9 + + def __init__(self, path=None, offset=0): + Archive.__init__(self, path, offset) + # dynamic import so not imported if not needed + global zlib + import zlib + + def extract(self, name): + (ispkg, pos, lngth) = self.toc.get(name, (0, None, 0)) + if pos is None: + return None + self.lib.seek(self.start + pos) + return ispkg, marshal.loads(zlib.decompress(self.lib.read(lngth))) + +## def add(self, entry): +## if self.os is None: +## import os +## self.os = os +## nm = entry[0] +## pth = entry[1] +## ispkg = self.os.path.splitext(self.os.path.basename(pth))[0] == '__init__' +## f = open(pth, 'rb') +## f.seek(8) #skip magic and timestamp +## obj = zlib.compress(f.read(), self.LEVEL) +## self.toc[nm] = (ispkg, self.lib.tell(), len(obj)) +## self.lib.write(obj) +## diff --git a/direct/src/pyinst/archivebuilder.py b/direct/src/pyinst/archivebuilder.py index 9a71a4154d..99e24cc131 100644 --- a/direct/src/pyinst/archivebuilder.py +++ b/direct/src/pyinst/archivebuilder.py @@ -1,81 +1,81 @@ -# copyright 1999 McMillan Enterprises, Inc. -# license: use as you please. No warranty. -# Gordon McMillan gmcm@hypernet.com -# -# A collection of routines for building a logical Table Of Contents -# that Archive (subclasses) use to build themselves. -# A logical Table of Contents is a sequence, each element of which is -# a sequence, with at least 2 entries - "name" and "path". - -import os - -import string - -import py_compile - -def GetCompiled(seq, lvl='c'): - """SEQ is a list of .py files, or a logical TOC. - Return as .pyc or .pyo files (LVL) after ensuring their existence""" - if len(seq) == 0: - return seq - rslt = [] - isTOC = 0 - if type(seq[0]) == type(()): - isTOC = 1 - for py in seq: - if isTOC: - (nm, fnm), rest = py[:2], py[2:] - else: - fnm = py - fnm = os.path.splitext(fnm)[0] + '.py' - cmpl = 1 - pyc = fnm + lvl - if os.path.exists(pyc): - pytm = long(os.stat(fnm)[8]) - ctm = long(os.stat(pyc)[8]) - if pytm < ctm: - cmpl = 0 - if cmpl: - py_compile.compile(fnm, pyc) - if isTOC: - rslt.append((nm, pyc)+rest) - else: - rslt.append(pyc) - return rslt - -import modulefinder -MF = modulefinder -import sys - -def Dependencies(script): - """Get a logical TOC directly from the dependencies of a script. - - The returned TOC does NOT contain the script. - It does contain extension modules. Uses modulefinder.""" - rslt = [] - (dir, name) = os.path.split(script) - if dir: - ppath = [os.path.normpath(dir)] + sys.path - else: - ppath = sys.path[:] - mf = MF.ModuleFinder(ppath, 0) - try: - mf.run_script(script) - except IOError: - print " Script not found:", script - return [] - del mf.modules['__main__'] - for (k, v) in mf.modules.items(): - if v.__file__ is None: - del mf.modules[k] # a builtin - for (k, v) in mf.modules.items(): - #ispkg = os.path.basename(v.__file__) == '__init__.py' - d = os.path.dirname(v.__file__) - if not d: - v.__file__ = os.path.join(os.getcwd(), v.__file__) - #if ispkg: - # rslt.append(k+'.__init__', v.__file__) - #else: - rslt.append( (k, v.__file__) ) - return rslt - +# copyright 1999 McMillan Enterprises, Inc. +# license: use as you please. No warranty. +# Gordon McMillan gmcm@hypernet.com +# +# A collection of routines for building a logical Table Of Contents +# that Archive (subclasses) use to build themselves. +# A logical Table of Contents is a sequence, each element of which is +# a sequence, with at least 2 entries - "name" and "path". + +import os + +import string + +import py_compile + +def GetCompiled(seq, lvl='c'): + """SEQ is a list of .py files, or a logical TOC. + Return as .pyc or .pyo files (LVL) after ensuring their existence""" + if len(seq) == 0: + return seq + rslt = [] + isTOC = 0 + if type(seq[0]) == type(()): + isTOC = 1 + for py in seq: + if isTOC: + (nm, fnm), rest = py[:2], py[2:] + else: + fnm = py + fnm = os.path.splitext(fnm)[0] + '.py' + cmpl = 1 + pyc = fnm + lvl + if os.path.exists(pyc): + pytm = long(os.stat(fnm)[8]) + ctm = long(os.stat(pyc)[8]) + if pytm < ctm: + cmpl = 0 + if cmpl: + py_compile.compile(fnm, pyc) + if isTOC: + rslt.append((nm, pyc)+rest) + else: + rslt.append(pyc) + return rslt + +import modulefinder +MF = modulefinder +import sys + +def Dependencies(script): + """Get a logical TOC directly from the dependencies of a script. + + The returned TOC does NOT contain the script. + It does contain extension modules. Uses modulefinder.""" + rslt = [] + (dir, name) = os.path.split(script) + if dir: + ppath = [os.path.normpath(dir)] + sys.path + else: + ppath = sys.path[:] + mf = MF.ModuleFinder(ppath, 0) + try: + mf.run_script(script) + except IOError: + print " Script not found:", script + return [] + del mf.modules['__main__'] + for (k, v) in mf.modules.items(): + if v.__file__ is None: + del mf.modules[k] # a builtin + for (k, v) in mf.modules.items(): + #ispkg = os.path.basename(v.__file__) == '__init__.py' + d = os.path.dirname(v.__file__) + if not d: + v.__file__ = os.path.join(os.getcwd(), v.__file__) + #if ispkg: + # rslt.append(k+'.__init__', v.__file__) + #else: + rslt.append( (k, v.__file__) ) + return rslt + diff --git a/direct/src/pyinst/bindepend.py b/direct/src/pyinst/bindepend.py index 20e4d737e3..de2487e6ae 100644 --- a/direct/src/pyinst/bindepend.py +++ b/direct/src/pyinst/bindepend.py @@ -1,169 +1,169 @@ -# copyright 1999 McMillan Enterprises, Inc. -# license: use as you please. No warranty. -# -# use dumpbin.exe (if present) to find the binary -# dependencies of an extension module. -# if dumpbin not available, pick apart the PE hdr of the binary -# while this appears to work well, it is complex and subject to -# problems with changes to PE hdrs (ie, this works only on 32 bit Intel -# Windows format binaries) -# -# Note also that you should check the results to make sure that the -# dlls are redistributable. I've listed most of the common MS dlls -# under "excludes" below; add to this list as necessary (or use the -# "excludes" option in the INSTALL section of the config file). - -import os -import time -import string -import sys -import tempfile -import finder - -seen = {} -excludes = {'KERNEL32.DLL':1, - 'ADVAPI.DLL':1, - 'MSVCRT.DLL':1, - 'ADVAPI32.DLL':1, - 'COMCTL32.DLL':1, - 'CRTDLL.DLL':1, - 'GDI32.DLL':1, - 'MFC42.DLL':1, - 'NTDLL.DLL':1, - 'OLE32.DLL':1, - 'OLEAUT32.DLL':1, - 'RPCRT4.DLL':1, - 'SHELL32.DLL':1, - 'USER32.DLL':1, - 'WINSPOOL.DRV':1, - 'WS2HELP.DLL':1, - 'WS2_32.DLL':1, - 'WSOCK32.DLL':1, - 'WINMM.DLL':1, - 'COMDLG32.DLL':1, - 'ZLIB.DLL':1, - 'ODBC32.DLL':1, - 'VERSION.DLL':1} - -def getfullnameof(mod, xtrapath = None): - """Return the full path name of MOD. - - MOD is the basename of a dll or pyd. - XTRAPATH is a path or list of paths to search first. - Return the full path name of MOD. - Will search the full Windows search path, as well as sys.path""" - epath = finder.getpath() - if mod[-4:] in ('.pyd', '.PYD'): - epath = epath + sys.path - if xtrapath is not None: - if type(xtrapath) == type(''): - epath.insert(0, xtrapath) - else: - epath = xtrapath + epath - for p in epath: - npth = os.path.join(p, mod) - if os.path.exists(npth): - return npth - return '' - -def getImports1(pth): - """Find the binary dependencies of PTH. - - This implementation (not used right now) uses the MSVC utility dumpbin""" - rslt = [] - tmpf = tempfile.mktemp() - os.system('dumpbin /IMPORTS "%s" >%s' %(pth, tmpf)) - time.sleep(0.1) - txt = open(tmpf,'r').readlines() - os.remove(tmpf) - i = 0 - while i < len(txt): - tokens = string.split(txt[i]) - if len(tokens) == 1 and string.find(tokens[0], '.') > 0: - rslt.append(string.strip(tokens[0])) - i = i + 1 - return rslt - -def getImports2(pth): - """Find the binary dependencies of PTH. - - This implementation walks through the PE header""" - import struct - rslt = [] - try: - f = open(pth, 'rb').read() - pehdrd = struct.unpack('l', f[60:64])[0] - magic = struct.unpack('l', f[pehdrd:pehdrd+4])[0] - numsecs = struct.unpack('h', f[pehdrd+6:pehdrd+8])[0] - numdirs = struct.unpack('l', f[pehdrd+116:pehdrd+120])[0] - idata = '' - if magic == 17744: - importsec, sz = struct.unpack('2l', f[pehdrd+128:pehdrd+136]) - secttbl = pehdrd + 120 + 8*numdirs - secttblfmt = '8s7l2h' - seclist = [] - for i in range(numsecs): - seclist.append(struct.unpack(secttblfmt, f[secttbl+i*40:secttbl+(i+1)*40])) - #nm, vsz, va, rsz, praw, preloc, plnnums, qrelocs, qlnnums, flags \ - # = seclist[-1] - for i in range(len(seclist)-1): - if seclist[i][2] <= importsec < seclist[i+1][2]: - break - vbase = seclist[i][2] - raw = seclist[i][4] - idatastart = raw + importsec - vbase - idata = f[idatastart:idatastart+seclist[i][1]] - i = 0 - while 1: - vsa = struct.unpack('5l', idata[i*20:i*20+20])[3] - if vsa == 0: - break - sa = raw + vsa - vbase - end = string.find(f, '\000', sa) - rslt.append(f[sa:end]) - i = i + 1 - except IOError: - print "bindepend cannot analyze %s - file not found!" - except struct.error: - print "bindepend cannot analyze %s - error walking thru pehdr" - return rslt - -def Dependencies(lTOC): - """Expand LTOC to include all the closure of binary dependencies. - - LTOC is a logical table of contents, ie, a seq of tuples (name, path). - Return LTOC expanded by all the binary dependencies of the entries - in LTOC, except those listed in the module global EXCLUDES""" - for (nm, pth) in lTOC: - fullnm = string.upper(os.path.basename(pth)) - if seen.get(string.upper(nm),0): - continue - print "analyzing", nm - seen[string.upper(nm)] = 1 - dlls = getImports(pth) - for lib in dlls: - print " found", lib - if excludes.get(string.upper(lib),0): - continue - if seen.get(string.upper(lib),0): - continue - npth = getfullnameof(lib) - if npth: - lTOC.append((lib, npth)) - else: - print " lib not found:", lib, "dependency of", - return lTOC - - -##if getfullnameof('dumpbin.exe') == '': -## def getImports(pth): -## return getImports2(pth) -##else: -## def getImports(pth): -## return getImports1(pth) - -def getImports(pth): - """Forwards to either getImports1 or getImports2 - """ - return getImports2(pth) - +# copyright 1999 McMillan Enterprises, Inc. +# license: use as you please. No warranty. +# +# use dumpbin.exe (if present) to find the binary +# dependencies of an extension module. +# if dumpbin not available, pick apart the PE hdr of the binary +# while this appears to work well, it is complex and subject to +# problems with changes to PE hdrs (ie, this works only on 32 bit Intel +# Windows format binaries) +# +# Note also that you should check the results to make sure that the +# dlls are redistributable. I've listed most of the common MS dlls +# under "excludes" below; add to this list as necessary (or use the +# "excludes" option in the INSTALL section of the config file). + +import os +import time +import string +import sys +import tempfile +import finder + +seen = {} +excludes = {'KERNEL32.DLL':1, + 'ADVAPI.DLL':1, + 'MSVCRT.DLL':1, + 'ADVAPI32.DLL':1, + 'COMCTL32.DLL':1, + 'CRTDLL.DLL':1, + 'GDI32.DLL':1, + 'MFC42.DLL':1, + 'NTDLL.DLL':1, + 'OLE32.DLL':1, + 'OLEAUT32.DLL':1, + 'RPCRT4.DLL':1, + 'SHELL32.DLL':1, + 'USER32.DLL':1, + 'WINSPOOL.DRV':1, + 'WS2HELP.DLL':1, + 'WS2_32.DLL':1, + 'WSOCK32.DLL':1, + 'WINMM.DLL':1, + 'COMDLG32.DLL':1, + 'ZLIB.DLL':1, + 'ODBC32.DLL':1, + 'VERSION.DLL':1} + +def getfullnameof(mod, xtrapath = None): + """Return the full path name of MOD. + + MOD is the basename of a dll or pyd. + XTRAPATH is a path or list of paths to search first. + Return the full path name of MOD. + Will search the full Windows search path, as well as sys.path""" + epath = finder.getpath() + if mod[-4:] in ('.pyd', '.PYD'): + epath = epath + sys.path + if xtrapath is not None: + if type(xtrapath) == type(''): + epath.insert(0, xtrapath) + else: + epath = xtrapath + epath + for p in epath: + npth = os.path.join(p, mod) + if os.path.exists(npth): + return npth + return '' + +def getImports1(pth): + """Find the binary dependencies of PTH. + + This implementation (not used right now) uses the MSVC utility dumpbin""" + rslt = [] + tmpf = tempfile.mktemp() + os.system('dumpbin /IMPORTS "%s" >%s' %(pth, tmpf)) + time.sleep(0.1) + txt = open(tmpf,'r').readlines() + os.remove(tmpf) + i = 0 + while i < len(txt): + tokens = string.split(txt[i]) + if len(tokens) == 1 and string.find(tokens[0], '.') > 0: + rslt.append(string.strip(tokens[0])) + i = i + 1 + return rslt + +def getImports2(pth): + """Find the binary dependencies of PTH. + + This implementation walks through the PE header""" + import struct + rslt = [] + try: + f = open(pth, 'rb').read() + pehdrd = struct.unpack('l', f[60:64])[0] + magic = struct.unpack('l', f[pehdrd:pehdrd+4])[0] + numsecs = struct.unpack('h', f[pehdrd+6:pehdrd+8])[0] + numdirs = struct.unpack('l', f[pehdrd+116:pehdrd+120])[0] + idata = '' + if magic == 17744: + importsec, sz = struct.unpack('2l', f[pehdrd+128:pehdrd+136]) + secttbl = pehdrd + 120 + 8*numdirs + secttblfmt = '8s7l2h' + seclist = [] + for i in range(numsecs): + seclist.append(struct.unpack(secttblfmt, f[secttbl+i*40:secttbl+(i+1)*40])) + #nm, vsz, va, rsz, praw, preloc, plnnums, qrelocs, qlnnums, flags \ + # = seclist[-1] + for i in range(len(seclist)-1): + if seclist[i][2] <= importsec < seclist[i+1][2]: + break + vbase = seclist[i][2] + raw = seclist[i][4] + idatastart = raw + importsec - vbase + idata = f[idatastart:idatastart+seclist[i][1]] + i = 0 + while 1: + vsa = struct.unpack('5l', idata[i*20:i*20+20])[3] + if vsa == 0: + break + sa = raw + vsa - vbase + end = string.find(f, '\000', sa) + rslt.append(f[sa:end]) + i = i + 1 + except IOError: + print "bindepend cannot analyze %s - file not found!" + except struct.error: + print "bindepend cannot analyze %s - error walking thru pehdr" + return rslt + +def Dependencies(lTOC): + """Expand LTOC to include all the closure of binary dependencies. + + LTOC is a logical table of contents, ie, a seq of tuples (name, path). + Return LTOC expanded by all the binary dependencies of the entries + in LTOC, except those listed in the module global EXCLUDES""" + for (nm, pth) in lTOC: + fullnm = string.upper(os.path.basename(pth)) + if seen.get(string.upper(nm),0): + continue + print "analyzing", nm + seen[string.upper(nm)] = 1 + dlls = getImports(pth) + for lib in dlls: + print " found", lib + if excludes.get(string.upper(lib),0): + continue + if seen.get(string.upper(lib),0): + continue + npth = getfullnameof(lib) + if npth: + lTOC.append((lib, npth)) + else: + print " lib not found:", lib, "dependency of", + return lTOC + + +##if getfullnameof('dumpbin.exe') == '': +## def getImports(pth): +## return getImports2(pth) +##else: +## def getImports(pth): +## return getImports1(pth) + +def getImports(pth): + """Forwards to either getImports1 or getImports2 + """ + return getImports2(pth) + diff --git a/direct/src/pyinst/carchive.py b/direct/src/pyinst/carchive.py index c56056036e..1da8c6acc1 100644 --- a/direct/src/pyinst/carchive.py +++ b/direct/src/pyinst/carchive.py @@ -1,204 +1,204 @@ -# copyright 1999 McMillan Enterprises, Inc. -# license: use as you please. No warranty. -# -# A subclass of Archive that can be understood -# by a C program. See uplaunch.cpp for unpacking -# from C. -import archive -import struct -import zlib -import strop - -class CTOC: - """A class encapsulating the table of contents of a CArchive. - - When written to disk, it is easily read from C.""" - ENTRYSTRUCT = 'iiiibc' #(structlen, dpos, dlen, ulen, flag, typcd) followed by name - def __init__(self): - self.data = [] - - def frombinary(self, s): - """Decode the binary string into an in memory list. - - S is a binary string.""" - entrylen = struct.calcsize(self.ENTRYSTRUCT) - p = 0 - while p -1: - typcd = 'M' - self.toc.add(where, dlen, ulen, flag, typcd, nm) - self.lib.write(s) - - def save_toc(self, tocpos): - """Save the table of contents to disk.""" - self.tocpos = tocpos - tocstr = self.toc.tobinary() - self.toclen = len(tocstr) - self.lib.write(tocstr) - - def save_trailer(self, tocpos): - """Save the trailer to disk. - - CArchives can be opened from the end - the trailer points - back to the start. """ - totallen = tocpos + self.toclen + self.TRLLEN - trl = struct.pack(self.TRLSTRUCT, self.MAGIC, totallen, - tocpos, self.toclen) - self.lib.write(trl) - - def openEmbedded(self, name): - """Open a CArchive of name NAME embedded within this CArchive.""" - ndx = self.toc.find(name) - if ndx == -1: - raise KeyError, "Member '%s' not found in %s" % (name, self.path) - (dpos, dlen, ulen, flag, typcd, nm) = self.toc.get(ndx) - if flag: - raise ValueError, "Cannot open compressed archive %s in place" - return CArchive(self.path, self.pkgstart+dpos, dlen) +# copyright 1999 McMillan Enterprises, Inc. +# license: use as you please. No warranty. +# +# A subclass of Archive that can be understood +# by a C program. See uplaunch.cpp for unpacking +# from C. +import archive +import struct +import zlib +import strop + +class CTOC: + """A class encapsulating the table of contents of a CArchive. + + When written to disk, it is easily read from C.""" + ENTRYSTRUCT = 'iiiibc' #(structlen, dpos, dlen, ulen, flag, typcd) followed by name + def __init__(self): + self.data = [] + + def frombinary(self, s): + """Decode the binary string into an in memory list. + + S is a binary string.""" + entrylen = struct.calcsize(self.ENTRYSTRUCT) + p = 0 + while p -1: + typcd = 'M' + self.toc.add(where, dlen, ulen, flag, typcd, nm) + self.lib.write(s) + + def save_toc(self, tocpos): + """Save the table of contents to disk.""" + self.tocpos = tocpos + tocstr = self.toc.tobinary() + self.toclen = len(tocstr) + self.lib.write(tocstr) + + def save_trailer(self, tocpos): + """Save the trailer to disk. + + CArchives can be opened from the end - the trailer points + back to the start. """ + totallen = tocpos + self.toclen + self.TRLLEN + trl = struct.pack(self.TRLSTRUCT, self.MAGIC, totallen, + tocpos, self.toclen) + self.lib.write(trl) + + def openEmbedded(self, name): + """Open a CArchive of name NAME embedded within this CArchive.""" + ndx = self.toc.find(name) + if ndx == -1: + raise KeyError, "Member '%s' not found in %s" % (name, self.path) + (dpos, dlen, ulen, flag, typcd, nm) = self.toc.get(ndx) + if flag: + raise ValueError, "Cannot open compressed archive %s in place" + return CArchive(self.path, self.pkgstart+dpos, dlen) diff --git a/direct/src/pyinst/carchive_rt.py b/direct/src/pyinst/carchive_rt.py index 33ab19b3da..c58d83eb18 100644 --- a/direct/src/pyinst/carchive_rt.py +++ b/direct/src/pyinst/carchive_rt.py @@ -1,157 +1,157 @@ -# copyright 1999 McMillan Enterprises, Inc. -# license: use as you please. No warranty. -# -# A subclass of Archive that can be understood -# by a C program. See uplaunch.cpp for unpacking -# from C. - -#carchive_rt is a stripped down version of MEInc.Dist.carchive. -#It has had all building logic removed. -#It's purpose is to bootstrap the Python installation. - -import archive_rt -import struct -import zlib -import strop - -class CTOC: - ENTRYSTRUCT = 'iiiibc' #(structlen, dpos, dlen, ulen, flag, typcd) followed by name - def __init__(self): - self.data = [] - - def frombinary(self, s): - entrylen = struct.calcsize(self.ENTRYSTRUCT) - p = 0 - while p -1: -## typcd = 'M' -## self.toc.add(where, dlen, ulen, flag, typcd, nm) -## self.lib.write(s) -## -## def save_toc(self, tocpos): -## self.tocpos = tocpos -## tocstr = self.toc.tobinary() -## self.toclen = len(tocstr) -## self.lib.write(tocstr) -## -## def save_trailer(self, tocpos): -## totallen = tocpos + self.toclen + self.TRLLEN -## trl = struct.pack(self.TRLSTRUCT, self.MAGIC, totallen, -## tocpos, self.toclen) -## self.lib.write(trl) - - def openEmbedded(self, name): - ndx = self.toc.find(name) - if ndx == -1: - raise KeyError, "Member '%s' not found in %s" % (name, self.path) - (dpos, dlen, ulen, flag, typcd, nm) = self.toc.get(ndx) - if flag: - raise ValueError, "Cannot open compressed archive %s in place" - return CArchive(self.path, self.pkgstart+dpos, dlen) +# copyright 1999 McMillan Enterprises, Inc. +# license: use as you please. No warranty. +# +# A subclass of Archive that can be understood +# by a C program. See uplaunch.cpp for unpacking +# from C. + +#carchive_rt is a stripped down version of MEInc.Dist.carchive. +#It has had all building logic removed. +#It's purpose is to bootstrap the Python installation. + +import archive_rt +import struct +import zlib +import strop + +class CTOC: + ENTRYSTRUCT = 'iiiibc' #(structlen, dpos, dlen, ulen, flag, typcd) followed by name + def __init__(self): + self.data = [] + + def frombinary(self, s): + entrylen = struct.calcsize(self.ENTRYSTRUCT) + p = 0 + while p -1: +## typcd = 'M' +## self.toc.add(where, dlen, ulen, flag, typcd, nm) +## self.lib.write(s) +## +## def save_toc(self, tocpos): +## self.tocpos = tocpos +## tocstr = self.toc.tobinary() +## self.toclen = len(tocstr) +## self.lib.write(tocstr) +## +## def save_trailer(self, tocpos): +## totallen = tocpos + self.toclen + self.TRLLEN +## trl = struct.pack(self.TRLSTRUCT, self.MAGIC, totallen, +## tocpos, self.toclen) +## self.lib.write(trl) + + def openEmbedded(self, name): + ndx = self.toc.find(name) + if ndx == -1: + raise KeyError, "Member '%s' not found in %s" % (name, self.path) + (dpos, dlen, ulen, flag, typcd, nm) = self.toc.get(ndx) + if flag: + raise ValueError, "Cannot open compressed archive %s in place" + return CArchive(self.path, self.pkgstart+dpos, dlen) diff --git a/direct/src/pyinst/finder.py b/direct/src/pyinst/finder.py index e64e80935f..830a5d3b00 100644 --- a/direct/src/pyinst/finder.py +++ b/direct/src/pyinst/finder.py @@ -1,178 +1,178 @@ -# copyright McMillan Enterprises, 1999 -import os, sys -import string - -SCRIPT = 1 -GSCRIPT = 2 -MODULE = 3 -PACKAGE = 4 -PBINARY = 5 -BINARY = 6 -ZLIB = 7 -DIRECTORY = 8 -DATA = 9 - -_bpath = None -_ppath = None -_pcache = {} - -def _locate(nm, xtrapath=None, base=None): - """Find a file / directory named NM in likely places. - - XTRAPATH is a list of paths to prepend to BASE. - If BASE is None, sys.path (as extended by packages) is used.""" - ppath = base - if base is None: - ppath = _ppath - if xtrapath: - ppath = xtrapath + ppath - for pth in ppath: - fullnm = os.path.join(pth, nm) - #print " _locate trying", fullnm - if os.path.exists(fullnm): - break - else: - return '' - return fullnm - -def _locatepython(name, xtrapath=None): - """Locate a Python resource named NAME. - - All of the standard file extensions will be tried. - XTRAPATH is prepended to sys.path.""" - for ext in ('.py', '.pyc', '.pyw', '.pyo', '.pyd', '.dll'): - fullnm = _locate(name+ext, xtrapath) - if fullnm: - break - else: - for ext in ('.pyd', '.dll'): - fullnm = _locate(name+ext, [], _bpath) - if fullnm: - break - return fullnm - -def ispackage(name): - """Determine if NAME is the name of a package.""" - if os.path.exists(os.path.join(name, '__init__.py')): - return 1 - if os.path.exists(os.path.join(name, '__init__.pyc')): - return 1 - if os.path.exists(os.path.join(name, '__init__.pyo')): - return 1 - return 0 - -def idtype(fullnm): - """Figure out what type of resource FULLNM refers to.""" - if os.path.isdir(fullnm): - if ispackage(fullnm): - return PACKAGE - return DIRECTORY - ext = os.path.splitext(fullnm)[1] - if ext: - if ext == '.pyd': - return PBINARY - if ext == '.dll': - return BINARY - if ext in ('.pyc', '.pyo'): - return MODULE - if ext == '.py': - return SCRIPT - if ext == '.pyw': - return GSCRIPT - if ext == '.pyz': - return ZLIB - return DATA - -def identify(name, xtrapath=None): - """Find, and identify the type of NAME, using XTRAPATH as the - first place to look. - - Return type, name and full path name. - NAME can be a logical or physical name. However, the logical - name of a Python module can easily conflict with the physical - name of something else, so beware.""" - if os.path.exists(name): - fullnm = name - else: - if xtrapath is None: - xtra = [] - elif _pcache.has_key(id(xtrapath)): - xtra = _pcache[id(xtrapath)] - else: - xtra = expand(xtrapath) - _pcache[id(xtrapath)] = xtra - fullnm = _locate(name, xtra) - if not fullnm: - fullnm = _locate(name, [], _bpath) - if not fullnm: - ext = os.path.splitext(name)[1] - if not ext: - fullnm = _locatepython(name, xtra) - if not fullnm: - raise ValueError, "%s not found" % name - else: - nm = name - while string.count(nm, '.'): - nm = string.replace(nm, '.', '/', 1) - fullnm = _locatepython(nm, xtra) - if fullnm: - break - else: - raise ValueError, "%s not found" % name - - typ = idtype(fullnm) - nm = name - if typ in (GSCRIPT, SCRIPT, MODULE, PACKAGE, PBINARY): - dir, nm = os.path.split(fullnm) - nm = os.path.splitext(nm)[0] - if typ == SCRIPT: - if os.path.exists(fullnm+'c') or os.path.exists(fullnm+'o'): - typ = MODULE - if typ in (MODULE, PACKAGE): - while idtype(dir) == PACKAGE: - dir, lnode = os.path.split(dir) - nm = lnode+'.'+nm - elif typ == BINARY: - nm = os.path.basename(fullnm) - return typ, nm, fullnm - -def expand(plist): - """ expand a list of paths (like sys.path) to include all the - directories that qualify as packages """ - pkgdirs = [] - for pth in plist: - os.path.walk(pth, pkgfinder, pkgdirs) - return plist + pkgdirs - -def pkgfinder(pkgdirs, dir, fnms): - i = 0 - while i < len(fnms): - fnm = os.path.join(dir, fnms[i]) - if os.path.isdir(fnm): - if ispackage(fnm): - pkgdirs.append(fnm) - i = i + 1 - else: - del fnms[i] - else: - i = i + 1 - -if _bpath is None: - try: - import win32api - except ImportError: - print "Cannot determine your Windows or System directories" - print "Please add them to your PATH if .dlls are not found" - _bpath = [] - else: - sysdir = win32api.GetSystemDirectory() - sysdir2 = os.path.join(sysdir, '../SYSTEM') - windir = win32api.GetWindowsDirectory() - _bpath = [sysdir, sysdir2, windir] - _bpath.extend(string.split(os.environ.get('PATH', ''), ';')) -if _ppath is None: - _ppath = expand(sys.path) - -def getpath(): - """Return the path that Windows will search for dlls.""" - return _bpath +# copyright McMillan Enterprises, 1999 +import os, sys +import string + +SCRIPT = 1 +GSCRIPT = 2 +MODULE = 3 +PACKAGE = 4 +PBINARY = 5 +BINARY = 6 +ZLIB = 7 +DIRECTORY = 8 +DATA = 9 + +_bpath = None +_ppath = None +_pcache = {} + +def _locate(nm, xtrapath=None, base=None): + """Find a file / directory named NM in likely places. + + XTRAPATH is a list of paths to prepend to BASE. + If BASE is None, sys.path (as extended by packages) is used.""" + ppath = base + if base is None: + ppath = _ppath + if xtrapath: + ppath = xtrapath + ppath + for pth in ppath: + fullnm = os.path.join(pth, nm) + #print " _locate trying", fullnm + if os.path.exists(fullnm): + break + else: + return '' + return fullnm + +def _locatepython(name, xtrapath=None): + """Locate a Python resource named NAME. + + All of the standard file extensions will be tried. + XTRAPATH is prepended to sys.path.""" + for ext in ('.py', '.pyc', '.pyw', '.pyo', '.pyd', '.dll'): + fullnm = _locate(name+ext, xtrapath) + if fullnm: + break + else: + for ext in ('.pyd', '.dll'): + fullnm = _locate(name+ext, [], _bpath) + if fullnm: + break + return fullnm + +def ispackage(name): + """Determine if NAME is the name of a package.""" + if os.path.exists(os.path.join(name, '__init__.py')): + return 1 + if os.path.exists(os.path.join(name, '__init__.pyc')): + return 1 + if os.path.exists(os.path.join(name, '__init__.pyo')): + return 1 + return 0 + +def idtype(fullnm): + """Figure out what type of resource FULLNM refers to.""" + if os.path.isdir(fullnm): + if ispackage(fullnm): + return PACKAGE + return DIRECTORY + ext = os.path.splitext(fullnm)[1] + if ext: + if ext == '.pyd': + return PBINARY + if ext == '.dll': + return BINARY + if ext in ('.pyc', '.pyo'): + return MODULE + if ext == '.py': + return SCRIPT + if ext == '.pyw': + return GSCRIPT + if ext == '.pyz': + return ZLIB + return DATA + +def identify(name, xtrapath=None): + """Find, and identify the type of NAME, using XTRAPATH as the + first place to look. + + Return type, name and full path name. + NAME can be a logical or physical name. However, the logical + name of a Python module can easily conflict with the physical + name of something else, so beware.""" + if os.path.exists(name): + fullnm = name + else: + if xtrapath is None: + xtra = [] + elif _pcache.has_key(id(xtrapath)): + xtra = _pcache[id(xtrapath)] + else: + xtra = expand(xtrapath) + _pcache[id(xtrapath)] = xtra + fullnm = _locate(name, xtra) + if not fullnm: + fullnm = _locate(name, [], _bpath) + if not fullnm: + ext = os.path.splitext(name)[1] + if not ext: + fullnm = _locatepython(name, xtra) + if not fullnm: + raise ValueError, "%s not found" % name + else: + nm = name + while string.count(nm, '.'): + nm = string.replace(nm, '.', '/', 1) + fullnm = _locatepython(nm, xtra) + if fullnm: + break + else: + raise ValueError, "%s not found" % name + + typ = idtype(fullnm) + nm = name + if typ in (GSCRIPT, SCRIPT, MODULE, PACKAGE, PBINARY): + dir, nm = os.path.split(fullnm) + nm = os.path.splitext(nm)[0] + if typ == SCRIPT: + if os.path.exists(fullnm+'c') or os.path.exists(fullnm+'o'): + typ = MODULE + if typ in (MODULE, PACKAGE): + while idtype(dir) == PACKAGE: + dir, lnode = os.path.split(dir) + nm = lnode+'.'+nm + elif typ == BINARY: + nm = os.path.basename(fullnm) + return typ, nm, fullnm + +def expand(plist): + """ expand a list of paths (like sys.path) to include all the + directories that qualify as packages """ + pkgdirs = [] + for pth in plist: + os.path.walk(pth, pkgfinder, pkgdirs) + return plist + pkgdirs + +def pkgfinder(pkgdirs, dir, fnms): + i = 0 + while i < len(fnms): + fnm = os.path.join(dir, fnms[i]) + if os.path.isdir(fnm): + if ispackage(fnm): + pkgdirs.append(fnm) + i = i + 1 + else: + del fnms[i] + else: + i = i + 1 + +if _bpath is None: + try: + import win32api + except ImportError: + print "Cannot determine your Windows or System directories" + print "Please add them to your PATH if .dlls are not found" + _bpath = [] + else: + sysdir = win32api.GetSystemDirectory() + sysdir2 = os.path.join(sysdir, '../SYSTEM') + windir = win32api.GetWindowsDirectory() + _bpath = [sysdir, sysdir2, windir] + _bpath.extend(string.split(os.environ.get('PATH', ''), ';')) +if _ppath is None: + _ppath = expand(sys.path) + +def getpath(): + """Return the path that Windows will search for dlls.""" + return _bpath diff --git a/direct/src/pyinst/icon.py b/direct/src/pyinst/icon.py index 9a0a8104d4..82601694c3 100644 --- a/direct/src/pyinst/icon.py +++ b/direct/src/pyinst/icon.py @@ -1,9 +1,9 @@ -# This code is courtesy of Thomas Heller, who -# has kindly donated it to this project. -RT_ICON = 3 -RT_GROUP_ICON = 14 -LOAD_LIBRARY_AS_DATAFILE = 2 - +# This code is courtesy of Thomas Heller, who +# has kindly donated it to this project. +RT_ICON = 3 +RT_GROUP_ICON = 14 +LOAD_LIBRARY_AS_DATAFILE = 2 + import struct class Structure: diff --git a/direct/src/pyinst/imputil.py b/direct/src/pyinst/imputil.py index bf7df384f0..3b1e3e39ea 100644 --- a/direct/src/pyinst/imputil.py +++ b/direct/src/pyinst/imputil.py @@ -1,487 +1,487 @@ -# -# imputil.py -# -# Written by Greg Stein. Public Domain. -# No Copyright, no Rights Reserved, and no Warranties. -# -# Utilities to help out with custom import mechanisms. -# -# Additional modifications were contribed by Marc-Andre Lemburg and -# Gordon McMillan. -# - -__version__ = '0.3' - -# note: avoid importing non-builtin modules -import imp -import sys -import strop -import __builtin__ ### why this instead of just using __builtins__ ?? - -# for the DirectoryImporter -import struct -import marshal - -class Importer: - "Base class for replacing standard import functions." - - def install(self): - self.__chain_import = __builtin__.__import__ - self.__chain_reload = __builtin__.reload - __builtin__.__import__ = self._import_hook - __builtin__.reload = self._reload_hook - - ###################################################################### - # - # PRIVATE METHODS - # - def _import_hook(self, name, globals=None, locals=None, fromlist=None): - """Python calls this hook to locate and import a module. - - This method attempts to load the (dotted) module name. If it cannot - find it, then it delegates the import to the next import hook in the - chain (where "next" is defined as the import hook that was in place - at the time this Importer instance was installed). - """ - - # determine the context of this import - parent = self._determine_import_context(globals) - - # import the module within the context, or from the default context - top, tail = self._import_top_module(parent, name) - if top is None: - # the module was not found; delegate to the next import hook - return self.__chain_import(name, globals, locals, fromlist) - - # the top module may be under the control of a different importer. - # if so, then defer to that importer for completion of the import. - # note it may be self, or is undefined so we (self) may as well - # finish the import. - importer = top.__dict__.get('__importer__', self) - return importer._finish_import(top, tail, fromlist) - - def _finish_import(self, top, tail, fromlist): - # if "a.b.c" was provided, then load the ".b.c" portion down from - # below the top-level module. - bottom = self._load_tail(top, tail) - - # if the form is "import a.b.c", then return "a" - if not fromlist: - # no fromlist: return the top of the import tree - return top - - # the top module was imported by self, or it was not imported through - # the Importer mechanism and self is simply handling the import of - # the sub-modules and fromlist. - # - # this means that the bottom module was also imported by self, or we - # are handling things in the absence of a prior Importer - # - # ### why the heck are we handling it? what is the example scenario - # ### where this happens? note that we can't determine is_package() - # ### for non-Importer modules. - # - # since we imported/handled the bottom module, this means that we can - # also handle its fromlist (and reliably determine is_package()). - - # if the bottom node is a package, then (potentially) import some modules. - # - # note: if it is not a package, then "fromlist" refers to names in - # the bottom module rather than modules. - # note: for a mix of names and modules in the fromlist, we will - # import all modules and insert those into the namespace of - # the package module. Python will pick up all fromlist names - # from the bottom (package) module; some will be modules that - # we imported and stored in the namespace, others are expected - # to be present already. - if self._is_package(bottom.__dict__): - self._import_fromlist(bottom, fromlist) - - # if the form is "from a.b import c, d" then return "b" - return bottom - - def _reload_hook(self, module): - "Python calls this hook to reload a module." - - # reloading of a module may or may not be possible (depending on the - # importer), but at least we can validate that it's ours to reload - importer = module.__dict__.get('__importer__', None) - if importer is not self: - return self.__chain_reload(module) - - # okay. it is ours, but we don't know what to do (yet) - ### we should blast the module dict and do another get_code(). need to - ### flesh this out and add proper docco... - raise SystemError, "reload not yet implemented" - - def _determine_import_context(self, globals): - """Returns the context in which a module should be imported. - - The context could be a loaded (package) module and the imported module - will be looked for within that package. The context could also be None, - meaning there is no context -- the module should be looked for as a - "top-level" module. - """ - - if not globals or \ - globals.get('__importer__', None) is not self: - # globals does not refer to one of our modules or packages. - # That implies there is no relative import context, and it - # should just pick it off the standard path. - return None - - # The globals refer to a module or package of ours. It will define - # the context of the new import. Get the module/package fqname. - parent_fqname = globals['__name__'] - - # for a package, return itself (imports refer to pkg contents) - if self._is_package(globals): - parent = sys.modules[parent_fqname] - assert globals is parent.__dict__ - return parent - - i = strop.rfind(parent_fqname, '.') - - # a module outside of a package has no particular import context - if i == -1: - return None - - # for a module in a package, return the package (imports refer to siblings) - parent_fqname = parent_fqname[:i] - parent = sys.modules[parent_fqname] - assert parent.__name__ == parent_fqname - return parent - - def _import_top_module(self, parent, name): - """Locate the top of the import tree (relative or absolute). - - parent defines the context in which the import should occur. See - _determine_import_context() for details. - - Returns a tuple (module, tail). module is the loaded (top-level) module, - or None if the module is not found. tail is the remaining portion of - the dotted name. - """ - i = strop.find(name, '.') - if i == -1: - head = name - tail = "" - else: - head = name[:i] - tail = name[i+1:] - if parent: - fqname = "%s.%s" % (parent.__name__, head) - else: - fqname = head - module = self._import_one(parent, head, fqname) - if module: - # the module was relative, or no context existed (the module was - # simply found on the path). - return module, tail - if parent: - # we tried relative, now try an absolute import (from the path) - module = self._import_one(None, head, head) - if module: - return module, tail - - # the module wasn't found - return None, None - - def _import_one(self, parent, modname, fqname): - "Import a single module." - - # has the module already been imported? - try: - return sys.modules[fqname] - except KeyError: - pass - - # load the module's code, or fetch the module itself - result = self.get_code(parent, modname, fqname) - if result is None: - return None - - # did get_code() return an actual module? (rather than a code object) - is_module = type(result[1]) is type(sys) - - # use the returned module, or create a new one to exec code into - if is_module: - module = result[1] - else: - module = imp.new_module(fqname) - - ### record packages a bit differently?? - module.__importer__ = self - module.__ispkg__ = result[0] - - # if present, the third item is a set of values to insert into the module - if len(result) > 2: - module.__dict__.update(result[2]) - - # the module is almost ready... make it visible - sys.modules[fqname] = module - - # execute the code within the module's namespace - if not is_module: - exec result[1] in module.__dict__ - - # insert the module into its parent - if parent: - setattr(parent, modname, module) - return module - - def _load_tail(self, m, tail): - """Import the rest of the modules, down from the top-level module. - - Returns the last module in the dotted list of modules. - """ - if tail: - for part in strop.splitfields(tail, '.'): - fqname = "%s.%s" % (m.__name__, part) - m = self._import_one(m, part, fqname) - if not m: - raise ImportError, "No module named " + fqname - return m - - def _import_fromlist(self, package, fromlist): - 'Import any sub-modules in the "from" list.' - - # if '*' is present in the fromlist, then look for the '__all__' variable - # to find additional items (modules) to import. - if '*' in fromlist: - fromlist = list(fromlist) + list(package.__dict__.get('__all__', [])) - - for sub in fromlist: - # if the name is already present, then don't try to import it (it - # might not be a module!). - if sub != '*' and not hasattr(package, sub): - subname = "%s.%s" % (package.__name__, sub) - submod = self._import_one(package, sub, subname) - if not submod: - raise ImportError, "cannot import name " + subname - - def _is_package(self, module_dict): - """Determine if a given module (dictionary) specifies a package. - - The package status is in the module-level name __ispkg__. The module - must also have been imported by self, so that we can reliably apply - semantic meaning to __ispkg__. - - ### weaken the test to issubclass(Importer)? - """ - return module_dict.get('__importer__', None) is self and \ - module_dict['__ispkg__'] - - ###################################################################### - # - # METHODS TO OVERRIDE - # - def get_code(self, parent, modname, fqname): - """Find and retrieve the code for the given module. - - parent specifies a parent module to define a context for importing. It - may be None, indicating no particular context for the search. - - modname specifies a single module (not dotted) within the parent. - - fqname specifies the fully-qualified module name. This is a (potentially) - dotted name from the "root" of the module namespace down to the modname. - If there is no parent, then modname==fqname. - - This method should return None, a 2-tuple, or a 3-tuple. - - * If the module was not found, then None should be returned. - - * The first item of the 2- or 3-tuple should be the integer 0 or 1, - specifying whether the module that was found is a package or not. - - * The second item is the code object for the module (it will be - executed within the new module's namespace). This item can also - be a fully-loaded module object (e.g. loaded from a shared lib). - - * If present, the third item is a dictionary of name/value pairs that - will be inserted into new module before the code object is executed. - This provided in case the module's code expects certain values (such - as where the module was found). When the second item is a module - object, then these names/values will be inserted *after* the module - has been loaded/initialized. - """ - raise RuntimeError, "get_code not implemented" - - -###################################################################### -# -# Simple function-based importer -# -class FuncImporter(Importer): - "Importer subclass to use a supplied function rather than method overrides." - def __init__(self, func): - self.func = func - def get_code(self, parent, modname, fqname): - return self.func(parent, modname, fqname) - -def install_with(func): - FuncImporter(func).install() - - -###################################################################### -# -# Base class for archive-based importing -# -class PackageArchiveImporter(Importer): - "Importer subclass to import from (file) archives." - - def get_code(self, parent, modname, fqname): - if parent: - # if a parent "package" is provided, then we are importing a sub-file - # from the archive. - result = self.get_subfile(parent.__archive__, modname) - if result is None: - return None - if type(result) == type(()): - return (0,) + result - return 0, result - - # no parent was provided, so the archive should exist somewhere on the - # default "path". - archive = self.get_archive(modname) - if archive is None: - return None - return 1, "", {'__archive__':archive} - - def get_archive(self, modname): - """Get an archive of modules. - - This method should locate an archive and return a value which can be - used by get_subfile to load modules from it. The value may be a simple - pathname, an open file, or a complex object that caches information - for future imports. - - Return None if the archive was not found. - """ - raise RuntimeError, "get_archive not implemented" - - def get_subfile(self, archive, modname): - """Get code from a subfile in the specified archive. - - Given the specified archive (as returned by get_archive()), locate - and return a code object for the specified module name. - - A 2-tuple may be returned, consisting of a code object and a dict - of name/values to place into the target module. - - Return None if the subfile was not found. - """ - raise RuntimeError, "get_subfile not implemented" - - -class PackageArchive(PackageArchiveImporter): - "PackageArchiveImporter subclass that refers to a specific archive." - - def __init__(self, modname, archive_pathname): - self.__modname = modname - self.__path = archive_pathname - - def get_archive(self, modname): - if modname == self.__modname: - return self.__path - return None - - # get_subfile is passed the full pathname of the archive - - -###################################################################### -# -# Emulate the standard directory-based import mechanism -# - -class DirectoryImporter(Importer): - "Importer subclass to emulate the standard importer." - - def __init__(self, dir): - self.dir = dir - self.ext_char = __debug__ and 'c' or 'o' - self.ext = '.py' + self.ext_char - - def get_code(self, parent, modname, fqname): - if parent: - dir = parent.__pkgdir__ - else: - dir = self.dir - - # pull the os module from our instance data. we don't do this at the - # top-level, because it isn't a builtin module (and we want to defer - # loading non-builtins until as late as possible). - try: - os = self.os - except AttributeError: - import os - self.os = os - - pathname = os.path.join(dir, modname) - if os.path.isdir(pathname): - values = { '__pkgdir__' : pathname } - ispkg = 1 - pathname = os.path.join(pathname, '__init__') - else: - values = { } - ispkg = 0 - - t_py = self._timestamp(pathname + '.py') - t_pyc = self._timestamp(pathname + self.ext) - if t_py is None and t_pyc is None: - return None - code = None - if t_py is None or (t_pyc is not None and t_pyc >= t_py): - f = open(pathname + self.ext, 'rb') - if f.read(4) == imp.get_magic(): - t = struct.unpack('' % (self.__class__.__module__, - self.__class__.__name__, - self.dir, - id(self)) - -def _test_dir(): - "Debug/test function to create DirectoryImporters from sys.path." - path = sys.path[:] - path.reverse() - for d in path: - DirectoryImporter(d).install() - -###################################################################### +# +# imputil.py +# +# Written by Greg Stein. Public Domain. +# No Copyright, no Rights Reserved, and no Warranties. +# +# Utilities to help out with custom import mechanisms. +# +# Additional modifications were contribed by Marc-Andre Lemburg and +# Gordon McMillan. +# + +__version__ = '0.3' + +# note: avoid importing non-builtin modules +import imp +import sys +import strop +import __builtin__ ### why this instead of just using __builtins__ ?? + +# for the DirectoryImporter +import struct +import marshal + +class Importer: + "Base class for replacing standard import functions." + + def install(self): + self.__chain_import = __builtin__.__import__ + self.__chain_reload = __builtin__.reload + __builtin__.__import__ = self._import_hook + __builtin__.reload = self._reload_hook + + ###################################################################### + # + # PRIVATE METHODS + # + def _import_hook(self, name, globals=None, locals=None, fromlist=None): + """Python calls this hook to locate and import a module. + + This method attempts to load the (dotted) module name. If it cannot + find it, then it delegates the import to the next import hook in the + chain (where "next" is defined as the import hook that was in place + at the time this Importer instance was installed). + """ + + # determine the context of this import + parent = self._determine_import_context(globals) + + # import the module within the context, or from the default context + top, tail = self._import_top_module(parent, name) + if top is None: + # the module was not found; delegate to the next import hook + return self.__chain_import(name, globals, locals, fromlist) + + # the top module may be under the control of a different importer. + # if so, then defer to that importer for completion of the import. + # note it may be self, or is undefined so we (self) may as well + # finish the import. + importer = top.__dict__.get('__importer__', self) + return importer._finish_import(top, tail, fromlist) + + def _finish_import(self, top, tail, fromlist): + # if "a.b.c" was provided, then load the ".b.c" portion down from + # below the top-level module. + bottom = self._load_tail(top, tail) + + # if the form is "import a.b.c", then return "a" + if not fromlist: + # no fromlist: return the top of the import tree + return top + + # the top module was imported by self, or it was not imported through + # the Importer mechanism and self is simply handling the import of + # the sub-modules and fromlist. + # + # this means that the bottom module was also imported by self, or we + # are handling things in the absence of a prior Importer + # + # ### why the heck are we handling it? what is the example scenario + # ### where this happens? note that we can't determine is_package() + # ### for non-Importer modules. + # + # since we imported/handled the bottom module, this means that we can + # also handle its fromlist (and reliably determine is_package()). + + # if the bottom node is a package, then (potentially) import some modules. + # + # note: if it is not a package, then "fromlist" refers to names in + # the bottom module rather than modules. + # note: for a mix of names and modules in the fromlist, we will + # import all modules and insert those into the namespace of + # the package module. Python will pick up all fromlist names + # from the bottom (package) module; some will be modules that + # we imported and stored in the namespace, others are expected + # to be present already. + if self._is_package(bottom.__dict__): + self._import_fromlist(bottom, fromlist) + + # if the form is "from a.b import c, d" then return "b" + return bottom + + def _reload_hook(self, module): + "Python calls this hook to reload a module." + + # reloading of a module may or may not be possible (depending on the + # importer), but at least we can validate that it's ours to reload + importer = module.__dict__.get('__importer__', None) + if importer is not self: + return self.__chain_reload(module) + + # okay. it is ours, but we don't know what to do (yet) + ### we should blast the module dict and do another get_code(). need to + ### flesh this out and add proper docco... + raise SystemError, "reload not yet implemented" + + def _determine_import_context(self, globals): + """Returns the context in which a module should be imported. + + The context could be a loaded (package) module and the imported module + will be looked for within that package. The context could also be None, + meaning there is no context -- the module should be looked for as a + "top-level" module. + """ + + if not globals or \ + globals.get('__importer__', None) is not self: + # globals does not refer to one of our modules or packages. + # That implies there is no relative import context, and it + # should just pick it off the standard path. + return None + + # The globals refer to a module or package of ours. It will define + # the context of the new import. Get the module/package fqname. + parent_fqname = globals['__name__'] + + # for a package, return itself (imports refer to pkg contents) + if self._is_package(globals): + parent = sys.modules[parent_fqname] + assert globals is parent.__dict__ + return parent + + i = strop.rfind(parent_fqname, '.') + + # a module outside of a package has no particular import context + if i == -1: + return None + + # for a module in a package, return the package (imports refer to siblings) + parent_fqname = parent_fqname[:i] + parent = sys.modules[parent_fqname] + assert parent.__name__ == parent_fqname + return parent + + def _import_top_module(self, parent, name): + """Locate the top of the import tree (relative or absolute). + + parent defines the context in which the import should occur. See + _determine_import_context() for details. + + Returns a tuple (module, tail). module is the loaded (top-level) module, + or None if the module is not found. tail is the remaining portion of + the dotted name. + """ + i = strop.find(name, '.') + if i == -1: + head = name + tail = "" + else: + head = name[:i] + tail = name[i+1:] + if parent: + fqname = "%s.%s" % (parent.__name__, head) + else: + fqname = head + module = self._import_one(parent, head, fqname) + if module: + # the module was relative, or no context existed (the module was + # simply found on the path). + return module, tail + if parent: + # we tried relative, now try an absolute import (from the path) + module = self._import_one(None, head, head) + if module: + return module, tail + + # the module wasn't found + return None, None + + def _import_one(self, parent, modname, fqname): + "Import a single module." + + # has the module already been imported? + try: + return sys.modules[fqname] + except KeyError: + pass + + # load the module's code, or fetch the module itself + result = self.get_code(parent, modname, fqname) + if result is None: + return None + + # did get_code() return an actual module? (rather than a code object) + is_module = type(result[1]) is type(sys) + + # use the returned module, or create a new one to exec code into + if is_module: + module = result[1] + else: + module = imp.new_module(fqname) + + ### record packages a bit differently?? + module.__importer__ = self + module.__ispkg__ = result[0] + + # if present, the third item is a set of values to insert into the module + if len(result) > 2: + module.__dict__.update(result[2]) + + # the module is almost ready... make it visible + sys.modules[fqname] = module + + # execute the code within the module's namespace + if not is_module: + exec result[1] in module.__dict__ + + # insert the module into its parent + if parent: + setattr(parent, modname, module) + return module + + def _load_tail(self, m, tail): + """Import the rest of the modules, down from the top-level module. + + Returns the last module in the dotted list of modules. + """ + if tail: + for part in strop.splitfields(tail, '.'): + fqname = "%s.%s" % (m.__name__, part) + m = self._import_one(m, part, fqname) + if not m: + raise ImportError, "No module named " + fqname + return m + + def _import_fromlist(self, package, fromlist): + 'Import any sub-modules in the "from" list.' + + # if '*' is present in the fromlist, then look for the '__all__' variable + # to find additional items (modules) to import. + if '*' in fromlist: + fromlist = list(fromlist) + list(package.__dict__.get('__all__', [])) + + for sub in fromlist: + # if the name is already present, then don't try to import it (it + # might not be a module!). + if sub != '*' and not hasattr(package, sub): + subname = "%s.%s" % (package.__name__, sub) + submod = self._import_one(package, sub, subname) + if not submod: + raise ImportError, "cannot import name " + subname + + def _is_package(self, module_dict): + """Determine if a given module (dictionary) specifies a package. + + The package status is in the module-level name __ispkg__. The module + must also have been imported by self, so that we can reliably apply + semantic meaning to __ispkg__. + + ### weaken the test to issubclass(Importer)? + """ + return module_dict.get('__importer__', None) is self and \ + module_dict['__ispkg__'] + + ###################################################################### + # + # METHODS TO OVERRIDE + # + def get_code(self, parent, modname, fqname): + """Find and retrieve the code for the given module. + + parent specifies a parent module to define a context for importing. It + may be None, indicating no particular context for the search. + + modname specifies a single module (not dotted) within the parent. + + fqname specifies the fully-qualified module name. This is a (potentially) + dotted name from the "root" of the module namespace down to the modname. + If there is no parent, then modname==fqname. + + This method should return None, a 2-tuple, or a 3-tuple. + + * If the module was not found, then None should be returned. + + * The first item of the 2- or 3-tuple should be the integer 0 or 1, + specifying whether the module that was found is a package or not. + + * The second item is the code object for the module (it will be + executed within the new module's namespace). This item can also + be a fully-loaded module object (e.g. loaded from a shared lib). + + * If present, the third item is a dictionary of name/value pairs that + will be inserted into new module before the code object is executed. + This provided in case the module's code expects certain values (such + as where the module was found). When the second item is a module + object, then these names/values will be inserted *after* the module + has been loaded/initialized. + """ + raise RuntimeError, "get_code not implemented" + + +###################################################################### +# +# Simple function-based importer +# +class FuncImporter(Importer): + "Importer subclass to use a supplied function rather than method overrides." + def __init__(self, func): + self.func = func + def get_code(self, parent, modname, fqname): + return self.func(parent, modname, fqname) + +def install_with(func): + FuncImporter(func).install() + + +###################################################################### +# +# Base class for archive-based importing +# +class PackageArchiveImporter(Importer): + "Importer subclass to import from (file) archives." + + def get_code(self, parent, modname, fqname): + if parent: + # if a parent "package" is provided, then we are importing a sub-file + # from the archive. + result = self.get_subfile(parent.__archive__, modname) + if result is None: + return None + if type(result) == type(()): + return (0,) + result + return 0, result + + # no parent was provided, so the archive should exist somewhere on the + # default "path". + archive = self.get_archive(modname) + if archive is None: + return None + return 1, "", {'__archive__':archive} + + def get_archive(self, modname): + """Get an archive of modules. + + This method should locate an archive and return a value which can be + used by get_subfile to load modules from it. The value may be a simple + pathname, an open file, or a complex object that caches information + for future imports. + + Return None if the archive was not found. + """ + raise RuntimeError, "get_archive not implemented" + + def get_subfile(self, archive, modname): + """Get code from a subfile in the specified archive. + + Given the specified archive (as returned by get_archive()), locate + and return a code object for the specified module name. + + A 2-tuple may be returned, consisting of a code object and a dict + of name/values to place into the target module. + + Return None if the subfile was not found. + """ + raise RuntimeError, "get_subfile not implemented" + + +class PackageArchive(PackageArchiveImporter): + "PackageArchiveImporter subclass that refers to a specific archive." + + def __init__(self, modname, archive_pathname): + self.__modname = modname + self.__path = archive_pathname + + def get_archive(self, modname): + if modname == self.__modname: + return self.__path + return None + + # get_subfile is passed the full pathname of the archive + + +###################################################################### +# +# Emulate the standard directory-based import mechanism +# + +class DirectoryImporter(Importer): + "Importer subclass to emulate the standard importer." + + def __init__(self, dir): + self.dir = dir + self.ext_char = __debug__ and 'c' or 'o' + self.ext = '.py' + self.ext_char + + def get_code(self, parent, modname, fqname): + if parent: + dir = parent.__pkgdir__ + else: + dir = self.dir + + # pull the os module from our instance data. we don't do this at the + # top-level, because it isn't a builtin module (and we want to defer + # loading non-builtins until as late as possible). + try: + os = self.os + except AttributeError: + import os + self.os = os + + pathname = os.path.join(dir, modname) + if os.path.isdir(pathname): + values = { '__pkgdir__' : pathname } + ispkg = 1 + pathname = os.path.join(pathname, '__init__') + else: + values = { } + ispkg = 0 + + t_py = self._timestamp(pathname + '.py') + t_pyc = self._timestamp(pathname + self.ext) + if t_py is None and t_pyc is None: + return None + code = None + if t_py is None or (t_pyc is not None and t_pyc >= t_py): + f = open(pathname + self.ext, 'rb') + if f.read(4) == imp.get_magic(): + t = struct.unpack('' % (self.__class__.__module__, + self.__class__.__name__, + self.dir, + id(self)) + +def _test_dir(): + "Debug/test function to create DirectoryImporters from sys.path." + path = sys.path[:] + path.reverse() + for d in path: + DirectoryImporter(d).install() + +###################################################################### diff --git a/direct/src/pyinst/installutils.py b/direct/src/pyinst/installutils.py index 66eca01346..23f4f8becb 100644 --- a/direct/src/pyinst/installutils.py +++ b/direct/src/pyinst/installutils.py @@ -1,91 +1,91 @@ -# copyright 1999 McMillan Enterprises, Inc. -# demo code - use as you please. -import os -import stat - -def copyFile(srcFiles, destFile, append=0): - ''' - Copy one or more files to another file. If srcFiles is a list, then all - will be concatenated together to destFile. The append flag is also valid - for single file copies. - - destFile will have the mode, ownership and timestamp of the last file - copied/appended. - ''' - if type(srcFiles) == type([]): - # in case we need to overwrite on the first file... - copyFile(srcFiles[0], destFile, append) - for file in srcFiles[1:]: - copyFile(file, destFile, 1) - return - - mode = 'wb' - if append: - mode = 'ab' - print " ", srcFiles, "->", - input = open(srcFiles, 'rb') - if input: - print destFile - output = open(destFile, mode) - while 1: - bytesRead = input.read(8192) - if bytesRead: - output.write(bytesRead) - else: - break - - input.close() - output.close() - - stats = os.stat(srcFiles) - os.chmod(destFile, stats[stat.ST_MODE]) - try: # FAT16 file systems have only one file time - os.utime(destFile, (stats[stat.ST_ATIME], stats[stat.ST_MTIME])) - except: - pass - try: - os.chown(destFile, stats[stat.ST_UID], stats[stat.ST_GID]) - except: - pass - -def ensure(dirct): - dirnm = dirct - plist = [] - try: - while not os.path.exists(dirnm): - dirnm, base = os.path.split(dirnm) - if base == '': - break - plist.insert(0, base) - for d in plist: - dirnm = os.path.join(dirnm, d) - os.mkdir(dirnm) - except: - return 0 - return 1 - -def getinstalldir(prompt="Enter an installation directory: "): - while 1: - installdir = raw_input("Enter an installation directory: ") - installdir = os.path.normpath(installdir) - if ensure(installdir): - break - else: - print installdir, "is not a valid pathname" - r = raw_input("Try again (y/n)?: ") - if r in 'nN': - sys.exit(0) - return installdir - -def installCArchive(nm, basedir, suffixdir): - import carchive_rt - fulldir = os.path.join(basedir, suffixdir) - if ensure(fulldir): - pkg = carchive_rt.CArchive(nm) - for fnm in pkg.contents(): - stuff = pkg.extract(fnm)[1] - outnm = os.path.join(fulldir, fnm) - if ensure(os.path.dirname(outnm)): - open(outnm, 'wb').write(stuff) - pkg = None - os.remove(nm) +# copyright 1999 McMillan Enterprises, Inc. +# demo code - use as you please. +import os +import stat + +def copyFile(srcFiles, destFile, append=0): + ''' + Copy one or more files to another file. If srcFiles is a list, then all + will be concatenated together to destFile. The append flag is also valid + for single file copies. + + destFile will have the mode, ownership and timestamp of the last file + copied/appended. + ''' + if type(srcFiles) == type([]): + # in case we need to overwrite on the first file... + copyFile(srcFiles[0], destFile, append) + for file in srcFiles[1:]: + copyFile(file, destFile, 1) + return + + mode = 'wb' + if append: + mode = 'ab' + print " ", srcFiles, "->", + input = open(srcFiles, 'rb') + if input: + print destFile + output = open(destFile, mode) + while 1: + bytesRead = input.read(8192) + if bytesRead: + output.write(bytesRead) + else: + break + + input.close() + output.close() + + stats = os.stat(srcFiles) + os.chmod(destFile, stats[stat.ST_MODE]) + try: # FAT16 file systems have only one file time + os.utime(destFile, (stats[stat.ST_ATIME], stats[stat.ST_MTIME])) + except: + pass + try: + os.chown(destFile, stats[stat.ST_UID], stats[stat.ST_GID]) + except: + pass + +def ensure(dirct): + dirnm = dirct + plist = [] + try: + while not os.path.exists(dirnm): + dirnm, base = os.path.split(dirnm) + if base == '': + break + plist.insert(0, base) + for d in plist: + dirnm = os.path.join(dirnm, d) + os.mkdir(dirnm) + except: + return 0 + return 1 + +def getinstalldir(prompt="Enter an installation directory: "): + while 1: + installdir = raw_input("Enter an installation directory: ") + installdir = os.path.normpath(installdir) + if ensure(installdir): + break + else: + print installdir, "is not a valid pathname" + r = raw_input("Try again (y/n)?: ") + if r in 'nN': + sys.exit(0) + return installdir + +def installCArchive(nm, basedir, suffixdir): + import carchive_rt + fulldir = os.path.join(basedir, suffixdir) + if ensure(fulldir): + pkg = carchive_rt.CArchive(nm) + for fnm in pkg.contents(): + stuff = pkg.extract(fnm)[1] + outnm = os.path.join(fulldir, fnm) + if ensure(os.path.dirname(outnm)): + open(outnm, 'wb').write(stuff) + pkg = None + os.remove(nm) diff --git a/direct/src/pyinst/mkarchive.py b/direct/src/pyinst/mkarchive.py index e673f4a3ba..afc61e7953 100644 --- a/direct/src/pyinst/mkarchive.py +++ b/direct/src/pyinst/mkarchive.py @@ -1,42 +1,42 @@ -import MkWrap -import imputil -import strop -import zlib -import os -import marshal - -class MkImporter: - def __init__(self, db, viewnm='pylib'): - self.db = db - self.view = db.getas(viewnm+'[name:S,ispkg:I,code:M]') # an MkWrap view object - def setImportHooks(self): - imputil.FuncImporter(self.get_code).install() - def get_code(self, parent, modname, fqname): - if self.view is None: - return None - ndx = self.view.search(name=fqname) - if ndx < len(self.view): - row = self.view[ndx] - if row.name == fqname: - return (row.ispkg, marshal.loads(zlib.decompress(row.code))) - return None - def build(self, lTOC): - for entry in lTOC: - nm, fnm = entry[0], entry[1] - ispkg = os.path.splitext(os.path.basename(fnm))[0] == '__init__' - ndx = self.view.search(name=nm) - if ndx < len(self.view): - row = self.view[ndx] - if row.name != nm: - self.view.insert(ndx, {}) - row = self.view[ndx] - else: - ndx = self.view.append({}) - row = self.view[ndx] - row.name = nm - row.ispkg = ispkg - f = open(fnm, 'rb') - f.seek(8) - obj = zlib.compress(f.read(), 9) - row.code = obj +import MkWrap +import imputil +import strop +import zlib +import os +import marshal + +class MkImporter: + def __init__(self, db, viewnm='pylib'): + self.db = db + self.view = db.getas(viewnm+'[name:S,ispkg:I,code:M]') # an MkWrap view object + def setImportHooks(self): + imputil.FuncImporter(self.get_code).install() + def get_code(self, parent, modname, fqname): + if self.view is None: + return None + ndx = self.view.search(name=fqname) + if ndx < len(self.view): + row = self.view[ndx] + if row.name == fqname: + return (row.ispkg, marshal.loads(zlib.decompress(row.code))) + return None + def build(self, lTOC): + for entry in lTOC: + nm, fnm = entry[0], entry[1] + ispkg = os.path.splitext(os.path.basename(fnm))[0] == '__init__' + ndx = self.view.search(name=nm) + if ndx < len(self.view): + row = self.view[ndx] + if row.name != nm: + self.view.insert(ndx, {}) + row = self.view[ndx] + else: + ndx = self.view.append({}) + row = self.view[ndx] + row.name = nm + row.ispkg = ispkg + f = open(fnm, 'rb') + f.seek(8) + obj = zlib.compress(f.read(), 9) + row.code = obj self.db.commit() \ No newline at end of file diff --git a/direct/src/pyinst/modulefinder.py b/direct/src/pyinst/modulefinder.py index 2693d0fc11..c2857876c3 100644 --- a/direct/src/pyinst/modulefinder.py +++ b/direct/src/pyinst/modulefinder.py @@ -1,436 +1,436 @@ -"""Find modules used by a script, using introspection.""" - -import dis -import imp -import marshal -import os -import re -import string -import sys - -if sys.platform=="win32": - # On Windows, we can locate modules in the registry with - # the help of the win32api package. - try: - import win32api - except ImportError: - print "The win32api module is not available - modules listed" - print "in the registry will not be found." - win32api = None - - -IMPORT_NAME = dis.opname.index('IMPORT_NAME') -IMPORT_FROM = dis.opname.index('IMPORT_FROM') - -# Modulefinder does a good job at simulating Python's, but it can not -# handle __path__ modifications packages make at runtime. Therefore there -# is a mechanism whereby you can register extra paths in this map for a -# package, and it will be honoured. - -# Note this is a mapping is lists of paths. -packagePathMap = {} - -# A Public interface -def AddPackagePath(packagename, path): - paths = packagePathMap.get(packagename, []) - paths.append(path) - packagePathMap[packagename] = paths - -class Module: - - def __init__(self, name, file=None, path=None): - self.__name__ = name - self.__file__ = file - self.__path__ = path - self.__code__ = None - - def __repr__(self): - s = "Module(%s" % `self.__name__` - if self.__file__ is not None: - s = s + ", %s" % `self.__file__` - if self.__path__ is not None: - s = s + ", %s" % `self.__path__` - s = s + ")" - return s - - -class ModuleFinder: - - def __init__(self, path=None, debug=0, excludes = []): - if path is None: - path = sys.path - self.path = path - self.modules = {} - self.badmodules = {} - self.debug = debug - self.indent = 0 - self.excludes = excludes - - def msg(self, level, str, *args): - if level <= self.debug: - for i in range(self.indent): - print " ", - print str, - for arg in args: - print repr(arg), - print - - def msgin(self, *args): - level = args[0] - if level <= self.debug: - self.indent = self.indent + 1 - apply(self.msg, args) - - def msgout(self, *args): - level = args[0] - if level <= self.debug: - self.indent = self.indent - 1 - apply(self.msg, args) - - def run_script(self, pathname): - self.msg(2, "run_script", pathname) - fp = open(pathname) - stuff = ("", "r", imp.PY_SOURCE) - self.load_module('__main__', fp, pathname, stuff) - - def load_file(self, pathname): - dir, name = os.path.split(pathname) - name, ext = os.path.splitext(name) - fp = open(pathname) - stuff = (ext, "r", imp.PY_SOURCE) - self.load_module(name, fp, pathname, stuff) - - def import_hook(self, name, caller=None, fromlist=None): - self.msg(3, "import_hook", name, caller, fromlist) - parent = self.determine_parent(caller) - q, tail = self.find_head_package(parent, name) - m = self.load_tail(q, tail) - if not fromlist: - return q - if m.__path__: - self.ensure_fromlist(m, fromlist) - - def determine_parent(self, caller): - self.msgin(4, "determine_parent", caller) - if not caller: - self.msgout(4, "determine_parent -> None") - return None - pname = caller.__name__ - if caller.__path__: - parent = self.modules[pname] - assert caller is parent - self.msgout(4, "determine_parent ->", parent) - return parent - if '.' in pname: - i = string.rfind(pname, '.') - pname = pname[:i] - parent = self.modules[pname] - assert parent.__name__ == pname - self.msgout(4, "determine_parent ->", parent) - return parent - self.msgout(4, "determine_parent -> None") - return None - - def find_head_package(self, parent, name): - self.msgin(4, "find_head_package", parent, name) - if '.' in name: - i = string.find(name, '.') - head = name[:i] - tail = name[i+1:] - else: - head = name - tail = "" - if parent: - qname = "%s.%s" % (parent.__name__, head) - else: - qname = head - q = self.import_module(head, qname, parent) - if q: - self.msgout(4, "find_head_package ->", (q, tail)) - return q, tail - if parent: - qname = head - parent = None - q = self.import_module(head, qname, parent) - if q: - self.msgout(4, "find_head_package ->", (q, tail)) - return q, tail - self.msgout(4, "raise ImportError: No module named", qname) - raise ImportError, "No module named " + qname - - def load_tail(self, q, tail): - self.msgin(4, "load_tail", q, tail) - m = q - while tail: - i = string.find(tail, '.') - if i < 0: i = len(tail) - head, tail = tail[:i], tail[i+1:] - mname = "%s.%s" % (m.__name__, head) - m = self.import_module(head, mname, m) - if not m: - self.msgout(4, "raise ImportError: No module named", mname) - raise ImportError, "No module named " + mname - self.msgout(4, "load_tail ->", m) - return m - - def ensure_fromlist(self, m, fromlist, recursive=0): - self.msg(4, "ensure_fromlist", m, fromlist, recursive) - for sub in fromlist: - if sub == "*": - if not recursive: - all = self.find_all_submodules(m) - if all: - self.ensure_fromlist(m, all, 1) - elif not hasattr(m, sub): - subname = "%s.%s" % (m.__name__, sub) - submod = self.import_module(sub, subname, m) - if not submod: - raise ImportError, "No module named " + subname - - def find_all_submodules(self, m): - if not m.__path__: - return - modules = {} - suffixes = [".py", ".pyc", ".pyo"] - for dir in m.__path__: - try: - names = os.listdir(dir) - except os.error: - self.msg(2, "can't list directory", dir) - continue - for name in names: - mod = None - for suff in suffixes: - n = len(suff) - if name[-n:] == suff: - mod = name[:-n] - break - if mod and mod != "__init__": - modules[mod] = mod - return modules.keys() - - def import_module(self, partname, fqname, parent): - self.msgin(3, "import_module", partname, fqname, parent) - try: - m = self.modules[fqname] - except KeyError: - pass - else: - self.msgout(3, "import_module ->", m) - return m - if self.badmodules.has_key(fqname): - self.msgout(3, "import_module -> None") - self.badmodules[fqname][parent.__name__] = None - return None - try: - fp, pathname, stuff = self.find_module(partname, - parent and parent.__path__) - except ImportError: - self.msgout(3, "import_module ->", None) - return None - try: - m = self.load_module(fqname, fp, pathname, stuff) - finally: - if fp: fp.close() - if parent: - setattr(parent, partname, m) - self.msgout(3, "import_module ->", m) - return m - - def load_module(self, fqname, fp, pathname, (suffix, mode, type)): - self.msgin(2, "load_module", fqname, fp and "fp", pathname) - if type == imp.PKG_DIRECTORY: - m = self.load_package(fqname, pathname) - self.msgout(2, "load_module ->", m) - return m - if type == imp.PY_SOURCE: - co = compile(fp.read()+'\n', pathname, 'exec') - elif type == imp.PY_COMPILED: - if fp.read(4) != imp.get_magic(): - self.msgout(2, "raise ImportError: Bad magic number", pathname) - raise ImportError, "Bad magic number in %s", pathname - fp.read(4) - co = marshal.load(fp) - else: - co = None - m = self.add_module(fqname) - m.__file__ = pathname - if co: - m.__code__ = co - self.scan_code(co, m) - self.msgout(2, "load_module ->", m) - return m - - def scan_code(self, co, m): - code = co.co_code - n = len(code) - i = 0 - lastname = None - while i < n: - c = code[i] - i = i+1 - op = ord(c) - if op >= dis.HAVE_ARGUMENT: - oparg = ord(code[i]) + ord(code[i+1])*256 - i = i+2 - if op == IMPORT_NAME: - name = lastname = co.co_names[oparg] - if not self.badmodules.has_key(lastname): - try: - self.import_hook(name, m) - except ImportError, msg: - self.msg(2, "ImportError:", str(msg)) - if not self.badmodules.has_key(name): - self.badmodules[name] = {} - self.badmodules[name][m.__name__] = None - elif op == IMPORT_FROM: - name = co.co_names[oparg] - assert lastname is not None - if not self.badmodules.has_key(lastname): - try: - self.import_hook(lastname, m, [name]) - except ImportError, msg: - self.msg(2, "ImportError:", str(msg)) - fullname = lastname + "." + name - if not self.badmodules.has_key(fullname): - self.badmodules[fullname] = {} - self.badmodules[fullname][m.__name__] = None - else: - lastname = None - for c in co.co_consts: - if isinstance(c, type(co)): - self.scan_code(c, m) - - def load_package(self, fqname, pathname): - self.msgin(2, "load_package", fqname, pathname) - m = self.add_module(fqname) - m.__file__ = pathname - m.__path__ = [pathname] - - # As per comment at top of file, simulate runtime __path__ additions. - m.__path__ = m.__path__ + packagePathMap.get(fqname, []) - - fp, buf, stuff = self.find_module("__init__", m.__path__) - self.load_module(fqname, fp, buf, stuff) - self.msgout(2, "load_package ->", m) - return m - - def add_module(self, fqname): - if self.modules.has_key(fqname): - return self.modules[fqname] - self.modules[fqname] = m = Module(fqname) - return m - - def find_module(self, name, path): - if name in self.excludes: - self.msgout(3, "find_module -> Excluded") - raise ImportError, name - - if path is None: - if name in sys.builtin_module_names: - return (None, None, ("", "", imp.C_BUILTIN)) - - # Emulate the Registered Module support on Windows. - if sys.platform=="win32" and win32api is not None: - HKEY_LOCAL_MACHINE = 0x80000002 - try: - pathname = win32api.RegQueryValue(HKEY_LOCAL_MACHINE, "Software\\Python\\PythonCore\\%s\\Modules\\%s" % (sys.winver, name)) - fp = open(pathname, "rb") - # XXX - To do - remove the hard code of C_EXTENSION. - stuff = "", "rb", imp.C_EXTENSION - return fp, pathname, stuff - except win32api.error: - pass - - path = self.path - return imp.find_module(name, path) - - def report(self): - print - print " %-25s %s" % ("Name", "File") - print " %-25s %s" % ("----", "----") - # Print modules found - keys = self.modules.keys() - keys.sort() - for key in keys: - m = self.modules[key] - if m.__path__: - print "P", - else: - print "m", - print "%-25s" % key, m.__file__ or "" - - # Print missing modules - keys = self.badmodules.keys() - keys.sort() - for key in keys: - # ... but not if they were explicitely excluded. - if key not in self.excludes: - mods = self.badmodules[key].keys() - mods.sort() - print "?", key, "from", string.join(mods, ', ') - - -def test(): - # Parse command line - import getopt - try: - opts, args = getopt.getopt(sys.argv[1:], "dmp:qx:") - except getopt.error, msg: - print msg - return - - # Process options - debug = 1 - domods = 0 - addpath = [] - exclude = [] - for o, a in opts: - if o == '-d': - debug = debug + 1 - if o == '-m': - domods = 1 - if o == '-p': - addpath = addpath + string.split(a, os.pathsep) - if o == '-q': - debug = 0 - if o == '-x': - exclude.append(a) - - # Provide default arguments - if not args: - script = "hello.py" - else: - script = args[0] - - # Set the path based on sys.path and the script directory - path = sys.path[:] - path[0] = os.path.dirname(script) - path = addpath + path - if debug > 1: - print "path:" - for item in path: - print " ", `item` - - # Create the module finder and turn its crank - mf = ModuleFinder(path, debug, exclude) - for arg in args[1:]: - if arg == '-m': - domods = 1 - continue - if domods: - if arg[-2:] == '.*': - mf.import_hook(arg[:-2], None, ["*"]) - else: - mf.import_hook(arg) - else: - mf.load_file(arg) - mf.run_script(script) - mf.report() - - -if __name__ == '__main__': - try: - test() - except KeyboardInterrupt: - print "\n[interrupt]" +"""Find modules used by a script, using introspection.""" + +import dis +import imp +import marshal +import os +import re +import string +import sys + +if sys.platform=="win32": + # On Windows, we can locate modules in the registry with + # the help of the win32api package. + try: + import win32api + except ImportError: + print "The win32api module is not available - modules listed" + print "in the registry will not be found." + win32api = None + + +IMPORT_NAME = dis.opname.index('IMPORT_NAME') +IMPORT_FROM = dis.opname.index('IMPORT_FROM') + +# Modulefinder does a good job at simulating Python's, but it can not +# handle __path__ modifications packages make at runtime. Therefore there +# is a mechanism whereby you can register extra paths in this map for a +# package, and it will be honoured. + +# Note this is a mapping is lists of paths. +packagePathMap = {} + +# A Public interface +def AddPackagePath(packagename, path): + paths = packagePathMap.get(packagename, []) + paths.append(path) + packagePathMap[packagename] = paths + +class Module: + + def __init__(self, name, file=None, path=None): + self.__name__ = name + self.__file__ = file + self.__path__ = path + self.__code__ = None + + def __repr__(self): + s = "Module(%s" % `self.__name__` + if self.__file__ is not None: + s = s + ", %s" % `self.__file__` + if self.__path__ is not None: + s = s + ", %s" % `self.__path__` + s = s + ")" + return s + + +class ModuleFinder: + + def __init__(self, path=None, debug=0, excludes = []): + if path is None: + path = sys.path + self.path = path + self.modules = {} + self.badmodules = {} + self.debug = debug + self.indent = 0 + self.excludes = excludes + + def msg(self, level, str, *args): + if level <= self.debug: + for i in range(self.indent): + print " ", + print str, + for arg in args: + print repr(arg), + print + + def msgin(self, *args): + level = args[0] + if level <= self.debug: + self.indent = self.indent + 1 + apply(self.msg, args) + + def msgout(self, *args): + level = args[0] + if level <= self.debug: + self.indent = self.indent - 1 + apply(self.msg, args) + + def run_script(self, pathname): + self.msg(2, "run_script", pathname) + fp = open(pathname) + stuff = ("", "r", imp.PY_SOURCE) + self.load_module('__main__', fp, pathname, stuff) + + def load_file(self, pathname): + dir, name = os.path.split(pathname) + name, ext = os.path.splitext(name) + fp = open(pathname) + stuff = (ext, "r", imp.PY_SOURCE) + self.load_module(name, fp, pathname, stuff) + + def import_hook(self, name, caller=None, fromlist=None): + self.msg(3, "import_hook", name, caller, fromlist) + parent = self.determine_parent(caller) + q, tail = self.find_head_package(parent, name) + m = self.load_tail(q, tail) + if not fromlist: + return q + if m.__path__: + self.ensure_fromlist(m, fromlist) + + def determine_parent(self, caller): + self.msgin(4, "determine_parent", caller) + if not caller: + self.msgout(4, "determine_parent -> None") + return None + pname = caller.__name__ + if caller.__path__: + parent = self.modules[pname] + assert caller is parent + self.msgout(4, "determine_parent ->", parent) + return parent + if '.' in pname: + i = string.rfind(pname, '.') + pname = pname[:i] + parent = self.modules[pname] + assert parent.__name__ == pname + self.msgout(4, "determine_parent ->", parent) + return parent + self.msgout(4, "determine_parent -> None") + return None + + def find_head_package(self, parent, name): + self.msgin(4, "find_head_package", parent, name) + if '.' in name: + i = string.find(name, '.') + head = name[:i] + tail = name[i+1:] + else: + head = name + tail = "" + if parent: + qname = "%s.%s" % (parent.__name__, head) + else: + qname = head + q = self.import_module(head, qname, parent) + if q: + self.msgout(4, "find_head_package ->", (q, tail)) + return q, tail + if parent: + qname = head + parent = None + q = self.import_module(head, qname, parent) + if q: + self.msgout(4, "find_head_package ->", (q, tail)) + return q, tail + self.msgout(4, "raise ImportError: No module named", qname) + raise ImportError, "No module named " + qname + + def load_tail(self, q, tail): + self.msgin(4, "load_tail", q, tail) + m = q + while tail: + i = string.find(tail, '.') + if i < 0: i = len(tail) + head, tail = tail[:i], tail[i+1:] + mname = "%s.%s" % (m.__name__, head) + m = self.import_module(head, mname, m) + if not m: + self.msgout(4, "raise ImportError: No module named", mname) + raise ImportError, "No module named " + mname + self.msgout(4, "load_tail ->", m) + return m + + def ensure_fromlist(self, m, fromlist, recursive=0): + self.msg(4, "ensure_fromlist", m, fromlist, recursive) + for sub in fromlist: + if sub == "*": + if not recursive: + all = self.find_all_submodules(m) + if all: + self.ensure_fromlist(m, all, 1) + elif not hasattr(m, sub): + subname = "%s.%s" % (m.__name__, sub) + submod = self.import_module(sub, subname, m) + if not submod: + raise ImportError, "No module named " + subname + + def find_all_submodules(self, m): + if not m.__path__: + return + modules = {} + suffixes = [".py", ".pyc", ".pyo"] + for dir in m.__path__: + try: + names = os.listdir(dir) + except os.error: + self.msg(2, "can't list directory", dir) + continue + for name in names: + mod = None + for suff in suffixes: + n = len(suff) + if name[-n:] == suff: + mod = name[:-n] + break + if mod and mod != "__init__": + modules[mod] = mod + return modules.keys() + + def import_module(self, partname, fqname, parent): + self.msgin(3, "import_module", partname, fqname, parent) + try: + m = self.modules[fqname] + except KeyError: + pass + else: + self.msgout(3, "import_module ->", m) + return m + if self.badmodules.has_key(fqname): + self.msgout(3, "import_module -> None") + self.badmodules[fqname][parent.__name__] = None + return None + try: + fp, pathname, stuff = self.find_module(partname, + parent and parent.__path__) + except ImportError: + self.msgout(3, "import_module ->", None) + return None + try: + m = self.load_module(fqname, fp, pathname, stuff) + finally: + if fp: fp.close() + if parent: + setattr(parent, partname, m) + self.msgout(3, "import_module ->", m) + return m + + def load_module(self, fqname, fp, pathname, (suffix, mode, type)): + self.msgin(2, "load_module", fqname, fp and "fp", pathname) + if type == imp.PKG_DIRECTORY: + m = self.load_package(fqname, pathname) + self.msgout(2, "load_module ->", m) + return m + if type == imp.PY_SOURCE: + co = compile(fp.read()+'\n', pathname, 'exec') + elif type == imp.PY_COMPILED: + if fp.read(4) != imp.get_magic(): + self.msgout(2, "raise ImportError: Bad magic number", pathname) + raise ImportError, "Bad magic number in %s", pathname + fp.read(4) + co = marshal.load(fp) + else: + co = None + m = self.add_module(fqname) + m.__file__ = pathname + if co: + m.__code__ = co + self.scan_code(co, m) + self.msgout(2, "load_module ->", m) + return m + + def scan_code(self, co, m): + code = co.co_code + n = len(code) + i = 0 + lastname = None + while i < n: + c = code[i] + i = i+1 + op = ord(c) + if op >= dis.HAVE_ARGUMENT: + oparg = ord(code[i]) + ord(code[i+1])*256 + i = i+2 + if op == IMPORT_NAME: + name = lastname = co.co_names[oparg] + if not self.badmodules.has_key(lastname): + try: + self.import_hook(name, m) + except ImportError, msg: + self.msg(2, "ImportError:", str(msg)) + if not self.badmodules.has_key(name): + self.badmodules[name] = {} + self.badmodules[name][m.__name__] = None + elif op == IMPORT_FROM: + name = co.co_names[oparg] + assert lastname is not None + if not self.badmodules.has_key(lastname): + try: + self.import_hook(lastname, m, [name]) + except ImportError, msg: + self.msg(2, "ImportError:", str(msg)) + fullname = lastname + "." + name + if not self.badmodules.has_key(fullname): + self.badmodules[fullname] = {} + self.badmodules[fullname][m.__name__] = None + else: + lastname = None + for c in co.co_consts: + if isinstance(c, type(co)): + self.scan_code(c, m) + + def load_package(self, fqname, pathname): + self.msgin(2, "load_package", fqname, pathname) + m = self.add_module(fqname) + m.__file__ = pathname + m.__path__ = [pathname] + + # As per comment at top of file, simulate runtime __path__ additions. + m.__path__ = m.__path__ + packagePathMap.get(fqname, []) + + fp, buf, stuff = self.find_module("__init__", m.__path__) + self.load_module(fqname, fp, buf, stuff) + self.msgout(2, "load_package ->", m) + return m + + def add_module(self, fqname): + if self.modules.has_key(fqname): + return self.modules[fqname] + self.modules[fqname] = m = Module(fqname) + return m + + def find_module(self, name, path): + if name in self.excludes: + self.msgout(3, "find_module -> Excluded") + raise ImportError, name + + if path is None: + if name in sys.builtin_module_names: + return (None, None, ("", "", imp.C_BUILTIN)) + + # Emulate the Registered Module support on Windows. + if sys.platform=="win32" and win32api is not None: + HKEY_LOCAL_MACHINE = 0x80000002 + try: + pathname = win32api.RegQueryValue(HKEY_LOCAL_MACHINE, "Software\\Python\\PythonCore\\%s\\Modules\\%s" % (sys.winver, name)) + fp = open(pathname, "rb") + # XXX - To do - remove the hard code of C_EXTENSION. + stuff = "", "rb", imp.C_EXTENSION + return fp, pathname, stuff + except win32api.error: + pass + + path = self.path + return imp.find_module(name, path) + + def report(self): + print + print " %-25s %s" % ("Name", "File") + print " %-25s %s" % ("----", "----") + # Print modules found + keys = self.modules.keys() + keys.sort() + for key in keys: + m = self.modules[key] + if m.__path__: + print "P", + else: + print "m", + print "%-25s" % key, m.__file__ or "" + + # Print missing modules + keys = self.badmodules.keys() + keys.sort() + for key in keys: + # ... but not if they were explicitely excluded. + if key not in self.excludes: + mods = self.badmodules[key].keys() + mods.sort() + print "?", key, "from", string.join(mods, ', ') + + +def test(): + # Parse command line + import getopt + try: + opts, args = getopt.getopt(sys.argv[1:], "dmp:qx:") + except getopt.error, msg: + print msg + return + + # Process options + debug = 1 + domods = 0 + addpath = [] + exclude = [] + for o, a in opts: + if o == '-d': + debug = debug + 1 + if o == '-m': + domods = 1 + if o == '-p': + addpath = addpath + string.split(a, os.pathsep) + if o == '-q': + debug = 0 + if o == '-x': + exclude.append(a) + + # Provide default arguments + if not args: + script = "hello.py" + else: + script = args[0] + + # Set the path based on sys.path and the script directory + path = sys.path[:] + path[0] = os.path.dirname(script) + path = addpath + path + if debug > 1: + print "path:" + for item in path: + print " ", `item` + + # Create the module finder and turn its crank + mf = ModuleFinder(path, debug, exclude) + for arg in args[1:]: + if arg == '-m': + domods = 1 + continue + if domods: + if arg[-2:] == '.*': + mf.import_hook(arg[:-2], None, ["*"]) + else: + mf.import_hook(arg) + else: + mf.load_file(arg) + mf.run_script(script) + mf.report() + + +if __name__ == '__main__': + try: + test() + except KeyboardInterrupt: + print "\n[interrupt]" diff --git a/direct/src/pyinst/resource.py b/direct/src/pyinst/resource.py index af27d03c9d..e562a2e4f9 100644 --- a/direct/src/pyinst/resource.py +++ b/direct/src/pyinst/resource.py @@ -1,317 +1,317 @@ -import os -import string -import archivebuilder -import carchive -import tocfilter -import bindepend -import finder - -_cache = {} - -def makeresource(name, xtrapath=None): - """Factory function that returns a resource subclass. - - NAME is the logical or physical name of a resource. - XTRAPTH is a path or list of paths to search first. - return one of the resource subclasses. - Warning - logical names can conflict; archive might return a directory, - when the module archive.py was desired.""" - typ, nm, fullname = finder.identify(name, xtrapath) - fullname = os.path.normpath(fullname) - if _cache.has_key(fullname): - return _cache[fullname] - elif typ in (finder.SCRIPT, finder.GSCRIPT): - rsrc = scriptresource(nm, fullname) - elif typ == finder.MODULE: - rsrc = moduleresource(nm, fullname) - elif typ == finder.PACKAGE: - rsrc = pkgresource(nm, fullname) - elif typ in (finder.PBINARY, finder.BINARY): - rsrc = binaryresource(nm, fullname) - elif typ == finder.ZLIB: - rsrc = zlibresource(nm, fullname) - elif typ == finder.DIRECTORY: - rsrc = dirresource(nm, fullname) - else: - try: - carchive.CArchive(fullname) - except: - rsrc = dataresource(nm, fullname) - else: - rsrc = archiveresource(nm, fullname) - _cache[fullname] = rsrc - return rsrc - -class resource: - """ Base class for all resources. - - contents() returns of list of what's contained (eg files in dirs) - dependencies() for Python resources returns a list of moduleresources - and binaryresources """ - def __init__(self, name, path, typ): - """NAME is the logical name of the resource. - PATH is the full path to the resource. - TYP is the type code. - No editting or sanity checks.""" - self.name = name - self.path = path - self.typ = typ - def __repr__(self): - return "(%(name)s, %(path)s, %(typ)s)" % self.__dict__ - def contents(self): - """A list of resources within this resource. - - Overridable. - Base implementation returns [self]""" - return [self] - def dependencies(self): - """A list of resources this resource requires. - - Overridable. - Base implementation returns []""" - return [] - def __cmp__(self, other): - if not isinstance(other, self.__class__): - return -1 - return cmp((self.typ, self.name), (other.typ, other.name)) - def asFilter(self): - """Create a tocfilter based on self. - - Pure virtual""" - raise NotImplementedError - def asSource(self): - """Return self in source form. - - Base implementation returns self""" - return self - def asBinary(self): - """Return self in binary form. - - Base implementation returns self""" - return self - -class pythonresource(resource): - """An empty base class. - - Used to classify resources.""" - pass - - -class scriptresource(pythonresource): - """ A top-level python resource. - - Has (lazily computed) attributes, modules and binaries, which together - are the scripts dependencies() """ - def __init__(self, name, fullname): - resource.__init__(self, name, fullname, 's') - def __getattr__(self, name): - if name == 'modules': - print "Analyzing python dependencies of", self.name, self.path - self.modules = [] - self._binaries = [] - nodes = string.split(self.name, '.')[:-1] # MEInc.Dist.archive -> ['MEInc', 'Dist'] - for i in range(len(nodes)): - nm = string.join(nodes[:i+1], '.') - rsrc = makeresource(nm+'.__init__') - rsrc.name = nm - self.modules.append(rsrc) - for (nm, path) in archivebuilder.Dependencies(self.path): - path = os.path.normcase(os.path.abspath(path)) - if os.path.splitext(path)[1] == '.py': - self.modules.append(moduleresource(nm, path)) - else: - self._binaries.append(binaryresource(nm, path)) - return self.modules - elif name == 'binaries': - x = self.modules - tmp = {} - for br in self._binaries: - tmp[br.name] = br - for br2 in br.dependencies(): - tmp[br2.name] = br2 - self.binaries = tmp.values() - return self.binaries - else: - raise AttributeError, "%s" % name - def dependencies(self): - """Return all dependencies (Python and binary) of self.""" - return self.modules + self.binaries - def asFilter(self): - """Return a ModFilter based on self.""" - return tocfilter.ModFilter([self.name]) - def asSource(self): - """Return self as a dataresource (ie, a text file wrapper).""" - r = dataresource(self.path) - r.name = apply(os.path.join, string.split(self.name, '.')[:-1]+[r.name]) - return r - -class moduleresource(scriptresource): - """ A module resource (differs from script in that it will generally - be worked with as a .pyc instead of in source form) """ - def __init__(self, name, fullname): - resource.__init__(self, name, fullname, 'm') - def asBinary(self): - """Return self as a dataresource (ie, a binary file wrapper).""" - r = dataresource(self.path) - r.name = os.path.basename(r.name) - r.typ = 'b' - return r - def asSource(self): - """Return self as a scriptresource (ie, uncompiled form).""" - return scriptresource(self.name, self.path[:-1]).asSource() - -class binaryresource(resource): - """A .dll or .pyd. - - dependencies() yields more binaryresources """ - def __init__(self, name, fullname): - if string.find(name, '.') == -1: - pth, bnm = os.path.split(fullname) - junk, ext = os.path.splitext(bnm) - fullname = os.path.join(pth, name + ext) - resource.__init__(self, name, fullname, 'b') - self._depends = None - def dependencies(self): - """Return a list of binary dependencies.""" - if self._depends is not None: - return self._depends - self._depends = [] - for (lib, path) in bindepend.Dependencies([(self.name, self.path)]): - self._depends.append(binaryresource(lib, path)) - return self._depends - def asFilter(self): - """Create a FileFilter from self.""" - return tocfilter.FileFilter([self.name]) - -class dataresource(resource): - """A subclass for arbitrary files. """ - def __init__(self, name, fullname=None): - resource.__init__(self, name, fullname or name, 'x') - def asFilter(self): - """Create a FileFilter from self.""" - return tocfilter.FileFilter([self.name]) - -class archiveresource(dataresource): - """A sublcass for CArchives. """ - def __init__(self, name, fullname=None): - resource.__init__(self, name, fullname or name, 'a') - -class zlibresource(dataresource): - """A subclass for ZlibArchives. """ - def __init__(self, name, fullname=None): - resource.__init__(self, name, fullname or name, 'z') - -class dirresource(resource): - """A sublcass for a directory. - - Generally transformed to a list of files through - contents() and filtered by file extensions or resource type. - Note that contents() is smart enough to regard a .py and .pyc - as the same resource. """ - RECURSIVE = 0 - def __init__(self, name, fullname=None): - resource.__init__(self, name, fullname or name, 'd') - self._contents = None - def contents(self, prefix=''): - """Return the list of (typed) resources in self.name""" - if self._contents is not None: - return self._contents - self._contents = [] - flist = os.listdir(self.path) - for fnm in flist: - try: - bnm, ext = os.path.splitext(fnm) - if ext == '.py' and (bnm+'.pyc' in flist or bnm+'.pyo' in flist): - pass - elif ext == '.pyo' and (bnm + '.pyc' in flist): - pass - else: - rsrc = makeresource(os.path.join(self.path,fnm)) - if isinstance(rsrc, pkgresource): - rsrc = self.__class__(rsrc.path) - if self.RECURSIVE: - if isinstance(rsrc, moduleresource) or isinstance(rsrc, scriptresource): - rsrc = rsrc.asSource() - fnm = os.path.basename(rsrc.path) - rsrc.name = os.path.join(prefix, fnm) - if rsrc.typ == 'd': - rsrc.RECURSIVE = 1 - self._contents.extend(rsrc.contents(rsrc.name)) - else: - self._contents.append(rsrc) - else: - self._contents.append(rsrc) - except ValueError,e: - raise RuntimeError, "Can't make resource from %s\n ValueError: %s" \ - % (os.path.join(self.path, fnm), `e.args`) - return self._contents - def asFilter(self): - return tocfilter.DirFilter([self.path]) - -class treeresource(dirresource): - """A subclass for a directory and subdirectories.""" - RECURSIVE = 1 - def __init__(self, name, fullname=None): - dirresource.__init__(self, name, fullname) - -class pkgresource(pythonresource): - """A Python package. - - Note that contents() can be fooled by fancy __path__ statements. """ - def __init__(self, nm, fullname): - resource.__init__(self, nm, fullname, 'p') - self._contents = None - self._depends = None - def contents(self, parent=None): - """Return a list of subpackages and modules in self.""" - if self._contents is not None: - return self._contents - if parent is None: - parent = self.name - self._contents = [] - cheat = treeresource(self.path) - for rsrc in cheat.contents(): - if os.path.splitext(rsrc.path)[1] == '.py': - rsrc = moduleresource(string.replace(rsrc.name[:-3], os.sep, '.'), - rsrc.path) - if rsrc.name[-8:] == '__init__': - rsrc.name = rsrc.name[:-9] - elif os.path.isdir(rsrc.path): - rsrc = makeresource(rsrc.path) - else: - continue - if rsrc.name: - rsrc.name = parent + '.' + rsrc.name - else: - rsrc.name = parent - if rsrc.typ == 'm': - self._contents.append(rsrc) - elif rsrc.typ == 'p': - self._contents.extend(rsrc.contents(rsrc.name)) - return self._contents - def dependencies(self): - """Return the list of accumulated dependencies of all modules in self.""" - if self._depends is not None: - return self._depends - self._depends = [] - tmp = {} - for rsrc in self.contents(): - for r in rsrc.dependencies(): - tmp[r.name] = r - self._depends = tmp.values() - return self._depends - def asFilter(self): - """Create a PkgFilter from self.""" - return tocfilter.PkgFilter([os.path.dirname(self.path)]) - - - - - - - -if __name__ == '__main__': - s = scriptresource('finder.py', './finder.py') - print "s.modules:", s.modules - print "s.binaries:", s.binaries - +import os +import string +import archivebuilder +import carchive +import tocfilter +import bindepend +import finder + +_cache = {} + +def makeresource(name, xtrapath=None): + """Factory function that returns a resource subclass. + + NAME is the logical or physical name of a resource. + XTRAPTH is a path or list of paths to search first. + return one of the resource subclasses. + Warning - logical names can conflict; archive might return a directory, + when the module archive.py was desired.""" + typ, nm, fullname = finder.identify(name, xtrapath) + fullname = os.path.normpath(fullname) + if _cache.has_key(fullname): + return _cache[fullname] + elif typ in (finder.SCRIPT, finder.GSCRIPT): + rsrc = scriptresource(nm, fullname) + elif typ == finder.MODULE: + rsrc = moduleresource(nm, fullname) + elif typ == finder.PACKAGE: + rsrc = pkgresource(nm, fullname) + elif typ in (finder.PBINARY, finder.BINARY): + rsrc = binaryresource(nm, fullname) + elif typ == finder.ZLIB: + rsrc = zlibresource(nm, fullname) + elif typ == finder.DIRECTORY: + rsrc = dirresource(nm, fullname) + else: + try: + carchive.CArchive(fullname) + except: + rsrc = dataresource(nm, fullname) + else: + rsrc = archiveresource(nm, fullname) + _cache[fullname] = rsrc + return rsrc + +class resource: + """ Base class for all resources. + + contents() returns of list of what's contained (eg files in dirs) + dependencies() for Python resources returns a list of moduleresources + and binaryresources """ + def __init__(self, name, path, typ): + """NAME is the logical name of the resource. + PATH is the full path to the resource. + TYP is the type code. + No editting or sanity checks.""" + self.name = name + self.path = path + self.typ = typ + def __repr__(self): + return "(%(name)s, %(path)s, %(typ)s)" % self.__dict__ + def contents(self): + """A list of resources within this resource. + + Overridable. + Base implementation returns [self]""" + return [self] + def dependencies(self): + """A list of resources this resource requires. + + Overridable. + Base implementation returns []""" + return [] + def __cmp__(self, other): + if not isinstance(other, self.__class__): + return -1 + return cmp((self.typ, self.name), (other.typ, other.name)) + def asFilter(self): + """Create a tocfilter based on self. + + Pure virtual""" + raise NotImplementedError + def asSource(self): + """Return self in source form. + + Base implementation returns self""" + return self + def asBinary(self): + """Return self in binary form. + + Base implementation returns self""" + return self + +class pythonresource(resource): + """An empty base class. + + Used to classify resources.""" + pass + + +class scriptresource(pythonresource): + """ A top-level python resource. + + Has (lazily computed) attributes, modules and binaries, which together + are the scripts dependencies() """ + def __init__(self, name, fullname): + resource.__init__(self, name, fullname, 's') + def __getattr__(self, name): + if name == 'modules': + print "Analyzing python dependencies of", self.name, self.path + self.modules = [] + self._binaries = [] + nodes = string.split(self.name, '.')[:-1] # MEInc.Dist.archive -> ['MEInc', 'Dist'] + for i in range(len(nodes)): + nm = string.join(nodes[:i+1], '.') + rsrc = makeresource(nm+'.__init__') + rsrc.name = nm + self.modules.append(rsrc) + for (nm, path) in archivebuilder.Dependencies(self.path): + path = os.path.normcase(os.path.abspath(path)) + if os.path.splitext(path)[1] == '.py': + self.modules.append(moduleresource(nm, path)) + else: + self._binaries.append(binaryresource(nm, path)) + return self.modules + elif name == 'binaries': + x = self.modules + tmp = {} + for br in self._binaries: + tmp[br.name] = br + for br2 in br.dependencies(): + tmp[br2.name] = br2 + self.binaries = tmp.values() + return self.binaries + else: + raise AttributeError, "%s" % name + def dependencies(self): + """Return all dependencies (Python and binary) of self.""" + return self.modules + self.binaries + def asFilter(self): + """Return a ModFilter based on self.""" + return tocfilter.ModFilter([self.name]) + def asSource(self): + """Return self as a dataresource (ie, a text file wrapper).""" + r = dataresource(self.path) + r.name = apply(os.path.join, string.split(self.name, '.')[:-1]+[r.name]) + return r + +class moduleresource(scriptresource): + """ A module resource (differs from script in that it will generally + be worked with as a .pyc instead of in source form) """ + def __init__(self, name, fullname): + resource.__init__(self, name, fullname, 'm') + def asBinary(self): + """Return self as a dataresource (ie, a binary file wrapper).""" + r = dataresource(self.path) + r.name = os.path.basename(r.name) + r.typ = 'b' + return r + def asSource(self): + """Return self as a scriptresource (ie, uncompiled form).""" + return scriptresource(self.name, self.path[:-1]).asSource() + +class binaryresource(resource): + """A .dll or .pyd. + + dependencies() yields more binaryresources """ + def __init__(self, name, fullname): + if string.find(name, '.') == -1: + pth, bnm = os.path.split(fullname) + junk, ext = os.path.splitext(bnm) + fullname = os.path.join(pth, name + ext) + resource.__init__(self, name, fullname, 'b') + self._depends = None + def dependencies(self): + """Return a list of binary dependencies.""" + if self._depends is not None: + return self._depends + self._depends = [] + for (lib, path) in bindepend.Dependencies([(self.name, self.path)]): + self._depends.append(binaryresource(lib, path)) + return self._depends + def asFilter(self): + """Create a FileFilter from self.""" + return tocfilter.FileFilter([self.name]) + +class dataresource(resource): + """A subclass for arbitrary files. """ + def __init__(self, name, fullname=None): + resource.__init__(self, name, fullname or name, 'x') + def asFilter(self): + """Create a FileFilter from self.""" + return tocfilter.FileFilter([self.name]) + +class archiveresource(dataresource): + """A sublcass for CArchives. """ + def __init__(self, name, fullname=None): + resource.__init__(self, name, fullname or name, 'a') + +class zlibresource(dataresource): + """A subclass for ZlibArchives. """ + def __init__(self, name, fullname=None): + resource.__init__(self, name, fullname or name, 'z') + +class dirresource(resource): + """A sublcass for a directory. + + Generally transformed to a list of files through + contents() and filtered by file extensions or resource type. + Note that contents() is smart enough to regard a .py and .pyc + as the same resource. """ + RECURSIVE = 0 + def __init__(self, name, fullname=None): + resource.__init__(self, name, fullname or name, 'd') + self._contents = None + def contents(self, prefix=''): + """Return the list of (typed) resources in self.name""" + if self._contents is not None: + return self._contents + self._contents = [] + flist = os.listdir(self.path) + for fnm in flist: + try: + bnm, ext = os.path.splitext(fnm) + if ext == '.py' and (bnm+'.pyc' in flist or bnm+'.pyo' in flist): + pass + elif ext == '.pyo' and (bnm + '.pyc' in flist): + pass + else: + rsrc = makeresource(os.path.join(self.path,fnm)) + if isinstance(rsrc, pkgresource): + rsrc = self.__class__(rsrc.path) + if self.RECURSIVE: + if isinstance(rsrc, moduleresource) or isinstance(rsrc, scriptresource): + rsrc = rsrc.asSource() + fnm = os.path.basename(rsrc.path) + rsrc.name = os.path.join(prefix, fnm) + if rsrc.typ == 'd': + rsrc.RECURSIVE = 1 + self._contents.extend(rsrc.contents(rsrc.name)) + else: + self._contents.append(rsrc) + else: + self._contents.append(rsrc) + except ValueError,e: + raise RuntimeError, "Can't make resource from %s\n ValueError: %s" \ + % (os.path.join(self.path, fnm), `e.args`) + return self._contents + def asFilter(self): + return tocfilter.DirFilter([self.path]) + +class treeresource(dirresource): + """A subclass for a directory and subdirectories.""" + RECURSIVE = 1 + def __init__(self, name, fullname=None): + dirresource.__init__(self, name, fullname) + +class pkgresource(pythonresource): + """A Python package. + + Note that contents() can be fooled by fancy __path__ statements. """ + def __init__(self, nm, fullname): + resource.__init__(self, nm, fullname, 'p') + self._contents = None + self._depends = None + def contents(self, parent=None): + """Return a list of subpackages and modules in self.""" + if self._contents is not None: + return self._contents + if parent is None: + parent = self.name + self._contents = [] + cheat = treeresource(self.path) + for rsrc in cheat.contents(): + if os.path.splitext(rsrc.path)[1] == '.py': + rsrc = moduleresource(string.replace(rsrc.name[:-3], os.sep, '.'), + rsrc.path) + if rsrc.name[-8:] == '__init__': + rsrc.name = rsrc.name[:-9] + elif os.path.isdir(rsrc.path): + rsrc = makeresource(rsrc.path) + else: + continue + if rsrc.name: + rsrc.name = parent + '.' + rsrc.name + else: + rsrc.name = parent + if rsrc.typ == 'm': + self._contents.append(rsrc) + elif rsrc.typ == 'p': + self._contents.extend(rsrc.contents(rsrc.name)) + return self._contents + def dependencies(self): + """Return the list of accumulated dependencies of all modules in self.""" + if self._depends is not None: + return self._depends + self._depends = [] + tmp = {} + for rsrc in self.contents(): + for r in rsrc.dependencies(): + tmp[r.name] = r + self._depends = tmp.values() + return self._depends + def asFilter(self): + """Create a PkgFilter from self.""" + return tocfilter.PkgFilter([os.path.dirname(self.path)]) + + + + + + + +if __name__ == '__main__': + s = scriptresource('finder.py', './finder.py') + print "s.modules:", s.modules + print "s.binaries:", s.binaries + diff --git a/direct/src/pyinst/tocfilter.py b/direct/src/pyinst/tocfilter.py index d087235a5d..aa0c0203e2 100644 --- a/direct/src/pyinst/tocfilter.py +++ b/direct/src/pyinst/tocfilter.py @@ -1,131 +1,131 @@ -import os -import finder -import re -import sys - -def makefilter(name, xtrapath=None): - typ, nm, fullname = finder.identify(name, xtrapath) - if typ in (finder.SCRIPT, finder.GSCRIPT, finder.MODULE): - return ModFilter([os.path.splitext(nm)[0]]) - if typ == finder.PACKAGE: - return PkgFilter([fullname]) - if typ == finder.DIRECTORY: - return DirFilter([fullname]) - if typ in (finder.BINARY, finder.PBINARY): - return FileFilter([nm]) - return FileFilter([fullname]) - -class _Filter: - def __repr__(self): - return '<'+self.__class__.__name__+' '+repr(self.elements)+'>' - -class _NameFilter(_Filter): - """ A filter mixin that matches (exactly) on name """ - def matches(self, res): - return self.elements.get(res.name, 0) - -class _PathFilter(_Filter): - """ A filter mixin that matches if the resource is below any of the paths""" - def matches(self, res): - p = os.path.normcase(os.path.abspath(res.path)) - while len(p) > 3: - p = os.path.dirname(p) - if self.elements.get(p, 0): - return 1 - return 0 - -class _ExtFilter(_Filter): - """ A filter mixin that matches based on file extensions (either way) """ - include = 0 - def matches(self, res): - fnd = self.elements.get(os.path.splitext(res.path)[1], 0) - if self.include: - return not fnd - return fnd - -class _TypeFilter(_Filter): - """ A filter mixin that matches on resource type (either way) """ - include = 0 - def matches(self, res): - fnd = self.elements.get(res.typ, 0) - if self.include: - return not fnd - return fnd - -class _PatternFilter(_Filter): - """ A filter that matches if re.search succeeds on the resource path """ - def matches(self, res): - for regex in self.elements: - if regex.search(res.path): - return 1 - return 0 - -class ExtFilter(_ExtFilter): - """ A file extension filter. - ExtFilter(extlist, include=0) - where extlist is a list of file extensions """ - def __init__(self, extlist, include=0): - self.elements = {} - for ext in extlist: - if ext[0:1] != '.': - ext = '.'+ext - self.elements[ext] = 1 - self.include = include - -class TypeFilter(_TypeFilter): - """ A filter for resource types. - TypeFilter(typlist, include=0) - where typlist is a subset of ['a','b','d','m','p','s','x','z'] """ - def __init__(self, typlist, include=0): - self.elements = {} - for typ in typlist: - self.elements[typ] = 1 - self.include = include - -class FileFilter(_NameFilter): - """ A filter for data files """ - def __init__(self, filelist): - self.elements = {} - for f in filelist: - self.elements[f] = 1 - -class ModFilter(_NameFilter): - """ A filter for Python modules. - ModFilter(modlist) where modlist is eg ['macpath', 'dospath'] """ - def __init__(self, modlist): - self.elements = {} - for mod in modlist: - self.elements[mod] = 1 - -class DirFilter(_PathFilter): - """ A filter based on directories. - DirFilter(dirlist) - dirs may be relative and will be normalized. - Subdirectories of dirs will be excluded. """ - def __init__(self, dirlist): - self.elements = {} - for pth in dirlist: - pth = os.path.normcase(os.path.abspath(pth)) - self.elements[pth] = 1 - -class PkgFilter(_PathFilter): - """At this time, identical to a DirFilter (being lazy) """ - def __init__(self, pkglist): - #warning - pkgs are expected to be full directories - self.elements = {} - for pkg in pkglist: - pth = os.path.normcase(os.path.abspath(pkg)) - self.elements[pth] = 1 - -class StdLibFilter(_PathFilter): - """ A filter that excludes anything found in the standard library """ - def __init__(self): - pth = os.path.normcase(os.path.join(sys.exec_prefix, 'lib')) - self.elements = {pth:1} - -class PatternFilter(_PatternFilter): - """ A filter that excludes if any pattern is found in resource's path """ - def __init__(self, patterns): - self.elements = [] - for pat in patterns: - self.elements.append(re.compile(pat)) +import os +import finder +import re +import sys + +def makefilter(name, xtrapath=None): + typ, nm, fullname = finder.identify(name, xtrapath) + if typ in (finder.SCRIPT, finder.GSCRIPT, finder.MODULE): + return ModFilter([os.path.splitext(nm)[0]]) + if typ == finder.PACKAGE: + return PkgFilter([fullname]) + if typ == finder.DIRECTORY: + return DirFilter([fullname]) + if typ in (finder.BINARY, finder.PBINARY): + return FileFilter([nm]) + return FileFilter([fullname]) + +class _Filter: + def __repr__(self): + return '<'+self.__class__.__name__+' '+repr(self.elements)+'>' + +class _NameFilter(_Filter): + """ A filter mixin that matches (exactly) on name """ + def matches(self, res): + return self.elements.get(res.name, 0) + +class _PathFilter(_Filter): + """ A filter mixin that matches if the resource is below any of the paths""" + def matches(self, res): + p = os.path.normcase(os.path.abspath(res.path)) + while len(p) > 3: + p = os.path.dirname(p) + if self.elements.get(p, 0): + return 1 + return 0 + +class _ExtFilter(_Filter): + """ A filter mixin that matches based on file extensions (either way) """ + include = 0 + def matches(self, res): + fnd = self.elements.get(os.path.splitext(res.path)[1], 0) + if self.include: + return not fnd + return fnd + +class _TypeFilter(_Filter): + """ A filter mixin that matches on resource type (either way) """ + include = 0 + def matches(self, res): + fnd = self.elements.get(res.typ, 0) + if self.include: + return not fnd + return fnd + +class _PatternFilter(_Filter): + """ A filter that matches if re.search succeeds on the resource path """ + def matches(self, res): + for regex in self.elements: + if regex.search(res.path): + return 1 + return 0 + +class ExtFilter(_ExtFilter): + """ A file extension filter. + ExtFilter(extlist, include=0) + where extlist is a list of file extensions """ + def __init__(self, extlist, include=0): + self.elements = {} + for ext in extlist: + if ext[0:1] != '.': + ext = '.'+ext + self.elements[ext] = 1 + self.include = include + +class TypeFilter(_TypeFilter): + """ A filter for resource types. + TypeFilter(typlist, include=0) + where typlist is a subset of ['a','b','d','m','p','s','x','z'] """ + def __init__(self, typlist, include=0): + self.elements = {} + for typ in typlist: + self.elements[typ] = 1 + self.include = include + +class FileFilter(_NameFilter): + """ A filter for data files """ + def __init__(self, filelist): + self.elements = {} + for f in filelist: + self.elements[f] = 1 + +class ModFilter(_NameFilter): + """ A filter for Python modules. + ModFilter(modlist) where modlist is eg ['macpath', 'dospath'] """ + def __init__(self, modlist): + self.elements = {} + for mod in modlist: + self.elements[mod] = 1 + +class DirFilter(_PathFilter): + """ A filter based on directories. + DirFilter(dirlist) + dirs may be relative and will be normalized. + Subdirectories of dirs will be excluded. """ + def __init__(self, dirlist): + self.elements = {} + for pth in dirlist: + pth = os.path.normcase(os.path.abspath(pth)) + self.elements[pth] = 1 + +class PkgFilter(_PathFilter): + """At this time, identical to a DirFilter (being lazy) """ + def __init__(self, pkglist): + #warning - pkgs are expected to be full directories + self.elements = {} + for pkg in pkglist: + pth = os.path.normcase(os.path.abspath(pkg)) + self.elements[pth] = 1 + +class StdLibFilter(_PathFilter): + """ A filter that excludes anything found in the standard library """ + def __init__(self): + pth = os.path.normcase(os.path.join(sys.exec_prefix, 'lib')) + self.elements = {pth:1} + +class PatternFilter(_PatternFilter): + """ A filter that excludes if any pattern is found in resource's path """ + def __init__(self, patterns): + self.elements = [] + for pat in patterns: + self.elements.append(re.compile(pat))