~bzr-pqm/bzr/bzr.dev

2052.3.2 by John Arbash Meinel
Change Copyright .. by Canonical to Copyright ... Canonical
1
# Copyright (C) 2006 Canonical Ltd
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
16
17
"""Functionality to create lazy evaluation objects.
18
19
This includes waiting to import a module until it is actually used.
1996.1.26 by John Arbash Meinel
Update HACKING and docstrings
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).
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
41
"""
42
43
44
class ScopeReplacer(object):
45
    """A lazy object that will replace itself in the appropriate scope.
46
47
    This object sits, ready to create the real object the first time it is
48
    needed.
49
    """
50
2399.1.8 by John Arbash Meinel
Change lazy_import to allow proxying when necessary.
51
    __slots__ = ('_scope', '_factory', '_name', '_real_obj')
2413.4.1 by John Arbash Meinel
Cherrypick just the epydoc builder changes.
52
2399.1.11 by John Arbash Meinel
Update lazy_import with tests for the new '_should_proxy' variable.
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
57
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
58
    def __init__(self, scope, factory, name):
59
        """Create a temporary object in the specified scope.
60
        Once used, a real object will be placed in the scope.
61
62
        :param scope: The scope the object should appear in
63
        :param factory: A callable that will create the real object.
64
            It will be passed (self, scope, name)
65
        :param name: The variable name in the given scope.
66
        """
1551.18.22 by Aaron Bentley
Fix exception when ScopeReplacer is assigned to before retrieving any members
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)
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
71
        scope[name] = self
72
73
    def _replace(self):
74
        """Actually replace self with other in the given scope"""
75
        name = object.__getattribute__(self, '_name')
1996.1.16 by John Arbash Meinel
Raise an exception when ScopeReplacer has been misused
76
        try:
77
            factory = object.__getattribute__(self, '_factory')
78
            scope = object.__getattribute__(self, '_scope')
79
        except AttributeError, e:
80
            # Because ScopeReplacer objects only replace a single
81
            # item, passing them to another variable before they are
82
            # replaced would cause them to keep getting replaced
83
            # (only they are replacing the wrong variable). So we
84
            # make it forbidden, and try to give a good error.
85
            raise errors.IllegalUseOfScopeReplacer(
86
                name, msg="Object already cleaned up, did you assign it"
1996.1.21 by John Arbash Meinel
fix typo
87
                          " to another variable?",
1996.1.16 by John Arbash Meinel
Raise an exception when ScopeReplacer has been misused
88
                extra=e)
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
89
        obj = factory(self, scope, name)
2399.1.11 by John Arbash Meinel
Update lazy_import with tests for the new '_should_proxy' variable.
90
        if ScopeReplacer._should_proxy:
1551.18.22 by Aaron Bentley
Fix exception when ScopeReplacer is assigned to before retrieving any members
91
            object.__setattr__(self, '_real_obj', obj)
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
92
        scope[name] = obj
93
        return obj
94
95
    def _cleanup(self):
96
        """Stop holding on to all the extra stuff"""
97
        del self._factory
98
        del self._scope
1996.1.16 by John Arbash Meinel
Raise an exception when ScopeReplacer has been misused
99
        # We keep _name, so that we can report errors
100
        # del self._name
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
101
102
    def __getattribute__(self, attr):
2399.1.8 by John Arbash Meinel
Change lazy_import to allow proxying when necessary.
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()
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
109
        return getattr(obj, attr)
110
1551.18.22 by Aaron Bentley
Fix exception when ScopeReplacer is assigned to before retrieving any members
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
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
120
    def __call__(self, *args, **kwargs):
1996.1.17 by John Arbash Meinel
Small cleanup
121
        _replace = object.__getattribute__(self, '_replace')
122
        obj = _replace()
123
        _cleanup = object.__getattribute__(self, '_cleanup')
124
        _cleanup()
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
125
        return obj(*args, **kwargs)
