mercurial/demandimport.py
changeset 3886 abaee83ce0a6
child 3888 3b628b5da9e9
child 3897 f47afa2401a2
equal deleted inserted replaced
3885:1e0b94cfba0e 3886:abaee83ce0a6
       
     1 # demandimport.py - global demand-loading of modules for Mercurial
       
     2 #
       
     3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
       
     4 #
       
     5 # This software may be used and distributed according to the terms
       
     6 # of the GNU General Public License, incorporated herein by reference.
       
     7 
       
     8 '''
       
     9 demandimport - automatic demandloading of modules
       
    10 
       
    11 To enable this module, do:
       
    12 
       
    13   import demandimport; demandimport.enable()
       
    14 
       
    15 Imports of the following forms will be demand-loaded:
       
    16 
       
    17   import a, b.c
       
    18   import a.b as c
       
    19   from a import b,c # a will be loaded immediately
       
    20 
       
    21 These imports will not be delayed:
       
    22 
       
    23   from a import *
       
    24   b = __import__(a)
       
    25 '''
       
    26 
       
    27 _origimport = __import__
       
    28 
       
    29 class _demandmod(object):
       
    30     """module demand-loader and proxy"""
       
    31     def __init__(self, name, globals, locals):
       
    32         if '.' in name:
       
    33             head, rest = name.split('.', 1)
       
    34             after = [rest]
       
    35         else:
       
    36             head = name
       
    37             after = []
       
    38         self.__dict__["_data"] = (head, globals, locals, after)
       
    39         self.__dict__["_module"] = None
       
    40     def _extend(self, name):
       
    41         """add to the list of submodules to load"""
       
    42         self._data[3].append(name)
       
    43     def _load(self):
       
    44         if not self._module:
       
    45             head, globals, locals, after = self._data
       
    46             mod = _origimport(head, globals, locals)
       
    47             # load submodules
       
    48             for x in after:
       
    49                 hx = x
       
    50                 if '.' in x:
       
    51                     hx = x.split('.')[0]
       
    52                 if not hasattr(mod, hx):
       
    53                     setattr(mod, hx, _demandmod(x, mod.__dict__, mod.__dict__))
       
    54             # are we in the locals dictionary still?
       
    55             if locals and locals.get(head) == self:
       
    56                 locals[head] = mod
       
    57             self.__dict__["_module"] = mod
       
    58     def __repr__(self):
       
    59         return "<unloaded module '%s'>" % self._data[0]
       
    60     def __call__(self, *args, **kwargs):
       
    61         raise TypeError("'unloaded module' object is not callable")
       
    62     def __getattr__(self, attr):
       
    63         self._load()
       
    64         return getattr(self._module, attr)
       
    65     def __setattr__(self, attr, val):
       
    66         self._load()
       
    67         setattr(self._module, attr, val)
       
    68 
       
    69 def _demandimport(name, globals=None, locals=None, fromlist=None):
       
    70     if not locals or name in ignore or fromlist == ('*',):
       
    71         # these cases we can't really delay
       
    72         return _origimport(name, globals, locals, fromlist)
       
    73     elif not fromlist:
       
    74         # import a [as b]
       
    75         if '.' in name: # a.b
       
    76             base, rest = name.split('.', 1)
       
    77             # if a is already demand-loaded, add b to its submodule list
       
    78             if base in locals:
       
    79                 if isinstance(locals[base], _demandmod):
       
    80                     locals[base]._extend(rest)
       
    81                 return locals[base]
       
    82         return _demandmod(name, globals, locals)
       
    83     else:
       
    84         # from a import b,c,d
       
    85         mod = _origimport(name, globals, locals)
       
    86         # recurse down the module chain
       
    87         for comp in name.split('.')[1:]:
       
    88             mod = getattr(mod, comp)
       
    89         for x in fromlist:
       
    90             # set requested submodules for demand load
       
    91             if not(hasattr(mod, x)):
       
    92                 setattr(mod, x, _demandmod(x, mod.__dict__, mod.__dict__))
       
    93         return mod
       
    94 
       
    95 ignore = []
       
    96 
       
    97 def enable():
       
    98     "enable global demand-loading of modules"
       
    99     __builtins__["__import__"] = _demandimport
       
   100 
       
   101 def disable():
       
   102     "disable global demand-loading of modules"
       
   103     __builtins__["__import__"] = _origimport
       
   104