~bzr-pqm/bzr/bzr.dev

5387.2.7 by John Arbash Meinel
Merge bzr.dev 5444 to resolve some small text conflicts.
1
# Copyright (C) 2006-2010 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
5891.1.2 by Andrew Bennetts
Fix a bunch of docstring formatting nits, making pydoctor a bit happier.
22
in an on-demand fashion. Typically use looks like::
23
1996.1.26 by John Arbash Meinel
Update HACKING and docstrings
24
    from bzrlib.lazy_import import lazy_import
25
    lazy_import(globals(), '''
26
    from bzrlib import (
27
        errors,
28
        osutils,
29
        branch,
30
        )
31
    import bzrlib.branch
32
    ''')
33
5891.1.2 by Andrew Bennetts
Fix a bunch of docstring formatting nits, making pydoctor a bit happier.
34
Then 'errors, osutils, branch' and 'bzrlib' will exist as lazy-loaded
35
objects which will be replaced with a real object on first use.
1996.1.26 by John Arbash Meinel
Update HACKING and docstrings
36
5891.1.2 by Andrew Bennetts
Fix a bunch of docstring formatting nits, making pydoctor a bit happier.
37
In general, it is best to only load modules in this way. This is because
38
it isn't safe to pass these variables to other functions before they
39
have been replaced. This is especially true for constants, sometimes
40
true for classes or functions (when used as a factory, or you want
41
to inherit from them).
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
42
"""
43
44
45
class ScopeReplacer(object):
46
    """A lazy object that will replace itself in the appropriate scope.
47
48
    This object sits, ready to create the real object the first time it is
49
    needed.
50
    """
51
2399.1.8 by John Arbash Meinel
Change lazy_import to allow proxying when necessary.
52
    __slots__ = ('_scope', '_factory', '_name', '_real_obj')
2413.4.1 by John Arbash Meinel
Cherrypick just the epydoc builder changes.
53
2399.1.11 by John Arbash Meinel
Update lazy_import with tests for the new '_should_proxy' variable.
54
    # Setting this to True will allow you to do x = y, and still access members
55
    # from both variables. This should not normally be enabled, but is useful
56
    # when building documentation.
57
    _should_proxy = False
58
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
59
    def __init__(self, scope, factory, name):
60
        """Create a temporary object in the specified scope.
61
        Once used, a real object will be placed in the scope.
62
63
        :param scope: The scope the object should appear in
64
        :param factory: A callable that will create the real object.
65
            It will be passed (self, scope, name)
66
        :param name: The variable name in the given scope.
67
        """
1551.18.22 by Aaron Bentley
Fix exception when ScopeReplacer is assigned to before retrieving any members
68
        object.__setattr__(self, '_scope', scope)
69
        object.__setattr__(self, '_factory', factory)
70
        object.__setattr__(self, '_name', name)
71
        object.__setattr__(self, '_real_obj', None)
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
72
        scope[name] = self
73
74
    def _replace(self):
75
        """Actually replace self with other in the given scope"""
76
        name = object.__getattribute__(self, '_name')
1996.1.16 by John Arbash Meinel
Raise an exception when ScopeReplacer has been misused
77
        try:
78
            factory = object.__getattribute__(self, '_factory')
79
            scope = object.__getattribute__(self, '_scope')
80
        except AttributeError, e:
81
            # Because ScopeReplacer objects only replace a single
82
            # item, passing them to another variable before they are
83
            # replaced would cause them to keep getting replaced
84
            # (only they are replacing the wrong variable). So we
85
            # make it forbidden, and try to give a good error.
86
            raise errors.IllegalUseOfScopeReplacer(
87
                name, msg="Object already cleaned up, did you assign it"
1996.1.21 by John Arbash Meinel
fix typo
88
                          " to another variable?",
1996.1.16 by John Arbash Meinel
Raise an exception when ScopeReplacer has been misused
89
                extra=e)
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
90
        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
91
        if obj is self:
92
            raise errors.IllegalUseOfScopeReplacer(name, msg="Object tried"
93
                " 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.
94
        if ScopeReplacer._should_proxy:
1551.18.22 by Aaron Bentley
Fix exception when ScopeReplacer is assigned to before retrieving any members
95
            object.__setattr__(self, '_real_obj', obj)
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
96
        scope[name] = obj
97
        return obj
98
99
    def _cleanup(self):
100
        """Stop holding on to all the extra stuff"""
101
        del self._factory
102
        del self._scope
1996.1.16 by John Arbash Meinel
Raise an exception when ScopeReplacer has been misused
103
        # We keep _name, so that we can report errors
104
        # del self._name
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
105
106
    def __getattribute__(self, attr):
2399.1.8 by John Arbash Meinel
Change lazy_import to allow proxying when necessary.
107
        obj = object.__getattribute__(self, '_real_obj')
108
        if obj is None:
109
            _replace = object.__getattribute__(self, '_replace')
110
            obj = _replace()
111
            _cleanup = object.__getattribute__(self, '_cleanup')
112
            _cleanup()
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
113
        return getattr(obj, attr)
114
1551.18.22 by Aaron Bentley
Fix exception when ScopeReplacer is assigned to before retrieving any members
115
    def __setattr__(self, attr, value):
116
        obj = object.__getattribute__(self, '_real_obj')
117
        if obj is None:
118
            _replace = object.__getattribute__(self, '_replace')
119
            obj = _replace()
120
            _cleanup = object.__getattribute__(self, '_cleanup')
121
            _cleanup()
122
        return setattr(obj, attr, value)
123
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
124
    def __call__(self, *args, **kwargs):
1996.1.17 by John Arbash Meinel
Small cleanup
125
        _replace = object.__getattribute__(self, '_replace')
126
        obj = _replace()
127
        _cleanup = object.__getattribute__(self, '_cleanup')
128
        _cleanup()
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
129
        return obj(*args, **kwargs)
130
131
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
132
class ImportReplacer(ScopeReplacer):
133
    """This is designed to replace only a portion of an import list.
