hgdemandimport/demandimportpy2.py
changeset 43076 2372284d9457
parent 39256 574e1d3bc667
child 43503 313e3a279828
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
    35 contextmanager = contextlib.contextmanager
    35 contextmanager = contextlib.contextmanager
    36 
    36 
    37 _origimport = __import__
    37 _origimport = __import__
    38 
    38 
    39 nothing = object()
    39 nothing = object()
       
    40 
    40 
    41 
    41 def _hgextimport(importfunc, name, globals, *args, **kwargs):
    42 def _hgextimport(importfunc, name, globals, *args, **kwargs):
    42     try:
    43     try:
    43         return importfunc(name, globals, *args, **kwargs)
    44         return importfunc(name, globals, *args, **kwargs)
    44     except ImportError:
    45     except ImportError:
    51         if nameroot != contextroot:
    52         if nameroot != contextroot:
    52             raise
    53             raise
    53         # retry to import with "hgext_" prefix
    54         # retry to import with "hgext_" prefix
    54         return importfunc(hgextname, globals, *args, **kwargs)
    55         return importfunc(hgextname, globals, *args, **kwargs)
    55 
    56 
       
    57 
    56 class _demandmod(object):
    58 class _demandmod(object):
    57     """module demand-loader and proxy
    59     """module demand-loader and proxy
    58 
    60 
    59     Specify 1 as 'level' argument at construction, to import module
    61     Specify 1 as 'level' argument at construction, to import module
    60     relatively.
    62     relatively.
    65             head, rest = name.split('.', 1)
    67             head, rest = name.split('.', 1)
    66             after = [rest]
    68             after = [rest]
    67         else:
    69         else:
    68             head = name
    70             head = name
    69             after = []
    71             after = []
    70         object.__setattr__(self, r"_data",
    72         object.__setattr__(
    71                            (head, globals, locals, after, level, set()))
    73             self, r"_data", (head, globals, locals, after, level, set())
       
    74         )
    72         object.__setattr__(self, r"_module", None)
    75         object.__setattr__(self, r"_module", None)
    73 
    76 
    74     def _extend(self, name):
    77     def _extend(self, name):
    75         """add to the list of submodules to load"""
    78         """add to the list of submodules to load"""
    76         self._data[3].append(name)
    79         self._data[3].append(name)
    89     def _load(self):
    92     def _load(self):
    90         if not self._module:
    93         if not self._module:
    91             with tracing.log('demandimport %s', self._data[0]):
    94             with tracing.log('demandimport %s', self._data[0]):
    92                 head, globals, locals, after, level, modrefs = self._data
    95                 head, globals, locals, after, level, modrefs = self._data
    93                 mod = _hgextimport(
    96                 mod = _hgextimport(
    94                     _origimport, head, globals, locals, None, level)
    97                     _origimport, head, globals, locals, None, level
       
    98                 )
    95                 if mod is self:
    99                 if mod is self:
    96                     # In this case, _hgextimport() above should imply
   100                     # In this case, _hgextimport() above should imply
    97                     # _demandimport(). Otherwise, _hgextimport() never
   101                     # _demandimport(). Otherwise, _hgextimport() never
    98                     # returns _demandmod. This isn't intentional behavior,
   102                     # returns _demandmod. This isn't intentional behavior,
    99                     # in fact. (see also issue5304 for detail)
   103                     # in fact. (see also issue5304 for detail)
   113                 def subload(mod, p):
   117                 def subload(mod, p):
   114                     h, t = p, None
   118                     h, t = p, None
   115                     if '.' in p:
   119                     if '.' in p:
   116                         h, t = p.split('.', 1)
   120                         h, t = p.split('.', 1)
   117                     if getattr(mod, h, nothing) is nothing:
   121                     if getattr(mod, h, nothing) is nothing:
   118                         setattr(mod, h, _demandmod(
   122                         setattr(
   119                             p, mod.__dict__, mod.__dict__, level=1))
   123                             mod,
       
   124                             h,
       
   125                             _demandmod(p, mod.__dict__, mod.__dict__, level=1),
       
   126                         )
   120                     elif t:
   127                     elif t:
   121                         subload(getattr(mod, h), t)
   128                         subload(getattr(mod, h), t)
   122 
   129 
   123                 for x in after:
   130                 for x in after:
   124                     subload(mod, x)
   131                     subload(mod, x)
   162     @property
   169     @property
   163     def __doc__(self):
   170     def __doc__(self):
   164         self._load()
   171         self._load()
   165         return self._module.__doc__
   172         return self._module.__doc__
   166 
   173 
       
   174 
   167 _pypy = '__pypy__' in sys.builtin_module_names
   175 _pypy = '__pypy__' in sys.builtin_module_names
       
   176 
   168 
   177 
   169 def _demandimport(name, globals=None, locals=None, fromlist=None, level=-1):
   178 def _demandimport(name, globals=None, locals=None, fromlist=None, level=-1):
   170     if locals is None or name in ignores or fromlist == ('*',):
   179     if locals is None or name in ignores or fromlist == ('*',):
   171         # these cases we can't really delay
   180         # these cases we can't really delay
   172         return _hgextimport(_origimport, name, globals, locals, fromlist, level)
   181         return _hgextimport(_origimport, name, globals, locals, fromlist, level)
   173     elif not fromlist:
   182     elif not fromlist:
   174         # import a [as b]
   183         # import a [as b]
   175         if '.' in name: # a.b
   184         if '.' in name:  # a.b
   176             base, rest = name.split('.', 1)
   185             base, rest = name.split('.', 1)
   177             # email.__init__ loading email.mime
   186             # email.__init__ loading email.mime
   178             if globals and globals.get('__name__', None) == base:
   187             if globals and globals.get('__name__', None) == base:
   179                 return _origimport(name, globals, locals, fromlist, level)
   188                 return _origimport(name, globals, locals, fromlist, level)
   180             # if a is already demand-loaded, add b to its submodule list
   189             # if a is already demand-loaded, add b to its submodule list
   242             return mod
   251             return mod
   243 
   252 
   244         if level >= 0:
   253         if level >= 0:
   245             if name:
   254             if name:
   246                 # "from a import b" or "from .a import b" style
   255                 # "from a import b" or "from .a import b" style
   247                 rootmod = _hgextimport(_origimport, name, globals, locals,
   256                 rootmod = _hgextimport(
   248                                        level=level)
   257                     _origimport, name, globals, locals, level=level
       
   258                 )
   249                 mod = chainmodules(rootmod, name)
   259                 mod = chainmodules(rootmod, name)
   250             elif _pypy:
   260             elif _pypy:
   251                 # PyPy's __import__ throws an exception if invoked
   261                 # PyPy's __import__ throws an exception if invoked
   252                 # with an empty name and no fromlist.  Recreate the
   262                 # with an empty name and no fromlist.  Recreate the
   253                 # desired behaviour by hand.
   263                 # desired behaviour by hand.
   258                     mod = sys.modules[mn]
   268                     mod = sys.modules[mn]
   259                 if level > 1:
   269                 if level > 1:
   260                     mn = mn.rsplit('.', level - 1)[0]
   270                     mn = mn.rsplit('.', level - 1)[0]
   261                     mod = sys.modules[mn]
   271                     mod = sys.modules[mn]
   262             else:
   272             else:
   263                 mod = _hgextimport(_origimport, name, globals, locals,
   273                 mod = _hgextimport(
   264                                    level=level)
   274                     _origimport, name, globals, locals, level=level
       
   275                 )
   265 
   276 
   266             for x in fromlist:
   277             for x in fromlist:
   267                 processfromitem(mod, x)
   278                 processfromitem(mod, x)
   268 
   279 
   269             return mod
   280             return mod
   276         for x in fromlist:
   287         for x in fromlist:
   277             processfromitem(mod, x)
   288             processfromitem(mod, x)
   278 
   289 
   279         return mod
   290         return mod
   280 
   291 
       
   292 
   281 ignores = set()
   293 ignores = set()
       
   294 
   282 
   295 
   283 def init(ignoreset):
   296 def init(ignoreset):
   284     global ignores
   297     global ignores
   285     ignores = ignoreset
   298     ignores = ignoreset
   286 
   299 
       
   300 
   287 def isenabled():
   301 def isenabled():
   288     return builtins.__import__ == _demandimport
   302     return builtins.__import__ == _demandimport
       
   303 
   289 
   304 
   290 def enable():
   305 def enable():
   291     "enable global demand-loading of modules"
   306     "enable global demand-loading of modules"
   292     builtins.__import__ = _demandimport
   307     builtins.__import__ = _demandimport
   293 
   308 
       
   309 
   294 def disable():
   310 def disable():
   295     "disable global demand-loading of modules"
   311     "disable global demand-loading of modules"
   296     builtins.__import__ = _origimport
   312     builtins.__import__ = _origimport
       
   313 
   297 
   314 
   298 @contextmanager
   315 @contextmanager
   299 def deactivated():
   316 def deactivated():
   300     "context manager for disabling demandimport in 'with' blocks"
   317     "context manager for disabling demandimport in 'with' blocks"
   301     demandenabled = isenabled()
   318     demandenabled = isenabled()