126
127
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
128
class ImportReplacer(ScopeReplacer):
129
    """This is designed to replace only a portion of an import list.
130
131
    It will replace itself with a module, and then make children
132
    entries also ImportReplacer objects.
133
134
    At present, this only supports 'import foo.bar.baz' syntax.
135
    """
136
1996.1.23 by John Arbash Meinel
Clean up comment as suggested by Robert
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.
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
144
    __slots__ = ('_import_replacer_children', '_member', '_module_path')
145
1996.1.15 by John Arbash Meinel
Everything is now hooked up
146
    def __init__(self, scope, name, module_path, member=None, children={}):
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
147
        """Upon request import 'module_path' as the name 'module_name'.
148
        When imported, prepare children to also be imported.
149
150
        :param scope: The scope that objects should be imported into.
151
            Typically this is globals()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
152
        :param name: The variable name. Often this is the same as the
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
153
            module_path. 'bzrlib'
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
154
        :param module_path: A list for the fully specified module path
155
            ['bzrlib', 'foo', 'bar']
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
156
        :param member: The member inside the module to import, often this is
157
            None, indicating the module is being imported.
158
        :param children: Children entries to be imported later.
1996.1.15 by John Arbash Meinel
Everything is now hooked up
159
            This should be a map of children specifications.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
160
            {'foo':(['bzrlib', 'foo'], None,
1996.1.15 by John Arbash Meinel
Everything is now hooked up
161
                {'bar':(['bzrlib', 'foo', 'bar'], None {})})
162
            }
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
163
        Examples:
164
            import foo => name='foo' module_path='foo',
1996.1.15 by John Arbash Meinel
Everything is now hooked up
165
                          member=None, children={}
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
166
            import foo.bar => name='foo' module_path='foo', member=None,
1996.1.15 by John Arbash Meinel
Everything is now hooked up
167
                              children={'bar':(['foo', 'bar'], None, {}}
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
168
            from foo import bar => name='bar' module_path='foo', member='bar'
1996.1.15 by John Arbash Meinel
Everything is now hooked up
169
                                   children={}
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
170
            from foo import bar, baz would get translated into 2 import
171
            requests. On for 'name=bar' and one for 'name=baz'
172
        """
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
173
        if (member is not None) and children:
174
            raise ValueError('Cannot supply both a member and children')
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
175
1551.18.22 by Aaron Bentley
Fix exception when ScopeReplacer is assigned to before retrieving any members
176
        object.__setattr__(self, '_import_replacer_children', children)
177
        object.__setattr__(self, '_member', member)
178
        object.__setattr__(self, '_module_path', module_path)
1996.1.3 by John Arbash Meinel
Basic single-level imports work
179
180
        # Indirecting through __class__ so that children can
181
        # override _import (especially our instrumented version)
182
        cls = object.__getattribute__(self, '__class__')
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
183
        ScopeReplacer.__init__(self, scope=scope, name=name,
1996.1.3 by John Arbash Meinel
Basic single-level imports work
184
                               factory=cls._import)
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
185
186
    def _import(self, scope, name):
187
        children = object.__getattribute__(self, '_import_replacer_children')
188
        member = object.__getattribute__(self, '_member')
189
        module_path = object.__getattribute__(self, '_module_path')
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
190
        module_python_path = '.'.join(module_path)
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
191
        if member is not None:
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
192
            module = __import__(module_python_path, scope, scope, [member])
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
193
            return getattr(module, member)
194
        else:
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
195
            module = __import__(module_python_path, scope, scope, [])
196
            for path in module_path[1:]:
197
                module = getattr(module, path)
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
198
199
        # Prepare the children to be imported
1996.1.15 by John Arbash Meinel
Everything is now hooked up
200
        for child_name, (child_path, child_member, grandchildren) in \
201
                children.iteritems():
1996.1.5 by John Arbash Meinel
Test that we can lazy import a module, and its children
202
            # Using self.__class__, so that children get children classes
203
            # instantiated. (This helps with instrumented tests)
204
            cls = object.__getattribute__(self, '__class__')
