139 lines · 4.6 KB · Python
RawDownload
# demandimport.py - global demand-loading of modules for Mercurial## Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>## This software may be used and distributed according to the terms of the# GNU General Public License version 2, 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)''' import __builtin___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 = []        object.__setattr__(self, "_data", (head, globals, locals, after))        object.__setattr__(self, "_module", None)    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            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__))                elif t:                    subload(getattr(mod, h), t)             for x in after:                subload(mod, x)             # are we in the locals dictionary still?            if locals and locals.get(head) == self:                locals[head] = mod            object.__setattr__(self, "_module", mod)     def __repr__(self):        if self._module:            return "<proxied module '%s'>" % self._data[0]        return "<unloaded module '%s'>" % self._data[0]    def __call__(self, *args, **kwargs):        raise TypeError("%s object is not callable" % repr(self))    def __getattribute__(self, attr):        if attr in ('_data', '_extend', '_load', '_module'):            return object.__getattribute__(self, attr)        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, level=None):    if not locals or name in ignore or fromlist == ('*',):        # these cases we can't really delay        if level is None:            return _origimport(name, globals, locals, fromlist)        else:            return _origimport(name, globals, locals, fromlist, level)    elif not fromlist:        # import a [as b]        if '.' in name: # a.b            base, rest = name.split('.', 1)            # email.__init__ loading email.mime            if globals and globals.get('__name__', None) == base:                return _origimport(name, globals, locals, fromlist)            # 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:        if level is not None:            # from . import b,c,d or from .a import b,c,d            return _origimport(name, globals, locals, fromlist, level)        # from a import b,c,d        mod = _origimport(name, globals, locals)        # recurse down the module chain        for comp in name.split('.')[1:]:            if not hasattr(mod, comp):                setattr(mod, comp, _demandmod(comp, mod.__dict__, mod.__dict__))            mod = getattr(mod, comp)        for x in fromlist:            # set requested submodules for demand load            if not(hasattr(mod, x)):                setattr(mod, x, _demandmod(x, mod.__dict__, locals))        return mod ignore = [    '_hashlib',    '_xmlplus',    'fcntl',    'win32com.gen_py',    'pythoncom',    # imported by tarfile, not available under Windows    'pwd',    'grp',    # imported by profile, itself imported by hotshot.stats,    # not available under Windows    'resource',    ] def enable():    "enable global demand-loading of modules"    __builtin__.__import__ = _demandimport def disable():    "disable global demand-loading of modules"    __builtin__.__import__ = _origimport