~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)
5409.2.1 by Martin
Add check in lazy import ScopeReplacer to ensure it's not trying to replace itself
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.")
2399.1.11 by John Arbash Meinel
Update lazy_import with tests for the new '_should_proxy' variable.
93
        if ScopeReplacer._should_proxy:
1551.18.22 by Aaron Bentley
Fix exception when ScopeReplacer is assigned to before retrieving any members
94
            object.__setattr__(self, '_real_obj', obj)
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
95
        scope[name] = obj
96
        return obj
97
98
    def _cleanup(self):
99
        """Stop holding on to all the extra stuff"""
100
        del self._factory
101
        del self._scope
1996.1.16 by John Arbash Meinel
Raise an exception when ScopeReplacer has been misused
102
        # We keep _name, so that we can report errors
103
        # del self._name
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
104
105
    def __getattribute__(self, attr):
2399.1.8 by John Arbash Meinel
Change lazy_import to allow proxying when necessary.
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()
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
112
        return getattr(obj, attr)
113
1551.18.22 by Aaron Bentley
Fix exception when ScopeReplacer is assigned to before retrieving any members
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
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
123
    def __call__(self, *args, **kwargs):
1996.1.17 by John Arbash Meinel
Small cleanup
124
        _replace = object.__getattribute__(self, '_replace')
125
        obj = _replace()
126
        _cleanup = object.__getattribute__(self, '_cleanup')
127
        _cleanup()
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
128
        return obj(*args, **kwargs)
129
130
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
131
class ImportReplacer(ScopeReplacer):
132
    """This is designed to replace only a portion of an import list.
133
134
    It will replace itself with a module, and then make children
135
    entries also ImportReplacer objects.
136
137
    At present, this only supports 'import foo.bar.baz' syntax.
138
    """
139
1996.1.23 by John Arbash Meinel
Clean up comment as suggested by Robert
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.
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
147
    __slots__ = ('_import_replacer_children', '_member', '_module_path')
148
1996.1.15 by John Arbash Meinel
Everything is now hooked up
149
    def __init__(self, scope, name, module_path, member=None, children={}):
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
150
        """Upon request import 'module_path' as the name 'module_name'.
151
        When imported, prepare children to also be imported.
152
153
        :param scope: The scope that objects should be imported into.
154
            Typically this is globals()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
155
        :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
156
            module_path. 'bzrlib'
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
157
        :param module_path: A list for the fully specified module path
158
            ['bzrlib', 'foo', 'bar']
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
159
        :param member: The member inside the module to import, often this is
160
            None, indicating the module is being imported.
161
        :param children: Children entries to be imported later.
1996.1.15 by John Arbash Meinel
Everything is now hooked up
162
            This should be a map of children specifications.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
163
            {'foo':(['bzrlib', 'foo'], None,
1996.1.15 by John Arbash Meinel
Everything is now hooked up
164
                {'bar':(['bzrlib', 'foo', 'bar'], None {})})
165
            }
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
166
        Examples:
167
            import foo => name='foo' module_path='foo',
1996.1.15 by John Arbash Meinel
Everything is now hooked up
168
                          member=None, children={}
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
169
            import foo.bar => name='foo' module_path='foo', member=None,
1996.1.15 by John Arbash Meinel
Everything is now hooked up
170
                              children={'bar':(['foo', 'bar'], None, {}}
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
171
            from foo import bar => name='bar' module_path='foo', member='bar'
1996.1.15 by John Arbash Meinel
Everything is now hooked up
172
                                   children={}
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
173
            from foo import bar, baz would get translated into 2 import
174
            requests. On for 'name=bar' and one for 'name=baz'
175
        """
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
176
        if (member is not None) and children:
177
            raise ValueError('Cannot supply both a member and children')
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
178
1551.18.22 by Aaron Bentley
Fix exception when ScopeReplacer is assigned to before retrieving any members
179
        object.__setattr__(self, '_import_replacer_children', children)
180
        object.__setattr__(self, '_member', member)
181
        object.__setattr__(self, '_module_path', module_path)
1996.1.3 by John Arbash Meinel
Basic single-level imports work
182
183
        # Indirecting through __class__ so that children can
184
        # override _import (especially our instrumented version)
185
        cls = object.__getattribute__(self, '__class__')
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
186
        ScopeReplacer.__init__(self, scope=scope, name=name,
1996.1.3 by John Arbash Meinel
Basic single-level imports work
187
                               factory=cls._import)
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
188
189
    def _import(self, scope, name):
