~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-12 19:37:02 UTC
  • mto: This revision was merged to the branch mainline in revision 2004.
  • Revision ID: john@arbash-meinel.com-20060912193702-22c752299731a663
HACKING and NEWS

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
import re
 
23
import sys
 
24
 
 
25
from bzrlib import (
 
26
    errors,
 
27
    )
 
28
 
43
29
 
44
30
class ScopeReplacer(object):
45
31
    """A lazy object that will replace itself in the appropriate scope.
48
34
    needed.
49
35
    """
50
36
 
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
 
37
    __slots__ = ('_scope', '_factory', '_name')
57
38
 
58
39
    def __init__(self, scope, factory, name):
59
40
        """Create a temporary object in the specified scope.
64
45
            It will be passed (self, scope, name)
65
46
        :param name: The variable name in the given scope.
66
47
        """
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
        self._scope = scope
 
49
        self._factory = factory
 
50
        self._name = name
71
51
        scope[name] = self
72
52
 
73
53
    def _replace(self):
84
64
            # make it forbidden, and try to give a good error.
85
65
            raise errors.IllegalUseOfScopeReplacer(
86
66
                name, msg="Object already cleaned up, did you assign it"
87
 
                          " to another variable?",
 
67
                          "to another variable?",
88
68
                extra=e)
89
69
        obj = factory(self, scope, name)
90
 
        if ScopeReplacer._should_proxy:
91
 
            object.__setattr__(self, '_real_obj', obj)
92
70
        scope[name] = obj
93
71
        return obj
94
72
 
100
78
        # del self._name
101
79
 
102
80
    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()
 
81
        _replace = object.__getattribute__(self, '_replace')
 
82
        obj = _replace()
 
83
        _cleanup = object.__getattribute__(self, '_cleanup')
 
84
        _cleanup()
109
85
        return getattr(obj, attr)
110
86
 
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
87
    def __call__(self, *args, **kwargs):
121
88
        _replace = object.__getattribute__(self, '_replace')
122
89
        obj = _replace()
134
101
    At present, this only supports 'import foo.bar.baz' syntax.
135
102
    """
136
103
 
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.
 
104
    # Intentially a long semi-unique name that won't likely exist
 
105
    # elsewhere. (We can't use isinstance because that accesses __class__
 
106
    # which causes the __getattribute__ to trigger)
144
107
    __slots__ = ('_import_replacer_children', '_member', '_module_path')
145
108
 
146
109
    def __init__(self, scope, name, module_path, member=None, children={}):
149
112
 
150
113
        :param scope: The scope that objects should be imported into.
151
114
            Typically this is globals()
152
 
        :param name: The variable name. Often this is the same as the
 
115
        :param name: The variable name. Often this is the same as the 
153
116
            module_path. 'bzrlib'
154
117
        :param module_path: A list for the fully specified module path
155
118
            ['bzrlib', 'foo', 'bar']
157
120
            None, indicating the module is being imported.
158
121
        :param children: Children entries to be imported later.
159
122
            This should be a map of children specifications.
160
 
            {'foo':(['bzrlib', 'foo'], None,
 
123
            {'foo':(['bzrlib', 'foo'], None, 
161
124
                {'bar':(['bzrlib', 'foo', 'bar'], None {})})
162
125
            }
163
126
        Examples:
170
133
            from foo import bar, baz would get translated into 2 import
171
134
            requests. On for 'name=bar' and one for 'name=baz'
172
135
        """
173
 
        if (member is not None) and children:
174
 
            raise ValueError('Cannot supply both a member and children')
 
136
        if member is not None:
 
137
            assert not children, \
 
138
                'Cannot supply both a member and children'
175
139
 
176
 
        object.__setattr__(self, '_import_replacer_children', children)
177
 
        object.__setattr__(self, '_member', member)
178
 
        object.__setattr__(self, '_module_path', module_path)
 
140
        self._import_replacer_children = children
 
141
        self._member = member
 
142
        self._module_path = module_path
179
143
 
180
144
        # Indirecting through __class__ so that children can
181
145
        # override _import (especially our instrumented version)
259
223
 
260
224
        :param import_str: The import string to process
261
225
        """
262
 
        if not import_str.startswith('import '):
263
 
            raise ValueError('bad import string %r' % (import_str,))
 
226
        assert import_str.startswith('import ')
264
227
        import_str = import_str[len('import '):]
265
228
 
266
229
        for path in import_str.split(','):
305
268
 
306
269
        :param from_str: The import string to process
307
270
        """
308
 
        if not from_str.startswith('from '):
309
 
            raise ValueError('bad from/import %r' % from_str)
 
271
        assert from_str.startswith('from ')
310
272
        from_str = from_str[len('from '):]
311
273
 
312
274
        from_module, import_list = from_str.split(' import ')
390
352
    # This is just a helper around ImportProcessor.lazy_import
391
353
    proc = ImportProcessor(lazy_import_class=lazy_import_class)
392
354
    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
 
""")