~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lazy_import.py

  • Committer: Tarmac
  • Author(s): Vincent Ladeuil
  • Date: 2017-01-30 14:42:05 UTC
  • mfrom: (6620.1.1 trunk)
  • Revision ID: tarmac-20170130144205-r8fh2xpmiuxyozpv
Merge  2.7 into trunk including fix for bug #1657238 [r=vila]

Show diffs side-by-side

added added

removed removed

Lines of Context:
41
41
to inherit from them).
42
42
"""
43
43
 
 
44
from __future__ import absolute_import
 
45
 
44
46
 
45
47
class ScopeReplacer(object):
46
48
    """A lazy object that will replace itself in the appropriate scope.
51
53
 
52
54
    __slots__ = ('_scope', '_factory', '_name', '_real_obj')
53
55
 
54
 
    # Setting this to True will allow you to do x = y, and still access members
55
 
    # from both variables. This should not normally be enabled, but is useful
56
 
    # when building documentation.
57
 
    _should_proxy = False
 
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.
 
60
    _should_proxy = True
58
61
 
59
62
    def __init__(self, scope, factory, name):
60
63
        """Create a temporary object in the specified scope.
71
74
        object.__setattr__(self, '_real_obj', None)
72
75
        scope[name] = self
73
76
 
74
 
    def _replace(self):
75
 
        """Actually replace self with other in the given scope"""
 
77
    def _resolve(self):
 
78
        """Return the real object for which this is a placeholder"""
76
79
        name = object.__getattribute__(self, '_name')
77
 
        try:
 
80
        real_obj = object.__getattribute__(self, '_real_obj')
 
81
        if real_obj is None:
 
82
            # No obj generated previously, so generate from factory and scope.
78
83
            factory = object.__getattribute__(self, '_factory')
79
84
            scope = object.__getattribute__(self, '_scope')
80
 
        except AttributeError, e:
81
 
            # Because ScopeReplacer objects only replace a single
82
 
            # item, passing them to another variable before they are
83
 
            # replaced would cause them to keep getting replaced
84
 
            # (only they are replacing the wrong variable). So we
85
 
            # make it forbidden, and try to give a good error.
 
85
            obj = factory(self, scope, name)
 
86
            if obj is self:
 
87
                raise errors.IllegalUseOfScopeReplacer(name, msg="Object tried"
 
88
                    " to replace itself, check it's not using its own scope.")
 
89
 
 
90
            # Check if another thread has jumped in while obj was generated.
 
91
            real_obj = object.__getattribute__(self, '_real_obj')
 
92
            if real_obj is None:
 
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)
 
97
                scope[name] = obj
 
98
                return obj
 
99
 
 
100
        # Raise if proxying is disabled as obj has already been generated.
 
101
        if not ScopeReplacer._should_proxy:
86
102
            raise errors.IllegalUseOfScopeReplacer(
87
 
                name, msg="Object already cleaned up, did you assign it"
88
 
                          " to another variable?",
89
 
                extra=e)
90
 
        obj = factory(self, scope, name)
91
 
        if obj is self:
92
 
            raise errors.IllegalUseOfScopeReplacer(name, msg="Object tried"
93
 
                " to replace itself, check it's not using its own scope.")
94
 
        if ScopeReplacer._should_proxy:
95
 
            object.__setattr__(self, '_real_obj', obj)
96
 
        scope[name] = obj
97
 
        return obj
98
 
 
99
 
    def _cleanup(self):
100
 
        """Stop holding on to all the extra stuff"""
101
 
        try:
102
 
            del self._factory
103
 
        except AttributeError:
104
 
            # Oops, we just lost a race with another caller of _cleanup.  Just
105
 
            # ignore it.
106
 
            pass
107
 
 
108
 
        try:
109
 
            del self._scope
110
 
        except AttributeError:
111
 
            # Another race loss.  See above.
112
 
            pass
113
 
 
114
 
        # We keep _name, so that we can report errors
115
 
        # del self._name
 
103
                name, msg="Object already replaced, did you assign it"
 
104
                          " to another variable?")
 
105
        return real_obj
116
106
 
117
107
    def __getattribute__(self, attr):
118
 
        obj = object.__getattribute__(self, '_real_obj')
119
 
        if obj is None:
120
 
            _replace = object.__getattribute__(self, '_replace')
121
 
            obj = _replace()
122
 
            _cleanup = object.__getattribute__(self, '_cleanup')
123
 
            _cleanup()
 
108
        obj = object.__getattribute__(self, '_resolve')()
124
109
        return getattr(obj, attr)
125
110
 
126
111
    def __setattr__(self, attr, value):
127
 
        obj = object.__getattribute__(self, '_real_obj')
128
 
        if obj is None:
129
 
            _replace = object.__getattribute__(self, '_replace')
130
 
            obj = _replace()
131
 
            _cleanup = object.__getattribute__(self, '_cleanup')
132
 
            _cleanup()
 
112
        obj = object.__getattribute__(self, '_resolve')()
133
113
        return setattr(obj, attr, value)
134
114
 
135
115
    def __call__(self, *args, **kwargs):
136
 
        _replace = object.__getattribute__(self, '_replace')
137
 
        obj = _replace()
138
 
        _cleanup = object.__getattribute__(self, '_cleanup')
139
 
        _cleanup()
 
116
        obj = object.__getattribute__(self, '_resolve')()
140
117
        return obj(*args, **kwargs)
141
118
 
142
119
 
 
120
def disallow_proxying():
 
121
    """Disallow lazily imported modules to be used as proxies.
 
122
 
 
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.
 
126
 
 
127
    Only lazy imports that happen after this call are affected.
 
128
    """
 
129
    ScopeReplacer._should_proxy = False
 
130
 
 
131
 
143
132
class ImportReplacer(ScopeReplacer):
144
133
    """This is designed to replace only a portion of an import list.
145
134
 
208
197
        module_path = object.__getattribute__(self, '_module_path')
209
198
        module_python_path = '.'.join(module_path)
210
199
        if member is not None:
211
 
            module = __import__(module_python_path, scope, scope, [member])
 
200
            module = __import__(module_python_path, scope, scope, [member], level=0)
212
201
            return getattr(module, member)
213
202
        else:
214
 
            module = __import__(module_python_path, scope, scope, [])
 
203
            module = __import__(module_python_path, scope, scope, [], level=0)
215
204
            for path in module_path[1:]:
216
205
                module = getattr(module, path)
217
206