134
135
    It will replace itself with a module, and then make children
136
    entries also ImportReplacer objects.
137
138
    At present, this only supports 'import foo.bar.baz' syntax.
139
    """
140
1996.1.23 by John Arbash Meinel
Clean up comment as suggested by Robert
141
    # '_import_replacer_children' is intentionally a long semi-unique name
142
    # that won't likely exist elsewhere. This allows us to detect an
143
    # ImportReplacer object by using
144
    #       object.__getattribute__(obj, '_import_replacer_children')
145
    # We can't just use 'isinstance(obj, ImportReplacer)', because that
146
    # accesses .__class__, which goes through __getattribute__, and triggers
147
    # the replacement.
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
148
    __slots__ = ('_import_replacer_children', '_member', '_module_path')
149
1996.1.15 by John Arbash Meinel
Everything is now hooked up
150
    def __init__(self, scope, name, module_path, member=None, children={}):
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
151
        """Upon request import 'module_path' as the name 'module_name'.
152
        When imported, prepare children to also be imported.
153
154
        :param scope: The scope that objects should be imported into.
155
            Typically this is globals()
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
156
        :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
157
            module_path. 'bzrlib'
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
158
        :param module_path: A list for the fully specified module path
159
            ['bzrlib', 'foo', 'bar']
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
160
        :param member: The member inside the module to import, often this is
161
            None, indicating the module is being imported.
162
        :param children: Children entries to be imported later.
1996.1.15 by John Arbash Meinel
Everything is now hooked up
163
            This should be a map of children specifications.
5891.1.2 by Andrew Bennetts
Fix a bunch of docstring formatting nits, making pydoctor a bit happier.
164
            ::
165
            
166
                {'foo':(['bzrlib', 'foo'], None,
167
                    {'bar':(['bzrlib', 'foo', 'bar'], None {})})
168
                }
169
170
        Examples::
171
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
172
            import foo => name='foo' module_path='foo',
1996.1.15 by John Arbash Meinel
Everything is now hooked up
173
                          member=None, children={}
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
174
            import foo.bar => name='foo' module_path='foo', member=None,
