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