| # |
| # importers.py |
| # |
| # Demonstration subclasses of imputil.Importer |
| # |
| |
| # There should be consideration for the imports below if it is desirable |
| # to have "all" modules be imported through the imputil system. |
| |
| # these are C extensions |
| import sys |
| import imp |
| import struct |
| import marshal |
| |
| # these are .py modules |
| import imputil |
| import os |
| |
| ###################################################################### |
| |
| _TupleType = type(()) |
| _StringType = type('') |
| |
| ###################################################################### |
| |
| # byte-compiled file suffic character |
| _suffix_char = __debug__ and 'c' or 'o' |
| |
| # byte-compiled file suffix |
| _suffix = '.py' + _suffix_char |
| |
| # the C_EXTENSION suffixes |
| _c_suffixes = filter(lambda x: x[2] == imp.C_EXTENSION, imp.get_suffixes()) |
| |
| def _timestamp(pathname): |
| "Return the file modification time as a Long." |
| try: |
| s = os.stat(pathname) |
| except OSError: |
| return None |
| return long(s[8]) |
| |
| def _fs_import(dir, modname, fqname): |
| "Fetch a module from the filesystem." |
| |
| pathname = os.path.join(dir, modname) |
| if os.path.isdir(pathname): |
| values = { '__pkgdir__' : pathname, '__path__' : [ pathname ] } |
| ispkg = 1 |
| pathname = os.path.join(pathname, '__init__') |
| else: |
| values = { } |
| ispkg = 0 |
| |
| # look for dynload modules |
| for desc in _c_suffixes: |
| file = pathname + desc[0] |
| try: |
| fp = open(file, desc[1]) |
| except IOError: |
| pass |
| else: |
| module = imp.load_module(fqname, fp, file, desc) |
| values['__file__'] = file |
| return 0, module, values |
| |
| t_py = _timestamp(pathname + '.py') |
| t_pyc = _timestamp(pathname + _suffix) |
| 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): |
| file = pathname + _suffix |
| f = open(file, 'rb') |
| if f.read(4) == imp.get_magic(): |
| t = struct.unpack('<I', f.read(4))[0] |
| if t == t_py: |
| code = marshal.load(f) |
| f.close() |
| if code is None: |
| file = pathname + '.py' |
| code = _compile(file, t_py) |
| |
| values['__file__'] = file |
| return ispkg, code, values |
| |
| ###################################################################### |
| # |
| # Simple function-based importer |
| # |
| class FuncImporter(imputil.Importer): |
| "Importer subclass to delegate to a 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(imputil.Importer): |
| """Importer subclass to import from (file) archives. |
| |
| This Importer handles imports of the style <archive>.<subfile>, where |
| <archive> can be located using a subclass-specific mechanism and the |
| <subfile> is found in the archive using a subclass-specific mechanism. |
| |
| This class defines two hooks for subclasses: one to locate an archive |
| (and possibly return some context for future subfile lookups), and one |
| to locate subfiles. |
| """ |
| |
| def get_code(self, parent, modname, fqname): |
| if parent: |
| # the Importer._finish_import logic ensures that we handle imports |
| # under the top level module (package / archive). |
| assert parent.__importer__ == self |
| |
| # 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 isinstance(result, _TupleType): |
| assert len(result) == 2 |
| 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(imputil.Importer): |
| "Importer subclass to emulate the standard importer." |
| |
| def __init__(self, dir): |
| self.dir = dir |
| |
| def get_code(self, parent, modname, fqname): |
| if parent: |
| dir = parent.__pkgdir__ |
| else: |
| dir = self.dir |
| |
| # Return the module (and other info) if found in the specified |
| # directory. Otherwise, return None. |
| return _fs_import(dir, modname, fqname) |
| |
| def __repr__(self): |
| return '<%s.%s for "%s" at 0x%x>' % (self.__class__.__module__, |
| self.__class__.__name__, |
| self.dir, |
| id(self)) |
| |
| |
| ###################################################################### |
| # |
| # Emulate the standard path-style import mechanism |
| # |
| class PathImporter(imputil.Importer): |
| def __init__(self, path=sys.path): |
| self.path = path |
| |
| def get_code(self, parent, modname, fqname): |
| if parent: |
| # we are looking for a module inside of a specific package |
| return _fs_import(parent.__pkgdir__, modname, fqname) |
| |
| # scan sys.path, looking for the requested module |
| for dir in self.path: |
| if isinstance(dir, _StringType): |
| result = _fs_import(dir, modname, fqname) |
| if result: |
| return result |
| |
| # not found |
| return None |
| |
| ###################################################################### |
| |
| def _test_dir(): |
| "Debug/test function to create DirectoryImporters from sys.path." |
| imputil.ImportManager().install() |
| path = sys.path[:] |
| path.reverse() |
| for d in path: |
| sys.path.insert(0, DirectoryImporter(d)) |
| sys.path.insert(0, imputil.BuiltinImporter()) |
| |
| def _test_revamp(): |
| "Debug/test function for the revamped import system." |
| imputil.ImportManager().install() |
| sys.path.insert(0, PathImporter()) |
| sys.path.insert(0, imputil.BuiltinImporter()) |