190
        children = object.__getattribute__(self, '_import_replacer_children')
191
        member = object.__getattribute__(self, '_member')
192
        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'
193
        module_python_path = '.'.join(module_path)
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
194
        if member is not None:
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, [member])
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
196
            return getattr(module, member)
197
        else:
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
198
            module = __import__(module_python_path, scope, scope, [])
199
            for path in module_path[1:]:
200
                module = getattr(module, path)
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
201
202
        # Prepare the children to be imported
1996.1.15 by John Arbash Meinel
Everything is now hooked up
203
        for child_name, (child_path, child_member, grandchildren) in \
204
                children.iteritems():
1996.1.5 by John Arbash Meinel
Test that we can lazy import a module, and its children
205
            # Using self.__class__, so that children get children classes
206
            # instantiated. (This helps with instrumented tests)
207
            cls = object.__getattribute__(self, '__class__')
208
            cls(module.__dict__, name=child_name,
1996.1.15 by John Arbash Meinel
Everything is now hooked up
209
                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
210
                children=grandchildren)
1996.1.3 by John Arbash Meinel
Basic single-level imports work
211
        return module
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
212
213
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
214
class ImportProcessor(object):
1996.1.15 by John Arbash Meinel
Everything is now hooked up
215
    """Convert text that users input into lazy import requests"""
216
1996.1.18 by John Arbash Meinel
Add more structured error handling
217
    # TODO: jam 20060912 This class is probably not strict enough about
218
    #       what type of text it allows. For example, you can do:
219
    #       import (foo, bar), which is not allowed by python.
220
    #       For now, it should be supporting a superset of python import
221
    #       syntax which is all we really care about.
222
1996.1.15 by John Arbash Meinel
Everything is now hooked up
223
    __slots__ = ['imports', '_lazy_import_class']
224
225
    def __init__(self, lazy_import_class=None):
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
226
        self.imports = {}
1996.1.15 by John Arbash Meinel
Everything is now hooked up
227
        if lazy_import_class is None:
228
            self._lazy_import_class = ImportReplacer
229
        else:
230
            self._lazy_import_class = lazy_import_class
231
1996.1.19 by John Arbash Meinel
Write a simple wrapper function to make lazy imports easy.
232
    def lazy_import(self, scope, text):
1996.1.15 by John Arbash Meinel
Everything is now hooked up
233
        """Convert the given text into a bunch of lazy import objects.
234
235
        This takes a text string, which should be similar to normal python
236
        import markup.
237
        """
238
        self._build_map(text)
239
        self._convert_imports(scope)
240
241
    def _convert_imports(self, scope):
242
        # Now convert the map into a set of imports
243
        for name, info in self.imports.iteritems():
244
            self._lazy_import_class(scope, name=name, module_path=info[0],
245
                                    member=info[1], children=info[2])
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
246
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
247
    def _build_map(self, text):
248
        """Take a string describing imports, and build up the internal map"""
249
        for line in self._canonicalize_import_text(text):
1996.1.18 by John Arbash Meinel
Add more structured error handling
250
            if line.startswith('import '):
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
251
                self._convert_import_str(line)
1996.1.18 by John Arbash Meinel
Add more structured error handling
252
            elif line.startswith('from '):
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
253
                self._convert_from_str(line)
1996.1.18 by John Arbash Meinel
Add more structured error handling
254
            else:
