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