mercurial/demandimport.py
branchstable
changeset 26813 b66e3ca0b90c
parent 26457 7e81305092a0
child 26830 65387a30430e
equal deleted inserted replaced
26535:d3712209921d 26813:b66e3ca0b90c
    22 
    22 
    23   from a import *
    23   from a import *
    24   b = __import__(a)
    24   b = __import__(a)
    25 '''
    25 '''
    26 
    26 
    27 import os, sys
    27 from __future__ import absolute_import
    28 from contextlib import contextmanager
    28 
       
    29 import contextlib
       
    30 import os
       
    31 import sys
    29 
    32 
    30 # __builtin__ in Python 2, builtins in Python 3.
    33 # __builtin__ in Python 2, builtins in Python 3.
    31 try:
    34 try:
    32     import __builtin__ as builtins
    35     import __builtin__ as builtins
    33 except ImportError:
    36 except ImportError:
    34     import builtins
    37     import builtins
    35 
    38 
       
    39 contextmanager = contextlib.contextmanager
       
    40 
    36 _origimport = __import__
    41 _origimport = __import__
    37 
    42 
    38 nothing = object()
    43 nothing = object()
    39 
    44 
    40 try:
    45 # Python 3 doesn't have relative imports nor level -1.
    41     # Python 3 doesn't have relative imports nor level -1.
    46 level = -1
    42     level = -1
    47 if sys.version_info[0] >= 3:
    43     if sys.version_info[0] >= 3:
    48     level = 0
    44         level = 0
    49 _import = _origimport
    45     _origimport(builtins.__name__, {}, {}, None, level)
    50 
    46 except TypeError: # no level argument
    51 def _hgextimport(importfunc, name, globals, *args, **kwargs):
    47     def _import(name, globals, locals, fromlist, level):
       
    48         "call _origimport with no level argument"
       
    49         return _origimport(name, globals, locals, fromlist)
       
    50 else:
       
    51     _import = _origimport
       
    52 
       
    53 def _hgextimport(importfunc, name, globals, *args):
       
    54     try:
    52     try:
    55         return importfunc(name, globals, *args)
    53         return importfunc(name, globals, *args, **kwargs)
    56     except ImportError:
    54     except ImportError:
    57         if not globals:
    55         if not globals:
    58             raise
    56             raise
    59         # extensions are loaded with "hgext_" prefix
    57         # extensions are loaded with "hgext_" prefix
    60         hgextname = 'hgext_%s' % name
    58         hgextname = 'hgext_%s' % name
    61         nameroot = hgextname.split('.', 1)[0]
    59         nameroot = hgextname.split('.', 1)[0]
    62         contextroot = globals.get('__name__', '').split('.', 1)[0]
    60         contextroot = globals.get('__name__', '').split('.', 1)[0]
    63         if nameroot != contextroot:
    61         if nameroot != contextroot:
    64             raise
    62             raise
    65         # retry to import with "hgext_" prefix
    63         # retry to import with "hgext_" prefix
    66         return importfunc(hgextname, globals, *args)
    64         return importfunc(hgextname, globals, *args, **kwargs)
    67 
    65 
    68 class _demandmod(object):
    66 class _demandmod(object):
    69     """module demand-loader and proxy"""
    67     """module demand-loader and proxy"""
    70     def __init__(self, name, globals, locals, level=level):
    68     def __init__(self, name, globals, locals, level=level):
    71         if '.' in name:
    69         if '.' in name:
    73             after = [rest]
    71             after = [rest]
    74         else:
    72         else:
    75             head = name
    73             head = name
    76             after = []
    74             after = []
    77         object.__setattr__(self, "_data",
    75         object.__setattr__(self, "_data",
    78                            (head, globals, locals, after, level))
    76                            (head, globals, locals, after, level, set()))
    79         object.__setattr__(self, "_module", None)
    77         object.__setattr__(self, "_module", None)
    80     def _extend(self, name):
    78     def _extend(self, name):
    81         """add to the list of submodules to load"""
    79         """add to the list of submodules to load"""
    82         self._data[3].append(name)
    80         self._data[3].append(name)
       
    81 
       
    82     def _addref(self, name):
       
    83         """Record that the named module ``name`` imports this module.
       
    84 
       
    85         References to this proxy class having the name of this module will be
       
    86         replaced at module load time. We assume the symbol inside the importing
       
    87         module is identical to the "head" name of this module. We don't
       
    88         actually know if "as X" syntax is being used to change the symbol name
       
    89         because this information isn't exposed to __import__.
       
    90         """
       
    91         self._data[5].add(name)
       
    92 
    83     def _load(self):
    93     def _load(self):
    84         if not self._module:
    94         if not self._module:
    85             head, globals, locals, after, level = self._data
    95             head, globals, locals, after, level, modrefs = self._data
    86             mod = _hgextimport(_import, head, globals, locals, None, level)
    96             mod = _hgextimport(_import, head, globals, locals, None, level)
    87             # load submodules
    97             # load submodules
    88             def subload(mod, p):
    98             def subload(mod, p):
    89                 h, t = p, None
    99                 h, t = p, None
    90                 if '.' in p:
   100                 if '.' in p:
    95                     subload(getattr(mod, h), t)
   105                     subload(getattr(mod, h), t)
    96 
   106 
    97             for x in after:
   107             for x in after:
    98                 subload(mod, x)
   108                 subload(mod, x)
    99 
   109 
   100             # are we in the locals dictionary still?
   110             # Replace references to this proxy instance with the actual module.
   101             if locals and locals.get(head) == self:
   111             if locals and locals.get(head) == self:
   102                 locals[head] = mod
   112                 locals[head] = mod
       
   113 
       
   114             for modname in modrefs:
       
   115                 modref = sys.modules.get(modname, None)
       
   116                 if modref and getattr(modref, head, None) == self:
       
   117                     setattr(modref, head, mod)
       
   118 
   103             object.__setattr__(self, "_module", mod)
   119             object.__setattr__(self, "_module", mod)
   104 
   120 
   105     def __repr__(self):
   121     def __repr__(self):
   106         if self._module:
   122         if self._module:
   107             return "<proxied module '%s'>" % self._data[0]
   123             return "<proxied module '%s'>" % self._data[0]
   108         return "<unloaded module '%s'>" % self._data[0]
   124         return "<unloaded module '%s'>" % self._data[0]
   109     def __call__(self, *args, **kwargs):
   125     def __call__(self, *args, **kwargs):
   110         raise TypeError("%s object is not callable" % repr(self))
   126         raise TypeError("%s object is not callable" % repr(self))
   111     def __getattribute__(self, attr):
   127     def __getattribute__(self, attr):
   112         if attr in ('_data', '_extend', '_load', '_module'):
   128         if attr in ('_data', '_extend', '_load', '_module', '_addref'):
   113             return object.__getattribute__(self, attr)
   129             return object.__getattribute__(self, attr)
   114         self._load()
   130         self._load()
   115         return getattr(self._module, attr)
   131         return getattr(self._module, attr)
   116     def __setattr__(self, attr, val):
   132     def __setattr__(self, attr, val):
   117         self._load()
   133         self._load()
   133                 if isinstance(locals[base], _demandmod):
   149                 if isinstance(locals[base], _demandmod):
   134                     locals[base]._extend(rest)
   150                     locals[base]._extend(rest)
   135                 return locals[base]
   151                 return locals[base]
   136         return _demandmod(name, globals, locals, level)
   152         return _demandmod(name, globals, locals, level)
   137     else:
   153     else:
   138         if level != -1:
   154         # There is a fromlist.
   139             # from . import b,c,d or from .a import b,c,d
       
   140             return _origimport(name, globals, locals, fromlist, level)
       
   141         # from a import b,c,d
   155         # from a import b,c,d
       
   156         # from . import b,c,d
       
   157         # from .a import b,c,d
       
   158 
       
   159         # level == -1: relative and absolute attempted (Python 2 only).
       
   160         # level >= 0: absolute only (Python 2 w/ absolute_import and Python 3).
       
   161         # The modern Mercurial convention is to use absolute_import everywhere,
       
   162         # so modern Mercurial code will have level >= 0.
       
   163 
       
   164         # The name of the module the import statement is located in.
       
   165         globalname = globals.get('__name__')
       
   166 
       
   167         def processfromitem(mod, attr, **kwargs):
       
   168             """Process an imported symbol in the import statement.
       
   169 
       
   170             If the symbol doesn't exist in the parent module, it must be a
       
   171             module. We set missing modules up as _demandmod instances.
       
   172             """
       
   173             symbol = getattr(mod, attr, nothing)
       
   174             if symbol is nothing:
       
   175                 symbol = _demandmod(attr, mod.__dict__, locals, **kwargs)
       
   176                 setattr(mod, attr, symbol)
       
   177 
       
   178             # Record the importing module references this symbol so we can
       
   179             # replace the symbol with the actual module instance at load
       
   180             # time.
       
   181             if globalname and isinstance(symbol, _demandmod):
       
   182                 symbol._addref(globalname)
       
   183 
       
   184         if level >= 0:
       
   185             # Mercurial's enforced import style does not use
       
   186             # "from a import b,c,d" or "from .a import b,c,d" syntax. In
       
   187             # addition, this appears to be giving errors with some modules
       
   188             # for unknown reasons. Since we shouldn't be using this syntax
       
   189             # much, work around the problems.
       
   190             if name:
       
   191                 return _hgextimport(_origimport, name, globals, locals,
       
   192                                     fromlist, level)
       
   193 
       
   194             mod = _hgextimport(_origimport, name, globals, locals, level=level)
       
   195 
       
   196             for x in fromlist:
       
   197                 processfromitem(mod, x, level=level)
       
   198 
       
   199             return mod
       
   200 
       
   201         # But, we still need to support lazy loading of standard library and 3rd
       
   202         # party modules. So handle level == -1.
   142         mod = _hgextimport(_origimport, name, globals, locals)
   203         mod = _hgextimport(_origimport, name, globals, locals)
   143         # recurse down the module chain
   204         # recurse down the module chain
   144         for comp in name.split('.')[1:]:
   205         for comp in name.split('.')[1:]:
   145             if getattr(mod, comp, nothing) is nothing:
   206             if getattr(mod, comp, nothing) is nothing:
   146                 setattr(mod, comp, _demandmod(comp, mod.__dict__, mod.__dict__))
   207                 setattr(mod, comp,
       
   208                         _demandmod(comp, mod.__dict__, mod.__dict__))
   147             mod = getattr(mod, comp)
   209             mod = getattr(mod, comp)
       
   210 
   148         for x in fromlist:
   211         for x in fromlist:
   149             # set requested submodules for demand load
   212             processfromitem(mod, x)
   150             if getattr(mod, x, nothing) is nothing:
   213 
   151                 setattr(mod, x, _demandmod(x, mod.__dict__, locals))
       
   152         return mod
   214         return mod
   153 
   215 
   154 ignore = [
   216 ignore = [
       
   217     '__future__',
   155     '_hashlib',
   218     '_hashlib',
   156     '_xmlplus',
   219     '_xmlplus',
   157     'fcntl',
   220     'fcntl',
   158     'win32com.gen_py',
   221     'win32com.gen_py',
   159     '_winreg', # 2.7 mimetypes needs immediate ImportError
   222     '_winreg', # 2.7 mimetypes needs immediate ImportError