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 (rimport.py), implementing a hierarchical module namespace (newimp.py)
12 or restricted execution (rexec.py).
14 This module defines three new concepts:
16 (1) A "file system hooks" class provides an interface to a filesystem.
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.
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.
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.
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.
39 One module importer class is defined (ModuleImporter), which uses a
40 module loader instance passed in (by default HookableModuleLoader is
43 The classes defined here should be used as base classes for extended
44 functionality along those lines.
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().
52 XXX Should the imp.load_* functions also be called via the hooks
64 from imp
import C_EXTENSION
, PY_SOURCE
, PY_COMPILED
71 def __init__(self
, verbose
= 0):
72 self
.verbose
= verbose
74 def get_verbose(self
):
77 def set_verbose(self
, verbose
):
78 self
.verbose
= verbose
80 # XXX The following is an experimental interface
82 def note(self
, *args
):
84 apply(self
.message
, args
)
86 def message(self
, format
, *args
):
90 class BasicModuleLoader(_Verbose
):
92 """Basic module loader.
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.
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.
107 def find_module(self
, name
, path
= None):
109 path
= [None] + self
.default_path()
111 stuff
= self
.find_module_in_dir(name
, dir)
112 if stuff
: return stuff
115 def default_path(self
):
118 def find_module_in_dir(self
, name
, dir):
120 return self
.find_builtin_module(name
)
123 return imp
.find_module(name
, [dir])
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
)
134 def load_module(self
, name
, stuff
):
135 file, filename
, (suff
, mode
, type) = stuff
137 if type == BUILTIN_MODULE
:
138 return imp
.init_builtin(name
)
139 if type == FROZEN_MODULE
:
140 return imp
.init_frozen(name
)
141 if type == C_EXTENSION
:
142 return imp
.load_dynamic(name
, filename
, file)
143 if type == PY_SOURCE
:
144 return imp
.load_source(name
, filename
, file)
145 if type == PY_COMPILED
:
146 return imp
.load_compiled(name
, filename
, file)
148 if file: file.close()
149 raise ImportError, "Unrecognized module type (%s) for %s" % \
153 class Hooks(_Verbose
):
155 """Hooks into the filesystem and interpreter.
157 By deriving a subclass you can redefine your filesystem interface,
158 e.g. to merge it with the URL space.
160 This base class behaves just like the native filesystem.
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)
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
)
186 def modules_dict(self
): return sys
.modules
187 def default_path(self
): return sys
.path
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
)
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
)
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
207 class ModuleLoader(BasicModuleLoader
):
209 """Default module loader; uses file system hooks.
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.
217 def __init__(self
, hooks
= None, verbose
= 0):
218 BasicModuleLoader
.__init
__(self
, verbose
)
219 self
.hooks
= hooks
or Hooks(verbose
)
221 def default_path(self
):
222 return self
.hooks
.default_path()
224 def modules_dict(self
):
225 return self
.hooks
.modules_dict()
230 def set_hooks(self
, hooks
):
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
)
240 def find_module_in_dir(self
, name
, dir):
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
)
247 fp
= self
.hooks
.openfile(fullname
, mode
)
248 return fp
, fullname
, info
249 except self
.hooks
.openfile_error
:
253 def load_module(self
, name
, stuff
):
254 file, filename
, (suff
, mode
, type) = stuff
256 if type == BUILTIN_MODULE
:
257 return self
.hooks
.init_builtin(name
)
258 if type == FROZEN_MODULE
:
259 return self
.hooks
.init_frozen(name
)
260 if type == C_EXTENSION
:
261 m
= self
.hooks
.load_dynamic(name
, filename
, file)
262 elif type == PY_SOURCE
:
263 m
= self
.hooks
.load_source(name
, filename
, file)
264 elif type == PY_COMPILED
:
265 m
= self
.hooks
.load_compiled(name
, filename
, file)
267 raise ImportError, "Unrecognized module type (%s) for %s" % \
270 if file: file.close()
271 m
.__file
__ = filename
275 class FancyModuleLoader(ModuleLoader
):
277 """Fancy module loader -- parses and execs the code itself."""
279 def load_module(self
, name
, stuff
):
280 file, filename
, (suff
, mode
, type) = stuff
281 if type == FROZEN_MODULE
:
282 code
= self
.hooks
.get_frozen_object(name
)
283 elif type == PY_COMPILED
:
286 code
= marshal
.load(file)
287 elif type == PY_SOURCE
:
289 code
= compile(data
, filename
, 'exec')
291 return ModuleLoader
.load_module(self
, name
, stuff
)
292 m
= self
.hooks
.add_module(name
)
293 m
.__file
__ = filename
294 exec code
in m
.__dict
__
298 class ModuleImporter(_Verbose
):
300 """Default module importer; uses module loader.
302 This provides the same functionality as built-in import, when
303 combined with ModuleLoader.
307 def __init__(self
, loader
= None, verbose
= 0):
308 _Verbose
.__init
__(self
, verbose
)
309 self
.loader
= loader
or ModuleLoader(None, verbose
)
310 self
.modules
= self
.loader
.modules_dict()
312 def get_loader(self
):
315 def set_loader(self
, loader
):
319 return self
.loader
.get_hooks()
321 def set_hooks(self
, hooks
):
322 return self
.loader
.set_hooks(hooks
)
324 def import_module(self
, name
, globals={}, locals={}, fromlist
=[]):
325 if self
.modules
.has_key(name
):
326 return self
.modules
[name
] # Fast path
327 stuff
= self
.loader
.find_module(name
)
329 raise ImportError, "No module named %s" % name
330 return self
.loader
.load_module(name
, stuff
)
332 def reload(self
, module
, path
= None):
333 name
= module
.__name
__
334 stuff
= self
.loader
.find_module(name
, path
)
336 raise ImportError, "Module %s not found for reload" % name
337 return self
.loader
.load_module(name
, stuff
)
339 def unload(self
, module
):
340 del self
.modules
[module
.__name
__]
341 # XXX Should this try to clear the module's namespace?
344 self
.save_import_module
= __builtin__
.__import
__
345 self
.save_reload
= __builtin__
.reload
346 if not hasattr(__builtin__
, 'unload'):
347 __builtin__
.unload
= None
348 self
.save_unload
= __builtin__
.unload
349 __builtin__
.__import
__ = self
.import_module
350 __builtin__
.reload = self
.reload
351 __builtin__
.unload
= self
.unload
354 __builtin__
.__import
__ = self
.save_import_module
355 __builtin__
.reload = self
.save_reload
356 __builtin__
.unload
= self
.save_unload
357 if not __builtin__
.unload
:
358 del __builtin__
.unload
361 default_importer
= None
362 current_importer
= None
364 def install(importer
= None):
365 global current_importer
366 current_importer
= importer
or default_importer
or ModuleImporter()
367 current_importer
.install()
370 global current_importer
371 current_importer
.uninstall()