blob: bdc48e1dfd6e3f0e6f3a0db1a6e03806873fc82b [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
136 if type == BUILTIN_MODULE:
137 return imp.init_builtin(name)
138 if type == FROZEN_MODULE:
139 return imp.init_frozen(name)
140 if type == C_EXTENSION:
141 return imp.load_dynamic(name, filename, file)
142 if type == PY_SOURCE:
143 return imp.load_source(name, filename, file)
144 if type == PY_COMPILED:
145 return imp.load_compiled(name, filename, file)
146 raise ImportError, "Unrecognized module type (%s) for %s" % \
147 (`type`, name)
148
149
150class 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
176 def add_module(self, name):
177 d = self.modules_dict()
178 if d.has_key(name): return d[name]
179 d[name] = m = self.new_module(name)
180 return m
181
182 # sys interface
183 def modules_dict(self): return sys.modules
184 def default_path(self): return sys.path
185
186 def path_split(self, x): return os.path.split(x)
187 def path_join(self, x, y): return os.path.join(x, y)
188 def path_isabs(self, x): return os.path.isabs(x)
189 # etc.
190
191 def path_exists(self, x): return os.path.exists(x)
192 def path_isdir(self, x): return os.path.isdir(x)
193 def path_isfile(self, x): return os.path.isfile(x)
194 def path_islink(self, x): return os.path.islink(x)
195 # etc.
196
197 def openfile(self, *x): return apply(open, x)
198 openfile_error = IOError
199 def listdir(self, x): return os.listdir(x)
200 listdir_error = os.error
201 # etc.
202
203
204class ModuleLoader(BasicModuleLoader):
205
206 """Default module loader; uses file system hooks.
207
208 By defining suitable hooks, you might be able to load modules from
209 other sources than the file system, e.g. from compressed or
210 encrypted files, tar files or (if you're brave!) URLs.
211
212 """
213
214 def __init__(self, hooks = None, verbose = 0):
215 BasicModuleLoader.__init__(self, verbose)
216 self.hooks = hooks or Hooks(verbose)
217
218 def default_path(self):
219 return self.hooks.default_path()
220
221 def modules_dict(self):
222 return self.hooks.modules_dict()
223
224 def get_hooks(self):
225 return self.hooks
226
227 def set_hooks(self, hooks):
228 self.hooks = hooks
229
230 def find_builtin_module(self, name):
231 if self.hooks.is_builtin(name):
232 return None, '', ('', '', BUILTIN_MODULE)
233 if self.hooks.is_frozen(name):
234 return None, '', ('', '', FROZEN_MODULE)
235 return None
236
237 def find_module_in_dir(self, name, dir):
238 if dir is None:
239 return self.find_builtin_module(name)
240 for info in self.hooks.get_suffixes():
241 suff, mode, type = info
242 fullname = self.hooks.path_join(dir, name+suff)
243 try:
244 fp = self.hooks.openfile(fullname, mode)
245 return fp, fullname, info
246 except self.hooks.openfile_error:
247 pass
248 return None
249
250 def load_module(self, name, stuff):
251 file, filename, (suff, mode, type) = stuff
252 if type == BUILTIN_MODULE:
253 return self.hooks.init_builtin(name)
254 if type == FROZEN_MODULE:
255 return self.hooks.init_frozen(name)
256 if type == C_EXTENSION:
257 return self.hooks.load_dynamic(name, filename, file)
258 if type == PY_SOURCE:
259 return self.hooks.load_source(name, filename, file)
260 if type == PY_COMPILED:
261 return self.hooks.load_compiled(name, filename, file)
262 raise ImportError, "Unrecognized module type (%s) for %s" % \
263 (`type`, name)
264
265
266class FancyModuleLoader(ModuleLoader):
267
268 """Fancy module loader -- parses and execs the code itself."""
269
270 def load_module(self, name, stuff):
271 file, filename, (suff, mode, type) = stuff
272 if type == FROZEN_MODULE:
273 code = self.hooks.get_frozen_object(name)
274 elif type == PY_COMPILED:
Guido van Rossum4e155991996-06-17 17:10:45 +0000275 import marshal
Guido van Rossume7e578f1995-08-04 04:00:20 +0000276 file.seek(8)
277 code = marshal.load(file)
278 elif type == PY_SOURCE:
279 data = file.read()
280 code = compile(data, filename, 'exec')
281 else:
282 return ModuleLoader.load_module(self, name, stuff)
283 m = self.hooks.add_module(name)
284 exec code in m.__dict__
285 return m
286
287
288class ModuleImporter(_Verbose):
289
290 """Default module importer; uses module loader.
291
292 This provides the same functionality as built-in import, when
293 combined with ModuleLoader.
294
295 """
296
297 def __init__(self, loader = None, verbose = 0):
298 _Verbose.__init__(self, verbose)
299 self.loader = loader or ModuleLoader(None, verbose)
300 self.modules = self.loader.modules_dict()
301
302 def get_loader(self):
303 return self.loader
304
305 def set_loader(self, loader):
306 self.loader = loader
307
308 def get_hooks(self):
309 return self.loader.get_hooks()
310
311 def set_hooks(self, hooks):
312 return self.loader.set_hooks(hooks)
313
314 def import_module(self, name, globals={}, locals={}, fromlist=[]):
315 if self.modules.has_key(name):
316 return self.modules[name] # Fast path
317 stuff = self.loader.find_module(name)
318 if not stuff:
319 raise ImportError, "No module named %s" % name
320 return self.loader.load_module(name, stuff)
321
322 def reload(self, module, path = None):
Guido van Rossum6a0691a1995-08-09 02:32:49 +0000323 name = module.__name__
Guido van Rossume7e578f1995-08-04 04:00:20 +0000324 stuff = self.loader.find_module(name, path)
325 if not stuff:
326 raise ImportError, "Module %s not found for reload" % name
327 return self.loader.load_module(name, stuff)
328
329 def unload(self, module):
330 del self.modules[module.__name__]
331 # XXX Should this try to clear the module's namespace?
332
333 def install(self):
334 self.save_import_module = __builtin__.__import__
335 self.save_reload = __builtin__.reload
336 if not hasattr(__builtin__, 'unload'):
337 __builtin__.unload = None
338 self.save_unload = __builtin__.unload
339 __builtin__.__import__ = self.import_module
340 __builtin__.reload = self.reload
341 __builtin__.unload = self.unload
342
343 def uninstall(self):
344 __builtin__.__import__ = self.save_import_module
345 __builtin__.reload = self.save_reload
346 __builtin__.unload = self.save_unload
347 if not __builtin__.unload:
348 del __builtin__.unload
349
350
351# XXX Some experimental hacks -- importing ihooks auto-installs!
352# XXX (That's supposed to be transparent anyway...)
353
354default_importer = None
355current_importer = None
356
357def install(importer = None):
358 global current_importer
359 current_importer = importer or default_importer or ModuleImporter()
360 current_importer.install()
361
362def uninstall():
363 global current_importer
364 current_importer.uninstall()
365
366
367install()