|
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 by rimport.py, or restricted execution by rexec.py. |
|
12 |
|
13 This module defines three new concepts: |
|
14 |
|
15 1) A "file system hooks" class provides an interface to a filesystem. |
|
16 |
|
17 One hooks class is defined (Hooks), which uses the interface provided |
|
18 by standard modules os and os.path. It should be used as the base |
|
19 class for other hooks classes. |
|
20 |
|
21 2) A "module loader" class provides an interface to search for a |
|
22 module in a search path and to load it. It defines a method which |
|
23 searches for a module in a single directory; by overriding this method |
|
24 one can redefine the details of the search. If the directory is None, |
|
25 built-in and frozen modules are searched instead. |
|
26 |
|
27 Two module loader class are defined, both implementing the search |
|
28 strategy used by the built-in __import__ function: ModuleLoader uses |
|
29 the imp module's find_module interface, while HookableModuleLoader |
|
30 uses a file system hooks class to interact with the file system. Both |
|
31 use the imp module's load_* interfaces to actually load the module. |
|
32 |
|
33 3) A "module importer" class provides an interface to import a |
|
34 module, as well as interfaces to reload and unload a module. It also |
|
35 provides interfaces to install and uninstall itself instead of the |
|
36 default __import__ and reload (and unload) functions. |
|
37 |
|
38 One module importer class is defined (ModuleImporter), which uses a |
|
39 module loader instance passed in (by default HookableModuleLoader is |
|
40 instantiated). |
|
41 |
|
42 The classes defined here should be used as base classes for extended |
|
43 functionality along those lines. |
|
44 |
|
45 If a module importer class supports dotted names, its import_module() |
|
46 must return a different value depending on whether it is called on |
|
47 behalf of a "from ... import ..." statement or not. (This is caused |
|
48 by the way the __import__ hook is used by the Python interpreter.) It |
|
49 would also do wise to install a different version of reload(). |
|
50 |
|
51 """ |
|
52 from warnings import warnpy3k |
|
53 warnpy3k("the ihooks module has been removed in Python 3.0", stacklevel=2) |
|
54 del warnpy3k |
|
55 |
|
56 import __builtin__ |
|
57 import imp |
|
58 import os |
|
59 import sys |
|
60 |
|
61 __all__ = ["BasicModuleLoader","Hooks","ModuleLoader","FancyModuleLoader", |
|
62 "BasicModuleImporter","ModuleImporter","install","uninstall"] |
|
63 |
|
64 VERBOSE = 0 |
|
65 |
|
66 |
|
67 from imp import C_EXTENSION, PY_SOURCE, PY_COMPILED |
|
68 from imp import C_BUILTIN, PY_FROZEN, PKG_DIRECTORY |
|
69 BUILTIN_MODULE = C_BUILTIN |
|
70 FROZEN_MODULE = PY_FROZEN |
|
71 |
|
72 |
|
73 class _Verbose: |
|
74 |
|
75 def __init__(self, verbose = VERBOSE): |
|
76 self.verbose = verbose |
|
77 |
|
78 def get_verbose(self): |
|
79 return self.verbose |
|
80 |
|
81 def set_verbose(self, verbose): |
|
82 self.verbose = verbose |
|
83 |
|
84 # XXX The following is an experimental interface |
|
85 |
|
86 def note(self, *args): |
|
87 if self.verbose: |
|
88 self.message(*args) |
|
89 |
|
90 def message(self, format, *args): |
|
91 if args: |
|
92 print format%args |
|
93 else: |
|
94 print format |
|
95 |
|
96 |
|
97 class BasicModuleLoader(_Verbose): |
|
98 |
|
99 """Basic module loader. |
|
100 |
|
101 This provides the same functionality as built-in import. It |
|
102 doesn't deal with checking sys.modules -- all it provides is |
|
103 find_module() and a load_module(), as well as find_module_in_dir() |
|
104 which searches just one directory, and can be overridden by a |
|
105 derived class to change the module search algorithm when the basic |
|
106 dependency on sys.path is unchanged. |
|
107 |
|
108 The interface is a little more convenient than imp's: |
|
109 find_module(name, [path]) returns None or 'stuff', and |
|
110 load_module(name, stuff) loads the module. |
|
111 |
|
112 """ |
|
113 |
|
114 def find_module(self, name, path = None): |
|
115 if path is None: |
|
116 path = [None] + self.default_path() |
|
117 for dir in path: |
|
118 stuff = self.find_module_in_dir(name, dir) |
|
119 if stuff: return stuff |
|
120 return None |
|
121 |
|
122 def default_path(self): |
|
123 return sys.path |
|
124 |
|
125 def find_module_in_dir(self, name, dir): |
|
126 if dir is None: |
|
127 return self.find_builtin_module(name) |
|
128 else: |
|
129 try: |
|
130 return imp.find_module(name, [dir]) |
|
131 except ImportError: |
|
132 return None |
|
133 |
|
134 def find_builtin_module(self, name): |
|
135 # XXX frozen packages? |
|
136 if imp.is_builtin(name): |
|
137 return None, '', ('', '', BUILTIN_MODULE) |
|
138 if imp.is_frozen(name): |
|
139 return None, '', ('', '', FROZEN_MODULE) |
|
140 return None |
|
141 |
|
142 def load_module(self, name, stuff): |
|
143 file, filename, info = stuff |
|
144 try: |
|
145 return imp.load_module(name, file, filename, info) |
|
146 finally: |
|
147 if file: file.close() |
|
148 |
|
149 |
|
150 class Hooks(_Verbose): |
|
151 |
|
152 """Hooks into the filesystem and interpreter. |
|
153 |
|
154 By deriving a subclass you can redefine your filesystem interface, |
|
155 e.g. to merge it with the URL space. |
|
156 |
|
157 This base class behaves just like the native filesystem. |
|
158 |
|
159 """ |
|
160 |
|
161 # imp interface |
|
162 def get_suffixes(self): return imp.get_suffixes() |
|
163 def new_module(self, name): return imp.new_module(name) |
|
164 def is_builtin(self, name): return imp.is_builtin(name) |
|
165 def init_builtin(self, name): return imp.init_builtin(name) |
|
166 def is_frozen(self, name): return imp.is_frozen(name) |
|
167 def init_frozen(self, name): return imp.init_frozen(name) |
|
168 def get_frozen_object(self, name): return imp.get_frozen_object(name) |
|
169 def load_source(self, name, filename, file=None): |
|
170 return imp.load_source(name, filename, file) |
|
171 def load_compiled(self, name, filename, file=None): |
|
172 return imp.load_compiled(name, filename, file) |
|
173 def load_dynamic(self, name, filename, file=None): |
|
174 return imp.load_dynamic(name, filename, file) |
|
175 def load_package(self, name, filename, file=None): |
|
176 return imp.load_module(name, file, filename, ("", "", PKG_DIRECTORY)) |
|
177 |
|
178 def add_module(self, name): |
|
179 d = self.modules_dict() |
|
180 if name in d: return d[name] |
|
181 d[name] = m = self.new_module(name) |
|
182 return m |
|
183 |
|
184 # sys interface |
|
185 def modules_dict(self): return sys.modules |
|
186 def default_path(self): return sys.path |
|
187 |
|
188 def path_split(self, x): return os.path.split(x) |
|
189 def path_join(self, x, y): return os.path.join(x, y) |
|
190 def path_isabs(self, x): return os.path.isabs(x) |
|
191 # etc. |
|
192 |
|
193 def path_exists(self, x): return os.path.exists(x) |
|
194 def path_isdir(self, x): return os.path.isdir(x) |
|
195 def path_isfile(self, x): return os.path.isfile(x) |
|
196 def path_islink(self, x): return os.path.islink(x) |
|
197 # etc. |
|
198 |
|
199 def openfile(self, *x): return open(*x) |
|
200 openfile_error = IOError |
|
201 def listdir(self, x): return os.listdir(x) |
|
202 listdir_error = os.error |
|
203 # etc. |
|
204 |
|
205 |
|
206 class ModuleLoader(BasicModuleLoader): |
|
207 |
|
208 """Default module loader; uses file system hooks. |
|
209 |
|
210 By defining suitable hooks, you might be able to load modules from |
|
211 other sources than the file system, e.g. from compressed or |
|
212 encrypted files, tar files or (if you're brave!) URLs. |
|
213 |
|
214 """ |
|
215 |
|
216 def __init__(self, hooks = None, verbose = VERBOSE): |
|
217 BasicModuleLoader.__init__(self, verbose) |
|
218 self.hooks = hooks or Hooks(verbose) |
|
219 |
|
220 def default_path(self): |
|
221 return self.hooks.default_path() |
|
222 |
|
223 def modules_dict(self): |
|
224 return self.hooks.modules_dict() |
|
225 |
|
226 def get_hooks(self): |
|
227 return self.hooks |
|
228 |
|
229 def set_hooks(self, hooks): |
|
230 self.hooks = hooks |
|
231 |
|
232 def find_builtin_module(self, name): |
|
233 # XXX frozen packages? |
|
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, allow_packages=1): |
|
241 if dir is None: |
|
242 return self.find_builtin_module(name) |
|
243 if allow_packages: |
|
244 fullname = self.hooks.path_join(dir, name) |
|
245 if self.hooks.path_isdir(fullname): |
|
246 stuff = self.find_module_in_dir("__init__", fullname, 0) |
|
247 if stuff: |
|
248 file = stuff[0] |
|
249 if file: file.close() |
|
250 return None, fullname, ('', '', PKG_DIRECTORY) |
|
251 for info in self.hooks.get_suffixes(): |
|
252 suff, mode, type = info |
|
253 fullname = self.hooks.path_join(dir, name+suff) |
|
254 try: |
|
255 fp = self.hooks.openfile(fullname, mode) |
|
256 return fp, fullname, info |
|
257 except self.hooks.openfile_error: |
|
258 pass |
|
259 return None |
|
260 |
|
261 def load_module(self, name, stuff): |
|
262 file, filename, info = stuff |
|
263 (suff, mode, type) = info |
|
264 try: |
|
265 if type == BUILTIN_MODULE: |
|
266 return self.hooks.init_builtin(name) |
|
267 if type == FROZEN_MODULE: |
|
268 return self.hooks.init_frozen(name) |
|
269 if type == C_EXTENSION: |
|
270 m = self.hooks.load_dynamic(name, filename, file) |
|
271 elif type == PY_SOURCE: |
|
272 m = self.hooks.load_source(name, filename, file) |
|
273 elif type == PY_COMPILED: |
|
274 m = self.hooks.load_compiled(name, filename, file) |
|
275 elif type == PKG_DIRECTORY: |
|
276 m = self.hooks.load_package(name, filename, file) |
|
277 else: |
|
278 raise ImportError, "Unrecognized module type (%r) for %s" % \ |
|
279 (type, name) |
|
280 finally: |
|
281 if file: file.close() |
|
282 m.__file__ = filename |
|
283 return m |
|
284 |
|
285 |
|
286 class FancyModuleLoader(ModuleLoader): |
|
287 |
|
288 """Fancy module loader -- parses and execs the code itself.""" |
|
289 |
|
290 def load_module(self, name, stuff): |
|
291 file, filename, (suff, mode, type) = stuff |
|
292 realfilename = filename |
|
293 path = None |
|
294 |
|
295 if type == PKG_DIRECTORY: |
|
296 initstuff = self.find_module_in_dir("__init__", filename, 0) |
|
297 if not initstuff: |
|
298 raise ImportError, "No __init__ module in package %s" % name |
|
299 initfile, initfilename, initinfo = initstuff |
|
300 initsuff, initmode, inittype = initinfo |
|
301 if inittype not in (PY_COMPILED, PY_SOURCE): |
|
302 if initfile: initfile.close() |
|
303 raise ImportError, \ |
|
304 "Bad type (%r) for __init__ module in package %s" % ( |
|
305 inittype, name) |
|
306 path = [filename] |
|
307 file = initfile |
|
308 realfilename = initfilename |
|
309 type = inittype |
|
310 |
|
311 if type == FROZEN_MODULE: |
|
312 code = self.hooks.get_frozen_object(name) |
|
313 elif type == PY_COMPILED: |
|
314 import marshal |
|
315 file.seek(8) |
|
316 code = marshal.load(file) |
|
317 elif type == PY_SOURCE: |
|
318 data = file.read() |
|
319 code = compile(data, realfilename, 'exec') |
|
320 else: |
|
321 return ModuleLoader.load_module(self, name, stuff) |
|
322 |
|
323 m = self.hooks.add_module(name) |
|
324 if path: |
|
325 m.__path__ = path |
|
326 m.__file__ = filename |
|
327 try: |
|
328 exec code in m.__dict__ |
|
329 except: |
|
330 d = self.hooks.modules_dict() |
|
331 if name in d: |
|
332 del d[name] |
|
333 raise |
|
334 return m |
|
335 |
|
336 |
|
337 class BasicModuleImporter(_Verbose): |
|
338 |
|
339 """Basic module importer; uses module loader. |
|
340 |
|
341 This provides basic import facilities but no package imports. |
|
342 |
|
343 """ |
|
344 |
|
345 def __init__(self, loader = None, verbose = VERBOSE): |
|
346 _Verbose.__init__(self, verbose) |
|
347 self.loader = loader or ModuleLoader(None, verbose) |
|
348 self.modules = self.loader.modules_dict() |
|
349 |
|
350 def get_loader(self): |
|
351 return self.loader |
|
352 |
|
353 def set_loader(self, loader): |
|
354 self.loader = loader |
|
355 |
|
356 def get_hooks(self): |
|
357 return self.loader.get_hooks() |
|
358 |
|
359 def set_hooks(self, hooks): |
|
360 return self.loader.set_hooks(hooks) |
|
361 |
|
362 def import_module(self, name, globals={}, locals={}, fromlist=[]): |
|
363 name = str(name) |
|
364 if name in self.modules: |
|
365 return self.modules[name] # Fast path |
|
366 stuff = self.loader.find_module(name) |
|
367 if not stuff: |
|
368 raise ImportError, "No module named %s" % name |
|
369 return self.loader.load_module(name, stuff) |
|
370 |
|
371 def reload(self, module, path = None): |
|
372 name = str(module.__name__) |
|
373 stuff = self.loader.find_module(name, path) |
|
374 if not stuff: |
|
375 raise ImportError, "Module %s not found for reload" % name |
|
376 return self.loader.load_module(name, stuff) |
|
377 |
|
378 def unload(self, module): |
|
379 del self.modules[str(module.__name__)] |
|
380 # XXX Should this try to clear the module's namespace? |
|
381 |
|
382 def install(self): |
|
383 self.save_import_module = __builtin__.__import__ |
|
384 self.save_reload = __builtin__.reload |
|
385 if not hasattr(__builtin__, 'unload'): |
|
386 __builtin__.unload = None |
|
387 self.save_unload = __builtin__.unload |
|
388 __builtin__.__import__ = self.import_module |
|
389 __builtin__.reload = self.reload |
|
390 __builtin__.unload = self.unload |
|
391 |
|
392 def uninstall(self): |
|
393 __builtin__.__import__ = self.save_import_module |
|
394 __builtin__.reload = self.save_reload |
|
395 __builtin__.unload = self.save_unload |
|
396 if not __builtin__.unload: |
|
397 del __builtin__.unload |
|
398 |
|
399 |
|
400 class ModuleImporter(BasicModuleImporter): |
|
401 |
|
402 """A module importer that supports packages.""" |
|
403 |
|
404 def import_module(self, name, globals=None, locals=None, fromlist=None): |
|
405 parent = self.determine_parent(globals) |
|
406 q, tail = self.find_head_package(parent, str(name)) |
|
407 m = self.load_tail(q, tail) |
|
408 if not fromlist: |
|
409 return q |
|
410 if hasattr(m, "__path__"): |
|
411 self.ensure_fromlist(m, fromlist) |
|
412 return m |
|
413 |
|
414 def determine_parent(self, globals): |
|
415 if not globals or not "__name__" in globals: |
|
416 return None |
|
417 pname = globals['__name__'] |
|
418 if "__path__" in globals: |
|
419 parent = self.modules[pname] |
|
420 assert globals is parent.__dict__ |
|
421 return parent |
|
422 if '.' in pname: |
|
423 i = pname.rfind('.') |
|
424 pname = pname[:i] |
|
425 parent = self.modules[pname] |
|
426 assert parent.__name__ == pname |
|
427 return parent |
|
428 return None |
|
429 |
|
430 def find_head_package(self, parent, name): |
|
431 if '.' in name: |
|
432 i = name.find('.') |
|
433 head = name[:i] |
|
434 tail = name[i+1:] |
|
435 else: |
|
436 head = name |
|
437 tail = "" |
|
438 if parent: |
|
439 qname = "%s.%s" % (parent.__name__, head) |
|
440 else: |
|
441 qname = head |
|
442 q = self.import_it(head, qname, parent) |
|
443 if q: return q, tail |
|
444 if parent: |
|
445 qname = head |
|
446 parent = None |
|
447 q = self.import_it(head, qname, parent) |
|
448 if q: return q, tail |
|
449 raise ImportError, "No module named " + qname |
|
450 |
|
451 def load_tail(self, q, tail): |
|
452 m = q |
|
453 while tail: |
|
454 i = tail.find('.') |
|
455 if i < 0: i = len(tail) |
|
456 head, tail = tail[:i], tail[i+1:] |
|
457 mname = "%s.%s" % (m.__name__, head) |
|
458 m = self.import_it(head, mname, m) |
|
459 if not m: |
|
460 raise ImportError, "No module named " + mname |
|
461 return m |
|
462 |
|
463 def ensure_fromlist(self, m, fromlist, recursive=0): |
|
464 for sub in fromlist: |
|
465 if sub == "*": |
|
466 if not recursive: |
|
467 try: |
|
468 all = m.__all__ |
|
469 except AttributeError: |
|
470 pass |
|
471 else: |
|
472 self.ensure_fromlist(m, all, 1) |
|
473 continue |
|
474 if sub != "*" and not hasattr(m, sub): |
|
475 subname = "%s.%s" % (m.__name__, sub) |
|
476 submod = self.import_it(sub, subname, m) |
|
477 if not submod: |
|
478 raise ImportError, "No module named " + subname |
|
479 |
|
480 def import_it(self, partname, fqname, parent, force_load=0): |
|
481 if not partname: |
|
482 raise ValueError, "Empty module name" |
|
483 if not force_load: |
|
484 try: |
|
485 return self.modules[fqname] |
|
486 except KeyError: |
|
487 pass |
|
488 try: |
|
489 path = parent and parent.__path__ |
|
490 except AttributeError: |
|
491 return None |
|
492 partname = str(partname) |
|
493 stuff = self.loader.find_module(partname, path) |
|
494 if not stuff: |
|
495 return None |
|
496 fqname = str(fqname) |
|
497 m = self.loader.load_module(fqname, stuff) |
|
498 if parent: |
|
499 setattr(parent, partname, m) |
|
500 return m |
|
501 |
|
502 def reload(self, module): |
|
503 name = str(module.__name__) |
|
504 if '.' not in name: |
|
505 return self.import_it(name, name, None, force_load=1) |
|
506 i = name.rfind('.') |
|
507 pname = name[:i] |
|
508 parent = self.modules[pname] |
|
509 return self.import_it(name[i+1:], name, parent, force_load=1) |
|
510 |
|
511 |
|
512 default_importer = None |
|
513 current_importer = None |
|
514 |
|
515 def install(importer = None): |
|
516 global current_importer |
|
517 current_importer = importer or default_importer or ModuleImporter() |
|
518 current_importer.install() |
|
519 |
|
520 def uninstall(): |
|
521 global current_importer |
|
522 current_importer.uninstall() |