Mercurial > cpython-lru_cache-weakref
annotate Lib/ihooks.py @ 4566:6403c94de50d legacy-trunk
Added safeguard against failure in __del__.
| author | Guido van Rossum <guido@python.org> |
|---|---|
| date | Wed, 27 Nov 1996 19:39:58 +0000 |
| parents | af754961acc5 |
| children | b8b6a88f7f44 |
| rev | line source |
|---|---|
| 2887 | 1 """Import hook support. |
| 2 | |
| 3 Consistent use of this module will make it possible to change the | |
| 4 different mechanisms involved in loading modules independently. | |
| 5 | |
| 6 While the built-in module imp exports interfaces to the built-in | |
| 7 module searching and loading algorithm, and it is possible to replace | |
| 8 the built-in function __import__ in order to change the semantics of | |
| 9 the import statement, until now it has been difficult to combine the | |
| 10 effect of different __import__ hacks, like loading modules from URLs | |
| 11 (rimport.py), implementing a hierarchical module namespace (newimp.py) | |
| 12 or restricted execution (rexec.py). | |
| 13 | |
| 14 This module defines three new concepts: | |
| 15 | |
| 16 (1) A "file system hooks" class provides an interface to a filesystem. | |
| 17 | |
| 18 One hooks class is defined (Hooks), which uses the interface provided | |
| 19 by standard modules os and os.path. It should be used as the base | |
| 20 class for other hooks classes. | |
| 21 | |
| 22 (2) A "module loader" class provides an interface to to search for a | |
| 23 module in a search path and to load it. It defines a method which | |
| 24 searches for a module in a single directory; by overriding this method | |
| 25 one can redefine the details of the search. If the directory is None, | |
| 26 built-in and frozen modules are searched instead. | |
| 27 | |
| 28 Two module loader class are defined, both implementing the search | |
| 29 strategy used by the built-in __import__ function: ModuleLoader uses | |
| 30 the imp module's find_module interface, while HookableModuleLoader | |
| 31 uses a file system hooks class to interact with the file system. Both | |
| 32 use the imp module's load_* interfaces to actually load the module. | |
| 33 | |
| 34 (3) A "module importer" class provides an interface to import a | |
| 35 module, as well as interfaces to reload and unload a module. It also | |
| 36 provides interfaces to install and uninstall itself instead of the | |
| 37 default __import__ and reload (and unload) functions. | |
| 38 | |
| 39 One module importer class is defined (ModuleImporter), which uses a | |
| 40 module loader instance passed in (by default HookableModuleLoader is | |
| 41 instantiated). | |
| 42 | |
| 43 The classes defined here should be used as base classes for extended | |
| 44 functionality along those lines. | |
| 45 | |
| 46 If a module mporter class supports dotted names, its import_module() | |
| 47 must return a different value depending on whether it is called on | |
| 48 behalf of a "from ... import ..." statement or not. (This is caused | |
| 49 by the way the __import__ hook is used by the Python interpreter.) It | |
| 50 would also do wise to install a different version of reload(). | |
| 51 | |
| 52 XXX Should the imp.load_* functions also be called via the hooks | |
| 53 instance? | |
| 54 | |
| 55 """ | |
| 56 | |
| 57 | |
| 58 import __builtin__ | |
| 59 import imp | |
| 60 import os | |
| 61 import sys | |
| 62 | |
| 63 | |
| 64 from imp import C_EXTENSION, PY_SOURCE, PY_COMPILED | |
| 65 BUILTIN_MODULE = 32 | |
| 66 FROZEN_MODULE = 33 | |
| 67 | |
| 68 | |
| 69 class _Verbose: | |
| 70 | |
| 71 def __init__(self, verbose = 0): | |
| 72 self.verbose = verbose | |
| 73 | |
| 74 def get_verbose(self): | |
| 75 return self.verbose | |
| 76 | |
| 77 def set_verbose(self, verbose): | |
| 78 self.verbose = verbose | |
| 79 | |
| 80 # XXX The following is an experimental interface | |
| 81 | |
| 82 def note(self, *args): | |
| 83 if self.verbose: | |
| 84 apply(self.message, args) | |
| 85 | |
| 86 def message(self, format, *args): | |
| 87 print format%args | |
| 88 | |
| 89 | |
| 90 class BasicModuleLoader(_Verbose): | |
| 91 | |
| 92 """Basic module loader. | |
| 93 | |
| 94 This provides the same functionality as built-in import. It | |
| 95 doesn't deal with checking sys.modules -- all it provides is | |
| 96 find_module() and a load_module(), as well as find_module_in_dir() | |
| 97 which searches just one directory, and can be overridden by a | |
| 98 derived class to change the module search algorithm when the basic | |
| 99 dependency on sys.path is unchanged. | |
| 100 | |
| 101 The interface is a little more convenient than imp's: | |
| 102 find_module(name, [path]) returns None or 'stuff', and | |
| 103 load_module(name, stuff) loads the module. | |
| 104 | |
| 105 """ | |
| 106 | |
| 107 def find_module(self, name, path = None): | |
| 108 if path is None: | |
| 109 path = [None] + self.default_path() | |
| 110 for dir in path: | |
| 111 stuff = self.find_module_in_dir(name, dir) | |
| 112 if stuff: return stuff | |
| 113 return None | |
| 114 | |
| 115 def default_path(self): | |
| 116 return sys.path | |
| 117 | |
| 118 def find_module_in_dir(self, name, dir): | |
| 119 if dir is None: | |
| 120 return self.find_builtin_module(name) | |
| 121 else: | |
| 122 try: | |
| 123 return imp.find_module(name, [dir]) | |
| 124 except ImportError: | |
| 125 return None | |
| 126 | |
| 127 def find_builtin_module(self, name): | |
| 128 if imp.is_builtin(name): | |
| 129 return None, '', ('', '', BUILTIN_MODULE) | |
| 130 if imp.is_frozen(name): | |
| 131 return None, '', ('', '', FROZEN_MODULE) | |
| 132 return None | |
| 133 | |
| 134 def load_module(self, name, stuff): | |
| 135 file, filename, (suff, mode, type) = stuff | |
|
4260
516ddf50194d
close module file after loading
Guido van Rossum <guido@python.org>
parents:
3686
diff
changeset
|
136 try: |
|
516ddf50194d
close module file after loading
Guido van Rossum <guido@python.org>
parents:
3686
diff
changeset
|
137 if type == BUILTIN_MODULE: |
|
516ddf50194d
close module file after loading
Guido van Rossum <guido@python.org>
parents:
3686
diff
changeset
|
138 return imp.init_builtin(name) |
|
516ddf50194d
close module file after loading
Guido van Rossum <guido@python.org>
parents:
3686
diff
changeset
|
139 if type == FROZEN_MODULE: |
|
516ddf50194d
close module file after loading
Guido van Rossum <guido@python.org>
parents:
3686
diff
changeset
|
140 return imp.init_frozen(name) |
|
516ddf50194d
close module file after loading
Guido van Rossum <guido@python.org>
parents:
3686
diff
changeset
|
141 if type == C_EXTENSION: |
|
516ddf50194d
close module file after loading
Guido van Rossum <guido@python.org>
parents:
3686
diff
changeset
|
142 return imp.load_dynamic(name, filename, file) |
|
516ddf50194d
close module file after loading
Guido van Rossum <guido@python.org>
parents:
3686
diff
changeset
|
143 if type == PY_SOURCE: |
|
516ddf50194d
close module file after loading
Guido van Rossum <guido@python.org>
parents:
3686
diff
changeset
|
144 return imp.load_source(name, filename, file) |
|
516ddf50194d
close module file after loading
Guido van Rossum <guido@python.org>
parents:
3686
diff
changeset
|
145 if type == PY_COMPILED: |
|
516ddf50194d
close module file after loading
Guido van Rossum <guido@python.org>
parents:
3686
diff
changeset
|
146 return imp.load_compiled(name, filename, file) |
|
516ddf50194d
close module file after loading
Guido van Rossum <guido@python.org>
parents:
3686
diff
changeset
|
147 finally: |
|
516ddf50194d
close module file after loading
Guido van Rossum <guido@python.org>
parents:
3686
diff
changeset
|
148 if file: file.close() |
| 2887 | 149 raise ImportError, "Unrecognized module type (%s) for %s" % \ |
| 150 (`type`, name) | |
| 151 | |
| 152 | |
| 153 class Hooks(_Verbose): | |
| 154 | |
| 155 """Hooks into the filesystem and interpreter. | |
| 156 | |
| 157 By deriving a subclass you can redefine your filesystem interface, | |
| 158 e.g. to merge it with the URL space. | |
| 159 | |
| 160 This base class behaves just like the native filesystem. | |
| 161 | |
| 162 """ | |
| 163 | |
| 164 # imp interface | |
| 165 def get_suffixes(self): return imp.get_suffixes() | |
| 166 def new_module(self, name): return imp.new_module(name) | |
| 167 def is_builtin(self, name): return imp.is_builtin(name) | |
| 168 def init_builtin(self, name): return imp.init_builtin(name) | |
| 169 def is_frozen(self, name): return imp.is_frozen(name) | |
| 170 def init_frozen(self, name): return imp.init_frozen(name) | |
| 171 def get_frozen_object(self, name): return imp.get_frozen_object(name) | |
| 172 def load_source(self, name, filename, file=None): | |
| 173 return imp.load_source(name, filename, file) | |
| 174 def load_compiled(self, name, filename, file=None): | |
| 175 return imp.load_compiled(name, filename, file) | |
| 176 def load_dynamic(self, name, filename, file=None): | |
| 177 return imp.load_dynamic(name, filename, file) | |
| 178 | |
| 179 def add_module(self, name): | |
| 180 d = self.modules_dict() | |
| 181 if d.has_key(name): return d[name] | |
| 182 d[name] = m = self.new_module(name) | |
| 183 return m | |
| 184 | |
| 185 # sys interface | |
| 186 def modules_dict(self): return sys.modules | |
| 187 def default_path(self): return sys.path | |
| 188 | |
| 189 def path_split(self, x): return os.path.split(x) | |
| 190 def path_join(self, x, y): return os.path.join(x, y) | |
| 191 def path_isabs(self, x): return os.path.isabs(x) | |
| 192 # etc. | |
| 193 | |
| 194 def path_exists(self, x): return os.path.exists(x) | |
| 195 def path_isdir(self, x): return os.path.isdir(x) | |
| 196 def path_isfile(self, x): return os.path.isfile(x) | |
| 197 def path_islink(self, x): return os.path.islink(x) | |
| 198 # etc. | |
| 199 | |
| 200 def openfile(self, *x): return apply(open, x) | |
| 201 openfile_error = IOError | |
| 202 def listdir(self, x): return os.listdir(x) | |
| 203 listdir_error = os.error | |
| 204 # etc. | |
| 205 | |
| 206 | |
| 207 class ModuleLoader(BasicModuleLoader): | |
| 208 | |
| 209 """Default module loader; uses file system hooks. | |
| 210 | |
| 211 By defining suitable hooks, you might be able to load modules from | |
| 212 other sources than the file system, e.g. from compressed or | |
| 213 encrypted files, tar files or (if you're brave!) URLs. | |
| 214 | |
| 215 """ | |
| 216 | |
| 217 def __init__(self, hooks = None, verbose = 0): | |
| 218 BasicModuleLoader.__init__(self, verbose) | |
| 219 self.hooks = hooks or Hooks(verbose) | |
| 220 | |
| 221 def default_path(self): | |
| 222 return self.hooks.default_path() | |
| 223 | |
| 224 def modules_dict(self): | |
| 225 return self.hooks.modules_dict() | |
| 226 | |
| 227 def get_hooks(self): | |
| 228 return self.hooks | |
| 229 | |
| 230 def set_hooks(self, hooks): | |
| 231 self.hooks = hooks | |
| 232 | |
| 233 def find_builtin_module(self, name): | |
| 234 if self.hooks.is_builtin(name): | |
| 235 return None, '', ('', '', BUILTIN_MODULE) | |
| 236 if self.hooks.is_frozen(name): | |
| 237 return None, '', ('', '', FROZEN_MODULE) | |
| 238 return None | |
| 239 | |
| 240 def find_module_in_dir(self, name, dir): | |
| 241 if dir is None: | |
| 242 return self.find_builtin_module(name) | |
| 243 for info in self.hooks.get_suffixes(): | |
| 244 suff, mode, type = info | |
| 245 fullname = self.hooks.path_join(dir, name+suff) | |
| 246 try: | |
| 247 fp = self.hooks.openfile(fullname, mode) | |
| 248 return fp, fullname, info | |
| 249 except self.hooks.openfile_error: | |
| 250 pass | |
| 251 return None | |
| 252 | |
| 253 def load_module(self, name, stuff): | |
| 254 file, filename, (suff, mode, type) = stuff | |
| 255 if type == BUILTIN_MODULE: | |
| 256 return self.hooks.init_builtin(name) | |
| 257 if type == FROZEN_MODULE: | |
| 258 return self.hooks.init_frozen(name) | |
| 259 if type == C_EXTENSION: | |
|
4361
af754961acc5
Set the __file__ attribute of the imported module in both versions of
Guido van Rossum <guido@python.org>
parents:
4356
diff
changeset
|
260 m = self.hooks.load_dynamic(name, filename, file) |
|
af754961acc5
Set the __file__ attribute of the imported module in both versions of
Guido van Rossum <guido@python.org>
parents:
4356
diff
changeset
|
261 elif type == PY_SOURCE: |
|
af754961acc5
Set the __file__ attribute of the imported module in both versions of
Guido van Rossum <guido@python.org>
parents:
4356
diff
changeset
|
262 m = self.hooks.load_source(name, filename, file) |
|
af754961acc5
Set the __file__ attribute of the imported module in both versions of
Guido van Rossum <guido@python.org>
parents:
4356
diff
changeset
|
263 elif type == PY_COMPILED: |
|
af754961acc5
Set the __file__ attribute of the imported module in both versions of
Guido van Rossum <guido@python.org>
parents:
4356
diff
changeset
|
264 m = self.hooks.load_compiled(name, filename, file) |
|
af754961acc5
Set the __file__ attribute of the imported module in both versions of
Guido van Rossum <guido@python.org>
parents:
4356
diff
changeset
|
265 else: |
|
af754961acc5
Set the __file__ attribute of the imported module in both versions of
Guido van Rossum <guido@python.org>
parents:
4356
diff
changeset
|
266 raise ImportError, "Unrecognized module type (%s) for %s" % \ |
|
af754961acc5
Set the __file__ attribute of the imported module in both versions of
Guido van Rossum <guido@python.org>
parents:
4356
diff
changeset
|
267 (`type`, name) |
|
af754961acc5
Set the __file__ attribute of the imported module in both versions of
Guido van Rossum <guido@python.org>
parents:
4356
diff
changeset
|
268 m.__file__ = filename |
|
af754961acc5
Set the __file__ attribute of the imported module in both versions of
Guido van Rossum <guido@python.org>
parents:
4356
diff
changeset
|
269 return m |
| 2887 | 270 |
| 271 | |
| 272 class FancyModuleLoader(ModuleLoader): | |
| 273 | |
| 274 """Fancy module loader -- parses and execs the code itself.""" | |
| 275 | |
| 276 def load_module(self, name, stuff): | |
| 277 file, filename, (suff, mode, type) = stuff | |
| 278 if type == FROZEN_MODULE: | |
| 279 code = self.hooks.get_frozen_object(name) | |
| 280 elif type == PY_COMPILED: | |
|
3686
bdbda939b999
Import marshal before using it :-(
Guido van Rossum <guido@python.org>
parents:
2935
diff
changeset
|
281 import marshal |
| 2887 | 282 file.seek(8) |
| 283 code = marshal.load(file) | |
| 284 elif type == PY_SOURCE: | |
| 285 data = file.read() | |
| 286 code = compile(data, filename, 'exec') | |
| 287 else: | |
| 288 return ModuleLoader.load_module(self, name, stuff) | |
| 289 m = self.hooks.add_module(name) | |
|
4361
af754961acc5
Set the __file__ attribute of the imported module in both versions of
Guido van Rossum <guido@python.org>
parents:
4356
diff
changeset
|
290 m.__file__ = filename |
| 2887 | 291 exec code in m.__dict__ |
| 292 return m | |
| 293 | |
| 294 | |
| 295 class ModuleImporter(_Verbose): | |
| 296 | |
| 297 """Default module importer; uses module loader. | |
| 298 | |
| 299 This provides the same functionality as built-in import, when | |
| 300 combined with ModuleLoader. | |
| 301 | |
| 302 """ | |
| 303 | |
| 304 def __init__(self, loader = None, verbose = 0): | |
| 305 _Verbose.__init__(self, verbose) | |
| 306 self.loader = loader or ModuleLoader(None, verbose) | |
| 307 self.modules = self.loader.modules_dict() | |
| 308 | |
| 309 def get_loader(self): | |
| 310 return self.loader | |
| 311 | |
| 312 def set_loader(self, loader): | |
| 313 self.loader = loader | |
| 314 | |
| 315 def get_hooks(self): | |
| 316 return self.loader.get_hooks() | |
| 317 | |
| 318 def set_hooks(self, hooks): | |
| 319 return self.loader.set_hooks(hooks) | |
| 320 | |
| 321 def import_module(self, name, globals={}, locals={}, fromlist=[]): | |
| 322 if self.modules.has_key(name): | |
| 323 return self.modules[name] # Fast path | |
| 324 stuff = self.loader.find_module(name) | |
| 325 if not stuff: | |
| 326 raise ImportError, "No module named %s" % name | |
| 327 return self.loader.load_module(name, stuff) | |
| 328 | |
| 329 def reload(self, module, path = None): | |
| 2935 | 330 name = module.__name__ |
| 2887 | 331 stuff = self.loader.find_module(name, path) |
| 332 if not stuff: | |
| 333 raise ImportError, "Module %s not found for reload" % name | |
| 334 return self.loader.load_module(name, stuff) | |
| 335 | |
| 336 def unload(self, module): | |
| 337 del self.modules[module.__name__] | |
| 338 # XXX Should this try to clear the module's namespace? | |
| 339 | |
| 340 def install(self): | |
| 341 self.save_import_module = __builtin__.__import__ | |
| 342 self.save_reload = __builtin__.reload | |
| 343 if not hasattr(__builtin__, 'unload'): | |
| 344 __builtin__.unload = None | |
| 345 self.save_unload = __builtin__.unload | |
| 346 __builtin__.__import__ = self.import_module | |
| 347 __builtin__.reload = self.reload | |
| 348 __builtin__.unload = self.unload | |
| 349 | |
| 350 def uninstall(self): | |
| 351 __builtin__.__import__ = self.save_import_module | |
| 352 __builtin__.reload = self.save_reload | |
| 353 __builtin__.unload = self.save_unload | |
| 354 if not __builtin__.unload: | |
| 355 del __builtin__.unload | |
| 356 | |
| 357 | |
| 358 default_importer = None | |
| 359 current_importer = None | |
| 360 | |
| 361 def install(importer = None): | |
| 362 global current_importer | |
| 363 current_importer = importer or default_importer or ModuleImporter() | |
| 364 current_importer.install() | |
| 365 | |
| 366 def uninstall(): | |
| 367 global current_importer | |
| 368 current_importer.uninstall() |
