blob: 8f64957dd733a3c4f247366a28046207d2cbd9ba [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
Guido van Rossum6dc61b11997-08-13 14:48:36 +0000255 try:
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)
266 else:
267 raise ImportError, "Unrecognized module type (%s) for %s" % \
268 (`type`, name)
269 finally:
270 if file: file.close()
Guido van Rossuma97b8ee1996-10-07 23:41:54 +0000271 m.__file__ = filename
272 return m
Guido van Rossume7e578f1995-08-04 04:00:20 +0000273
274
275class FancyModuleLoader(ModuleLoader):
276
277 """Fancy module loader -- parses and execs the code itself."""
278
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:
Guido van Rossum4e155991996-06-17 17:10:45 +0000284 import marshal
Guido van Rossume7e578f1995-08-04 04:00:20 +0000285 file.seek(8)
286 code = marshal.load(file)
287 elif type == PY_SOURCE:
288 data = file.read()
289 code = compile(data, filename, 'exec')
290 else:
291 return ModuleLoader.load_module(self, name, stuff)
292 m = self.hooks.add_module(name)
Guido van Rossuma97b8ee1996-10-07 23:41:54 +0000293 m.__file__ = filename
Guido van Rossume7e578f1995-08-04 04:00:20 +0000294 exec code in m.__dict__
295 return m
296
297
298class ModuleImporter(_Verbose):
299
300 """Default module importer; uses module loader.
301
302 This provides the same functionality as built-in import, when
303 combined with ModuleLoader.
304
305 """
306
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()
311
312 def get_loader(self):
313 return self.loader
314
315 def set_loader(self, loader):
316 self.loader = loader
317
318 def get_hooks(self):
319 return self.loader.get_hooks()
320
321 def set_hooks(self, hooks):
322 return self.loader.set_hooks(hooks)
323
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)
328 if not stuff:
329 raise ImportError, "No module named %s" % name
330 return self.loader.load_module(name, stuff)
331
332 def reload(self, module, path = None):
Guido van Rossum6a0691a1995-08-09 02:32:49 +0000333 name = module.__name__
Guido van Rossume7e578f1995-08-04 04:00:20 +0000334 stuff = self.loader.find_module(name, path)
335 if not stuff:
336 raise ImportError, "Module %s not found for reload" % name
337 return self.loader.load_module(name, stuff)
338
339 def unload(self, module):
340 del self.modules[module.__name__]
341 # XXX Should this try to clear the module's namespace?
342
343 def install(self):
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
352
353 def uninstall(self):
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
359
360
Guido van Rossume7e578f1995-08-04 04:00:20 +0000361default_importer = None
362current_importer = None
363
364def install(importer = None):
365 global current_importer
366 current_importer = importer or default_importer or ModuleImporter()
367 current_importer.install()
368
369def uninstall():
370 global current_importer
371 current_importer.uninstall()