1996.1.15 by John Arbash Meinel
Everything is now hooked up
175
                              children={'bar':(['foo', 'bar'], None, {}}
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
176
            from foo import bar => name='bar' module_path='foo', member='bar'
1996.1.15 by John Arbash Meinel
Everything is now hooked up
177
                                   children={}
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
178
            from foo import bar, baz would get translated into 2 import
179
            requests. On for 'name=bar' and one for 'name=baz'
180
        """
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
181
        if (member is not None) and children:
182
            raise ValueError('Cannot supply both a member and children')
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
183
1551.18.22 by Aaron Bentley
Fix exception when ScopeReplacer is assigned to before retrieving any members
184
        object.__setattr__(self, '_import_replacer_children', children)
185
        object.__setattr__(self, '_member', member)
186
        object.__setattr__(self, '_module_path', module_path)
1996.1.3 by John Arbash Meinel
Basic single-level imports work
187
188
        # Indirecting through __class__ so that children can
189
        # override _import (especially our instrumented version)
190
        cls = object.__getattribute__(self, '__class__')
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
191
        ScopeReplacer.__init__(self, scope=scope, name=name,
1996.1.3 by John Arbash Meinel
Basic single-level imports work
192
                               factory=cls._import)
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
193
194
    def _import(self, scope, name):
195
        children = object.__getattribute__(self, '_import_replacer_children')
196
        member = object.__getattribute__(self, '_member')
197
        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'
198
        module_python_path = '.'.join(module_path)
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
199
        if member is not None:
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
200
            module = __import__(module_python_path, scope, scope, [member])
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
201
            return getattr(module, member)
202
        else:
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
203
            module = __import__(module_python_path, scope, scope, [])
204
            for path in module_path[1:]:
205
                module = getattr(module, path)
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
206
207
        # Prepare the children to be imported
1996.1.15 by John Arbash Meinel
Everything is now hooked up
208
        for child_name, (child_path, child_member, grandchildren) in \
209
                children.iteritems():
1996.1.5 by John Arbash Meinel
Test that we can lazy import a module, and its children
210
            # Using self.__class__, so that children get children classes
211
            # instantiated. (This helps with instrumented tests)
212
            cls = object.__getattribute__(self, '__class__')
213
            cls(module.__dict__, name=child_name,
1996.1.15 by John Arbash Meinel
Everything is now hooked up
214
                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
215
                children=grandchildren)
1996.1.3 by John Arbash Meinel
Basic single-level imports work
216
        return module
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
217
218
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
219
class ImportProcessor(object):
1996.1.15 by John Arbash Meinel
Everything is now hooked up
220
    """Convert text that users input into lazy import requests"""
221
1996.1.18 by John Arbash Meinel
Add more structured error handling
222
    # TODO: jam 20060912 This class is probably not strict enough about
223
    #       what type of text it allows. For example, you can do:
224
    #       import (foo, bar), which is not allowed by python.
225
    #       For now, it should be supporting a superset of python import
226
    #       syntax which is all we really care about.
227
1996.1.15 by John Arbash Meinel
Everything is now hooked up
228
    __slots__ = ['imports', '_lazy_import_class']
229
230
    def __init__(self, lazy_import_class=None):
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
231
        self.imports = {}
1996.1.15 by John Arbash Meinel
Everything is now hooked up
232
        if lazy_import_class is None:
233
            self._lazy_import_class = ImportReplacer
234
        else:
235
            self._lazy_import_class = lazy_import_class
236
1996.1.19 by John Arbash Meinel
Write a simple wrapper function to make lazy imports easy.
237
    def lazy_import(self, scope, text):
1996.1.15 by John Arbash Meinel
Everything is now hooked up
238
        """Convert the given text into a bunch of lazy import objects.
239
240
        This takes a text string, which should be similar to normal python
241
        import markup.
242
        """
243
        self._build_map(text)
244
        self._convert_imports(scope)
245
246
    def _convert_imports(self, scope):
247
        # Now convert the map into a set of imports
248
        for name, info in self.imports.iteritems():
249
            self._lazy_import_class(scope, name=name, module_path=info[0],
250
                                    member=info[1], children=info[2])
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
251
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
252
    def _build_map(self, text):
253
        """Take a string describing imports, and build up the internal map"""
254
        for line in self._canonicalize_import_text(text):
1996.1.18 by John Arbash Meinel
Add more structured error handling
255
            if line.startswith('import '):
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
256
                self._convert_import_str(line)
1996.1.18 by John Arbash Meinel
Add more structured error handling
257
            elif line.startswith('from '):
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
258
                self._convert_from_str(line)
1996.1.18 by John Arbash Meinel
Add more structured error handling
259
            else:
260
                raise errors.InvalidImportLine(line,
261
                    "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
262
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
263
    def _convert_import_str(self, import_str):
264
        """This converts a import string into an import map.
265
266
        This only understands 'import foo, foo.bar, foo.bar.baz as bing'
267
268
        :param import_str: The import string to process
269
        """
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
270
        if not import_str.startswith('import '):
3376.2.8 by Martin Pool
Some review cleanups for assertion removal
271
            raise ValueError('bad import string %r' % (import_str,))
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
272
        import_str = import_str[len('import '):]
273
274
        for path in import_str.split(','):
275
            path = path.strip()
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
276
            if not path:
277
                continue
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
278
            as_hunks = path.split(' as ')
279
            if len(as_hunks) == 2:
280
                # We have 'as' so this is a different style of import
281
                # 'import foo.bar.baz as bing' creates a local variable
282
                # named 'bing' which points to 'foo.bar.baz'
283
                name = as_hunks[1].strip()
284
                module_path = as_hunks[0].strip().split('.')
1996.1.18 by John Arbash Meinel
Add more structured error handling
285
                if name in self.imports:
286
                    raise errors.ImportNameCollision(name)
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
287
                # No children available in 'import foo as bar'
288
                self.imports[name] = (module_path, None, {})
289
            else:
290
                # Now we need to handle
291
                module_path = path.split('.')
292
                name = module_path[0]
293
                if name not in self.imports:
294
                    # This is a new import that we haven't seen before
295
                    module_def = ([name], None, {})
296
                    self.imports[name] = module_def
297
                else:
298
                    module_def = self.imports[name]
299
300
                cur_path = [name]
301
                cur = module_def[2]
302
                for child in module_path[1:]:
303
                    cur_path.append(child)
304
                    if child in cur:
1996.1.15 by John Arbash Meinel
Everything is now hooked up
305
                        cur = cur[child][2]
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
306
                    else:
307
                        next = (cur_path[:], None, {})
308
                        cur[child] = next
309
                        cur = next[2]
310
311
    def _convert_from_str(self, from_str):
312
        """This converts a 'from foo import bar' string into an import map.
313
314
        :param from_str: The import string to process
315
        """
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
316
        if not from_str.startswith('from '):
317
            raise ValueError('bad from/import %r' % from_str)
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
318
        from_str = from_str[len('from '):]
319
320
        from_module, import_list = from_str.split(' import ')
321
322
        from_module_path = from_module.split('.')
323
324
        for path in import_list.split(','):
325
            path = path.strip()
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
326
            if not path:
327
                continue
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
328
            as_hunks = path.split(' as ')
329
            if len(as_hunks) == 2:
330
                # We have 'as' so this is a different style of import
331
                # 'import foo.bar.baz as bing' creates a local variable
332
                # named 'bing' which points to 'foo.bar.baz'
333
                name = as_hunks[1].strip()
334
                module = as_hunks[0].strip()
335
            else:
336
                name = module = path
1996.1.18 by John Arbash Meinel
Add more structured error handling
337
            if name in self.imports:
338
                raise errors.ImportNameCollision(name)
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
339
            self.imports[name] = (from_module_path, module, {})
340
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
341
    def _canonicalize_import_text(self, text):
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
342
        """Take a list of imports, and split it into regularized form.
343
344
        This is meant to take regular import text, and convert it to
345
        the forms that the rest of the converters prefer.
346
        """
347
        out = []
348
        cur = None
349
        continuing = False
350
351
        for line in text.split('\n'):
352
            line = line.strip()
353
            loc = line.find('#')
354
            if loc != -1:
355
                line = line[:loc].strip()
356
357
            if not line:
358
                continue
359
            if cur is not None:
360
                if line.endswith(')'):
361
                    out.append(cur + ' ' + line[:-1])
362
                    cur = None
363
                else:
364
                    cur += ' ' + line
365
            else:
366
                if '(' in line and ')' not in line:
367
                    cur = line.replace('(', '')
368
                else:
369
                    out.append(line.replace('(', '').replace(')', ''))
1996.1.18 by John Arbash Meinel
Add more structured error handling
370
        if cur is not None:
371
            raise errors.InvalidImportLine(cur, 'Unmatched parenthesis')
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
372
        return out
1996.1.19 by John Arbash Meinel
Write a simple wrapper function to make lazy imports easy.
373
374
375
def lazy_import(scope, text, lazy_import_class=None):
376
    """Create lazy imports for all of the imports in text.
377
5891.1.2 by Andrew Bennetts
Fix a bunch of docstring formatting nits, making pydoctor a bit happier.
378
    This is typically used as something like::
379
380
        from bzrlib.lazy_import import lazy_import
381
        lazy_import(globals(), '''
382
        from bzrlib import (
383
            foo,
384
            bar,
385
            baz,
386
            )
387
        import bzrlib.branch
388
        import bzrlib.transport
389
        ''')
1996.1.19 by John Arbash Meinel
Write a simple wrapper function to make lazy imports easy.
390
391
    Then 'foo, bar, baz' and 'bzrlib' will exist as lazy-loaded
392
    objects which will be replaced with a real object on first use.
393
394
    In general, it is best to only load modules in this way. This is
395
    because other objects (functions/classes/variables) are frequently
396
    used without accessing a member, which means we cannot tell they
397
    have been used.
398
    """
399
    # This is just a helper around ImportProcessor.lazy_import
400
    proc = ImportProcessor(lazy_import_class=lazy_import_class)
401
    return proc.lazy_import(scope, text)
1996.3.19 by John Arbash Meinel
make lazy_import lazily import errors
402
403
404
# The only module that this module depends on is 'bzrlib.errors'. But it
405
# can actually be imported lazily, since we only need it if there is a
406
# problem.
407
408
lazy_import(globals(), """
409
from bzrlib import errors
410
""")