205
            cls(module.__dict__, name=child_name,
1996.1.15 by John Arbash Meinel
Everything is now hooked up
206
                module_path=child_path, member=child_member,
1996.1.5 by John Arbash Meinel
Test that we can lazy import a module, and its children
207
                children=grandchildren)
1996.1.3 by John Arbash Meinel
Basic single-level imports work
208
        return module
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
209
210
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
211
class ImportProcessor(object):
1996.1.15 by John Arbash Meinel
Everything is now hooked up
212
    """Convert text that users input into lazy import requests"""
213
1996.1.18 by John Arbash Meinel
Add more structured error handling
214
    # TODO: jam 20060912 This class is probably not strict enough about
215
    #       what type of text it allows. For example, you can do:
216
    #       import (foo, bar), which is not allowed by python.
217
    #       For now, it should be supporting a superset of python import
218
    #       syntax which is all we really care about.
219
1996.1.15 by John Arbash Meinel
Everything is now hooked up
220
    __slots__ = ['imports', '_lazy_import_class']
221
222
    def __init__(self, lazy_import_class=None):
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
223
        self.imports = {}
1996.1.15 by John Arbash Meinel
Everything is now hooked up
224
        if lazy_import_class is None:
225
            self._lazy_import_class = ImportReplacer
226
        else:
227
            self._lazy_import_class = lazy_import_class
228
1996.1.19 by John Arbash Meinel
Write a simple wrapper function to make lazy imports easy.
229
    def lazy_import(self, scope, text):
1996.1.15 by John Arbash Meinel
Everything is now hooked up
230
        """Convert the given text into a bunch of lazy import objects.
231
232
        This takes a text string, which should be similar to normal python
233
        import markup.
234
        """
235
        self._build_map(text)
236
        self._convert_imports(scope)
237
238
    def _convert_imports(self, scope):
239
        # Now convert the map into a set of imports
240
        for name, info in self.imports.iteritems():
241
            self._lazy_import_class(scope, name=name, module_path=info[0],
242
                                    member=info[1], children=info[2])
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
243
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
244
    def _build_map(self, text):
245
        """Take a string describing imports, and build up the internal map"""
246
        for line in self._canonicalize_import_text(text):
1996.1.18 by John Arbash Meinel
Add more structured error handling
247
            if line.startswith('import '):
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
248
                self._convert_import_str(line)
1996.1.18 by John Arbash Meinel
Add more structured error handling
249
            elif line.startswith('from '):
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
250
                self._convert_from_str(line)
1996.1.18 by John Arbash Meinel
Add more structured error handling
251
            else:
252
                raise errors.InvalidImportLine(line,
253
                    "doesn't start with 'import ' or 'from '")
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
254
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
255
    def _convert_import_str(self, import_str):
256
        """This converts a import string into an import map.
257
258
        This only understands 'import foo, foo.bar, foo.bar.baz as bing'
259
260
        :param import_str: The import string to process
261
        """
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
262
        if not import_str.startswith('import '):
3376.2.8 by Martin Pool
Some review cleanups for assertion removal
263
            raise ValueError('bad import string %r' % (import_str,))
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
264
        import_str = import_str[len('import '):]
265
266
        for path in import_str.split(','):
267
            path = path.strip()
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
268
            if not path:
269
                continue
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
270
            as_hunks = path.split(' as ')
271
            if len(as_hunks) == 2:
272
                # We have 'as' so this is a different style of import
273
                # 'import foo.bar.baz as bing' creates a local variable
274
                # named 'bing' which points to 'foo.bar.baz'
275
                name = as_hunks[1].strip()
276
                module_path = as_hunks[0].strip().split('.')
1996.1.18 by John Arbash Meinel
Add more structured error handling
277
                if name in self.imports:
278
                    raise errors.ImportNameCollision(name)
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
279
                # No children available in 'import foo as bar'
280
                self.imports[name] = (module_path, None, {})
281
            else:
282
                # Now we need to handle
283
                module_path = path.split('.')
284
                name = module_path[0]
285
                if name not in self.imports:
