summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/imputil.py295
1 files changed, 231 insertions, 64 deletions
diff --git a/Lib/imputil.py b/Lib/imputil.py
index b40cb04..04344e0 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)
@@ -323,6 +326,146 @@ class Importer:
######################################################################
#
+# 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 <pathname> 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('<I', timestamp))
+ marshal.dump(code, f)
+ f.flush()
+ f.seek(0, 0)
+ f.write(imp.get_magic())
+ f.close()
+
+ return code
+
+_os_stat = _os_path_join = None
+def _os_bootstrap():
+ "Set up 'os' module replacement functions for use during import bootstrap."
+
+ names = sys.builtin_module_names
+
+ join = None
+ if 'posix' in names:
+ sep = '/'
+ from posix import stat
+ elif 'nt' in names:
+ sep = '\\'
+ from nt import stat
+ elif 'dos' in names:
+ sep = '\\'
+ from dos import stat
+ elif 'os2' in names:
+ sep = '\\'
+ from os2 import stat
+ elif 'mac' in names:
+ from mac import stat
+ def join(a, b):
+ if a == '':
+ return b
+ path = s
+ if ':' not in a:
+ a = ':' + a
+ if a[-1:] <> ':':
+ 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('<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(Importer):
@@ -341,10 +484,23 @@ def install_with(func):
# Base class for archive-based importing
#
class PackageArchiveImporter(Importer):
- "Importer subclass to import from (file) archives."
+ """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)
@@ -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
-
- 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('<I', f.read(4))[0]
- if t == t_py:
- code = marshal.load(f)
- f.close()
- if code is None:
- code = self._compile(pathname + '.py', t_py)
- return ispkg, code, values
-
- def _timestamp(self, pathname):
- try:
- s = self.os.stat(pathname)
- except OSError:
- return None
- return long(s[8])
-
- def _compile(self, pathname, timestamp):
- 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 + self.ext_char, 'wb')
- f.write('\0\0\0\0')
- f.write(struct.pack('<I', timestamp))
- marshal.dump(code, f)
- f.flush()
- f.seek(0, 0)
- f.write(imp.get_magic())
- f.close()
- except OSError:
- pass
+ # defer the loading of OS-related facilities
+ if not _os_stat:
+ _os_bootstrap()
- return code
+ # Return the module (and other info) if found in the specified
+ # directory. Otherwise, return None.
+ return _fs_import(dir, modname)
def __repr__(self):
return '<%s.%s for "%s" at 0x%x>' % (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'
+
######################################################################