21
21
Most commonly, the 'lazy_import' function is used to import other modules
22
22
in an on-demand fashion. Typically use looks like::
24
from .lazy_import import lazy_import
24
from brzlib.lazy_import import lazy_import
25
25
lazy_import(globals(), '''
34
Then 'errors, osutils, branch' and 'breezy' will exist as lazy-loaded
34
Then 'errors, osutils, branch' and 'brzlib' will exist as lazy-loaded
35
35
objects which will be replaced with a real object on first use.
37
37
In general, it is best to only load modules in this way. This is because
41
41
to inherit from them).
44
from .errors import BzrError, InternalBzrError
47
class ImportNameCollision(InternalBzrError):
49
_fmt = ("Tried to import an object to the same name as"
50
" an existing object. %(name)s")
52
def __init__(self, name):
53
BzrError.__init__(self)
57
class IllegalUseOfScopeReplacer(InternalBzrError):
59
_fmt = ("ScopeReplacer object %(name)r was used incorrectly:"
62
def __init__(self, name, msg, extra=None):
63
BzrError.__init__(self)
67
self.extra = ': ' + str(extra)
72
class InvalidImportLine(InternalBzrError):
74
_fmt = "Not a valid import statement: %(msg)\n%(text)s"
76
def __init__(self, text, msg):
77
BzrError.__init__(self)
44
from __future__ import absolute_import
82
47
class ScopeReplacer(object):
119
84
scope = object.__getattribute__(self, '_scope')
120
85
obj = factory(self, scope, name)
122
raise IllegalUseOfScopeReplacer(
123
name, msg="Object tried"
87
raise errors.IllegalUseOfScopeReplacer(name, msg="Object tried"
124
88
" to replace itself, check it's not using its own scope.")
126
90
# Check if another thread has jumped in while obj was generated.
136
100
# Raise if proxying is disabled as obj has already been generated.
137
101
if not ScopeReplacer._should_proxy:
138
raise IllegalUseOfScopeReplacer(
102
raise errors.IllegalUseOfScopeReplacer(
139
103
name, msg="Object already replaced, did you assign it"
140
104
" to another variable?")
193
154
:param scope: The scope that objects should be imported into.
194
155
Typically this is globals()
195
156
:param name: The variable name. Often this is the same as the
196
module_path. 'breezy'
157
module_path. 'brzlib'
197
158
:param module_path: A list for the fully specified module path
198
['breezy', 'foo', 'bar']
159
['brzlib', 'foo', 'bar']
199
160
:param member: The member inside the module to import, often this is
200
161
None, indicating the module is being imported.
201
162
:param children: Children entries to be imported later.
202
163
This should be a map of children specifications.
205
{'foo':(['breezy', 'foo'], None,
206
{'bar':(['breezy', 'foo', 'bar'], None {})})
166
{'foo':(['brzlib', 'foo'], None,
167
{'bar':(['brzlib', 'foo', 'bar'], None {})})
234
195
children = object.__getattribute__(self, '_import_replacer_children')
235
196
member = object.__getattribute__(self, '_member')
236
197
module_path = object.__getattribute__(self, '_module_path')
237
name = '.'.join(module_path)
198
module_python_path = '.'.join(module_path)
238
199
if member is not None:
239
module = _builtin_import(name, scope, scope, [member], level=0)
200
module = __import__(module_python_path, scope, scope, [member], level=0)
240
201
return getattr(module, member)
242
module = _builtin_import(name, scope, scope, [], level=0)
203
module = __import__(module_python_path, scope, scope, [], level=0)
243
204
for path in module_path[1:]:
244
205
module = getattr(module, path)
246
207
# Prepare the children to be imported
247
208
for child_name, (child_path, child_member, grandchildren) in \
209
children.iteritems():
249
210
# Using self.__class__, so that children get children classes
250
211
# instantiated. (This helps with instrumented tests)
251
212
cls = object.__getattribute__(self, '__class__')
285
246
def _convert_imports(self, scope):
286
247
# Now convert the map into a set of imports
287
for name, info in self.imports.items():
248
for name, info in self.imports.iteritems():
288
249
self._lazy_import_class(scope, name=name, module_path=info[0],
289
250
member=info[1], children=info[2])
296
257
elif line.startswith('from '):
297
258
self._convert_from_str(line)
299
raise InvalidImportLine(
300
line, "doesn't start with 'import ' or 'from '")
260
raise errors.InvalidImportLine(line,
261
"doesn't start with 'import ' or 'from '")
302
263
def _convert_import_str(self, import_str):
303
264
"""This converts a import string into an import map.
322
283
name = as_hunks[1].strip()
323
284
module_path = as_hunks[0].strip().split('.')
324
285
if name in self.imports:
325
raise ImportNameCollision(name)
326
if not module_path[0]:
327
raise ImportError(path)
286
raise errors.ImportNameCollision(name)
328
287
# No children available in 'import foo as bar'
329
288
self.imports[name] = (module_path, None, {})
331
290
# Now we need to handle
332
291
module_path = path.split('.')
333
292
name = module_path[0]
335
raise ImportError(path)
336
293
if name not in self.imports:
337
294
# This is a new import that we haven't seen before
338
295
module_def = ([name], None, {})
365
322
from_module_path = from_module.split('.')
367
if not from_module_path[0]:
368
raise ImportError(from_module)
370
324
for path in import_list.split(','):
371
325
path = path.strip()
382
336
name = module = path
383
337
if name in self.imports:
384
raise ImportNameCollision(name)
338
raise errors.ImportNameCollision(name)
385
339
self.imports[name] = (from_module_path, module, {})
387
341
def _canonicalize_import_text(self, text):
414
369
out.append(line.replace('(', '').replace(')', ''))
415
370
if cur is not None:
416
raise InvalidImportLine(cur, 'Unmatched parenthesis')
371
raise errors.InvalidImportLine(cur, 'Unmatched parenthesis')
423
378
This is typically used as something like::
425
from breezy.lazy_import import lazy_import
380
from brzlib.lazy_import import lazy_import
426
381
lazy_import(globals(), '''
433
import breezy.transport
388
import brzlib.transport
436
Then 'foo, bar, baz' and 'breezy' will exist as lazy-loaded
391
Then 'foo, bar, baz' and 'brzlib' will exist as lazy-loaded
437
392
objects which will be replaced with a real object on first use.
439
394
In general, it is best to only load modules in this way. This is
444
399
# This is just a helper around ImportProcessor.lazy_import
445
400
proc = ImportProcessor(lazy_import_class=lazy_import_class)
446
401
return proc.lazy_import(scope, text)
404
# The only module that this module depends on is 'brzlib.errors'. But it
405
# can actually be imported lazily, since we only need it if there is a
408
lazy_import(globals(), """
409
from brzlib import errors