/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to brzlib/lazy_import.py

  • Committer: Jelmer Vernooij
  • Date: 2017-05-21 12:41:27 UTC
  • mto: This revision was merged to the branch mainline in revision 6623.
  • Revision ID: jelmer@jelmer.uk-20170521124127-iv8etg0vwymyai6y
s/bzr/brz/ in apport config.

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
Most commonly, the 'lazy_import' function is used to import other modules
22
22
in an on-demand fashion. Typically use looks like::
23
23
 
24
 
    from .lazy_import import lazy_import
 
24
    from brzlib.lazy_import import lazy_import
25
25
    lazy_import(globals(), '''
26
 
    from breezy import (
 
26
    from brzlib import (
27
27
        errors,
28
28
        osutils,
29
29
        branch,
30
30
        )
31
 
    import breezy.branch
 
31
    import brzlib.branch
32
32
    ''')
33
33
 
34
 
Then 'errors, osutils, branch' and 'breezy' will exist as lazy-loaded
 
34
Then 'errors, osutils, branch' and 'brzlib' will exist as lazy-loaded
35
35
objects which will be replaced with a real object on first use.
36
36
 
37
37
In general, it is best to only load modules in this way. This is because
41
41
to inherit from them).
42
42
"""
43
43
 
44
 
from .errors import BzrError, InternalBzrError
45
 
 
46
 
 
47
 
class ImportNameCollision(InternalBzrError):
48
 
 
49
 
    _fmt = ("Tried to import an object to the same name as"
50
 
            " an existing object. %(name)s")
51
 
 
52
 
    def __init__(self, name):
53
 
        BzrError.__init__(self)
54
 
        self.name = name
55
 
 
56
 
 
57
 
class IllegalUseOfScopeReplacer(InternalBzrError):
58
 
 
59
 
    _fmt = ("ScopeReplacer object %(name)r was used incorrectly:"
60
 
            " %(msg)s%(extra)s")
61
 
 
62
 
    def __init__(self, name, msg, extra=None):
63
 
        BzrError.__init__(self)
64
 
        self.name = name
65
 
        self.msg = msg
66
 
        if extra:
67
 
            self.extra = ': ' + str(extra)
68
 
        else:
69
 
            self.extra = ''
70
 
 
71
 
 
72
 
class InvalidImportLine(InternalBzrError):
73
 
 
74
 
    _fmt = "Not a valid import statement: %(msg)\n%(text)s"
75
 
 
76
 
    def __init__(self, text, msg):
77
 
        BzrError.__init__(self)
78
 
        self.text = text
79
 
        self.msg = msg
 
44
from __future__ import absolute_import
80
45
 
81
46
 
82
47
class ScopeReplacer(object):
119
84
            scope = object.__getattribute__(self, '_scope')
120
85
            obj = factory(self, scope, name)
121
86
            if obj is self:
122
 
                raise IllegalUseOfScopeReplacer(
123
 
                    name, msg="Object tried"
 
87
                raise errors.IllegalUseOfScopeReplacer(name, msg="Object tried"
124
88
                    " to replace itself, check it's not using its own scope.")
125
89
 
126
90
            # Check if another thread has jumped in while obj was generated.
135
99
 
136
100
        # Raise if proxying is disabled as obj has already been generated.
137
101
        if not ScopeReplacer._should_proxy:
138
 
            raise IllegalUseOfScopeReplacer(
 
102
            raise errors.IllegalUseOfScopeReplacer(
139
103
                name, msg="Object already replaced, did you assign it"
140
104
                          " to another variable?")
141
105
        return real_obj
165
129
    ScopeReplacer._should_proxy = False
166
130
 
167
131
 
168
 
_builtin_import = __import__
169
 
 
170
 
 
171
132
class ImportReplacer(ScopeReplacer):
172
133
    """This is designed to replace only a portion of an import list.
173
134
 
193
154
        :param scope: The scope that objects should be imported into.
194
155
            Typically this is globals()
195
156
        :param name: The variable name. Often this is the same as the
196
 
            module_path. 'breezy'
 
157
            module_path. 'brzlib'
197
158
        :param module_path: A list for the fully specified module path
198
 
            ['breezy', 'foo', 'bar']
 
159
            ['brzlib', 'foo', 'bar']
199
160
        :param member: The member inside the module to import, often this is
200
161
            None, indicating the module is being imported.
201
162
        :param children: Children entries to be imported later.
202
163
            This should be a map of children specifications.
203
164
            ::
