| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 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): | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 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]) | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 43 |  | 
 | 44 | def _fs_import(dir, modname, fqname): | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 45 |     "Fetch a module from the filesystem." | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 46 |  | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 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 | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 55 |  | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 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 | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 67 |  | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 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) | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 84 |  | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 85 |     values['__file__'] = file | 
 | 86 |     return ispkg, code, values | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 87 |  | 
 | 88 | ###################################################################### | 
 | 89 | # | 
 | 90 | # Simple function-based importer | 
 | 91 | # | 
 | 92 | class FuncImporter(imputil.Importer): | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 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) | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 98 |  | 
 | 99 | def install_with(func): | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 100 |     FuncImporter(func).install() | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 101 |  | 
 | 102 |  | 
 | 103 | ###################################################################### | 
 | 104 | # | 
 | 105 | # Base class for archive-based importing | 
 | 106 | # | 
 | 107 | class PackageArchiveImporter(imputil.Importer): | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 108 |     """Importer subclass to import from (file) archives. | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 109 |  | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 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. | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 113 |  | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 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. | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 117 |     """ | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 118 |  | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 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 | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 124 |  | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 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, {} | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 134 |  | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 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} | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 141 |  | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 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" | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 166 |  | 
 | 167 |  | 
 | 168 | class PackageArchive(PackageArchiveImporter): | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 169 |     "PackageArchiveImporter subclass that refers to a specific archive." | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 170 |  | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 171 |     def __init__(self, modname, archive_pathname): | 
 | 172 |         self.__modname = modname | 
 | 173 |         self.__path = archive_pathname | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 174 |  | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 175 |     def get_archive(self, modname): | 
 | 176 |         if modname == self.__modname: | 
 | 177 |             return self.__path | 
 | 178 |         return None | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 179 |  | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 180 |     # get_subfile is passed the full pathname of the archive | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 181 |  | 
 | 182 |  | 
 | 183 | ###################################################################### | 
 | 184 | # | 
 | 185 | # Emulate the standard directory-based import mechanism | 
 | 186 | # | 
 | 187 | class DirectoryImporter(imputil.Importer): | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 188 |     "Importer subclass to emulate the standard importer." | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 189 |  | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 190 |     def __init__(self, dir): | 
 | 191 |         self.dir = dir | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 192 |  | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 193 |     def get_code(self, parent, modname, fqname): | 
 | 194 |         if parent: | 
 | 195 |             dir = parent.__pkgdir__ | 
 | 196 |         else: | 
 | 197 |             dir = self.dir | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 198 |  | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 199 |         # Return the module (and other info) if found in the specified | 
 | 200 |         # directory. Otherwise, return None. | 
 | 201 |         return _fs_import(dir, modname, fqname) | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 202 |  | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 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)) | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 208 |  | 
 | 209 |  | 
 | 210 | ###################################################################### | 
 | 211 | # | 
 | 212 | # Emulate the standard path-style import mechanism | 
 | 213 | # | 
 | 214 | class PathImporter(imputil.Importer): | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 215 |     def __init__(self, path=sys.path): | 
 | 216 |         self.path = path | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 217 |  | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 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) | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 222 |  | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 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 | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 229 |  | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 230 |         # not found | 
 | 231 |         return None | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 232 |  | 
 | 233 | ###################################################################### | 
 | 234 |  | 
 | 235 | def _test_dir(): | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 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()) | 
| Greg Stein | e97ecf9 | 2000-06-29 11:05:30 +0000 | [diff] [blame] | 243 |  | 
 | 244 | def _test_revamp(): | 
| Greg Stein | dd6eefb | 2000-07-18 09:09:48 +0000 | [diff] [blame] | 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()) |