diff --git a/Lib/imputil.py b/Lib/imputil.py index b40cb049301..04344e0203d 100644 --- a/Lib/imputil.py +++ b/Lib/imputil.py @@ -56,6 +56,9 @@ class Importer: ### returns a context regardless of Importer used. generate an ### fqname and look in sys.modules for it. + ### note that given module a.b which imports c, if c is already + ### loaded, python still wants to look for a.c + # determine the context of this import parent = self._determine_import_context(globals) @@ -321,6 +324,146 @@ class Importer: raise RuntimeError, "get_code not implemented" +###################################################################### +# +# Some handy stuff for the Importers +# + +# byte-compiled file suffic character +_suffix_char = __debug__ and 'c' or 'o' + +# byte-compiled file suffix +_suffix = '.py' + _suffix_char + + +def _compile(pathname, timestamp): + """Compile (and cache) a Python source file. + + The file specified by is compiled to a code object and + returned. + + Presuming the appropriate privileges exist, the bytecodes will be + saved back to the filesystem for future imports. The source file's + modification timestamp must be provided as a Long value. + """ + codestring = open(pathname, 'r').read() + if codestring and codestring[-1] != '\n': + codestring = codestring + '\n' + code = __builtin__.compile(codestring, pathname, 'exec') + + # try to cache the compiled code + try: + f = open(pathname + _suffix_char, 'wb') + except IOError: + pass + else: + f.write('\0\0\0\0') + f.write(struct.pack(' ':': + a = a + ':' + return a + b + else: + raise ImportError, 'no os specific module found' + + if join is None: + def join(a, b, sep=sep): + if a == '': + return b + lastchar = a[-1:] + if lastchar == '/' or lastchar == sep: + return a + b + return a + sep + b + + global _os_stat + _os_stat = stat + + global _os_path_join + _os_path_join = join + +def _os_path_isdir(pathname): + "Local replacement for os.path.isdir()." + try: + s = _os_stat(pathname) + except OSError: + return None + return (s[0] & 0170000) == 0040000 + +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): + "Fetch a module from the filesystem." + + 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 = _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('., where + can be located using a subclass-specific mechanism and the + 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) @@ -406,14 +562,11 @@ class PackageArchive(PackageArchiveImporter): # # 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: @@ -421,67 +574,13 @@ class DirectoryImporter(Importer): 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 + # defer the loading of OS-related facilities + if not _os_stat: + _os_bootstrap() - 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__, @@ -489,6 +588,60 @@ class DirectoryImporter(Importer): self.dir, id(self)) +###################################################################### +# +# Emulate the standard sys.path import mechanism +# +class SysPathImporter(Importer): + def __init__(self): + + # we're definitely going to be importing something in the future, + # so let's just load the OS-related facilities. + if not _os_stat: + _os_bootstrap() + + 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) + + # scan sys.path, looking for the requested module + for dir in sys.path: + result = _fs_import(dir, modname) + if result: + result[2]['__path__'] = [ dir ] # backwards compat + return result + + # not found + return None + + +###################################################################### +# +# Emulate the import mechanism for builtin and frozen modules +# +class BuiltinImporter(Importer): + def get_code(self, parent, modname, fqname): + if parent: + # these modules definitely do not occur within a package context + return None + + # look for the module + if imp.is_builtin(modname): + type = imp.C_BUILTIN + elif imp.is_frozen(modname): + type = imp.PY_FROZEN + else: + # not found + return None + + # got it. now load and return it. + module = imp.load_module(modname, None, modname, ('', '', type)) + return 0, module, { } + + +###################################################################### + def _test_dir(): "Debug/test function to create DirectoryImporters from sys.path." path = sys.path[:] @@ -496,4 +649,18 @@ def _test_dir(): for d in path: DirectoryImporter(d).install() +def _test_revamp(): + "Debug/test function for the revamped import system." + BuiltinImporter().install() + SysPathImporter().install() + +def _print_importers(): + items = sys.modules.items() + items.sort() + for name, module in items: + if module: + print name, module.__dict__.get('__importer__', '-- no importer') + else: + print name, '-- non-existent module' + ######################################################################