demandimport.py
131 lines
| 4.3 KiB
| text/x-python
|
PythonLexer
/ mercurial / demandimport.py
|
r3877 | # demandimport.py - global demand-loading of modules for Mercurial | ||
# | ||||
|
r4635 | # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com> | ||
|
r3877 | # | ||
# This software may be used and distributed according to the terms | ||||
# of the GNU General Public License, incorporated herein by reference. | ||||
''' | ||||
demandimport - automatic demandloading of modules | ||||
To enable this module, do: | ||||
import demandimport; demandimport.enable() | ||||
Imports of the following forms will be demand-loaded: | ||||
import a, b.c | ||||
import a.b as c | ||||
from a import b,c # a will be loaded immediately | ||||
These imports will not be delayed: | ||||
from a import * | ||||
b = __import__(a) | ||||
''' | ||||
_origimport = __import__ | ||||
class _demandmod(object): | ||||
"""module demand-loader and proxy""" | ||||
def __init__(self, name, globals, locals): | ||||
if '.' in name: | ||||
head, rest = name.split('.', 1) | ||||
after = [rest] | ||||
else: | ||||
head = name | ||||
after = [] | ||||
|
r3896 | object.__setattr__(self, "_data", (head, globals, locals, after)) | ||
object.__setattr__(self, "_module", None) | ||||
|
r3877 | def _extend(self, name): | ||
"""add to the list of submodules to load""" | ||||
self._data[3].append(name) | ||||
def _load(self): | ||||
if not self._module: | ||||
head, globals, locals, after = self._data | ||||
mod = _origimport(head, globals, locals) | ||||
# load submodules | ||||
|
r3921 | def subload(mod, p): | ||
h, t = p, None | ||||
if '.' in p: | ||||
h, t = p.split('.', 1) | ||||
if not hasattr(mod, h): | ||||
setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__)) | ||||
|
r3926 | elif t: | ||
|
r3921 | subload(getattr(mod, h), t) | ||
|
r3877 | for x in after: | ||
|
r3921 | subload(mod, x) | ||
|
r3877 | # are we in the locals dictionary still? | ||
if locals and locals.get(head) == self: | ||||
locals[head] = mod | ||||
|
r3896 | object.__setattr__(self, "_module", mod) | ||
|
r4631 | |||
|
r3877 | def __repr__(self): | ||
|
r4631 | if self._module: | ||
return "<proxied module '%s'>" % self._data[0] | ||||
|
r3877 | return "<unloaded module '%s'>" % self._data[0] | ||
def __call__(self, *args, **kwargs): | ||||
raise TypeError("'unloaded module' object is not callable") | ||||
|
r3903 | def __getattribute__(self, attr): | ||
if attr in ('_data', '_extend', '_load', '_module'): | ||||
return object.__getattribute__(self, attr) | ||||
|
r3877 | self._load() | ||
return getattr(self._module, attr) | ||||
def __setattr__(self, attr, val): | ||||
self._load() | ||||
setattr(self._module, attr, val) | ||||
def _demandimport(name, globals=None, locals=None, fromlist=None): | ||||
if not locals or name in ignore or fromlist == ('*',): | ||||
# these cases we can't really delay | ||||
return _origimport(name, globals, locals, fromlist) | ||||
elif not fromlist: | ||||
# import a [as b] | ||||
if '.' in name: # a.b | ||||
base, rest = name.split('.', 1) | ||||
|
r3903 | # email.__init__ loading email.mime | ||
if globals and globals.get('__name__', None) == base: | ||||
return _origimport(name, globals, locals, fromlist) | ||||
|
r3877 | # if a is already demand-loaded, add b to its submodule list | ||
if base in locals: | ||||
if isinstance(locals[base], _demandmod): | ||||
locals[base]._extend(rest) | ||||
return locals[base] | ||||
return _demandmod(name, globals, locals) | ||||
else: | ||||
# from a import b,c,d | ||||
mod = _origimport(name, globals, locals) | ||||
# recurse down the module chain | ||||
for comp in name.split('.')[1:]: | ||||
|
r4626 | if not hasattr(mod, comp): | ||
setattr(mod, comp, _demandmod(comp, mod.__dict__, mod.__dict__)) | ||||
|
r3877 | mod = getattr(mod, comp) | ||
for x in fromlist: | ||||
# set requested submodules for demand load | ||||
if not(hasattr(mod, x)): | ||||
|
r4631 | setattr(mod, x, _demandmod(x, mod.__dict__, locals)) | ||
|
r3877 | return mod | ||
|
r5098 | ignore = [ | ||
'_hashlib', | ||||
'_xmlplus', | ||||
'fcntl', | ||||
'win32com.gen_py', | ||||
# imported by tarfile, not available under Windows | ||||
'pwd', | ||||
'grp', | ||||
# imported by profile, itself imported by hotshot.stats, | ||||
# not available under Windows | ||||
'resource', | ||||
] | ||||
|
r3877 | |||
def enable(): | ||||
"enable global demand-loading of modules" | ||||
__builtins__["__import__"] = _demandimport | ||||
def disable(): | ||||
"disable global demand-loading of modules" | ||||
__builtins__["__import__"] = _origimport | ||||