home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 15 / AACD15.ISO / AACD / Programming / Python2 / Python20_source / Demo / imputil / importers.py
Encoding:
Python Source  |  2000-10-25  |  7.6 KB  |  249 lines

  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
  11. import sys
  12. import imp
  13. import struct
  14. import marshal
  15.  
  16. # these are .py modules
  17. import imputil
  18. import 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.  
  36. def _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.  
  44. def _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. #
  92. class 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.  
  99. def install_with(func):
  100.     FuncImporter(func).install()
  101.  
  102.  
  103. ######################################################################
  104. #
  105. # Base class for archive-based importing
  106. #
  107. class 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.  
  168. class 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. #
  187. class 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. #
  214. class 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.  
  235. def _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.  
  244. def _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())
  249.