19
19
This includes waiting to import a module until it is actually used.
21
21
Most commonly, the 'lazy_import' function is used to import other modules
22
in an on-demand fashion. Typically use looks like:
23
from bzrlib.lazy_import import lazy_import
22
in an on-demand fashion. Typically use looks like::
24
from .lazy_import import lazy_import
24
25
lazy_import(globals(), '''
33
Then 'errors, osutils, branch' and 'bzrlib' will exist as lazy-loaded
34
objects which will be replaced with a real object on first use.
34
Then 'errors, osutils, branch' and 'breezy' will exist as lazy-loaded
35
objects which will be replaced with a real object on first use.
36
In general, it is best to only load modules in this way. This is because
37
it isn't safe to pass these variables to other functions before they
38
have been replaced. This is especially true for constants, sometimes
39
true for classes or functions (when used as a factory, or you want
40
to inherit from them).
37
In general, it is best to only load modules in this way. This is because
38
it isn't safe to pass these variables to other functions before they
39
have been replaced. This is especially true for constants, sometimes
40
true for classes or functions (when used as a factory, or you want
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
82
class ScopeReplacer(object):
45
83
"""A lazy object that will replace itself in the appropriate scope.
51
89
__slots__ = ('_scope', '_factory', '_name', '_real_obj')
53
# Setting this to True will allow you to do x = y, and still access members
54
# from both variables. This should not normally be enabled, but is useful
55
# when building documentation.
91
# If you to do x = y, setting this to False will disallow access to
92
# members from the second variable (i.e. x). This should normally
93
# be enabled for reasons of thread safety and documentation, but
94
# will be disabled during the selftest command to check for abuse.
58
97
def __init__(self, scope, factory, name):
59
98
"""Create a temporary object in the specified scope.
70
109
object.__setattr__(self, '_real_obj', None)
71
110
scope[name] = self
74
"""Actually replace self with other in the given scope"""
113
"""Return the real object for which this is a placeholder"""
75
114
name = object.__getattribute__(self, '_name')
115
real_obj = object.__getattribute__(self, '_real_obj')
117
# No obj generated previously, so generate from factory and scope.
77
118
factory = object.__getattribute__(self, '_factory')
78
119
scope = object.__getattribute__(self, '_scope')
79
except AttributeError, e:
80
# Because ScopeReplacer objects only replace a single
81
# item, passing them to another variable before they are
82
# replaced would cause them to keep getting replaced
83
# (only they are replacing the wrong variable). So we
84
# make it forbidden, and try to give a good error.
85
raise errors.IllegalUseOfScopeReplacer(
86
name, msg="Object already cleaned up, did you assign it"
87
" to another variable?",
89
obj = factory(self, scope, name)
90
if ScopeReplacer._should_proxy:
91
object.__setattr__(self, '_real_obj', obj)
96
"""Stop holding on to all the extra stuff"""
99
# We keep _name, so that we can report errors
120
obj = factory(self, scope, name)
122
raise IllegalUseOfScopeReplacer(
123
name, msg="Object tried"
124
" to replace itself, check it's not using its own scope.")
126
# Check if another thread has jumped in while obj was generated.
127
real_obj = object.__getattribute__(self, '_real_obj')
129
# Still no prexisting obj, so go ahead and assign to scope and
130
# return. There is still a small window here where races will
131
# not be detected, but safest to avoid additional locking.
132
object.__setattr__(self, '_real_obj', obj)
136
# Raise if proxying is disabled as obj has already been generated.
137
if not ScopeReplacer._should_proxy:
138
raise IllegalUseOfScopeReplacer(
139
name, msg="Object already replaced, did you assign it"
140
" to another variable?")
102
143
def __getattribute__(self, attr):
103
obj = object.__getattribute__(self, '_real_obj')
105
_replace = object.__getattribute__(self, '_replace')
107
_cleanup = object.__getattribute__(self, '_cleanup')
144
obj = object.__getattribute__(self, '_resolve')()
109
145
return getattr(obj, attr)
111
147
def __setattr__(self, attr, value):
112
obj = object.__getattribute__(self, '_real_obj')
114
_replace = object.__getattribute__(self, '_replace')
116
_cleanup = object.__getattribute__(self, '_cleanup')
148
obj = object.__getattribute__(self, '_resolve')()
118
149
return setattr(obj, attr, value)
120
151
def __call__(self, *args, **kwargs):
121
_replace = object.__getattribute__(self, '_replace')
123
_cleanup = object.__getattribute__(self, '_cleanup')
152
obj = object.__getattribute__(self, '_resolve')()
125
153
return obj(*args, **kwargs)
156
def disallow_proxying():
157
"""Disallow lazily imported modules to be used as proxies.
159
Calling this function might cause problems with concurrent imports
160
in multithreaded environments, but will help detecting wasteful
161
indirection, so it should be called when executing unit tests.
163
Only lazy imports that happen after this call are affected.
165
ScopeReplacer._should_proxy = False
168
_builtin_import = __import__
128
171
class ImportReplacer(ScopeReplacer):
129
172
"""This is designed to replace only a portion of an import list.
150
193
:param scope: The scope that objects should be imported into.
151
194
Typically this is globals()
152
195
:param name: The variable name. Often this is the same as the
153
module_path. 'bzrlib'
196
module_path. 'breezy'
154
197
:param module_path: A list for the fully specified module path
155
['bzrlib', 'foo', 'bar']
198
['breezy', 'foo', 'bar']
156
199
:param member: The member inside the module to import, often this is
157
200
None, indicating the module is being imported.
158
201
:param children: Children entries to be imported later.
159
202
This should be a map of children specifications.
160
{'foo':(['bzrlib', 'foo'], None,
161
{'bar':(['bzrlib', 'foo', 'bar'], None {})})
205
{'foo':(['breezy', 'foo'], None,
206
{'bar':(['breezy', 'foo', 'bar'], None {})})
164
211
import foo => name='foo' module_path='foo',
165
212
member=None, children={}
166
213
import foo.bar => name='foo' module_path='foo', member=None,
187
234
children = object.__getattribute__(self, '_import_replacer_children')
188
235
member = object.__getattribute__(self, '_member')
189
236
module_path = object.__getattribute__(self, '_module_path')
190
module_python_path = '.'.join(module_path)
237
name = '.'.join(module_path)
191
238
if member is not None:
192
module = __import__(module_python_path, scope, scope, [member])
239
module = _builtin_import(name, scope, scope, [member], level=0)
193
240
return getattr(module, member)
195
module = __import__(module_python_path, scope, scope, [])
242
module = _builtin_import(name, scope, scope, [], level=0)
196
243
for path in module_path[1:]:
197
244
module = getattr(module, path)
199
246
# Prepare the children to be imported
200
247
for child_name, (child_path, child_member, grandchildren) in \
201
children.iteritems():
202
249
# Using self.__class__, so that children get children classes
203
250
# instantiated. (This helps with instrumented tests)
204
251
cls = object.__getattribute__(self, '__class__')
361
414
out.append(line.replace('(', '').replace(')', ''))
362
415
if cur is not None:
363
raise errors.InvalidImportLine(cur, 'Unmatched parenthesis')
416
raise InvalidImportLine(cur, 'Unmatched parenthesis')
367
420
def lazy_import(scope, text, lazy_import_class=None):
368
421
"""Create lazy imports for all of the imports in text.
370
This is typically used as something like:
371
from bzrlib.lazy_import import lazy_import
372
lazy_import(globals(), '''
379
import bzrlib.transport
382
Then 'foo, bar, baz' and 'bzrlib' will exist as lazy-loaded
423
This is typically used as something like::
425
from breezy.lazy_import import lazy_import
426
lazy_import(globals(), '''
433
import breezy.transport
436
Then 'foo, bar, baz' and 'breezy' will exist as lazy-loaded
383
437
objects which will be replaced with a real object on first use.
385
439
In general, it is best to only load modules in this way. This is