30
31
import bzrlib.branch
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 'bzrlib' 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 __future__ import absolute_import
44
47
class ScopeReplacer(object):
45
48
"""A lazy object that will replace itself in the appropriate scope.
51
54
__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.
56
# If you to do x = y, setting this to False will disallow access to
57
# members from the second variable (i.e. x). This should normally
58
# be enabled for reasons of thread safety and documentation, but
59
# will be disabled during the selftest command to check for abuse.
58
62
def __init__(self, scope, factory, name):
59
63
"""Create a temporary object in the specified scope.
70
74
object.__setattr__(self, '_real_obj', None)
74
"""Actually replace self with other in the given scope"""
78
"""Return the real object for which this is a placeholder"""
75
79
name = object.__getattribute__(self, '_name')
80
real_obj = object.__getattribute__(self, '_real_obj')
82
# No obj generated previously, so generate from factory and scope.
77
83
factory = object.__getattribute__(self, '_factory')
78
84
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
obj = factory(self, scope, name)
87
raise errors.IllegalUseOfScopeReplacer(name, msg="Object tried"
88
" to replace itself, check it's not using its own scope.")
90
# Check if another thread has jumped in while obj was generated.
91
real_obj = object.__getattribute__(self, '_real_obj')
93
# Still no prexisting obj, so go ahead and assign to scope and
94
# return. There is still a small window here where races will
95
# not be detected, but safest to avoid additional locking.
96
object.__setattr__(self, '_real_obj', obj)
100
# Raise if proxying is disabled as obj has already been generated.
101
if not ScopeReplacer._should_proxy:
85
102
raise errors.IllegalUseOfScopeReplacer(
86
name, msg="Object already cleaned up, did you assign it"
87
" to another variable?",
89
obj = factory(self, scope, name)
91
raise errors.IllegalUseOfScopeReplacer(name, msg="Object tried"
92
" to replace itself, check it's not using its own scope.")
93
if ScopeReplacer._should_proxy:
94
object.__setattr__(self, '_real_obj', obj)
99
"""Stop holding on to all the extra stuff"""
102
# We keep _name, so that we can report errors
103
name, msg="Object already replaced, did you assign it"
104
" to another variable?")
105
107
def __getattribute__(self, attr):
106
obj = object.__getattribute__(self, '_real_obj')
108
_replace = object.__getattribute__(self, '_replace')
110
_cleanup = object.__getattribute__(self, '_cleanup')
108
obj = object.__getattribute__(self, '_resolve')()
112
109
return getattr(obj, attr)
114
111
def __setattr__(self, attr, value):
115
obj = object.__getattribute__(self, '_real_obj')
117
_replace = object.__getattribute__(self, '_replace')
119
_cleanup = object.__getattribute__(self, '_cleanup')
112
obj = object.__getattribute__(self, '_resolve')()
121
113
return setattr(obj, attr, value)
123
115
def __call__(self, *args, **kwargs):
124
_replace = object.__getattribute__(self, '_replace')
126
_cleanup = object.__getattribute__(self, '_cleanup')
116
obj = object.__getattribute__(self, '_resolve')()
128
117
return obj(*args, **kwargs)
120
def disallow_proxying():
121
"""Disallow lazily imported modules to be used as proxies.
123
Calling this function might cause problems with concurrent imports
124
in multithreaded environments, but will help detecting wasteful
125
indirection, so it should be called when executing unit tests.
127
Only lazy imports that happen after this call are affected.
129
ScopeReplacer._should_proxy = False
131
132
class ImportReplacer(ScopeReplacer):
132
133
"""This is designed to replace only a portion of an import list.
160
161
None, indicating the module is being imported.
161
162
:param children: Children entries to be imported later.
162
163
This should be a map of children specifications.
163
{'foo':(['bzrlib', 'foo'], None,
164
{'bar':(['bzrlib', 'foo', 'bar'], None {})})
166
{'foo':(['bzrlib', 'foo'], None,
167
{'bar':(['bzrlib', 'foo', 'bar'], None {})})
167
172
import foo => name='foo' module_path='foo',
168
173
member=None, children={}
169
174
import foo.bar => name='foo' module_path='foo', member=None,
192
197
module_path = object.__getattribute__(self, '_module_path')
193
198
module_python_path = '.'.join(module_path)
194
199
if member is not None:
195
module = __import__(module_python_path, scope, scope, [member])
200
module = __import__(module_python_path, scope, scope, [member], level=0)
196
201
return getattr(module, member)
198
module = __import__(module_python_path, scope, scope, [])
203
module = __import__(module_python_path, scope, scope, [], level=0)
199
204
for path in module_path[1:]:
200
205
module = getattr(module, path)