0001 """Import hook support. 0002 0003 Consistent use of this module will make it possible to change the 0004 different mechanisms involved in loading modules independently. 0005 0006 While the built-in module imp exports interfaces to the built-in 0007 module searching and loading algorithm, and it is possible to replace 0008 the built-in function __import__ in order to change the semantics of 0009 the import statement, until now it has been difficult to combine the 0010 effect of different __import__ hacks, like loading modules from URLs 0011 by rimport.py, or restricted execution by rexec.py. 0012 0013 This module defines three new concepts: 0014 0015 1) A "file system hooks" class provides an interface to a filesystem. 0016 0017 One hooks class is defined (Hooks), which uses the interface provided 0018 by standard modules os and os.path. It should be used as the base 0019 class for other hooks classes. 0020 0021 2) A "module loader" class provides an interface to search for a 0022 module in a search path and to load it. It defines a method which 0023 searches for a module in a single directory; by overriding this method 0024 one can redefine the details of the search. If the directory is None, 0025 built-in and frozen modules are searched instead. 0026 0027 Two module loader class are defined, both implementing the search 0028 strategy used by the built-in __import__ function: ModuleLoader uses 0029 the imp module's find_module interface, while HookableModuleLoader 0030 uses a file system hooks class to interact with the file system. Both 0031 use the imp module's load_* interfaces to actually load the module. 0032 0033 3) A "module importer" class provides an interface to import a 0034 module, as well as interfaces to reload and unload a module. It also 0035 provides interfaces to install and uninstall itself instead of the 0036 default __import__ and reload (and unload) functions. 0037 0038 One module importer class is defined (ModuleImporter), which uses a 0039 module loader instance passed in (by default HookableModuleLoader is 0040 instantiated). 0041 0042 The classes defined here should be used as base classes for extended 0043 functionality along those lines. 0044 0045 If a module importer class supports dotted names, its import_module() 0046 must return a different value depending on whether it is called on 0047 behalf of a "from ... import ..." statement or not. (This is caused 0048 by the way the __import__ hook is used by the Python interpreter.) It 0049 would also do wise to install a different version of reload(). 0050 0051 """ 0052 0053 0054 import __builtin__ 0055 import imp 0056 import os 0057 import sys 0058 0059 __all__ = ["BasicModuleLoader","Hooks","ModuleLoader","FancyModuleLoader", 0060 "BasicModuleImporter","ModuleImporter","install","uninstall"] 0061 0062 VERBOSE = 0 0063 0064 0065 from imp import C_EXTENSION, PY_SOURCE, PY_COMPILED 0066 from imp import C_BUILTIN, PY_FROZEN, PKG_DIRECTORY 0067 BUILTIN_MODULE = C_BUILTIN 0068 FROZEN_MODULE = PY_FROZEN 0069 0070 0071 class _Verbose: 0072 0073 def __init__(self, verbose = VERBOSE): 0074 self.verbose = verbose 0075 0076 def get_verbose(self): 0077 return self.verbose 0078 0079 def set_verbose(self, verbose): 0080 self.verbose = verbose 0081 0082 # XXX The following is an experimental interface 0083 0084 def note(self, *args): 0085 if self.verbose: 0086 self.message(*args) 0087 0088 def message(self, format, *args): 0089 if args: 0090 print format%args 0091 else: 0092 print format 0093 0094 0095 class BasicModuleLoader(_Verbose): 0096 0097 """Basic module loader. 0098 0099 This provides the same functionality as built-in import. It 0100 doesn't deal with checking sys.modules -- all it provides is 0101 find_module() and a load_module(), as well as find_module_in_dir() 0102 which searches just one directory, and can be overridden by a 0103 derived class to change the module search algorithm when the basic 0104 dependency on sys.path is unchanged. 0105 0106 The interface is a little more convenient than imp's: 0107 find_module(name, [path]) returns None or 'stuff', and 0108 load_module(name, stuff) loads the module. 0109 0110 """ 0111 0112 def find_module(self, name, path = None): 0113 if path is None: 0114 path = [None] + self.default_path() 0115 for dir in path: 0116 stuff = self.find_module_in_dir(name, dir) 0117 if stuff: return stuff 0118 return None 0119 0120 def default_path(self): 0121 return sys.path 0122 0123 def find_module_in_dir(self, name, dir): 0124 if dir is None: 0125 return self.find_builtin_module(name) 0126 else: 0127 try: 0128 return imp.find_module(name, [dir]) 0129 except ImportError: 0130 return None 0131 0132 def find_builtin_module(self, name): 0133 # XXX frozen packages? 0134 if imp.is_builtin(name): 0135 return None, '', ('', '', BUILTIN_MODULE) 0136 if imp.is_frozen(name): 0137 return None, '', ('', '', FROZEN_MODULE) 0138 return None 0139 0140 def load_module(self, name, stuff): 0141 file, filename, info = stuff 0142 try: 0143 return imp.load_module(name, file, filename, info) 0144 finally: 0145 if file: file.close() 0146 0147 0148 class Hooks(_Verbose): 0149 0150 """Hooks into the filesystem and interpreter. 0151 0152 By deriving a subclass you can redefine your filesystem interface, 0153 e.g. to merge it with the URL space. 0154 0155 This base class behaves just like the native filesystem. 0156 0157 """ 0158 0159 # imp interface 0160 def get_suffixes(self): return imp.get_suffixes() 0161 def new_module(self, name): return imp.new_module(name) 0162 def is_builtin(self, name): return imp.is_builtin(name) 0163 def init_builtin(self, name): return imp.init_builtin(name) 0164 def is_frozen(self, name): return imp.is_frozen(name) 0165 def init_frozen(self, name): return imp.init_frozen(name) 0166 def get_frozen_object(self, name): return imp.get_frozen_object(name) 0167 def load_source(self, name, filename, file=None): 0168 return imp.load_source(name, filename, file) 0169 def load_compiled(self, name, filename, file=None): 0170 return imp.load_compiled(name, filename, file) 0171 def load_dynamic(self, name, filename, file=None): 0172 return imp.load_dynamic(name, filename, file) 0173 def load_package(self, name, filename, file=None): 0174 return imp.load_module(name, file, filename, ("", "", PKG_DIRECTORY)) 0175 0176 def add_module(self, name): 0177 d = self.modules_dict() 0178 if name in d: return d[name] 0179 d[name] = m = self.new_module(name) 0180 return m 0181 0182 # sys interface 0183 def modules_dict(self): return sys.modules 0184 def default_path(self): return sys.path 0185 0186 def path_split(self, x): return os.path.split(x) 0187 def path_join(self, x, y): return os.path.join(x, y) 0188 def path_isabs(self, x): return os.path.isabs(x) 0189 # etc. 0190 0191 def path_exists(self, x): return os.path.exists(x) 0192 def path_isdir(self, x): return os.path.isdir(x) 0193 def path_isfile(self, x): return os.path.isfile(x) 0194 def path_islink(self, x): return os.path.islink(x) 0195 # etc. 0196 0197 def openfile(self, *x): return open(*x) 0198 openfile_error = IOError 0199 def listdir(self, x): return os.listdir(x) 0200 listdir_error = os.error 0201 # etc. 0202 0203 0204 class ModuleLoader(BasicModuleLoader): 0205 0206 """Default module loader; uses file system hooks. 0207 0208 By defining suitable hooks, you might be able to load modules from 0209 other sources than the file system, e.g. from compressed or 0210 encrypted files, tar files or (if you're brave!) URLs. 0211 0212 """ 0213 0214 def __init__(self, hooks = None, verbose = VERBOSE): 0215 BasicModuleLoader.__init__(self, verbose) 0216 self.hooks = hooks or Hooks(verbose) 0217 0218 def default_path(self): 0219 return self.hooks.default_path() 0220 0221 def modules_dict(self): 0222 return self.hooks.modules_dict() 0223 0224 def get_hooks(self): 0225 return self.hooks 0226 0227 def set_hooks(self, hooks): 0228 self.hooks = hooks 0229 0230 def find_builtin_module(self, name): 0231 # XXX frozen packages? 0232 if self.hooks.is_builtin(name): 0233 return None, '', ('', '', BUILTIN_MODULE) 0234 if self.hooks.is_frozen(name): 0235 return None, '', ('', '', FROZEN_MODULE) 0236 return None 0237 0238 def find_module_in_dir(self, name, dir, allow_packages=1): 0239 if dir is None: 0240 return self.find_builtin_module(name) 0241 if allow_packages: 0242 fullname = self.hooks.path_join(dir, name) 0243 if self.hooks.path_isdir(fullname): 0244 stuff = self.find_module_in_dir("__init__", fullname, 0) 0245 if stuff: 0246 file = stuff[0] 0247 if file: file.close() 0248 return None, fullname, ('', '', PKG_DIRECTORY) 0249 for info in self.hooks.get_suffixes(): 0250 suff, mode, type = info 0251 fullname = self.hooks.path_join(dir, name+suff) 0252 try: 0253 fp = self.hooks.openfile(fullname, mode) 0254 return fp, fullname, info 0255 except self.hooks.openfile_error: 0256 pass 0257 return None 0258 0259 def load_module(self, name, stuff): 0260 file, filename, info = stuff 0261 (suff, mode, type) = info 0262 try: 0263 if type == BUILTIN_MODULE: 0264 return self.hooks.init_builtin(name) 0265 if type == FROZEN_MODULE: 0266 return self.hooks.init_frozen(name) 0267 if type == C_EXTENSION: 0268 m = self.hooks.load_dynamic(name, filename, file) 0269 elif type == PY_SOURCE: 0270 m = self.hooks.load_source(name, filename, file) 0271 elif type == PY_COMPILED: 0272 m = self.hooks.load_compiled(name, filename, file) 0273 elif type == PKG_DIRECTORY: 0274 m = self.hooks.load_package(name, filename, file) 0275 else: 0276 raise ImportError, "Unrecognized module type (%r) for %s" % \ 0277 (type, name) 0278 finally: 0279 if file: file.close() 0280 m.__file__ = filename 0281 return m 0282 0283 0284 class FancyModuleLoader(ModuleLoader): 0285 0286 """Fancy module loader -- parses and execs the code itself.""" 0287 0288 def load_module(self, name, stuff): 0289 file, filename, (suff, mode, type) = stuff 0290 realfilename = filename 0291 path = None 0292 0293 if type == PKG_DIRECTORY: 0294 initstuff = self.find_module_in_dir("__init__", filename, 0) 0295 if not initstuff: 0296 raise ImportError, "No __init__ module in package %s" % name 0297 initfile, initfilename, initinfo = initstuff 0298 initsuff, initmode, inittype = initinfo 0299 if inittype not in (PY_COMPILED, PY_SOURCE): 0300 if initfile: initfile.close() 0301 raise ImportError, \ 0302 "Bad type (%r) for __init__ module in package %s" % ( 0303 inittype, name) 0304 path = [filename] 0305 file = initfile 0306 realfilename = initfilename 0307 type = inittype 0308 0309 if type == FROZEN_MODULE: 0310 code = self.hooks.get_frozen_object(name) 0311 elif type == PY_COMPILED: 0312 import marshal 0313 file.seek(8) 0314 code = marshal.load(file) 0315 elif type == PY_SOURCE: 0316 data = file.read() 0317 code = compile(data, realfilename, 'exec') 0318 else: 0319 return ModuleLoader.load_module(self, name, stuff) 0320 0321 m = self.hooks.add_module(name) 0322 if path: 0323 m.__path__ = path 0324 m.__file__ = filename 0325 try: 0326 exec code in m.__dict__ 0327 except: 0328 d = self.hooks.modules_dict() 0329 if name in d: 0330 del d[name] 0331 raise 0332 return m 0333 0334 0335 class BasicModuleImporter(_Verbose): 0336 0337 """Basic module importer; uses module loader. 0338 0339 This provides basic import facilities but no package imports. 0340 0341 """ 0342 0343 def __init__(self, loader = None, verbose = VERBOSE): 0344 _Verbose.__init__(self, verbose) 0345 self.loader = loader or ModuleLoader(None, verbose) 0346 self.modules = self.loader.modules_dict() 0347 0348 def get_loader(self): 0349 return self.loader 0350 0351 def set_loader(self, loader): 0352 self.loader = loader 0353 0354 def get_hooks(self): 0355 return self.loader.get_hooks() 0356 0357 def set_hooks(self, hooks): 0358 return self.loader.set_hooks(hooks) 0359 0360 def import_module(self, name, globals={}, locals={}, fromlist=[]): 0361 name = str(name) 0362 if name in self.modules: 0363 return self.modules[name] # Fast path 0364 stuff = self.loader.find_module(name) 0365 if not stuff: 0366 raise ImportError, "No module named %s" % name 0367 return self.loader.load_module(name, stuff) 0368 0369 def reload(self, module, path = None): 0370 name = str(module.__name__) 0371 stuff = self.loader.find_module(name, path) 0372 if not stuff: 0373 raise ImportError, "Module %s not found for reload" % name 0374 return self.loader.load_module(name, stuff) 0375 0376 def unload(self, module): 0377 del self.modules[str(module.__name__)] 0378 # XXX Should this try to clear the module's namespace? 0379 0380 def install(self): 0381 self.save_import_module = __builtin__.__import__ 0382 self.save_reload = __builtin__.reload 0383 if not hasattr(__builtin__, 'unload'): 0384 __builtin__.unload = None 0385 self.save_unload = __builtin__.unload 0386 __builtin__.__import__ = self.import_module 0387 __builtin__.reload = self.reload 0388 __builtin__.unload = self.unload 0389 0390 def uninstall(self): 0391 __builtin__.__import__ = self.save_import_module 0392 __builtin__.reload = self.save_reload 0393 __builtin__.unload = self.save_unload 0394 if not __builtin__.unload: 0395 del __builtin__.unload 0396 0397 0398 class ModuleImporter(BasicModuleImporter): 0399 0400 """A module importer that supports packages.""" 0401 0402 def import_module(self, name, globals=None, locals=None, fromlist=None): 0403 parent = self.determine_parent(globals) 0404 q, tail = self.find_head_package(parent, str(name)) 0405 m = self.load_tail(q, tail) 0406 if not fromlist: 0407 return q 0408 if hasattr(m, "__path__"): 0409 self.ensure_fromlist(m, fromlist) 0410 return m 0411 0412 def determine_parent(self, globals): 0413 if not globals or not "__name__" in globals: 0414 return None 0415 pname = globals['__name__'] 0416 if "__path__" in globals: 0417 parent = self.modules[pname] 0418 assert globals is parent.__dict__ 0419 return parent 0420 if '.' in pname: 0421 i = pname.rfind('.') 0422 pname = pname[:i] 0423 parent = self.modules[pname] 0424 assert parent.__name__ == pname 0425 return parent 0426 return None 0427 0428 def find_head_package(self, parent, name): 0429 if '.' in name: 0430 i = name.find('.') 0431 head = name[:i] 0432 tail = name[i+1:] 0433 else: 0434 head = name 0435 tail = "" 0436 if parent: 0437 qname = "%s.%s" % (parent.__name__, head) 0438 else: 0439 qname = head 0440 q = self.import_it(head, qname, parent) 0441 if q: return q, tail 0442 if parent: 0443 qname = head 0444 parent = None 0445 q = self.import_it(head, qname, parent) 0446 if q: return q, tail 0447 raise ImportError, "No module named " + qname 0448 0449 def load_tail(self, q, tail): 0450 m = q 0451 while tail: 0452 i = tail.find('.') 0453 if i < 0: i = len(tail) 0454 head, tail = tail[:i], tail[i+1:] 0455 mname = "%s.%s" % (m.__name__, head) 0456 m = self.import_it(head, mname, m) 0457 if not m: 0458 raise ImportError, "No module named " + mname 0459 return m 0460 0461 def ensure_fromlist(self, m, fromlist, recursive=0): 0462 for sub in fromlist: 0463 if sub == "*": 0464 if not recursive: 0465 try: 0466 all = m.__all__ 0467 except AttributeError: 0468 pass 0469 else: 0470 self.ensure_fromlist(m, all, 1) 0471 continue 0472 if sub != "*" and not hasattr(m, sub): 0473 subname = "%s.%s" % (m.__name__, sub) 0474 submod = self.import_it(sub, subname, m) 0475 if not submod: 0476 raise ImportError, "No module named " + subname 0477 0478 def import_it(self, partname, fqname, parent, force_load=0): 0479 if not partname: 0480 raise ValueError, "Empty module name" 0481 if not force_load: 0482 try: 0483 return self.modules[fqname] 0484 except KeyError: 0485 pass 0486 try: 0487 path = parent and parent.__path__ 0488 except AttributeError: 0489 return None 0490 partname = str(partname) 0491 stuff = self.loader.find_module(partname, path) 0492 if not stuff: 0493 return None 0494 fqname = str(fqname) 0495 m = self.loader.load_module(fqname, stuff) 0496 if parent: 0497 setattr(parent, partname, m) 0498 return m 0499 0500 def reload(self, module): 0501 name = str(module.__name__) 0502 if '.' not in name: 0503 return self.import_it(name, name, None, force_load=1) 0504 i = name.rfind('.') 0505 pname = name[:i] 0506 parent = self.modules[pname] 0507 return self.import_it(name[i+1:], name, parent, force_load=1) 0508 0509 0510 default_importer = None 0511 current_importer = None 0512 0513 def install(importer = None): 0514 global current_importer 0515 current_importer = importer or default_importer or ModuleImporter() 0516 current_importer.install() 0517 0518 def uninstall(): 0519 global current_importer 0520 current_importer.uninstall() 0521
Generated by PyXR 0.9.4