~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lazy_import.py

  • Committer: Jelmer Vernooij
  • Date: 2015-11-15 02:30:05 UTC
  • mto: This revision was merged to the branch mainline in revision 6609.
  • Revision ID: jelmer@jelmer.uk-20151115023005-fcfi763b5eu1ne2o
Fix auodoc_rstx when running with LANG=C.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Functionality to create lazy evaluation objects.
18
18
 
19
19
This includes waiting to import a module until it is actually used.
20
20
 
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::
 
23
 
23
24
    from bzrlib.lazy_import import lazy_import
24
25
    lazy_import(globals(), '''
25
26
    from bzrlib import (
30
31
    import bzrlib.branch
31
32
    ''')
32
33
 
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.
35
36
 
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).
41
42
"""
42
43
 
 
44
from __future__ import absolute_import
 
45
 
43
46
 
44
47
class ScopeReplacer(object):
45
48
    """A lazy object that will replace itself in the appropriate scope.
48
51
    needed.
49
52
    """
50
53
 
51
 
    __slots__ = ('_scope', '_factory', '_name')
 
54
    __slots__ = ('_scope', '_factory', '_name', '_real_obj')
 
55
 
 
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
52
61
 
53
62
    def __init__(self, scope, factory, name):
54
63
        """Create a temporary object in the specified scope.
59
68
            It will be passed (self, scope, name)
60
69
        :param name: The variable name in the given scope.
61
70
        """
62
 
        self._scope = scope
63
 
        self._factory = factory
64
 
        self._name = name
 
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)
65
75
        scope[name] = self
66
76
 
67
 
    def _replace(self):
68
 
        """Actually replace self with other in the given scope"""
 
77
    def _resolve(self):
 
78
        """Return the real object for which this is a placeholder"""
69
79
        name = object.__getattribute__(self, '_name')
70
 
        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.
71
83
            factory = object.__getattribute__(self, '_factory')
72
84
            scope = object.__getattribute__(self, '_scope')
73
 
        except AttributeError, e:
74
 
            # Because ScopeReplacer objects only replace a single
75
 
            # item, passing them to another variable before they are
76
 
            # replaced would cause them to keep getting replaced
77
 
            # (only they are replacing the wrong variable). So we
78
 
            # 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:
79
102
            raise errors.IllegalUseOfScopeReplacer(
80
 
                name, msg="Object already cleaned up, did you assign it"
81
 
                          " to another variable?",
82
 
                extra=e)
83
 
        obj = factory(self, scope, name)
84
 
        scope[name] = obj
85
 
        return obj
86
 
 
87
 
    def _cleanup(self):
88
 
        """Stop holding on to all the extra stuff"""
89
 
        del self._factory
90
 
        del self._scope
91
 
        # We keep _name, so that we can report errors
92
 
        # del self._name
 
103
                name, msg="Object already replaced, did you assign it"
 
104
                          " to another variable?")
 
105
        return real_obj
93
106
 
94
107
    def __getattribute__(self, attr):
95
 
        _replace = object.__getattribute__(self, '_replace')
96
 
        obj = _replace()
97
 
        _cleanup = object.__getattribute__(self, '_cleanup')
98
 
        _cleanup()
 
108
        obj = object.__getattribute__(self, '_resolve')()
99
109
        return getattr(obj, attr)
100
110
 
 
111
    def __setattr__(self, attr, value):
 
112
        obj = object.__getattribute__(self, '_resolve')()
 
113
        return setattr(obj, attr, value)
 
114
 
101
115
    def __call__(self, *args, **kwargs):
102
 
        _replace = object.__getattribute__(self, '_replace')
103
 
        obj = _replace()
104
 
        _cleanup = object.__getattribute__(self, '_cleanup')
105
 
        _cleanup()
 
116
        obj = object.__getattribute__(self, '_resolve')()
106
117
        return obj(*args, **kwargs)
107
118
 
108
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
 
109
132
class ImportReplacer(ScopeReplacer):
110
133
    """This is designed to replace only a portion of an import list.
111
134
 
130
153
 
131
154
        :param scope: The scope that objects should be imported into.
132
155
            Typically this is globals()