204
 
 
205
 
                {'foo':(['breezy', 'foo'], None,
206
 
                    {'bar':(['breezy', 'foo', 'bar'], None {})})
 
165
            
 
166
                {'foo':(['brzlib', 'foo'], None,
 
167
                    {'bar':(['brzlib', 'foo', 'bar'], None {})})
207
168
                }
208
169
 
209
170
        Examples::
234
195
        children = object.__getattribute__(self, '_import_replacer_children')
235
196
        member = object.__getattribute__(self, '_member')
236
197
        module_path = object.__getattribute__(self, '_module_path')
237
 
        name = '.'.join(module_path)
 
198
        module_python_path = '.'.join(module_path)
238
199
        if member is not None:
239
 
            module = _builtin_import(name, scope, scope, [member], level=0)
 
200
            module = __import__(module_python_path, scope, scope, [member], level=0)
240
201
            return getattr(module, member)
241
202
        else:
242
 
            module = _builtin_import(name, scope, scope, [], level=0)
 
203
            module = __import__(module_python_path, scope, scope, [], level=0)
243
204
            for path in module_path[1:]:
244
205
                module = getattr(module, path)
245
206
 
246
207
        # Prepare the children to be imported
247
208
        for child_name, (child_path, child_member, grandchildren) in \
248
 
                children.items():
 
209
                children.iteritems():
249
210
            # Using self.__class__, so that children get children classes
250
211
            # instantiated. (This helps with instrumented tests)
251
212
            cls = object.__getattribute__(self, '__class__')
284
245
 
285
246
    def _convert_imports(self, scope):
286
247
        # Now convert the map into a set of imports
287
 
        for name, info in self.imports.items():
 
248
        for name, info in self.imports.iteritems():
288
249
            self._lazy_import_class(scope, name=name, module_path=info[0],
289
250
                                    member=info[1], children=info[2])
290
251
 
296
257
            elif line.startswith('from '):
297
258
                self._convert_from_str(line)
298
259
            else:
299
 
                raise InvalidImportLine(
300
 
                    line, "doesn't start with 'import ' or 'from '")
 
260
                raise errors.InvalidImportLine(line,
 
261
                    "doesn't start with 'import ' or 'from '")
301
262
 
302
263
    def _convert_import_str(self, import_str):
303
264
        """This converts a import string into an import map.
322
283
                name = as_hunks[1].strip()
323
284
                module_path = as_hunks[0].strip().split('.')
324
285
                if name in self.imports:
325
 
                    raise ImportNameCollision(name)
326
 
                if not module_path[0]:
327
 
                    raise ImportError(path)
 
286
                    raise errors.ImportNameCollision(name)
328
287
                # No children available in 'import foo as bar'
329
288
                self.imports[name] = (module_path, None, {})
330
289
            else:
331
290
                # Now we need to handle
332
291
                module_path = path.split('.')
333
292
                name = module_path[0]
334
 
                if not name:
335
 
                    raise ImportError(path)
336
293
                if name not in self.imports:
337
294
                    # This is a new import that we haven't seen before
338
295
                    module_def = ([name], None, {})
364
321
 
365
322
        from_module_path = from_module.split('.')
366
323
 
367
 
        if not from_module_path[0]:
368
 
            raise ImportError(from_module)
369
 
 
370
324
        for path in import_list.split(','):
371
325
            path = path.strip()
372
326
            if not path:
381
335
            else:
382
336
                name = module = path
383
337
            if name in self.imports:
384
 
                raise ImportNameCollision(name)
 
338
                raise errors.ImportNameCollision(name)
385
339
            self.imports[name] = (from_module_path, module, {})
386
340
 
387
341
    def _canonicalize_import_text(self, text):
392
346
        """
393
347
        out = []
394
348
        cur = None
 
349
        continuing = False
395
350
 
396
351
        for line in text.split('\n'):
397
352
            line = line.strip()
413
368
                else:
414
369
                    out.append(line.replace('(', '').replace(')', ''))
415
370
        if cur is not None:
416
 
            raise InvalidImportLine(cur, 'Unmatched parenthesis')
 
371
            raise errors.InvalidImportLine(cur, 'Unmatched parenthesis')
417
372
        return out
418
373
 
419
374
 
422
377
 
423
378
    This is typically used as something like::
424
379
 
425
 
        from breezy.lazy_import import lazy_import
 
380
        from brzlib.lazy_import import lazy_import
426
381
        lazy_import(globals(), '''
427
 
        from breezy import (
 
382
        from brzlib import (
428
383
            foo,
429
384
            bar,
430
385
            baz,
431
386
            )
432
 
        import breezy.branch
433
 
        import breezy.transport
 
387
        import brzlib.branch
 
388
        import brzlib.transport
434
389
        ''')
435
390
 
436
 
    Then 'foo, bar, baz' and 'breezy' will exist as lazy-loaded
 
391
    Then 'foo, bar, baz' and 'brzlib' will exist as lazy-loaded
437
392
    objects which will be replaced with a real object on first use.
438
393
 
439
394
    In general, it is best to only load modules in this way. This is
444
399
    # This is just a helper around ImportProcessor.lazy_import
445
400
    proc = ImportProcessor(lazy_import_class=lazy_import_class)
446
401
    return proc.lazy_import(scope, text)
 
402
 
 
403
 
 
404
# The only module that this module depends on is 'brzlib.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 brzlib import errors
 
410
""")