255
                raise errors.InvalidImportLine(line,
256
                    "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
257
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
258
    def _convert_import_str(self, import_str):
259
        """This converts a import string into an import map.
260
261
        This only understands 'import foo, foo.bar, foo.bar.baz as bing'
262
263
        :param import_str: The import string to process
264
        """
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
265
        if not import_str.startswith('import '):
3376.2.8 by Martin Pool
Some review cleanups for assertion removal
266
            raise ValueError('bad import string %r' % (import_str,))
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
267
        import_str = import_str[len('import '):]
268
269
        for path in import_str.split(','):
270
            path = path.strip()
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
271
            if not path:
272
                continue
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
273
            as_hunks = path.split(' as ')
274
            if len(as_hunks) == 2:
275
                # We have 'as' so this is a different style of import
276
                # 'import foo.bar.baz as bing' creates a local variable
277
                # named 'bing' which points to 'foo.bar.baz'
278
                name = as_hunks[1].strip()
279
                module_path = as_hunks[0].strip().split('.')
1996.1.18 by John Arbash Meinel
Add more structured error handling
280
                if name in self.imports:
281
                    raise errors.ImportNameCollision(name)
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
282
                # No children available in 'import foo as bar'
283
                self.imports[name] = (module_path, None, {})
284
            else:
285
                # Now we need to handle
286
                module_path = path.split('.')
287
                name = module_path[0]
288
                if name not in self.imports:
289
                    # This is a new import that we haven't seen before
290
                    module_def = ([name], None, {})
291
                    self.imports[name] = module_def
292
                else:
293
                    module_def = self.imports[name]
294
295
                cur_path = [name]
296
                cur = module_def[2]
297
                for child in module_path[1:]:
298
                    cur_path.append(child)
299
                    if child in cur:
1996.1.15 by John Arbash Meinel
Everything is now hooked up
300
                        cur = cur[child][2]
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
301
                    else:
302
                        next = (cur_path[:], None, {})
303
                        cur[child] = next
304
                        cur = next[2]
305
306
    def _convert_from_str(self, from_str):
307
        """This converts a 'from foo import bar' string into an import map.
308
309
        :param from_str: The import string to process
310
        """
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
311
        if not from_str.startswith('from '):
312
            raise ValueError('bad from/import %r' % from_str)
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
313
        from_str = from_str[len('from '):]
314
315
        from_module, import_list = from_str.split(' import ')
316
317
        from_module_path = from_module.split('.')
318
319
        for path in import_list.split(','):
320
            path = path.strip()
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
321
            if not path:
322
                continue
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
323
            as_hunks = path.split(' as ')
324
            if len(as_hunks) == 2:
325
                # We have 'as' so this is a different style of import
326
                # 'import foo.bar.baz as bing' creates a local variable
327
                # named 'bing' which points to 'foo.bar.baz'
328
                name = as_hunks[1].strip()
329
                module = as_hunks[0].strip()
330
            else:
331
                name = module = path
1996.1.18 by John Arbash Meinel
Add more structured error handling
332
            if name in self.imports:
333
                raise errors.ImportNameCollision(name)
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
334
            self.imports[name] = (from_module_path, module, {})
335
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
336
    def _canonicalize_import_text(self, text):
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
337
        """Take a list of imports, and split it into regularized form.
338
339
        This is meant to take regular import text, and convert it to
340
        the forms that the rest of the converters prefer.
341
        """
342
        out = []
343
        cur = None
344
        continuing = False
345
346
        for line in text.split('\n'):
347
            line = line.strip()
348
            loc = line.find('#')
349
            if loc != -1:
350
                line = line[:loc].strip()
351
352
            if not line:
353
                continue
354
            if cur is not None:
355
                if line.endswith(')'):
356
                    out.append(cur + ' ' + line[:-1])
357
                    cur = None
358
                else:
359
                    cur += ' ' + line
360
            else:
361
                if '(' in line and ')' not in line:
362
                    cur = line.replace('(', '')
363
                else:
364
                    out.append(line.replace('(', '').replace(')', ''))
1996.1.18 by John Arbash Meinel
Add more structured error handling
365
        if cur is not None:
366
            raise errors.InvalidImportLine(cur, 'Unmatched parenthesis')
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
367
        return out
1996.1.19 by John Arbash Meinel
Write a simple wrapper function to make lazy imports easy.
368
369
370
def lazy_import(scope, text, lazy_import_class=None):
371
    """Create lazy imports for all of the imports in text.
372
373
    This is typically used as something like:
374
    from bzrlib.lazy_import import lazy_import
375
    lazy_import(globals(), '''
376
    from bzrlib import (
377
        foo,
378
        bar,
379
        baz,
380
        )
381
    import bzrlib.branch
382
    import bzrlib.transport
383
    ''')
384
385
    Then 'foo, bar, baz' and 'bzrlib' will exist as lazy-loaded
386
    objects which will be replaced with a real object on first use.
387
388
    In general, it is best to only load modules in this way. This is
389
    because other objects (functions/classes/variables) are frequently
390
    used without accessing a member, which means we cannot tell they
391
    have been used.
392
    """
393
    # This is just a helper around ImportProcessor.lazy_import
394
    proc = ImportProcessor(lazy_import_class=lazy_import_class)
395
    return proc.lazy_import(scope, text)
1996.3.19 by John Arbash Meinel
make lazy_import lazily import errors
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
""")