| 
									
										
										
										
											1995-08-04 04:00:20 +00:00
										 |  |  | """Import hook support.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Consistent use of this module will make it possible to change the | 
					
						
							|  |  |  | different mechanisms involved in loading modules independently. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | While the built-in module imp exports interfaces to the built-in | 
					
						
							|  |  |  | module searching and loading algorithm, and it is possible to replace | 
					
						
							|  |  |  | the built-in function __import__ in order to change the semantics of | 
					
						
							|  |  |  | the import statement, until now it has been difficult to combine the | 
					
						
							|  |  |  | effect of different __import__ hacks, like loading modules from URLs | 
					
						
							|  |  |  | (rimport.py), implementing a hierarchical module namespace (newimp.py) | 
					
						
							|  |  |  | or restricted execution (rexec.py). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This module defines three new concepts: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | (1) A "file system hooks" class provides an interface to a filesystem. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | One hooks class is defined (Hooks), which uses the interface provided | 
					
						
							|  |  |  | by standard modules os and os.path.  It should be used as the base | 
					
						
							|  |  |  | class for other hooks classes. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | (2) A "module loader" class provides an interface to to search for a | 
					
						
							|  |  |  | module in a search path and to load it.  It defines a method which | 
					
						
							|  |  |  | searches for a module in a single directory; by overriding this method | 
					
						
							|  |  |  | one can redefine the details of the search.  If the directory is None, | 
					
						
							|  |  |  | built-in and frozen modules are searched instead. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Two module loader class are defined, both implementing the search | 
					
						
							|  |  |  | strategy used by the built-in __import__ function: ModuleLoader uses | 
					
						
							|  |  |  | the imp module's find_module interface, while HookableModuleLoader | 
					
						
							|  |  |  | uses a file system hooks class to interact with the file system.  Both | 
					
						
							|  |  |  | use the imp module's load_* interfaces to actually load the module. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | (3) A "module importer" class provides an interface to import a | 
					
						
							|  |  |  | module, as well as interfaces to reload and unload a module.  It also | 
					
						
							|  |  |  | provides interfaces to install and uninstall itself instead of the | 
					
						
							|  |  |  | default __import__ and reload (and unload) functions. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | One module importer class is defined (ModuleImporter), which uses a | 
					
						
							|  |  |  | module loader instance passed in (by default HookableModuleLoader is | 
					
						
							|  |  |  | instantiated). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The classes defined here should be used as base classes for extended | 
					
						
							|  |  |  | functionality along those lines. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | If a module mporter class supports dotted names, its import_module() | 
					
						
							|  |  |  | must return a different value depending on whether it is called on | 
					
						
							|  |  |  | behalf of a "from ... import ..." statement or not.  (This is caused | 
					
						
							|  |  |  | by the way the __import__ hook is used by the Python interpreter.)  It | 
					
						
							|  |  |  | would also do wise to install a different version of reload(). | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | XXX Should the imp.load_* functions also be called via the hooks | 
					
						
							|  |  |  | instance? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import __builtin__ | 
					
						
							|  |  |  | import imp | 
					
						
							|  |  |  | import os | 
					
						
							|  |  |  | import sys | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from imp import C_EXTENSION, PY_SOURCE, PY_COMPILED | 
					
						
							|  |  |  | BUILTIN_MODULE = 32 | 
					
						
							|  |  |  | FROZEN_MODULE = 33 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _Verbose: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, verbose = 0): | 
					
						
							|  |  |  | 	self.verbose = verbose | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_verbose(self): | 
					
						
							|  |  |  | 	return self.verbose | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def set_verbose(self, verbose): | 
					
						
							|  |  |  | 	self.verbose = verbose | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # XXX The following is an experimental interface | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def note(self, *args): | 
					
						
							|  |  |  | 	if self.verbose: | 
					
						
							|  |  |  | 	    apply(self.message, args) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def message(self, format, *args): | 
					
						
							|  |  |  | 	print format%args | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class BasicModuleLoader(_Verbose): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """Basic module loader.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     This provides the same functionality as built-in import.  It | 
					
						
							|  |  |  |     doesn't deal with checking sys.modules -- all it provides is | 
					
						
							|  |  |  |     find_module() and a load_module(), as well as find_module_in_dir() | 
					
						
							|  |  |  |     which searches just one directory, and can be overridden by a | 
					
						
							|  |  |  |     derived class to change the module search algorithm when the basic | 
					
						
							|  |  |  |     dependency on sys.path is unchanged. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     The interface is a little more convenient than imp's: | 
					
						
							|  |  |  |     find_module(name, [path]) returns None or 'stuff', and | 
					
						
							|  |  |  |     load_module(name, stuff) loads the module. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def find_module(self, name, path = None): | 
					
						
							|  |  |  | 	if path is None:  | 
					
						
							|  |  |  | 	    path = [None] + self.default_path() | 
					
						
							|  |  |  | 	for dir in path: | 
					
						
							|  |  |  | 	    stuff = self.find_module_in_dir(name, dir) | 
					
						
							|  |  |  | 	    if stuff: return stuff | 
					
						
							|  |  |  | 	return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def default_path(self): | 
					
						
							|  |  |  | 	return sys.path | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def find_module_in_dir(self, name, dir): | 
					
						
							|  |  |  | 	if dir is None: | 
					
						
							|  |  |  | 	    return self.find_builtin_module(name) | 
					
						
							|  |  |  | 	else: | 
					
						
							|  |  |  | 	    try: | 
					
						
							|  |  |  | 		return imp.find_module(name, [dir]) | 
					
						
							|  |  |  | 	    except ImportError: | 
					
						
							|  |  |  | 		return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def find_builtin_module(self, name): | 
					
						
							|  |  |  | 	if imp.is_builtin(name): | 
					
						
							|  |  |  | 	    return None, '', ('', '', BUILTIN_MODULE) | 
					
						
							|  |  |  | 	if imp.is_frozen(name): | 
					
						
							|  |  |  | 	    return None, '', ('', '', FROZEN_MODULE) | 
					
						
							|  |  |  | 	return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def load_module(self, name, stuff): | 
					
						
							|  |  |  | 	file, filename, (suff, mode, type) = stuff | 
					
						
							|  |  |  | 	if type == BUILTIN_MODULE: | 
					
						
							|  |  |  | 	    return imp.init_builtin(name) | 
					
						
							|  |  |  | 	if type == FROZEN_MODULE: | 
					
						
							|  |  |  | 	    return imp.init_frozen(name) | 
					
						
							|  |  |  | 	if type == C_EXTENSION: | 
					
						
							|  |  |  | 	    return imp.load_dynamic(name, filename, file) | 
					
						
							|  |  |  | 	if type == PY_SOURCE: | 
					
						
							|  |  |  | 	    return imp.load_source(name, filename, file) | 
					
						
							|  |  |  | 	if type == PY_COMPILED: | 
					
						
							|  |  |  | 	    return imp.load_compiled(name, filename, file) | 
					
						
							|  |  |  | 	raise ImportError, "Unrecognized module type (%s) for %s" % \ | 
					
						
							|  |  |  | 			   (`type`, name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Hooks(_Verbose): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """Hooks into the filesystem and interpreter.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     By deriving a subclass you can redefine your filesystem interface, | 
					
						
							|  |  |  |     e.g. to merge it with the URL space. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     This base class behaves just like the native filesystem. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # imp interface | 
					
						
							|  |  |  |     def get_suffixes(self): return imp.get_suffixes() | 
					
						
							|  |  |  |     def new_module(self, name): return imp.new_module(name) | 
					
						
							|  |  |  |     def is_builtin(self, name): return imp.is_builtin(name) | 
					
						
							|  |  |  |     def init_builtin(self, name): return imp.init_builtin(name) | 
					
						
							|  |  |  |     def is_frozen(self, name): return imp.is_frozen(name) | 
					
						
							|  |  |  |     def init_frozen(self, name): return imp.init_frozen(name) | 
					
						
							|  |  |  |     def get_frozen_object(self, name): return imp.get_frozen_object(name) | 
					
						
							|  |  |  |     def load_source(self, name, filename, file=None): | 
					
						
							|  |  |  | 	return imp.load_source(name, filename, file) | 
					
						
							|  |  |  |     def load_compiled(self, name, filename, file=None): | 
					
						
							|  |  |  | 	return imp.load_compiled(name, filename, file) | 
					
						
							|  |  |  |     def load_dynamic(self, name, filename, file=None): | 
					
						
							|  |  |  | 	return imp.load_dynamic(name, filename, file) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def add_module(self, name): | 
					
						
							|  |  |  | 	d = self.modules_dict() | 
					
						
							|  |  |  | 	if d.has_key(name): return d[name] | 
					
						
							|  |  |  | 	d[name] = m = self.new_module(name) | 
					
						
							|  |  |  | 	return m | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # sys interface | 
					
						
							|  |  |  |     def modules_dict(self): return sys.modules | 
					
						
							|  |  |  |     def default_path(self): return sys.path | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def path_split(self, x): return os.path.split(x) | 
					
						
							|  |  |  |     def path_join(self, x, y): return os.path.join(x, y) | 
					
						
							|  |  |  |     def path_isabs(self, x): return os.path.isabs(x) | 
					
						
							|  |  |  |     # etc. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def path_exists(self, x): return os.path.exists(x) | 
					
						
							|  |  |  |     def path_isdir(self, x): return os.path.isdir(x) | 
					
						
							|  |  |  |     def path_isfile(self, x): return os.path.isfile(x) | 
					
						
							|  |  |  |     def path_islink(self, x): return os.path.islink(x) | 
					
						
							|  |  |  |     # etc. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def openfile(self, *x): return apply(open, x) | 
					
						
							|  |  |  |     openfile_error = IOError | 
					
						
							|  |  |  |     def listdir(self, x): return os.listdir(x) | 
					
						
							|  |  |  |     listdir_error = os.error | 
					
						
							|  |  |  |     # etc. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ModuleLoader(BasicModuleLoader): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """Default module loader; uses file system hooks.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     By defining suitable hooks, you might be able to load modules from | 
					
						
							|  |  |  |     other sources than the file system, e.g. from compressed or | 
					
						
							|  |  |  |     encrypted files, tar files or (if you're brave!) URLs. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, hooks = None, verbose = 0): | 
					
						
							|  |  |  | 	BasicModuleLoader.__init__(self, verbose) | 
					
						
							|  |  |  | 	self.hooks = hooks or Hooks(verbose) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def default_path(self): | 
					
						
							|  |  |  | 	return self.hooks.default_path() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def modules_dict(self): | 
					
						
							|  |  |  | 	return self.hooks.modules_dict() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_hooks(self): | 
					
						
							|  |  |  | 	return self.hooks | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def set_hooks(self, hooks): | 
					
						
							|  |  |  | 	self.hooks = hooks | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def find_builtin_module(self, name): | 
					
						
							|  |  |  | 	if self.hooks.is_builtin(name): | 
					
						
							|  |  |  | 	    return None, '', ('', '', BUILTIN_MODULE) | 
					
						
							|  |  |  | 	if self.hooks.is_frozen(name): | 
					
						
							|  |  |  | 	    return None, '', ('', '', FROZEN_MODULE) | 
					
						
							|  |  |  | 	return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def find_module_in_dir(self, name, dir): | 
					
						
							|  |  |  | 	if dir is None: | 
					
						
							|  |  |  | 	    return self.find_builtin_module(name) | 
					
						
							|  |  |  | 	for info in self.hooks.get_suffixes(): | 
					
						
							|  |  |  | 	    suff, mode, type = info | 
					
						
							|  |  |  | 	    fullname = self.hooks.path_join(dir, name+suff) | 
					
						
							|  |  |  | 	    try: | 
					
						
							|  |  |  | 		fp = self.hooks.openfile(fullname, mode) | 
					
						
							|  |  |  | 		return fp, fullname, info | 
					
						
							|  |  |  | 	    except self.hooks.openfile_error: | 
					
						
							|  |  |  | 		pass | 
					
						
							|  |  |  | 	return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def load_module(self, name, stuff): | 
					
						
							|  |  |  | 	file, filename, (suff, mode, type) = stuff | 
					
						
							|  |  |  | 	if type == BUILTIN_MODULE: | 
					
						
							|  |  |  | 	    return self.hooks.init_builtin(name) | 
					
						
							|  |  |  | 	if type == FROZEN_MODULE: | 
					
						
							|  |  |  | 	    return self.hooks.init_frozen(name) | 
					
						
							|  |  |  | 	if type == C_EXTENSION: | 
					
						
							|  |  |  | 	    return self.hooks.load_dynamic(name, filename, file) | 
					
						
							|  |  |  | 	if type == PY_SOURCE: | 
					
						
							|  |  |  | 	    return self.hooks.load_source(name, filename, file) | 
					
						
							|  |  |  | 	if type == PY_COMPILED: | 
					
						
							|  |  |  | 	    return self.hooks.load_compiled(name, filename, file) | 
					
						
							|  |  |  | 	raise ImportError, "Unrecognized module type (%s) for %s" % \ | 
					
						
							|  |  |  | 			   (`type`, name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class FancyModuleLoader(ModuleLoader): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """Fancy module loader -- parses and execs the code itself.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def load_module(self, name, stuff): | 
					
						
							|  |  |  | 	file, filename, (suff, mode, type) = stuff | 
					
						
							|  |  |  | 	if type == FROZEN_MODULE: | 
					
						
							|  |  |  | 	    code = self.hooks.get_frozen_object(name) | 
					
						
							|  |  |  | 	elif type == PY_COMPILED: | 
					
						
							|  |  |  | 	    file.seek(8) | 
					
						
							|  |  |  | 	    code = marshal.load(file) | 
					
						
							|  |  |  | 	elif type == PY_SOURCE: | 
					
						
							|  |  |  | 	    data = file.read() | 
					
						
							|  |  |  | 	    code = compile(data, filename, 'exec') | 
					
						
							|  |  |  | 	else: | 
					
						
							|  |  |  | 	    return ModuleLoader.load_module(self, name, stuff) | 
					
						
							|  |  |  | 	m = self.hooks.add_module(name) | 
					
						
							|  |  |  | 	exec code in m.__dict__ | 
					
						
							|  |  |  | 	return m | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ModuleImporter(_Verbose): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """Default module importer; uses module loader.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     This provides the same functionality as built-in import, when | 
					
						
							|  |  |  |     combined with ModuleLoader. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, loader = None, verbose = 0): | 
					
						
							|  |  |  | 	_Verbose.__init__(self, verbose) | 
					
						
							|  |  |  | 	self.loader = loader or ModuleLoader(None, verbose) | 
					
						
							|  |  |  | 	self.modules = self.loader.modules_dict() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_loader(self): | 
					
						
							|  |  |  | 	return self.loader | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def set_loader(self, loader): | 
					
						
							|  |  |  | 	self.loader = loader | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_hooks(self): | 
					
						
							|  |  |  | 	return self.loader.get_hooks() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def set_hooks(self, hooks): | 
					
						
							|  |  |  | 	return self.loader.set_hooks(hooks) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def import_module(self, name, globals={}, locals={}, fromlist=[]): | 
					
						
							|  |  |  | 	if self.modules.has_key(name): | 
					
						
							|  |  |  | 	    return self.modules[name] # Fast path | 
					
						
							|  |  |  | 	stuff = self.loader.find_module(name) | 
					
						
							|  |  |  | 	if not stuff: | 
					
						
							|  |  |  | 	    raise ImportError, "No module named %s" % name | 
					
						
							|  |  |  | 	return self.loader.load_module(name, stuff) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def reload(self, module, path = None): | 
					
						
							| 
									
										
										
										
											1995-08-09 02:32:49 +00:00
										 |  |  | 	name = module.__name__ | 
					
						
							| 
									
										
										
										
											1995-08-04 04:00:20 +00:00
										 |  |  | 	stuff = self.loader.find_module(name, path) | 
					
						
							|  |  |  | 	if not stuff: | 
					
						
							|  |  |  | 	    raise ImportError, "Module %s not found for reload" % name | 
					
						
							|  |  |  | 	return self.loader.load_module(name, stuff) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def unload(self, module): | 
					
						
							|  |  |  | 	del self.modules[module.__name__] | 
					
						
							|  |  |  | 	# XXX Should this try to clear the module's namespace? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def install(self): | 
					
						
							|  |  |  | 	self.save_import_module = __builtin__.__import__ | 
					
						
							|  |  |  | 	self.save_reload = __builtin__.reload | 
					
						
							|  |  |  | 	if not hasattr(__builtin__, 'unload'): | 
					
						
							|  |  |  | 	    __builtin__.unload = None | 
					
						
							|  |  |  | 	self.save_unload = __builtin__.unload | 
					
						
							|  |  |  | 	__builtin__.__import__ = self.import_module | 
					
						
							|  |  |  | 	__builtin__.reload = self.reload | 
					
						
							|  |  |  | 	__builtin__.unload = self.unload | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def uninstall(self): | 
					
						
							|  |  |  | 	__builtin__.__import__ = self.save_import_module | 
					
						
							|  |  |  | 	__builtin__.reload = self.save_reload | 
					
						
							|  |  |  | 	__builtin__.unload = self.save_unload | 
					
						
							|  |  |  | 	if not __builtin__.unload: | 
					
						
							|  |  |  | 	    del __builtin__.unload | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # XXX Some experimental hacks -- importing ihooks auto-installs! | 
					
						
							|  |  |  | # XXX (That's supposed to be transparent anyway...) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | default_importer = None | 
					
						
							|  |  |  | current_importer = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def install(importer = None): | 
					
						
							|  |  |  |     global current_importer | 
					
						
							|  |  |  |     current_importer = importer or default_importer or ModuleImporter() | 
					
						
							|  |  |  |     current_importer.install() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def uninstall(): | 
					
						
							|  |  |  |     global current_importer | 
					
						
							|  |  |  |     current_importer.uninstall() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | install() |