71 after = [rest] |
71 after = [rest] |
72 else: |
72 else: |
73 head = name |
73 head = name |
74 after = [] |
74 after = [] |
75 object.__setattr__(self, "_data", |
75 object.__setattr__(self, "_data", |
76 (head, globals, locals, after, level)) |
76 (head, globals, locals, after, level, set())) |
77 object.__setattr__(self, "_module", None) |
77 object.__setattr__(self, "_module", None) |
78 def _extend(self, name): |
78 def _extend(self, name): |
79 """add to the list of submodules to load""" |
79 """add to the list of submodules to load""" |
80 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 |
81 def _load(self): |
93 def _load(self): |
82 if not self._module: |
94 if not self._module: |
83 head, globals, locals, after, level = self._data |
95 head, globals, locals, after, level, modrefs = self._data |
84 mod = _hgextimport(_import, head, globals, locals, None, level) |
96 mod = _hgextimport(_import, head, globals, locals, None, level) |
85 # load submodules |
97 # load submodules |
86 def subload(mod, p): |
98 def subload(mod, p): |
87 h, t = p, None |
99 h, t = p, None |
88 if '.' in p: |
100 if '.' in p: |
93 subload(getattr(mod, h), t) |
105 subload(getattr(mod, h), t) |
94 |
106 |
95 for x in after: |
107 for x in after: |
96 subload(mod, x) |
108 subload(mod, x) |
97 |
109 |
98 # are we in the locals dictionary still? |
110 # Replace references to this proxy instance with the actual module. |
99 if locals and locals.get(head) == self: |
111 if locals and locals.get(head) == self: |
100 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 |
101 object.__setattr__(self, "_module", mod) |
119 object.__setattr__(self, "_module", mod) |
102 |
120 |
103 def __repr__(self): |
121 def __repr__(self): |
104 if self._module: |
122 if self._module: |
105 return "<proxied module '%s'>" % self._data[0] |
123 return "<proxied module '%s'>" % self._data[0] |
106 return "<unloaded module '%s'>" % self._data[0] |
124 return "<unloaded module '%s'>" % self._data[0] |
107 def __call__(self, *args, **kwargs): |
125 def __call__(self, *args, **kwargs): |
108 raise TypeError("%s object is not callable" % repr(self)) |
126 raise TypeError("%s object is not callable" % repr(self)) |
109 def __getattribute__(self, attr): |
127 def __getattribute__(self, attr): |
110 if attr in ('_data', '_extend', '_load', '_module'): |
128 if attr in ('_data', '_extend', '_load', '_module', '_addref'): |
111 return object.__getattribute__(self, attr) |
129 return object.__getattribute__(self, attr) |
112 self._load() |
130 self._load() |
113 return getattr(self._module, attr) |
131 return getattr(self._module, attr) |
114 def __setattr__(self, attr, val): |
132 def __setattr__(self, attr, val): |
115 self._load() |
133 self._load() |
141 # level == -1: relative and absolute attempted (Python 2 only). |
159 # level == -1: relative and absolute attempted (Python 2 only). |
142 # level >= 0: absolute only (Python 2 w/ absolute_import and Python 3). |
160 # level >= 0: absolute only (Python 2 w/ absolute_import and Python 3). |
143 # The modern Mercurial convention is to use absolute_import everywhere, |
161 # The modern Mercurial convention is to use absolute_import everywhere, |
144 # so modern Mercurial code will have level >= 0. |
162 # so modern Mercurial code will have level >= 0. |
145 |
163 |
|
164 # The name of the module the import statement is located in. |
|
165 globalname = globals.get('__name__') |
|
166 |
146 def processfromitem(mod, attr, **kwargs): |
167 def processfromitem(mod, attr, **kwargs): |
147 """Process an imported symbol in the import statement. |
168 """Process an imported symbol in the import statement. |
148 |
169 |
149 If the symbol doesn't exist in the parent module, it must be a |
170 If the symbol doesn't exist in the parent module, it must be a |
150 module. We set missing modules up as _demandmod instances. |
171 module. We set missing modules up as _demandmod instances. |
151 """ |
172 """ |
152 symbol = getattr(mod, attr, nothing) |
173 symbol = getattr(mod, attr, nothing) |
153 if symbol is nothing: |
174 if symbol is nothing: |
154 symbol = _demandmod(attr, mod.__dict__, locals, **kwargs) |
175 symbol = _demandmod(attr, mod.__dict__, locals, **kwargs) |
155 setattr(mod, attr, symbol) |
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) |
156 |
183 |
157 if level >= 0: |
184 if level >= 0: |
158 # Mercurial's enforced import style does not use |
185 # Mercurial's enforced import style does not use |
159 # "from a import b,c,d" or "from .a import b,c,d" syntax. In |
186 # "from a import b,c,d" or "from .a import b,c,d" syntax. In |
160 # addition, this appears to be giving errors with some modules |
187 # addition, this appears to be giving errors with some modules |