PageRenderTime 486ms CodeModel.GetById 252ms app.highlight 16ms RepoModel.GetById 215ms app.codeStats 0ms

/Demo/imputil/importers.py

http://unladen-swallow.googlecode.com/
Python | 248 lines | 233 code | 5 blank | 10 comment | 1 complexity | 03bd18d2da08a95b50d5737e9cc28254 MD5 | raw file
  1#
  2# importers.py
  3#
  4# Demonstration subclasses of imputil.Importer
  5#
  6
  7# There should be consideration for the imports below if it is desirable
  8# to have "all" modules be imported through the imputil system.
  9
 10# these are C extensions
 11import sys
 12import imp
 13import struct
 14import marshal
 15
 16# these are .py modules
 17import imputil
 18import os
 19
 20######################################################################
 21
 22_TupleType = type(())
 23_StringType = type('')
 24
 25######################################################################
 26
 27# byte-compiled file suffic character
 28_suffix_char = __debug__ and 'c' or 'o'
 29
 30# byte-compiled file suffix
 31_suffix = '.py' + _suffix_char
 32
 33# the C_EXTENSION suffixes
 34_c_suffixes = filter(lambda x: x[2] == imp.C_EXTENSION, imp.get_suffixes())
 35
 36def _timestamp(pathname):
 37    "Return the file modification time as a Long."
 38    try:
 39        s = os.stat(pathname)
 40    except OSError:
 41        return None
 42    return long(s[8])
 43
 44def _fs_import(dir, modname, fqname):
 45    "Fetch a module from the filesystem."
 46
 47    pathname = os.path.join(dir, modname)
 48    if os.path.isdir(pathname):
 49        values = { '__pkgdir__' : pathname, '__path__' : [ pathname ] }
 50        ispkg = 1
 51        pathname = os.path.join(pathname, '__init__')
 52    else:
 53        values = { }
 54        ispkg = 0
 55
 56        # look for dynload modules
 57        for desc in _c_suffixes:
 58            file = pathname + desc[0]
 59            try:
 60                fp = open(file, desc[1])
 61            except IOError:
 62                pass
 63            else:
 64                module = imp.load_module(fqname, fp, file, desc)
 65                values['__file__'] = file
 66                return 0, module, values
 67
 68    t_py = _timestamp(pathname + '.py')
 69    t_pyc = _timestamp(pathname + _suffix)
 70    if t_py is None and t_pyc is None:
 71        return None
 72    code = None
 73    if t_py is None or (t_pyc is not None and t_pyc >= t_py):
 74        file = pathname + _suffix
 75        f = open(file, 'rb')
 76        if f.read(4) == imp.get_magic():
 77            t = struct.unpack('<I', f.read(4))[0]
 78            if t == t_py:
 79                code = marshal.load(f)
 80        f.close()
 81    if code is None:
 82        file = pathname + '.py'
 83        code = _compile(file, t_py)
 84
 85    values['__file__'] = file
 86    return ispkg, code, values
 87
 88######################################################################
 89#
 90# Simple function-based importer
 91#
 92class FuncImporter(imputil.Importer):
 93    "Importer subclass to delegate to a function rather than method overrides."
 94    def __init__(self, func):
 95        self.func = func
 96    def get_code(self, parent, modname, fqname):
 97        return self.func(parent, modname, fqname)
 98
 99def install_with(func):
