/Demo/imputil/importers.py

http://unladen-swallow.googlecode.com/ · Python · 248 lines · 132 code · 43 blank · 73 comment · 30 complexity · 03bd18d2da08a95b50d5737e9cc28254 MD5 · raw file

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