1 """Import hook support.
3 Consistent use of this module will make it possible to change the
4 different mechanisms involved in loading modules independently.
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.
13 This module defines three new concepts:
15 1) A "file system hooks" class provides an interface to a filesystem.
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.
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.
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.
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.
38 One module importer class is defined (ModuleImporter), which uses a
39 module loader instance passed in (by default HookableModuleLoader is
42 The classes defined here should be used as base classes for extended
43 functionality along those lines.
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().
52 from warnings
import warnpy3k
, warn
53 warnpy3k("the ihooks module has been removed in Python 3.0", stacklevel
=2)
61 __all__
= ["BasicModuleLoader","Hooks","ModuleLoader","FancyModuleLoader",
62 "BasicModuleImporter","ModuleImporter","install","uninstall"]
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
75 def __init__(self
, verbose
= VERBOSE
):
76 self
.verbose
= verbose
78 def get_verbose(self
):
81 def set_verbose(self
, verbose
):
82 self
.verbose
= verbose
84 # XXX The following is an experimental interface
86 def note(self
, *args
):
90 def message(self
, format
, *args
):
97 class BasicModuleLoader(_Verbose
):
99 """Basic module loader.
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.
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.
114 def find_module(self
, name
, path
= None):
116 path
= [None] + self
.default_path()
118 stuff
= self
.find_module_in_dir(name
, dir)
119 if stuff
: return stuff
122 def default_path(self
):
125 def find_module_in_dir(self
, name
, dir):
127 return self
.find_builtin_module(name
)
130 return imp
.find_module(name
, [dir])
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
)
142 def load_module(self
, name
, stuff
):
143 file, filename
, info
= stuff
145 return imp
.load_module(name
, file, filename
, info
)
147 if file: file.close()
150 class Hooks(_Verbose
):
152 """Hooks into the filesystem and interpreter.
154 By deriving a subclass you can redefine your filesystem interface,
155 e.g. to merge it with the URL space.
157 This base class behaves just like the native filesystem.
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
))
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
)
185 def modules_dict(self
): return sys
.modules
186 def default_path(self
): return sys
.path
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
)
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
)
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
206 class ModuleLoader(BasicModuleLoader
):
208 """Default module loader; uses file system hooks.
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.
216 def __init__(self
, hooks
= None, verbose
= VERBOSE
):
217 BasicModuleLoader
.__init
__(self
, verbose
)
218 self
.hooks
= hooks
or Hooks(verbose
)
220 def default_path(self
):
221 return self
.hooks
.default_path()
223 def modules_dict(self
):
224 return self
.hooks
.modules_dict()
229 def set_hooks(self
, hooks
):
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
)
240 def find_module_in_dir(self
, name
, dir, allow_packages
=1):
242 return self
.find_builtin_module(name
)
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)
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
)
255 fp
= self
.hooks
.openfile(fullname
, mode
)
256 return fp
, fullname
, info
257 except self
.hooks
.openfile_error
:
261 def load_module(self
, name
, stuff
):
262 file, filename
, info
= stuff
263 (suff
, mode
, type) = info
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)
278 raise ImportError, "Unrecognized module type (%r) for %s" % \
281 if file: file.close()
282 m
.__file
__ = filename
286 class FancyModuleLoader(ModuleLoader
):
288 """Fancy module loader -- parses and execs the code itself."""
290 def load_module(self
, name
, stuff
):
291 file, filename
, (suff
, mode
, type) = stuff
292 realfilename
= filename
295 if type == PKG_DIRECTORY
:
296 initstuff
= self
.find_module_in_dir("__init__", filename
, 0)
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()
304 "Bad type (%r) for __init__ module in package %s" % (
308 realfilename
= initfilename
311 if type == FROZEN_MODULE
:
312 code
= self
.hooks
.get_frozen_object(name
)
313 elif type == PY_COMPILED
:
316 code
= marshal
.load(file)
317 elif type == PY_SOURCE
:
319 code
= compile(data
, realfilename
, 'exec')
321 return ModuleLoader
.load_module(self
, name
, stuff
)
323 m
= self
.hooks
.add_module(name
)
326 m
.__file
__ = filename
328 exec code
in m
.__dict
__
330 d
= self
.hooks
.modules_dict()
337 class BasicModuleImporter(_Verbose
):
339 """Basic module importer; uses module loader.
341 This provides basic import facilities but no package imports.
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()
350 def get_loader(self
):
353 def set_loader(self
, loader
):
357 return self
.loader
.get_hooks()
359 def set_hooks(self
, hooks
):
360 return self
.loader
.set_hooks(hooks
)
362 def import_module(self
, name
, globals={}, locals={}, fromlist
=[]):
364 if name
in self
.modules
:
365 return self
.modules
[name
] # Fast path
366 stuff
= self
.loader
.find_module(name
)
368 raise ImportError, "No module named %s" % name
369 return self
.loader
.load_module(name
, stuff
)
371 def reload(self
, module
, path
= None):
372 name
= str(module
.__name
__)
373 stuff
= self
.loader
.find_module(name
, path
)
375 raise ImportError, "Module %s not found for reload" % name
376 return self
.loader
.load_module(name
, stuff
)
378 def unload(self
, module
):
379 del self
.modules
[str(module
.__name
__)]
380 # XXX Should this try to clear the module's namespace?
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
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
400 class ModuleImporter(BasicModuleImporter
):
402 """A module importer that supports packages."""
404 def import_module(self
, name
, globals=None, locals=None, fromlist
=None,
406 parent
= self
.determine_parent(globals, level
)
407 q
, tail
= self
.find_head_package(parent
, str(name
))
408 m
= self
.load_tail(q
, tail
)
411 if hasattr(m
, "__path__"):
412 self
.ensure_fromlist(m
, fromlist
)
415 def determine_parent(self
, globals, level
=-1):
416 if not globals or not level
:
418 pkgname
= globals.get('__package__')
419 if pkgname
is not None:
420 if not pkgname
and level
> 0:
421 raise ValueError, 'Attempted relative import in non-package'
423 # __package__ not set, figure it out and set it
424 modname
= globals.get('__name__')
427 if "__path__" in globals:
428 # __path__ is set so modname is already the package name
431 # normal module, work out package name if any
432 if '.' not in modname
:
434 raise ValueError, ('Attempted relative import in '
436 globals['__package__'] = None
438 pkgname
= modname
.rpartition('.')[0]
439 globals['__package__'] = pkgname
442 for x
in range(level
, 1, -1):
444 dot
= pkgname
.rindex('.', 0, dot
)
446 raise ValueError('attempted relative import beyond '
448 pkgname
= pkgname
[:dot
]
450 return sys
.modules
[pkgname
]
453 warn("Parent module '%s' not found while handling "
454 "absolute import" % pkgname
, RuntimeWarning, 1)
457 raise SystemError, ("Parent module '%s' not loaded, cannot "
458 "perform relative import" % pkgname
)
460 def find_head_package(self
, parent
, name
):
469 qname
= "%s.%s" % (parent
.__name
__, head
)
472 q
= self
.import_it(head
, qname
, parent
)
477 q
= self
.import_it(head
, qname
, parent
)
479 raise ImportError, "No module named '%s'" % qname
481 def load_tail(self
, q
, tail
):
485 if i
< 0: i
= len(tail
)
486 head
, tail
= tail
[:i
], tail
[i
+1:]
487 mname
= "%s.%s" % (m
.__name
__, head
)
488 m
= self
.import_it(head
, mname
, m
)
490 raise ImportError, "No module named '%s'" % mname
493 def ensure_fromlist(self
, m
, fromlist
, recursive
=0):
499 except AttributeError:
502 self
.ensure_fromlist(m
, all
, 1)
504 if sub
!= "*" and not hasattr(m
, sub
):
505 subname
= "%s.%s" % (m
.__name
__, sub
)
506 submod
= self
.import_it(sub
, subname
, m
)
508 raise ImportError, "No module named '%s'" % subname
510 def import_it(self
, partname
, fqname
, parent
, force_load
=0):
512 # completely empty module name should only happen in
513 # 'from . import' or __import__("")
517 return self
.modules
[fqname
]
521 path
= parent
and parent
.__path
__
522 except AttributeError:
524 partname
= str(partname
)
525 stuff
= self
.loader
.find_module(partname
, path
)
529 m
= self
.loader
.load_module(fqname
, stuff
)
531 setattr(parent
, partname
, m
)
534 def reload(self
, module
):
535 name
= str(module
.__name
__)
537 return self
.import_it(name
, name
, None, force_load
=1)
540 parent
= self
.modules
[pname
]
541 return self
.import_it(name
[i
+1:], name
, parent
, force_load
=1)
544 default_importer
= None
545 current_importer
= None
547 def install(importer
= None):
548 global current_importer
549 current_importer
= importer
or default_importer
or ModuleImporter()
550 current_importer
.install()
553 global current_importer
554 current_importer
.uninstall()