286
                    # This is a new import that we haven't seen before
287
                    module_def = ([name], None, {})
288
                    self.imports[name] = module_def
289
                else:
290
                    module_def = self.imports[name]
291
292
                cur_path = [name]
293
                cur = module_def[2]
294
                for child in module_path[1:]:
295
                    cur_path.append(child)
296
                    if child in cur:
1996.1.15 by John Arbash Meinel
Everything is now hooked up
297
                        cur = cur[child][2]
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
298
                    else:
299
                        next = (cur_path[:], None, {})
300
                        cur[child] = next
301
                        cur = next[2]
302
303
    def _convert_from_str(self, from_str):
304
        """This converts a 'from foo import bar' string into an import map.
305
306
        :param from_str: The import string to process
307
        """
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
308
        if not from_str.startswith('from '):
309
            raise ValueError('bad from/import %r' % from_str)
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
310
        from_str = from_str[len('from '):]
311
312
        from_module, import_list = from_str.split(' import ')
313
314
        from_module_path = from_module.split('.')
315
316
        for path in import_list.split(','):
317
            path = path.strip()
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
318
            if not path:
319
                continue
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
320
            as_hunks = path.split(' as ')
321
            if len(as_hunks) == 2:
322
                # We have 'as' so this is a different style of import
323
                # 'import foo.bar.baz as bing' creates a local variable
324
                # named 'bing' which points to 'foo.bar.baz'
325
                name = as_hunks[1].strip()
326
                module = as_hunks[0].strip()
327
            else:
328
                name = module = path
1996.1.18 by John Arbash Meinel
Add more structured error handling
329
            if name in self.imports:
330
                raise errors.ImportNameCollision(name)
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
331
            self.imports[name] = (from_module_path, module, {})
332
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
333
    def _canonicalize_import_text(self, text):
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
334
        """Take a list of imports, and split it into regularized form.
335
336
        This is meant to take regular import text, and convert it to
337
        the forms that the rest of the converters prefer.
338
        """
339
        out = []
340
        cur = None
341
        continuing = False
342
343
        for line in text.split('\n'):
344
            line = line.strip()
345
            loc = line.find('#')
346
            if loc != -1:
347
                line = line[:loc].strip()
348
349
            if not line:
350
                continue
351
            if cur is not None:
352
                if line.endswith(')'):
353
                    out.append(cur + ' ' + line[:-1])
354
                    cur = None
355
                else:
356
                    cur += ' ' + line
357
            else:
358
                if '(' in line and ')' not in line:
359
                    cur = line.replace('(', '')
360
                else:
361
                    out.append(line.replace('(', '').replace(')', ''))
1996.1.18 by John Arbash Meinel
Add more structured error handling
362
        if cur is not None:
363
            raise errors.InvalidImportLine(cur, 'Unmatched parenthesis')
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
364
        return out
1996.1.19 by John Arbash Meinel
Write a simple wrapper function to make lazy imports easy.
365
366
367
def lazy_import(scope, text, lazy_import_class=None):
368
    """Create lazy imports for all of the imports in text.
369
370
    This is typically used as something like:
371
    from bzrlib.lazy_import import lazy_import
372
    lazy_import(globals(), '''
373
    from bzrlib import (
374
        foo,
375
        bar,
376
        baz,
377
        )
378
    import bzrlib.branch
379
    import bzrlib.transport
380
    ''')
381
382
    Then 'foo, bar, baz' and 'bzrlib' will exist as lazy-loaded
383
    objects which will be replaced with a real object on first use.
384
385
    In general, it is best to only load modules in this way. This is
386
    because other objects (functions/classes/variables) are frequently
387
    used without accessing a member, which means we cannot tell they
388
    have been used.
389
    """
390
    # This is just a helper around ImportProcessor.lazy_import
391
    proc = ImportProcessor(lazy_import_class=lazy_import_class)
392
    return proc.lazy_import(scope, text)
1996.3.19 by John Arbash Meinel
make lazy_import lazily import errors
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
""")