133
 
        :param name: The variable name. Often this is the same as the 
 
156
        :param name: The variable name. Often this is the same as the
134
157
            module_path. 'bzrlib'
135
158
        :param module_path: A list for the fully specified module path
136
159
            ['bzrlib', 'foo', 'bar']
138
161
            None, indicating the module is being imported.
139
162
        :param children: Children entries to be imported later.
140
163
            This should be a map of children specifications.
141
 
            {'foo':(['bzrlib', 'foo'], None, 
142
 
                {'bar':(['bzrlib', 'foo', 'bar'], None {})})
143
 
            }
144
 
        Examples:
 
164
            ::
 
165
            
 
166
                {'foo':(['bzrlib', 'foo'], None,
 
167
                    {'bar':(['bzrlib', 'foo', 'bar'], None {})})
 
168
                }
 
169
 
 
170
        Examples::
 
171
 
145
172
            import foo => name='foo' module_path='foo',
146
173
                          member=None, children={}
147
174
            import foo.bar => name='foo' module_path='foo', member=None,
151
178
            from foo import bar, baz would get translated into 2 import
152
179
            requests. On for 'name=bar' and one for 'name=baz'
153
180
        """
154
 
        if member is not None:
155
 
            assert not children, \
156
 
                'Cannot supply both a member and children'
 
181
        if (member is not None) and children:
 
182
            raise ValueError('Cannot supply both a member and children')
157
183
 
158
 
        self._import_replacer_children = children
159
 
        self._member = member
160
 
        self._module_path = module_path
 
184
        object.__setattr__(self, '_import_replacer_children', children)
 
185
        object.__setattr__(self, '_member', member)
 
186
        object.__setattr__(self, '_module_path', module_path)
161
187
 
162
188
        # Indirecting through __class__ so that children can
163
189
        # override _import (especially our instrumented version)
171
197
        module_path = object.__getattribute__(self, '_module_path')
172
198
        module_python_path = '.'.join(module_path)
173
199
        if member is not None:
174
 
            module = __import__(module_python_path, scope, scope, [member])
 
200
            module = __import__(module_python_path, scope, scope, [member], level=0)
175
201
            return getattr(module, member)
176
202
        else:
177
 
            module = __import__(module_python_path, scope, scope, [])
 
203
            module = __import__(module_python_path, scope, scope, [], level=0)
178
204
            for path in module_path[1:]:
179
205
                module = getattr(module, path)
180
206
 
241
267
 
242
268
        :param import_str: The import string to process
243
269
        """
244
 
        assert import_str.startswith('import ')
 
270
        if not import_str.startswith('import '):
 
271
            raise ValueError('bad import string %r' % (import_str,))
245
272
        import_str = import_str[len('import '):]
246
273
 
247
274
        for path in import_str.split(','):
286
313
 
287
314
        :param from_str: The import string to process
288
315
        """
289
 
        assert from_str.startswith('from ')
 
316
        if not from_str.startswith('from '):
 
317
            raise ValueError('bad from/import %r' % from_str)
290
318
        from_str = from_str[len('from '):]
291
319
 
292
320
        from_module, import_list = from_str.split(' import ')
347
375
def lazy_import(scope, text, lazy_import_class=None):
348
376
    """Create lazy imports for all of the imports in text.
349
377
 
350
 
    This is typically used as something like:
351
 
    from bzrlib.lazy_import import lazy_import
352
 
    lazy_import(globals(), '''
353
 
    from bzrlib import (
354
 
        foo,
355
 
        bar,
356
 
        baz,
357
 
        )
358
 
    import bzrlib.branch
359
 
    import bzrlib.transport
360
 
    ''')
 
378
    This is typically used as something like::
 
379
 
 
380
        from bzrlib.lazy_import import lazy_import
 
381
        lazy_import(globals(), '''
 
382
        from bzrlib import (
 
383
            foo,
 
384
            bar,
 
385
            baz,
 
386
            )
 
387
        import bzrlib.branch
 
388
        import bzrlib.transport
 
389
        ''')
361
390
 
362
391
    Then 'foo, bar, baz' and 'bzrlib' will exist as lazy-loaded
363
392
    objects which will be replaced with a real object on first use.