~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lazy_import.py

  • Committer: John Arbash Meinel
  • Date: 2006-09-13 02:44:23 UTC
  • mto: This revision was merged to the branch mainline in revision 2071.
  • Revision ID: john@arbash-meinel.com-20060913024423-2f0729076ddd4e31
lazy_import osutils and sign_my_commits
Move doc tests into test_osutils, since lazy_import doesn't play nicely
with DocTest

Show diffs side-by-side

added added

removed removed

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