blob: dfe29cfea961c548788ffaaca0e7fe82acb292b3 [file] [log] [blame]
Guido van Rossume7e578f1995-08-04 04:00:20 +00001"""Import hook support.
2
3Consistent use of this module will make it possible to change the
4different mechanisms involved in loading modules independently.
5
6While the built-in module imp exports interfaces to the built-in
7module searching and loading algorithm, and it is possible to replace
8the built-in function __import__ in order to change the semantics of
9the import statement, until now it has been difficult to combine the
10effect of different __import__ hacks, like loading modules from URLs
11(rimport.py), implementing a hierarchical module namespace (newimp.py)
12or restricted execution (rexec.py).
13
14This module defines three new concepts:
15
16(1) A "file system hooks" class provides an interface to a filesystem.
17
18One hooks class is defined (Hooks), which uses the interface provided
19by standard modules os and os.path. It should be used as the base
20class for other hooks classes.
21
22(2) A "module loader" class provides an interface to to search for a
23module in a search path and to load it. It defines a method which
24searches for a module in a single directory; by overriding this method
25one can redefine the details of the search. If the directory is None,
26built-in and frozen modules are searched instead.
27
28Two module loader class are defined, both implementing the search
29strategy used by the built-in __import__ function: ModuleLoader uses
30the imp module's find_module interface, while HookableModuleLoader
31uses a file system hooks class to interact with the file system. Both
32use the imp module's load_* interfaces to actually load the module.
33
34(3) A "module importer" class provides an interface to import a
35module, as well as interfaces to reload and unload a module. It also
36provides interfaces to install and uninstall itself instead of the
37default __import__ and reload (and unload) functions.
38
39One module importer class is defined (ModuleImporter), which uses a
40module loader instance passed in (by default HookableModuleLoader is
41instantiated).
42
43The classes defined here should be used as base classes for extended
44functionality along those lines.
45
46If a module mporter class supports dotted names, its import_module()
47must return a different value depending on whether it is called on
48behalf of a "from ... import ..." statement or not. (This is caused
49by the way the __import__ hook is used by the Python interpreter.) It
50would also do wise to install a different version of reload().
51
52XXX Should the imp.load_* functions also be called via the hooks
53instance?
54
55"""
56
57
58import __builtin__
59import imp
60import os
61import sys
62
63
64from imp import C_EXTENSION, PY_SOURCE, PY_COMPILED
65BUILTIN_MODULE = 32
66FROZEN_MODULE = 33
67
68
69class _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
90class 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
Guido van Rossum2ea05091996-09-10 17:37:44 +0000136 try:
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)
147 finally:
148 if file: file.close()
Guido van Rossume7e578f1995-08-04 04:00:20 +0000149 raise ImportError, "Unrecognized module type (%s) for %s" % \
150 (`type`, name)
151
152
153class 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
207class 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:
Guido van Rossuma97b8ee1996-10-07 23:41:54 +0000260 m = self.hooks.load_dynamic(name, filename, file)
261 elif type == PY_SOURCE:
262 m = self.hooks.load_source(name, filename, file)
263 elif type == PY_COMPILED:
264 m = self.hooks.load_compiled(name, filename, file)
265 else:
266 raise ImportError, "Unrecognized module type (%s) for %s" % \
267 (`type`, name)
268 m.__file__ = filename
269 return m
Guido van Rossume7e578f1995-08-04 04:00:20 +0000270
271
272class 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:
Guido van Rossum4e155991996-06-17 17:10:45 +0000281 import marshal
Guido van Rossume7e578f1995-08-04 04:00:20 +0000282 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)
Guido van Rossuma97b8ee1996-10-07 23:41:54 +0000290 m.__file__ = filename
Guido van Rossume7e578f1995-08-04 04:00:20 +0000291 exec code in m.__dict__
292 return m
293
294
295class 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):
Guido van Rossum6a0691a1995-08-09 02:32:49 +0000330 name = module.__name__
Guido van Rossume7e578f1995-08-04 04:00:20 +0000331 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
Guido van Rossume7e578f1995-08-04 04:00:20 +0000358default_importer = None
359current_importer = None
360
361def install(importer = None):
362 global current_importer
363 current_importer = importer or default_importer or ModuleImporter()
364 current_importer.install()
365
366def uninstall():
367 global current_importer
368 current_importer.uninstall()