13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
"""Functionality to create lazy evaluation objects.
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::
22
in an on-demand fashion. Typically use looks like:
24
23
from bzrlib.lazy_import import lazy_import
25
24
lazy_import(globals(), '''
26
25
from bzrlib import (
31
30
import bzrlib.branch
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.
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.
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).
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).
44
from __future__ import absolute_import
47
44
class ScopeReplacer(object):
48
45
"""A lazy object that will replace itself in the appropriate scope.
54
51
__slots__ = ('_scope', '_factory', '_name', '_real_obj')
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.
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.
62
58
def __init__(self, scope, factory, name):
63
59
"""Create a temporary object in the specified scope.
68
64
It will be passed (self, scope, name)
69
65
:param name: The variable name in the given scope.
71
object.__setattr__(self, '_scope', scope)
72
object.__setattr__(self, '_factory', factory)
73
object.__setattr__(self, '_name', name)
74
object.__setattr__(self, '_real_obj', None)
68
self._factory = factory
78
"""Return the real object for which this is a placeholder"""
74
"""Actually replace self with other in the given scope"""
79
75
name = object.__getattribute__(self, '_name')
80
real_obj = object.__getattribute__(self, '_real_obj')
82
# No obj generated previously, so generate from factory and scope.
83
77
factory = object.__getattribute__(self, '_factory')
84
78
scope = object.__getattribute__(self, '_scope')
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:
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.
102
85
raise errors.IllegalUseOfScopeReplacer(
103
name, msg="Object already replaced, did you assign it"
104
" to another variable?")
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:
96
"""Stop holding on to all the extra stuff"""
99
# We keep _name, so that we can report errors
107
102
def __getattribute__(self, attr):
108
obj = object.__getattribute__(self, '_resolve')()
103
obj = object.__getattribute__(self, '_real_obj')
105
_replace = object.__getattribute__(self, '_replace')
107
_cleanup = object.__getattribute__(self, '_cleanup')
109
109
return getattr(obj, attr)
111
def __setattr__(self, attr, value):
112
obj = object.__getattribute__(self, '_resolve')()
113
return setattr(obj, attr, value)
115
111
def __call__(self, *args, **kwargs):
116
obj = object.__getattribute__(self, '_resolve')()
112
_replace = object.__getattribute__(self, '_replace')
114
_cleanup = object.__getattribute__(self, '_cleanup')
117
116
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
132
119
class ImportReplacer(ScopeReplacer):
133
120
"""This is designed to replace only a portion of an import list.
161
148
None, indicating the module is being imported.
162
149
:param children: Children entries to be imported later.
163
150
This should be a map of children specifications.
166
{'foo':(['bzrlib', 'foo'], None,
167
{'bar':(['bzrlib', 'foo', 'bar'], None {})})
151
{'foo':(['bzrlib', 'foo'], None,
152
{'bar':(['bzrlib', 'foo', 'bar'], None {})})
172
155
import foo => name='foo' module_path='foo',
173
156
member=None, children={}
174
157
import foo.bar => name='foo' module_path='foo', member=None,
178
161
from foo import bar, baz would get translated into 2 import
179
162
requests. On for 'name=bar' and one for 'name=baz'
181
if (member is not None) and children:
182
raise ValueError('Cannot supply both a member and children')
164
if member is not None:
165
assert not children, \
166
'Cannot supply both a member and children'
184
object.__setattr__(self, '_import_replacer_children', children)
185
object.__setattr__(self, '_member', member)
186
object.__setattr__(self, '_module_path', module_path)
168
self._import_replacer_children = children
169
self._member = member
170
self._module_path = module_path
188
172
# Indirecting through __class__ so that children can
189
173
# override _import (especially our instrumented version)
197
181
module_path = object.__getattribute__(self, '_module_path')
198
182
module_python_path = '.'.join(module_path)
199
183
if member is not None:
200
module = __import__(module_python_path, scope, scope, [member], level=0)
184
module = __import__(module_python_path, scope, scope, [member])
201
185
return getattr(module, member)
203
module = __import__(module_python_path, scope, scope, [], level=0)
187
module = __import__(module_python_path, scope, scope, [])
204
188
for path in module_path[1:]:
205
189
module = getattr(module, path)