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 |