~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lazy_import.py

  • Committer: Martin Packman
  • Date: 2011-12-08 19:00:14 UTC
  • mto: This revision was merged to the branch mainline in revision 6359.
  • Revision ID: martin.packman@canonical.com-20111208190014-mi8jm6v7jygmhb0r
Use --include-duplicates for make update-pot which already combines multiple msgid strings prettily

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
 
 
46
44
 
47
45
class ScopeReplacer(object):
48
46
    """A lazy object that will replace itself in the appropriate scope.
53
51
 
54
52
    __slots__ = ('_scope', '_factory', '_name', '_real_obj')
55
53
 
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
 
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
61
58
 
62
59
    def __init__(self, scope, factory, name):
63
60
        """Create a temporary object in the specified scope.
74
71
        object.__setattr__(self, '_real_obj', None)
75
72
        scope[name] = self
76
73
 
77
 
    def _resolve(self):
78
 
        """Return the real object for which this is a placeholder"""
 
74
    def _replace(self):
 
75
        """Actually replace self with other in the given scope"""
79
76
        name = object.__getattribute__(self, '_name')
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.
 
77
        try:
83
78
            factory = object.__getattribute__(self, '_factory')
84
79
            scope = object.__getattribute__(self, '_scope')
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:
 
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.
102
86
            raise errors.IllegalUseOfScopeReplacer(
103
 
                name, msg="Object already replaced, did you assign it"
104
 
                          " to another variable?")
105
 
        return real_obj
 
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
106
116
 
107
117
    def __getattribute__(self, attr):
108
 
        obj = object.__getattribute__(self, '_resolve')()
 
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()
109
124
        return getattr(obj, attr)
110
125
 
111
126
    def __setattr__(self, attr, value):
112
 
        obj = object.__getattribute__(self, '_resolve')()
 
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()
113
133
        return setattr(obj, attr, value)
114
134
 
115
135
    def __call__(self, *args, **kwargs):
116
 
        obj = object.__getattribute__(self, '_resolve')()
 
136
        _replace = object.__getattribute__(self, '_replace')
 
137
        obj = _replace()
 
138
        _cleanup = object.__getattribute__(self, '_cleanup')
 
139
        _cleanup()
117
140
        return obj(*args, **kwargs)
118
141
 
119
142
 
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
 
 
132
143
class ImportReplacer(ScopeReplacer):
133
144
    """This is designed to replace only a portion of an import list.
134
145
 
197
208
        module_path = object.__getattribute__(self, '_module_path')
198
209
        module_python_path = '.'.join(module_path)
199
210
        if member is not None:
200
 
            module = __import__(module_python_path, scope, scope, [member], level=0)
 
211
            module = __import__(module_python_path, scope, scope, [member])
201
212
            return getattr(module, member)
202
213
        else:
203
 
            module = __import__(module_python_path, scope, scope, [], level=0)
 
214
            module = __import__(module_python_path, scope, scope, [])
204
215
            for path in module_path[1:]:
205
216
                module = getattr(module, path)
206
217