100    FuncImporter(func).install()
101
102
103######################################################################
104#
105# Base class for archive-based importing
106#
107class PackageArchiveImporter(imputil.Importer):
108    """Importer subclass to import from (file) archives.
109
110    This Importer handles imports of the style <archive>.<subfile>, where
111    <archive> can be located using a subclass-specific mechanism and the
112    <subfile> is found in the archive using a subclass-specific mechanism.
113
114    This class defines two hooks for subclasses: one to locate an archive
115    (and possibly return some context for future subfile lookups), and one
116    to locate subfiles.
117    """
118
119    def get_code(self, parent, modname, fqname):
120        if parent:
121            # the Importer._finish_import logic ensures that we handle imports
122            # under the top level module (package / archive).
123            assert parent.__importer__ == self
124
125            # if a parent "package" is provided, then we are importing a
126            # sub-file from the archive.
127            result = self.get_subfile(parent.__archive__, modname)
128            if result is None:
129                return None
130            if isinstance(result, _TupleType):
131                assert len(result) == 2
132                return (0,) + result
133            return 0, result, {}
134
135        # no parent was provided, so the archive should exist somewhere on the
136        # default "path".
137        archive = self.get_archive(modname)
138        if archive is None:
139            return None
140        return 1, "", {'__archive__':archive}
141
142    def get_archive(self, modname):
143        """Get an archive of modules.
144
145        This method should locate an archive and return a value which can be
146        used by get_subfile to load modules from it. The value may be a simple
147        pathname, an open file, or a complex object that caches information
148        for future imports.
149
150        Return None if the archive was not found.
151        """
152        raise RuntimeError, "get_archive not implemented"
153
154    def get_subfile(self, archive, modname):
155        """Get code from a subfile in the specified archive.
156
157        Given the specified archive (as returned by get_archive()), locate
158        and return a code object for the specified module name.
159
160        A 2-tuple may be returned, consisting of a code object and a dict
161        of name/values to place into the target module.
162
163        Return None if the subfile was not found.
164        """
165        raise RuntimeError, "get_subfile not implemented"
166
167
168class PackageArchive(PackageArchiveImporter):
169    "PackageArchiveImporter subclass that refers to a specific archive."
170
171    def __init__(self, modname, archive_pathname):
172        self.__modname = modname
173        self.__path = archive_pathname
174
175    def get_archive(self, modname):
176        if modname == self.__modname:
177            return self.__path
178        return None
179
180    # get_subfile is passed the full pathname of the archive
181
182
183######################################################################
184#
185# Emulate the standard directory-based import mechanism
186#
187class DirectoryImporter(imputil.Importer):
188    "Importer subclass to emulate the standard importer."
189
190    def __init__(self, dir):
191        self.dir = dir
192
193    def get_code(self, parent, modname, fqname):
194        if parent:
195            dir = parent.__pkgdir__
196        else:
197            dir = self.dir
198
199        # Return the module (and other info) if found in the specified
200        # directory. Otherwise, return None.
201        return _fs_import(dir, modname, fqname)
202
203    def __repr__(self):
204        return '<%s.%s for "%s" at 0x%x>' % (self.__class__.__module__,
205                                             self.__class__.__name__,
206                                             self.dir,
207                                             id(self))
208
209
210######################################################################
211#
212# Emulate the standard path-style import mechanism
213#
214class PathImporter(imputil.Importer):
215    def __init__(self, path=sys.path):
216        self.path = path
217
218    def get_code(self, parent, modname, fqname):
219        if parent:
220            # we are looking for a module inside of a specific package
221            return _fs_import(parent.__pkgdir__, modname, fqname)
222
223        # scan sys.path, looking for the requested module
224        for dir in self.path:
225            if isinstance(dir, _StringType):
226                result = _fs_import(dir, modname, fqname)
227                if result:
228                    return result
229
230        # not found
231        return None
232
233######################################################################
234
235def _test_dir():
236    "Debug/test function to create DirectoryImporters from sys.path."
237    imputil.ImportManager().install()
238    path = sys.path[:]
239    path.reverse()
240    for d in path:
241        sys.path.insert(0, DirectoryImporter(d))
242    sys.path.insert(0, imputil.BuiltinImporter())
243
244def _test_revamp():
245    "Debug/test function for the revamped import system."
246    imputil.ImportManager().install()
247    sys.path.insert(0, PathImporter())
248    sys.path.insert(0, imputil.BuiltinImporter())