demandimport.py
160 lines
| 5.5 KiB
| text/x-python
|
PythonLexer
/ mercurial / demandimport.py
Matt Mackall
|
r3877 | # demandimport.py - global demand-loading of modules for Mercurial | ||
# | ||||
Thomas Arendsen Hein
|
r4635 | # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com> | ||
Matt Mackall
|
r3877 | # | ||
Martin Geisler
|
r8225 | # This software may be used and distributed according to the terms of the | ||
Matt Mackall
|
r10263 | # GNU General Public License version 2 or any later version. | ||
Matt Mackall
|
r3877 | |||
''' | ||||
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) | ||||
''' | ||||
Dirkjan Ochtman
|
r7727 | import __builtin__ | ||
Matt Mackall
|
r3877 | _origimport = __import__ | ||
class _demandmod(object): | ||||
"""module demand-loader and proxy""" | ||||
Dan Villiom Podlaski Christiansen
|
r11942 | def __init__(self, name, globals, locals, level): | ||
Matt Mackall
|
r3877 | if '.' in name: | ||
head, rest = name.split('.', 1) | ||||
after = [rest] | ||||
else: | ||||
head = name | ||||
after = [] | ||||
Dan Villiom Podlaski Christiansen
|
r11942 | object.__setattr__(self, "_data", (head, globals, locals, after, level)) | ||
Benoit Boissinot
|
r3896 | object.__setattr__(self, "_module", None) | ||
Matt Mackall
|
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: | ||||
Dan Villiom Podlaski Christiansen
|
r11942 | head, globals, locals, after, level = self._data | ||
if level is not None: | ||||
mod = _origimport(head, globals, locals, level) | ||||
else: | ||||
mod = _origimport(head, globals, locals) | ||||
Matt Mackall
|
r3877 | # load submodules | ||
Matt Mackall
|
r3921 | def subload(mod, p): | ||
h, t = p, None | ||||
if '.' in p: | ||||
h, t = p.split('.', 1) | ||||
if not hasattr(mod, h): | ||||
Dan Villiom Podlaski Christiansen
|
r11942 | # TODO: should we adjust the level here? | ||
submod = _demandmod(p, mod.__dict__, mod.__dict__, | ||||
level=level) | ||||
setattr(mod, h, submod) | ||||
Brendan Cully
|
r3926 | elif t: | ||
Matt Mackall
|
r3921 | subload(getattr(mod, h), t) | ||
Matt Mackall
|
r3877 | for x in after: | ||
Matt Mackall
|
r3921 | subload(mod, x) | ||
Matt Mackall
|
r3877 | # are we in the locals dictionary still? | ||
if locals and locals.get(head) == self: | ||||
locals[head] = mod | ||||
Benoit Boissinot
|
r3896 | object.__setattr__(self, "_module", mod) | ||
Matt Mackall
|
r4631 | |||
Matt Mackall
|
r3877 | def __repr__(self): | ||
Matt Mackall
|
r4631 | if self._module: | ||
return "<proxied module '%s'>" % self._data[0] | ||||
Matt Mackall
|
r3877 | return "<unloaded module '%s'>" % self._data[0] | ||
def __call__(self, *args, **kwargs): | ||||
Matt Mackall
|
r5639 | raise TypeError("%s object is not callable" % repr(self)) | ||
Brendan Cully
|
r3903 | def __getattribute__(self, attr): | ||
if attr in ('_data', '_extend', '_load', '_module'): | ||||
return object.__getattribute__(self, attr) | ||||
Matt Mackall
|
r3877 | self._load() | ||
return getattr(self._module, attr) | ||||
def __setattr__(self, attr, val): | ||||
self._load() | ||||
setattr(self._module, attr, val) | ||||
Ali Gholami Rudi
|
r5929 | def _demandimport(name, globals=None, locals=None, fromlist=None, level=None): | ||
Matt Mackall
|
r3877 | if not locals or name in ignore or fromlist == ('*',): | ||
# these cases we can't really delay | ||||
Dan Villiom Podlaski Christiansen
|
r9315 | if level is None: | ||
return _origimport(name, globals, locals, fromlist) | ||||
else: | ||||
return _origimport(name, globals, locals, fromlist, level) | ||||
Matt Mackall
|
r3877 | elif not fromlist: | ||
# import a [as b] | ||||
if '.' in name: # a.b | ||||
base, rest = name.split('.', 1) | ||||
Brendan Cully
|
r3903 | # email.__init__ loading email.mime | ||
if globals and globals.get('__name__', None) == base: | ||||
Dan Villiom Podlaski Christiansen
|
r11942 | if level is not None: | ||
return _origimport(name, globals, locals, fromlist, level) | ||||
else: | ||||
return _origimport(name, globals, locals, fromlist) | ||||
Matt Mackall
|
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] | ||||
Dan Villiom Podlaski Christiansen
|
r11942 | return _demandmod(name, globals, locals, level=level) | ||
Matt Mackall
|
r3877 | else: | ||
Dan Villiom Podlaski Christiansen
|
r11942 | # from a import b,c,d | ||
Ali Gholami Rudi
|
r5929 | if level is not None: | ||
Dan Villiom Podlaski Christiansen
|
r11942 | mod = _origimport(name, globals, locals, level=level) | ||
else: | ||||
mod = _origimport(name, globals, locals) | ||||
Matt Mackall
|
r3877 | # recurse down the module chain | ||
for comp in name.split('.')[1:]: | ||||
Lee Cantey
|
r4626 | if not hasattr(mod, comp): | ||
Dan Villiom Podlaski Christiansen
|
r11942 | # TODO: should we adjust the level here? | ||
submod = _demandmod(comp, mod.__dict__, mod.__dict__, | ||||
level=level) | ||||
setattr(mod, comp, submod) | ||||
Matt Mackall
|
r3877 | mod = getattr(mod, comp) | ||
for x in fromlist: | ||||
# set requested submodules for demand load | ||||
if not(hasattr(mod, x)): | ||||
Dan Villiom Podlaski Christiansen
|
r11942 | # TODO: should we adjust the level here? | ||
submod = _demandmod(x, mod.__dict__, locals, level=level) | ||||
setattr(mod, x, submod) | ||||
Matt Mackall
|
r3877 | return mod | ||
Patrick Mezard
|
r5098 | ignore = [ | ||
'_hashlib', | ||||
'_xmlplus', | ||||
'fcntl', | ||||
'win32com.gen_py', | ||||
Dirkjan Ochtman
|
r10242 | '_winreg', # 2.7 mimetypes needs immediate ImportError | ||
Steve Borho
|
r7861 | 'pythoncom', | ||
Patrick Mezard
|
r5098 | # imported by tarfile, not available under Windows | ||
'pwd', | ||||
'grp', | ||||
# imported by profile, itself imported by hotshot.stats, | ||||
# not available under Windows | ||||
'resource', | ||||
Steve Borho
|
r9458 | # this trips up many extension authors | ||
'gtk', | ||||
Greg Ward
|
r10598 | # setuptools' pkg_resources.py expects "from __main__ import x" to | ||
# raise ImportError if x not defined | ||||
'__main__', | ||||
Dirkjan Ochtman
|
r10612 | '_ssl', # conditional imports in the stdlib, issue1964 | ||
Patrick Mezard
|
r5098 | ] | ||
Matt Mackall
|
r3877 | |||
def enable(): | ||||
"enable global demand-loading of modules" | ||||
Dirkjan Ochtman
|
r7727 | __builtin__.__import__ = _demandimport | ||
Matt Mackall
|
r3877 | |||
def disable(): | ||||
"disable global demand-loading of modules" | ||||
Dirkjan Ochtman
|
r7727 | __builtin__.__import__ = _origimport | ||
Matt Mackall
|
r3877 | |||