srcanamdw/codescanner/pyinstaller/archive.py
changeset 1 22878952f6e2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/srcanamdw/codescanner/pyinstaller/archive.py	Thu Feb 18 12:29:02 2010 +0530
@@ -0,0 +1,413 @@
+# Copyright (C) 2005, Giovanni Bajo
+# Based on previous work under copyright (c) 2002 McMillan Enterprises, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# In addition to the permissions in the GNU General Public License, the
+# authors give you unlimited permission to link or embed the compiled
+# version of this file into combinations with other programs, and to
+# distribute those combinations without any restriction coming from the
+# use of this file. (The General Public License restrictions do apply in
+# other respects; for example, they cover modification of the file, and
+# distribution when not linked into a combine executable.)
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+
+# 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.
+
+_verbose = 0
+_listdir = None
+_environ = None
+
+# **NOTE** This module is used during bootstrap. Import *ONLY* builtin modules.
+import marshal
+import struct
+import imp
+import sys
+
+_c_suffixes = filter(lambda x: x[2] == imp.C_EXTENSION, imp.get_suffixes())
+
+for nm in ('nt', 'posix', 'dos', 'os2', 'mac'):
+    if nm in sys.builtin_module_names:
+        mod = __import__(nm)
+        _listdir = mod.listdir
+        _environ = mod.environ
+        break
+
+if hasattr(sys, 'version_info'):
+    versuffix = '%d%d'%(sys.version_info[0],sys.version_info[1])
+else:
+    vers = sys.version
+    dot1 = dot2 = 0
+    for i in range(len(vers)):
+        if vers[i] == '.':
+            if dot1:
+                dot2 = i
+                break
+            else:
+                dot1 = i
+    else:
+        dot2 = len(vers)
+    versuffix = '%s%s' % (vers[:dot1], vers[dot1+1:dot2])
+
+if "-vi" in sys.argv[1:]:
+    _verbose = 1
+
+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
+    _bincache = 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)
+        self.lib.read(4)
+
+    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.
+        #XXX obsolete - imputil only code
+        ##  def get_code(self, parent, modname, fqname):
+        ####    if _verbose:
+        ####      print "I: get_code(%s, %s, %s, %s)" % (self, parent, modname, fqname)
+        ##    iname = fqname
+        ##    if parent:
+        ##        iname = '%s.%s' % (parent.__dict__.get('__iname__', parent.__name__), modname)
+        ####        if _verbose:
+        ####            print "I: get_code: iname is %s" % iname
+        ##    rslt = self.extract(iname) # None if not found, (ispkg, code) otherwise
+        ####    if _verbose:
+        ####        print 'I: get_code: rslt', rslt
+        ##    if rslt is None:
+        ####      if _verbose:
+        ####          print 'I: get_code: importer', getattr(parent, "__importer__", None),'self',self
+        ##      # check the cache if there is no parent or self is the parents importer
+        ##      if parent is None or getattr(parent, "__importer__", None) is self:
+        ####            if _verbose:
+        ####                print 'I: get_code: cached 1',iname
+        ##            file, desc = Archive._bincache.get(iname, (None, None))
+        ####            if _verbose:
+        ####                print 'I: get_code: file',file,'desc',desc
+        ##            if file:
+        ##              try:
+        ##                fp = open(file, desc[1])
+        ##              except IOError:
+        ##                pass
+        ##              else:
+        ##                module = imp.load_module(fqname, fp, file, desc)
+        ##                if _verbose:
+        ##                    print "I: import %s found %s" % (fqname, file)
+        ##                return 0, module, {'__file__':file}
+        ##      if _verbose:
+        ##          print "I: import %s failed" % fqname
+        ##
+        ##      return None
+        ##
+        ##    ispkg, code = rslt
+        ##    values = {'__file__' : code.co_filename, '__iname__' : iname}
+        ##    if ispkg:
+        ##      values['__path__'] = [fqname]
+        ##    if _verbose:
+        ##        print "I: import %s found %s" % (fqname, iname)
+        ##    return ispkg, code, values
+
+        ####### 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]
+        pynm, ext = self.os.path.splitext(self.os.path.basename(pth))
+        ispkg = pynm == '__init__'
+        assert ext in ('.pyc', '.pyo')
+        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))
+
+class DummyZlib:
+    def decompress(self, data):
+        return data
+    def compress(self, data, lvl):
+        return data
+
+import iu
+##############################################################
+#
+# ZlibArchive - an archive with compressed entries
+#
+class ZlibArchive(Archive):
+    MAGIC = 'PYZ\0'
+    TOCPOS = 8
+    HDRLEN = 16
+    TRLLEN = 0
+    TOCTMPLT = {}
+    LEVEL = 9
+
+    def __init__(self, path=None, offset=None, level=9):
+        if path is None:
+            offset = 0
+        elif offset is None:
+            for i in range(len(path)-1, -1, -1):
+                if path[i] == '?':
+                    offset = int(path[i+1:])
+                    path = path[:i]
+                    break
+            else:
+                offset = 0
+        self.LEVEL = level
+        Archive.__init__(self, path, offset)
+        # dynamic import so not imported if not needed
+        global zlib
+        if self.LEVEL:
+            try:
+                import zlib
+            except ImportError:
+                zlib = DummyZlib()
+        else:
+            zlib = DummyZlib()
+
+
+    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)
+        try:
+            co = marshal.loads(zlib.decompress(self.lib.read(lngth)))
+        except EOFError:
+            raise ImportError, "PYZ entry '%s' failed to unmarshal" % name
+        return ispkg, co
+
+    def add(self, entry):
+        if self.os is None:
+            import os
+            self.os = os
+        nm = entry[0]
+        pth = entry[1]
+        base, ext = self.os.path.splitext(self.os.path.basename(pth))
+        ispkg = base == '__init__'
+        try:
+            txt = open(pth[:-1], 'r').read()+'\n'
+        except (IOError, OSError):
+            try:
+                f = open(pth, 'rb')
+                f.seek(8)	#skip magic and timestamp
+                bytecode = f.read()
+                marshal.loads(bytecode).co_filename # to make sure it's valid
+                obj = zlib.compress(bytecode, self.LEVEL)
+            except (IOError, ValueError, EOFError, AttributeError):
+                raise ValueError("bad bytecode in %s and no source" % pth)
+        else:
+            txt = iu._string_replace(txt, '\r\n', '\n')
+            try:
+                co = compile(txt, "%s/%s" % (self.path, nm), 'exec')
+            except SyntaxError, e:
+                print "Syntax error in", pth[:-1]
+                print e.args
+                raise
+            obj = zlib.compress(marshal.dumps(co), self.LEVEL)
+        self.toc[nm] = (ispkg, self.lib.tell(), len(obj))
+        self.lib.write(obj)
+    def update_headers(self, tocpos):
+        """add level"""
+        Archive.update_headers(self, tocpos)
+        self.lib.write(struct.pack('!i', self.LEVEL))
+    def checkmagic(self):
+        Archive.checkmagic(self)
+        self.LEVEL = struct.unpack('!i', self.lib.read(4))[0]
+
+class PYZOwner(iu.Owner):
+    def __init__(self, path):
+        self.pyz = ZlibArchive(path)
+        iu.Owner.__init__(self, path)
+    def getmod(self, nm, newmod=imp.new_module):
+        rslt = self.pyz.extract(nm)
+        if rslt is None:
+            return None
+        ispkg, co = rslt
+        mod = newmod(nm)
+        try:
+            mod.__file__ = co.co_filename
+        except AttributeError:
+            raise ImportError, "PYZ entry '%s' (%s) is not a valid code object" % (nm, repr(co))
+        if ispkg:
+            if _environ.has_key('_MEIPASS2'):
+                localpath = _environ['_MEIPASS2'][:-1]
+            else:
+                localpath = iu._os_path_dirname(self.path)
+            mod.__path__ = [self.path, localpath, iu._os_path_dirname(mod.__file__)]
+            #print "PYZOwner setting %s's __path__: %s" % (nm, mod.__path__)
+            importer = iu.PathImportDirector(mod.__path__,
+                                              {self.path:PkgInPYZImporter(nm, self),
+                                               localpath:ExtInPkgImporter(localpath, nm)},
+                                              [iu.DirOwner])
+            mod.__importsub__ = importer.getmod
+        mod.__co__ = co
+        return mod
+
+class PkgInPYZImporter:
+    def __init__(self, name, owner):
+        self.name = name
+        self.owner = owner
+    def getmod(self, nm):
+        #print "PkgInPYZImporter.getmod %s -> %s" % (nm, self.name+'.'+nm)
+        return self.owner.getmod(self.name+'.'+nm)
+class ExtInPkgImporter(iu.DirOwner):
+    def __init__(self, path, prefix):
+        iu.DirOwner.__init__(self, path)
+        self.prefix = prefix
+    def getmod(self, nm):
+        return iu.DirOwner.getmod(self, self.prefix+'.'+nm)
+
+        #XXX this should also get moved out
+        ##iu._globalownertypes.insert(0, PYZOwner)
+        ##iu.ImportManager().install()