mirror of
https://github.com/Sneed-Group/Poodletooth-iLand
synced 2024-12-29 06:32:40 -06:00
227 lines
7.2 KiB
Python
227 lines
7.2 KiB
Python
|
#
|
||
|
# 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)
|
||
|
##
|