~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lazy_import.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-10-18 11:57:18 UTC
  • mfrom: (5505.1.1 trunk)
  • Revision ID: pqm@pqm.ubuntu.com-20101018115718-cbuoc2gafnjldngk
(vila) Document hunk editing when shelving.(Neil Martinsen-Burrell)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 by 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
 
 
21
Most commonly, the 'lazy_import' function is used to import other modules
 
22
in an on-demand fashion. Typically use looks like:
 
23
    from bzrlib.lazy_import import lazy_import
 
24
    lazy_import(globals(), '''
 
25
    from bzrlib import (
 
26
        errors,
 
27
        osutils,
 
28
        branch,
 
29
        )
 
30
    import bzrlib.branch
 
31
    ''')
 
32
 
 
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.
 
35
 
 
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).
20
41
"""
21
42
 
22
 
from bzrlib import (
23
 
    errors,
24
 
    )
25
 
 
26
43
 
27
44
class ScopeReplacer(object):
28
45
    """A lazy object that will replace itself in the appropriate scope.
31
48
    needed.
32
49
    """
33
50
 
34
 
    __slots__ = ('_scope', '_factory', '_name')
 
51
    __slots__ = ('_scope', '_factory', '_name', '_real_obj')
 
52
 
 
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
    _should_proxy = False
35
57
 
36
58
    def __init__(self, scope, factory, name):
37
59
        """Create a temporary object in the specified scope.
42
64
            It will be passed (self, scope, name)
43
65
        :param name: The variable name in the given scope.
44
66
        """
45
 
        self._scope = scope
46
 
        self._factory = factory
47
 
        self._name = name
 
67
        object.__setattr__(self, '_scope', scope)
 
68
        object.__setattr__(self, '_factory', factory)
 
69
        object.__setattr__(self, '_name', name)
 
70
        object.__setattr__(self, '_real_obj', None)
48
71
        scope[name] = self
49
72
 
50
73
    def _replace(self):
61
84
            # make it forbidden, and try to give a good error.
62
85
            raise errors.IllegalUseOfScopeReplacer(
63
86
                name, msg="Object already cleaned up, did you assign it"
64
 
                          "to another variable?",
 
87
                          " to another variable?",
65
88
                extra=e)
66
89
        obj = factory(self, scope, name)
 
90
        if obj is self:
 
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)
67
95
        scope[name] = obj
68
96
        return obj
69
97
 
75
103
        # del self._name
76
104
 
77
105
    def __getattribute__(self, attr):
78
 
        _replace = object.__getattribute__(self, '_replace')
79
 
        obj = _replace()
80
 
        _cleanup = object.__getattribute__(self, '_cleanup')
81
 
        _cleanup()
 
106
        obj = object.__getattribute__(self, '_real_obj')
 
107
        if obj is None:
 
108
            _replace = object.__getattribute__(self, '_replace')
 
109
            obj = _replace()
 
110
            _cleanup = object.__getattribute__(self, '_cleanup')
 
111
            _cleanup()
82
112
        return getattr(obj, attr)
83
113
 
 
114
    def __setattr__(self, attr, value):
 
115
        obj = object.__getattribute__(self, '_real_obj')
 
116
        if obj is None:
 
117
            _replace = object.__getattribute__(self, '_replace')
 
118
            obj = _replace()
 
119
            _cleanup = object.__getattribute__(self, '_cleanup')
 
120
            _cleanup()
 
121
        return setattr(obj, attr, value)
 
122
 
84
123
    def __call__(self, *args, **kwargs):
85
124
        _replace = object.__getattribute__(self, '_replace')
86
125
        obj = _replace()
98
137
    At present, this only supports 'import foo.bar.baz' syntax.
99
138
    """
100
139
 
101
 
    # Intentially a long semi-unique name that won't likely exist
102
 
    # elsewhere. (We can't use isinstance because that accesses __class__
103
 
    # which causes the __getattribute__ to trigger)
 
140
    # '_import_replacer_children' is intentionally a long semi-unique name
 
141
    # that won't likely exist elsewhere. This allows us to detect an
 
142
    # ImportReplacer object by using
 
143
    #       object.__getattribute__(obj, '_import_replacer_children')
 
144
    # We can't just use 'isinstance(obj, ImportReplacer)', because that
 
145
    # accesses .__class__, which goes through __getattribute__, and triggers
 
146
    # the replacement.
104
147
    __slots__ = ('_import_replacer_children', '_member', '_module_path')
105
148
 
106
149
    def __init__(self, scope, name, module_path, member=None, children={}):
109
152
 
110
153
        :param scope: The scope that objects should be imported into.
111
154
            Typically this is globals()
112
 
        :param name: The variable name. Often this is the same as the 
 
155
        :param name: The variable name. Often this is the same as the
113
156
            module_path. 'bzrlib'
114
157
        :param module_path: A list for the fully specified module path
115
158
            ['bzrlib', 'foo', 'bar']
117
160
            None, indicating the module is being imported.
118
161
        :param children: Children entries to be imported later.
119
162
            This should be a map of children specifications.
120
 
            {'foo':(['bzrlib', 'foo'], None, 
 
163
            {'foo':(['bzrlib', 'foo'], None,
121
164
                {'bar':(['bzrlib', 'foo', 'bar'], None {})})
122
165
            }
123
166
        Examples:
130
173
            from foo import bar, baz would get translated into 2 import
131
174
            requests. On for 'name=bar' and one for 'name=baz'
132
175
        """
133
 
        if member is not None:
134
 
            assert not children, \
135
 
                'Cannot supply both a member and children'
 
176
        if (member is not None) and children:
 
177
            raise ValueError('Cannot supply both a member and children')
136
178
 
137
 
        self._import_replacer_children = children
138
 
        self._member = member
139
 
        self._module_path = module_path
 
179
        object.__setattr__(self, '_import_replacer_children', children)
 
180
        object.__setattr__(self, '_member', member)
 
181
        object.__setattr__(self, '_module_path', module_path)
140
182
 
141
183
        # Indirecting through __class__ so that children can
142
184
        # override _import (especially our instrumented version)
220
262
 
221
263
        :param import_str: The import string to process
222
264
        """
223
 
        assert import_str.startswith('import ')
 
265
        if not import_str.startswith('import '):
 
266
            raise ValueError('bad import string %r' % (import_str,))
224
267
        import_str = import_str[len('import '):]
225
268
 
226
269
        for path in import_str.split(','):
265
308
 
266
309
        :param from_str: The import string to process
267
310
        """
268
 
        assert from_str.startswith('from ')
 
311
        if not from_str.startswith('from '):
 
312
            raise ValueError('bad from/import %r' % from_str)
269
313
        from_str = from_str[len('from '):]
270
314
 
271
315
        from_module, import_list = from_str.split(' import ')
349
393
    # This is just a helper around ImportProcessor.lazy_import
350
394
    proc = ImportProcessor(lazy_import_class=lazy_import_class)
351
395
    return proc.lazy_import(scope, text)
 
396
 
 
397
 
 
398
# The only module that this module depends on is 'bzrlib.errors'. But it
 
399
# can actually be imported lazily, since we only need it if there is a
 
400
# problem.
 
401
 
 
402
lazy_import(globals(), """
 
403
from bzrlib import errors
 
404
""")