+++ /dev/null
-"""Import hook support.\r
-\r
-Consistent use of this module will make it possible to change the\r
-different mechanisms involved in loading modules independently.\r
-\r
-While the built-in module imp exports interfaces to the built-in\r
-module searching and loading algorithm, and it is possible to replace\r
-the built-in function __import__ in order to change the semantics of\r
-the import statement, until now it has been difficult to combine the\r
-effect of different __import__ hacks, like loading modules from URLs\r
-by rimport.py, or restricted execution by rexec.py.\r
-\r
-This module defines three new concepts:\r
-\r
-1) A "file system hooks" class provides an interface to a filesystem.\r
-\r
-One hooks class is defined (Hooks), which uses the interface provided\r
-by standard modules os and os.path. It should be used as the base\r
-class for other hooks classes.\r
-\r
-2) A "module loader" class provides an interface to search for a\r
-module in a search path and to load it. It defines a method which\r
-searches for a module in a single directory; by overriding this method\r
-one can redefine the details of the search. If the directory is None,\r
-built-in and frozen modules are searched instead.\r
-\r
-Two module loader class are defined, both implementing the search\r
-strategy used by the built-in __import__ function: ModuleLoader uses\r
-the imp module's find_module interface, while HookableModuleLoader\r
-uses a file system hooks class to interact with the file system. Both\r
-use the imp module's load_* interfaces to actually load the module.\r
-\r
-3) A "module importer" class provides an interface to import a\r
-module, as well as interfaces to reload and unload a module. It also\r
-provides interfaces to install and uninstall itself instead of the\r
-default __import__ and reload (and unload) functions.\r
-\r
-One module importer class is defined (ModuleImporter), which uses a\r
-module loader instance passed in (by default HookableModuleLoader is\r
-instantiated).\r
-\r
-The classes defined here should be used as base classes for extended\r
-functionality along those lines.\r
-\r
-If a module importer class supports dotted names, its import_module()\r
-must return a different value depending on whether it is called on\r
-behalf of a "from ... import ..." statement or not. (This is caused\r
-by the way the __import__ hook is used by the Python interpreter.) It\r
-would also do wise to install a different version of reload().\r
-\r
-"""\r
-from warnings import warnpy3k, warn\r
-warnpy3k("the ihooks module has been removed in Python 3.0", stacklevel=2)\r
-del warnpy3k\r
-\r
-import __builtin__\r
-import imp\r
-import os\r
-import sys\r
-\r
-__all__ = ["BasicModuleLoader","Hooks","ModuleLoader","FancyModuleLoader",\r
- "BasicModuleImporter","ModuleImporter","install","uninstall"]\r
-\r
-VERBOSE = 0\r
-\r
-\r
-from imp import C_EXTENSION, PY_SOURCE, PY_COMPILED\r
-from imp import C_BUILTIN, PY_FROZEN, PKG_DIRECTORY\r
-BUILTIN_MODULE = C_BUILTIN\r
-FROZEN_MODULE = PY_FROZEN\r
-\r
-\r
-class _Verbose:\r
-\r
- def __init__(self, verbose = VERBOSE):\r
- self.verbose = verbose\r
-\r
- def get_verbose(self):\r
- return self.verbose\r
-\r
- def set_verbose(self, verbose):\r
- self.verbose = verbose\r
-\r
- # XXX The following is an experimental interface\r
-\r
- def note(self, *args):\r
- if self.verbose:\r
- self.message(*args)\r
-\r
- def message(self, format, *args):\r
- if args:\r
- print format%args\r
- else:\r
- print format\r
-\r
-\r
-class BasicModuleLoader(_Verbose):\r
-\r
- """Basic module loader.\r
-\r
- This provides the same functionality as built-in import. It\r
- doesn't deal with checking sys.modules -- all it provides is\r
- find_module() and a load_module(), as well as find_module_in_dir()\r
- which searches just one directory, and can be overridden by a\r
- derived class to change the module search algorithm when the basic\r
- dependency on sys.path is unchanged.\r
-\r
- The interface is a little more convenient than imp's:\r
- find_module(name, [path]) returns None or 'stuff', and\r
- load_module(name, stuff) loads the module.\r
-\r
- """\r
-\r
- def find_module(self, name, path = None):\r
- if path is None:\r
- path = [None] + self.default_path()\r
- for dir in path:\r
- stuff = self.find_module_in_dir(name, dir)\r
- if stuff: return stuff\r
- return None\r
-\r
- def default_path(self):\r
- return sys.path\r
-\r
- def find_module_in_dir(self, name, dir):\r
- if dir is None:\r
- return self.find_builtin_module(name)\r
- else:\r
- try:\r
- return imp.find_module(name, [dir])\r
- except ImportError:\r
- return None\r
-\r
- def find_builtin_module(self, name):\r
- # XXX frozen packages?\r
- if imp.is_builtin(name):\r
- return None, '', ('', '', BUILTIN_MODULE)\r
- if imp.is_frozen(name):\r
- return None, '', ('', '', FROZEN_MODULE)\r
- return None\r
-\r
- def load_module(self, name, stuff):\r
- file, filename, info = stuff\r
- try:\r
- return imp.load_module(name, file, filename, info)\r
- finally:\r
- if file: file.close()\r
-\r
-\r
-class Hooks(_Verbose):\r
-\r
- """Hooks into the filesystem and interpreter.\r
-\r
- By deriving a subclass you can redefine your filesystem interface,\r
- e.g. to merge it with the URL space.\r
-\r
- This base class behaves just like the native filesystem.\r
-\r
- """\r
-\r
- # imp interface\r
- def get_suffixes(self): return imp.get_suffixes()\r
- def new_module(self, name): return imp.new_module(name)\r
- def is_builtin(self, name): return imp.is_builtin(name)\r
- def init_builtin(self, name): return imp.init_builtin(name)\r
- def is_frozen(self, name): return imp.is_frozen(name)\r
- def init_frozen(self, name): return imp.init_frozen(name)\r
- def get_frozen_object(self, name): return imp.get_frozen_object(name)\r
- def load_source(self, name, filename, file=None):\r
- return imp.load_source(name, filename, file)\r
- def load_compiled(self, name, filename, file=None):\r
- return imp.load_compiled(name, filename, file)\r
- def load_dynamic(self, name, filename, file=None):\r
- return imp.load_dynamic(name, filename, file)\r
- def load_package(self, name, filename, file=None):\r
- return imp.load_module(name, file, filename, ("", "", PKG_DIRECTORY))\r
-\r
- def add_module(self, name):\r
- d = self.modules_dict()\r
- if name in d: return d[name]\r
- d[name] = m = self.new_module(name)\r
- return m\r
-\r
- # sys interface\r
- def modules_dict(self): return sys.modules\r
- def default_path(self): return sys.path\r
-\r
- def path_split(self, x): return os.path.split(x)\r
- def path_join(self, x, y): return os.path.join(x, y)\r
- def path_isabs(self, x): return os.path.isabs(x)\r
- # etc.\r
-\r
- def path_exists(self, x): return os.path.exists(x)\r
- def path_isdir(self, x): return os.path.isdir(x)\r
- def path_isfile(self, x): return os.path.isfile(x)\r
- def path_islink(self, x): return os.path.islink(x)\r
- # etc.\r
-\r
- def openfile(self, *x): return open(*x)\r
- openfile_error = IOError\r
- def listdir(self, x): return os.listdir(x)\r
- listdir_error = os.error\r
- # etc.\r
-\r
-\r
-class ModuleLoader(BasicModuleLoader):\r
-\r
- """Default module loader; uses file system hooks.\r
-\r
- By defining suitable hooks, you might be able to load modules from\r
- other sources than the file system, e.g. from compressed or\r
- encrypted files, tar files or (if you're brave!) URLs.\r
-\r
- """\r
-\r
- def __init__(self, hooks = None, verbose = VERBOSE):\r
- BasicModuleLoader.__init__(self, verbose)\r
- self.hooks = hooks or Hooks(verbose)\r
-\r
- def default_path(self):\r
- return self.hooks.default_path()\r
-\r
- def modules_dict(self):\r
- return self.hooks.modules_dict()\r
-\r
- def get_hooks(self):\r
- return self.hooks\r
-\r
- def set_hooks(self, hooks):\r
- self.hooks = hooks\r
-\r
- def find_builtin_module(self, name):\r
- # XXX frozen packages?\r
- if self.hooks.is_builtin(name):\r
- return None, '', ('', '', BUILTIN_MODULE)\r
- if self.hooks.is_frozen(name):\r
- return None, '', ('', '', FROZEN_MODULE)\r
- return None\r
-\r
- def find_module_in_dir(self, name, dir, allow_packages=1):\r
- if dir is None:\r
- return self.find_builtin_module(name)\r
- if allow_packages:\r
- fullname = self.hooks.path_join(dir, name)\r
- if self.hooks.path_isdir(fullname):\r
- stuff = self.find_module_in_dir("__init__", fullname, 0)\r
- if stuff:\r
- file = stuff[0]\r
- if file: file.close()\r
- return None, fullname, ('', '', PKG_DIRECTORY)\r
- for info in self.hooks.get_suffixes():\r
- suff, mode, type = info\r
- fullname = self.hooks.path_join(dir, name+suff)\r
- try:\r
- fp = self.hooks.openfile(fullname, mode)\r
- return fp, fullname, info\r
- except self.hooks.openfile_error:\r
- pass\r
- return None\r
-\r
- def load_module(self, name, stuff):\r
- file, filename, info = stuff\r
- (suff, mode, type) = info\r
- try:\r
- if type == BUILTIN_MODULE:\r
- return self.hooks.init_builtin(name)\r
- if type == FROZEN_MODULE:\r
- return self.hooks.init_frozen(name)\r
- if type == C_EXTENSION:\r
- m = self.hooks.load_dynamic(name, filename, file)\r
- elif type == PY_SOURCE:\r
- m = self.hooks.load_source(name, filename, file)\r
- elif type == PY_COMPILED:\r
- m = self.hooks.load_compiled(name, filename, file)\r
- elif type == PKG_DIRECTORY:\r
- m = self.hooks.load_package(name, filename, file)\r
- else:\r
- raise ImportError, "Unrecognized module type (%r) for %s" % \\r
- (type, name)\r
- finally:\r
- if file: file.close()\r
- m.__file__ = filename\r
- return m\r
-\r
-\r
-class FancyModuleLoader(ModuleLoader):\r
-\r
- """Fancy module loader -- parses and execs the code itself."""\r
-\r
- def load_module(self, name, stuff):\r
- file, filename, (suff, mode, type) = stuff\r
- realfilename = filename\r
- path = None\r
-\r
- if type == PKG_DIRECTORY:\r
- initstuff = self.find_module_in_dir("__init__", filename, 0)\r
- if not initstuff:\r
- raise ImportError, "No __init__ module in package %s" % name\r
- initfile, initfilename, initinfo = initstuff\r
- initsuff, initmode, inittype = initinfo\r
- if inittype not in (PY_COMPILED, PY_SOURCE):\r
- if initfile: initfile.close()\r
- raise ImportError, \\r
- "Bad type (%r) for __init__ module in package %s" % (\r
- inittype, name)\r
- path = [filename]\r
- file = initfile\r
- realfilename = initfilename\r
- type = inittype\r
-\r
- if type == FROZEN_MODULE:\r
- code = self.hooks.get_frozen_object(name)\r
- elif type == PY_COMPILED:\r
- import marshal\r
- file.seek(8)\r
- code = marshal.load(file)\r
- elif type == PY_SOURCE:\r
- data = file.read()\r
- code = compile(data, realfilename, 'exec')\r
- else:\r
- return ModuleLoader.load_module(self, name, stuff)\r
-\r
- m = self.hooks.add_module(name)\r
- if path:\r
- m.__path__ = path\r
- m.__file__ = filename\r
- try:\r
- exec code in m.__dict__\r
- except:\r
- d = self.hooks.modules_dict()\r
- if name in d:\r
- del d[name]\r
- raise\r
- return m\r
-\r
-\r
-class BasicModuleImporter(_Verbose):\r
-\r
- """Basic module importer; uses module loader.\r
-\r
- This provides basic import facilities but no package imports.\r
-\r
- """\r
-\r
- def __init__(self, loader = None, verbose = VERBOSE):\r
- _Verbose.__init__(self, verbose)\r
- self.loader = loader or ModuleLoader(None, verbose)\r
- self.modules = self.loader.modules_dict()\r
-\r
- def get_loader(self):\r
- return self.loader\r
-\r
- def set_loader(self, loader):\r
- self.loader = loader\r
-\r
- def get_hooks(self):\r
- return self.loader.get_hooks()\r
-\r
- def set_hooks(self, hooks):\r
- return self.loader.set_hooks(hooks)\r
-\r
- def import_module(self, name, globals={}, locals={}, fromlist=[]):\r
- name = str(name)\r
- if name in self.modules:\r
- return self.modules[name] # Fast path\r
- stuff = self.loader.find_module(name)\r
- if not stuff:\r
- raise ImportError, "No module named %s" % name\r
- return self.loader.load_module(name, stuff)\r
-\r
- def reload(self, module, path = None):\r
- name = str(module.__name__)\r
- stuff = self.loader.find_module(name, path)\r
- if not stuff:\r
- raise ImportError, "Module %s not found for reload" % name\r
- return self.loader.load_module(name, stuff)\r
-\r
- def unload(self, module):\r
- del self.modules[str(module.__name__)]\r
- # XXX Should this try to clear the module's namespace?\r
-\r
- def install(self):\r
- self.save_import_module = __builtin__.__import__\r
- self.save_reload = __builtin__.reload\r
- if not hasattr(__builtin__, 'unload'):\r
- __builtin__.unload = None\r
- self.save_unload = __builtin__.unload\r
- __builtin__.__import__ = self.import_module\r
- __builtin__.reload = self.reload\r
- __builtin__.unload = self.unload\r
-\r
- def uninstall(self):\r
- __builtin__.__import__ = self.save_import_module\r
- __builtin__.reload = self.save_reload\r
- __builtin__.unload = self.save_unload\r
- if not __builtin__.unload:\r
- del __builtin__.unload\r
-\r
-\r
-class ModuleImporter(BasicModuleImporter):\r
-\r
- """A module importer that supports packages."""\r
-\r
- def import_module(self, name, globals=None, locals=None, fromlist=None,\r
- level=-1):\r
- parent = self.determine_parent(globals, level)\r
- q, tail = self.find_head_package(parent, str(name))\r
- m = self.load_tail(q, tail)\r
- if not fromlist:\r
- return q\r
- if hasattr(m, "__path__"):\r
- self.ensure_fromlist(m, fromlist)\r
- return m\r
-\r
- def determine_parent(self, globals, level=-1):\r
- if not globals or not level:\r
- return None\r
- pkgname = globals.get('__package__')\r
- if pkgname is not None:\r
- if not pkgname and level > 0:\r
- raise ValueError, 'Attempted relative import in non-package'\r
- else:\r
- # __package__ not set, figure it out and set it\r
- modname = globals.get('__name__')\r
- if modname is None:\r
- return None\r
- if "__path__" in globals:\r
- # __path__ is set so modname is already the package name\r
- pkgname = modname\r
- else:\r
- # normal module, work out package name if any\r
- if '.' not in modname:\r
- if level > 0:\r
- raise ValueError, ('Attempted relative import in '\r
- 'non-package')\r
- globals['__package__'] = None\r
- return None\r
- pkgname = modname.rpartition('.')[0]\r
- globals['__package__'] = pkgname\r
- if level > 0:\r
- dot = len(pkgname)\r
- for x in range(level, 1, -1):\r
- try:\r
- dot = pkgname.rindex('.', 0, dot)\r
- except ValueError:\r
- raise ValueError('attempted relative import beyond '\r
- 'top-level package')\r
- pkgname = pkgname[:dot]\r
- try:\r
- return sys.modules[pkgname]\r
- except KeyError:\r
- if level < 1:\r
- warn("Parent module '%s' not found while handling "\r
- "absolute import" % pkgname, RuntimeWarning, 1)\r
- return None\r
- else:\r
- raise SystemError, ("Parent module '%s' not loaded, cannot "\r
- "perform relative import" % pkgname)\r
-\r
- def find_head_package(self, parent, name):\r
- if '.' in name:\r
- i = name.find('.')\r
- head = name[:i]\r
- tail = name[i+1:]\r
- else:\r
- head = name\r
- tail = ""\r
- if parent:\r
- qname = "%s.%s" % (parent.__name__, head)\r
- else:\r
- qname = head\r
- q = self.import_it(head, qname, parent)\r
- if q: return q, tail\r
- if parent:\r
- qname = head\r
- parent = None\r
- q = self.import_it(head, qname, parent)\r
- if q: return q, tail\r
- raise ImportError, "No module named '%s'" % qname\r
-\r
- def load_tail(self, q, tail):\r
- m = q\r
- while tail:\r
- i = tail.find('.')\r
- if i < 0: i = len(tail)\r
- head, tail = tail[:i], tail[i+1:]\r
- mname = "%s.%s" % (m.__name__, head)\r
- m = self.import_it(head, mname, m)\r
- if not m:\r
- raise ImportError, "No module named '%s'" % mname\r
- return m\r
-\r
- def ensure_fromlist(self, m, fromlist, recursive=0):\r
- for sub in fromlist:\r
- if sub == "*":\r
- if not recursive:\r
- try:\r
- all = m.__all__\r
- except AttributeError:\r
- pass\r
- else:\r
- self.ensure_fromlist(m, all, 1)\r
- continue\r
- if sub != "*" and not hasattr(m, sub):\r
- subname = "%s.%s" % (m.__name__, sub)\r
- submod = self.import_it(sub, subname, m)\r
- if not submod:\r
- raise ImportError, "No module named '%s'" % subname\r
-\r
- def import_it(self, partname, fqname, parent, force_load=0):\r
- if not partname:\r
- # completely empty module name should only happen in\r
- # 'from . import' or __import__("")\r
- return parent\r
- if not force_load:\r
- try:\r
- return self.modules[fqname]\r
- except KeyError:\r
- pass\r
- try:\r
- path = parent and parent.__path__\r
- except AttributeError:\r
- return None\r
- partname = str(partname)\r
- stuff = self.loader.find_module(partname, path)\r
- if not stuff:\r
- return None\r
- fqname = str(fqname)\r
- m = self.loader.load_module(fqname, stuff)\r
- if parent:\r
- setattr(parent, partname, m)\r
- return m\r
-\r
- def reload(self, module):\r
- name = str(module.__name__)\r
- if '.' not in name:\r
- return self.import_it(name, name, None, force_load=1)\r
- i = name.rfind('.')\r
- pname = name[:i]\r
- parent = self.modules[pname]\r
- return self.import_it(name[i+1:], name, parent, force_load=1)\r
-\r
-\r
-default_importer = None\r
-current_importer = None\r
-\r
-def install(importer = None):\r
- global current_importer\r
- current_importer = importer or default_importer or ModuleImporter()\r
- current_importer.install()\r
-\r
-def uninstall():\r
- global current_importer\r
- current_importer.uninstall()\r