/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
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
"""Test the lazy_import functionality."""
18
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
19
import os
20
import sys
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
21
22
from bzrlib import (
1996.1.16 by John Arbash Meinel
Raise an exception when ScopeReplacer has been misused
23
    errors,
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
24
    lazy_import,
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
25
    osutils,
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
26
    )
27
from bzrlib.tests import TestCase, TestCaseInTempDir
28
29
30
class InstrumentedReplacer(lazy_import.ScopeReplacer):
31
    """Track what actions are done"""
32
33
    @staticmethod
34
    def use_actions(actions):
35
        InstrumentedReplacer.actions = actions
36
37
    def _replace(self):
38
        InstrumentedReplacer.actions.append('_replace')
39
        return lazy_import.ScopeReplacer._replace(self)
40
41
    def __getattribute__(self, attr):
42
        InstrumentedReplacer.actions.append(('__getattribute__', attr))
43
        return lazy_import.ScopeReplacer.__getattribute__(self, attr)
44
45
    def __call__(self, *args, **kwargs):
46
        InstrumentedReplacer.actions.append(('__call__', args, kwargs))
47
        return lazy_import.ScopeReplacer.__call__(self, *args, **kwargs)
48
49
1996.1.3 by John Arbash Meinel
Basic single-level imports work
50
class InstrumentedImportReplacer(lazy_import.ImportReplacer):
51
52
    @staticmethod
53
    def use_actions(actions):
54
        InstrumentedImportReplacer.actions = actions
55
56
    def _import(self, scope, name):
57
        InstrumentedImportReplacer.actions.append(('_import', name))
58
        return lazy_import.ImportReplacer._import(self, scope, name)
59
60
    def _replace(self):
61
        InstrumentedImportReplacer.actions.append('_replace')
62
        return lazy_import.ScopeReplacer._replace(self)
63
64
    def __getattribute__(self, attr):
65
        InstrumentedImportReplacer.actions.append(('__getattribute__', attr))
66
        return lazy_import.ScopeReplacer.__getattribute__(self, attr)
67
68
    def __call__(self, *args, **kwargs):
69
        InstrumentedImportReplacer.actions.append(('__call__', args, kwargs))
70
        return lazy_import.ScopeReplacer.__call__(self, *args, **kwargs)
71
72
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
73
class TestClass(object):
74
    """Just a simple test class instrumented for the test cases"""
75
76
    class_member = 'class_member'
77
78
    @staticmethod
79
    def use_actions(actions):
80
        TestClass.actions = actions
81
82
    def __init__(self):
83
        TestClass.actions.append('init')
84
85
    def foo(self, x):
86
        TestClass.actions.append(('foo', x))
87
        return 'foo'
88
89
90
class TestScopeReplacer(TestCase):
91
    """Test the ability of the replacer to put itself into the correct scope.
92
93
    In these tests we use the global scope, because we cannot replace
94
    variables in the local scope. This means that we need to be careful
95
    and not have the replacing objects use the same name, or we would
96
    get collisions.
97
    """
98
2399.1.11 by John Arbash Meinel
Update lazy_import with tests for the new '_should_proxy' variable.
99
    def setUp(self):
100
        TestCase.setUp(self)
101
        # These tests assume we will not be proxying, so make sure proxying is
102
        # disabled.
103
        orig_proxy = lazy_import.ScopeReplacer._should_proxy
104
        def restore():
105
            lazy_import.ScopeReplacer._should_proxy = orig_proxy
106
        lazy_import.ScopeReplacer._should_proxy = False
107
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
108
    def test_object(self):
1996.1.27 by John Arbash Meinel
Add a test for side-effects from using ScopeReplacer
109
        """ScopeReplacer can create an instance in local scope.
110
        
111
        An object should appear in globals() by constructing a ScopeReplacer,
112
        and it will be replaced with the real object upon the first request.
113
        """
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
114
        actions = []
115
        InstrumentedReplacer.use_actions(actions)
116
        TestClass.use_actions(actions)
117
118
        def factory(replacer, scope, name):
119
            actions.append('factory')
120
            return TestClass()
121
122
        try:
123
            test_obj1
124
        except NameError:
125
            # test_obj1 shouldn't exist yet
126
            pass
127
        else:
128
            self.fail('test_obj1 was not supposed to exist yet')
129
1996.1.27 by John Arbash Meinel
Add a test for side-effects from using ScopeReplacer
130
        orig_globals = set(globals().keys())
131
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
132
        InstrumentedReplacer(scope=globals(), name='test_obj1',
133
                             factory=factory)
134
1996.1.27 by John Arbash Meinel
Add a test for side-effects from using ScopeReplacer
135
        new_globals = set(globals().keys())
136
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
137
        # We can't use isinstance() because that uses test_obj1.__class__
138
        # and that goes through __getattribute__ which would activate
139
        # the replacement
140
        self.assertEqual(InstrumentedReplacer,
141
                         object.__getattribute__(test_obj1, '__class__'))
142
        self.assertEqual('foo', test_obj1.foo(1))
143
        self.assertIsInstance(test_obj1, TestClass)
144
        self.assertEqual('foo', test_obj1.foo(2))
145
        self.assertEqual([('__getattribute__', 'foo'),
146
                          '_replace',
147
                          'factory',
148
                          'init',
149
                          ('foo', 1),
150
                          ('foo', 2),
151
                         ], actions)
152
1551.18.22 by Aaron Bentley
Fix exception when ScopeReplacer is assigned to before retrieving any members
153
    def test_setattr_replaces(self):
154
        """ScopeReplacer can create an instance in local scope.
155
156
        An object should appear in globals() by constructing a ScopeReplacer,
157
        and it will be replaced with the real object upon the first request.
158
        """
159
        def factory(replacer, scope, name):
160
            return TestClass()
161
        try:
162
            test_obj6
163
        except NameError:
164
            # test_obj6 shouldn't exist yet
165
            pass
166
        else:
167
            self.fail('test_obj6 was not supposed to exist yet')
168
169
        orig_globals = set(globals().keys())
170
171
        lazy_import.ScopeReplacer(scope=globals(), name='test_obj6',
172
                                  factory=factory)
173
174
        new_globals = set(globals().keys())
175
176
        # We can't use isinstance() because that uses test_obj6.__class__
177
        # and that goes through __getattribute__ which would activate
178
        # the replacement
179
        self.assertEqual(lazy_import.ScopeReplacer,
180
                         object.__getattribute__(test_obj6, '__class__'))
181
        test_obj6.bar = 'test'
182
        self.assertNotEqual(lazy_import.ScopeReplacer,
183
                            object.__getattribute__(test_obj6, '__class__'))
1551.18.23 by Aaron Bentley
Ensure that the attribute is actually set
184
        self.assertEqual('test', test_obj6.bar)
1551.18.22 by Aaron Bentley
Fix exception when ScopeReplacer is assigned to before retrieving any members
185
1996.1.27 by John Arbash Meinel
Add a test for side-effects from using ScopeReplacer
186
    def test_replace_side_effects(self):
187
        """Creating a new object should only create one entry in globals.
188
189
        And only that entry even after replacement.
190
        """
191
        try:
192
            test_scope1
193
        except NameError:
194
            # test_scope1 shouldn't exist yet
195
            pass
196
        else:
197
            self.fail('test_scope1 was not supposed to exist yet')
198
199
        # ignore the logged actions
200
        TestClass.use_actions([])
201
202
        def factory(replacer, scope, name):
203
            return TestClass()
204
205
        orig_globals = set(globals().keys())
206
207
        lazy_import.ScopeReplacer(scope=globals(), name='test_scope1',
208
                                  factory=factory)
209
210
        new_globals = set(globals().keys())
211
212
        self.assertEqual(lazy_import.ScopeReplacer,
213
                         object.__getattribute__(test_scope1, '__class__'))
214
        self.assertEqual('foo', test_scope1.foo(1))
215
        self.assertIsInstance(test_scope1, TestClass)
216
217
        final_globals = set(globals().keys())
218
219
        self.assertEqual(set(['test_scope1']), new_globals - orig_globals)
220
        self.assertEqual(set(), orig_globals - new_globals)
221
        self.assertEqual(set(), final_globals - new_globals)
222
        self.assertEqual(set(), new_globals - final_globals)
223
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
224
    def test_class(self):
225
        actions = []
226
        InstrumentedReplacer.use_actions(actions)
227
        TestClass.use_actions(actions)
228
229
        def factory(replacer, scope, name):
230
            actions.append('factory')
231
            return TestClass
232
233
        try:
234
            test_class1
235
        except NameError:
236
            # test_class2 shouldn't exist yet
237
            pass
238
        else:
239
            self.fail('test_class1 was not supposed to exist yet')
240
241
        InstrumentedReplacer(scope=globals(), name='test_class1',
242
                             factory=factory)
243
244
        self.assertEqual('class_member', test_class1.class_member)
245
        self.assertEqual(test_class1, TestClass)
246
        self.assertEqual([('__getattribute__', 'class_member'),
247
                          '_replace',
248
                          'factory',
249
                         ], actions)
250
251
    def test_call_class(self):
252
        actions = []
253
        InstrumentedReplacer.use_actions(actions)
254
        TestClass.use_actions(actions)
255
256
        def factory(replacer, scope, name):
257
            actions.append('factory')
258
            return TestClass
259
260
        try:
261
            test_class2
262
        except NameError:
263
            # test_class2 shouldn't exist yet
264
            pass
265
        else:
266
            self.fail('test_class2 was not supposed to exist yet')
267
268
        InstrumentedReplacer(scope=globals(), name='test_class2',
269
                             factory=factory)
270
271
        self.failIf(test_class2 is TestClass)
272
        obj = test_class2()
273
        self.assertIs(test_class2, TestClass)
274
        self.assertIsInstance(obj, TestClass)
275
        self.assertEqual('class_member', obj.class_member)
276
        self.assertEqual([('__call__', (), {}),
277
                          '_replace',
278
                          'factory',
279
                          'init',
280
                         ], actions)
281
282
    def test_call_func(self):
283
        actions = []
284
        InstrumentedReplacer.use_actions(actions)
285
286
        def func(a, b, c=None):
287
            actions.append('func')
288
            return (a, b, c)
289
290
        def factory(replacer, scope, name):
291
            actions.append('factory')
292
            return func
293
294
        try:
295
            test_func1
296
        except NameError:
297
            # test_func1 shouldn't exist yet
298
            pass
299
        else:
300
            self.fail('test_func1 was not supposed to exist yet')
301
        InstrumentedReplacer(scope=globals(), name='test_func1',
302
                             factory=factory)
303
304
        self.failIf(test_func1 is func)
305
        val = test_func1(1, 2, c='3')
306
        self.assertIs(test_func1, func)
307
308
        self.assertEqual((1,2,'3'), val)
309
        self.assertEqual([('__call__', (1,2), {'c':'3'}),
310
                          '_replace',
311
                          'factory',
312
                          'func',
313
                         ], actions)
314
1996.1.16 by John Arbash Meinel
Raise an exception when ScopeReplacer has been misused
315
    def test_other_variable(self):
316
        """Test when a ScopeReplacer is assigned to another variable.
317
318
        This test could be updated if we find a way to trap '=' rather
319
        than just giving a belated exception.
320
        ScopeReplacer only knows about the variable it was created as,
321
        so until the object is replaced, it is illegal to pass it to
322
        another variable. (Though discovering this may take a while)
323
        """
324
        actions = []
325
        InstrumentedReplacer.use_actions(actions)
326
        TestClass.use_actions(actions)
327
328
        def factory(replacer, scope, name):
329
            actions.append('factory')
330
            return TestClass()
331
332
        try:
333
            test_obj2
334
        except NameError:
335
            # test_obj2 shouldn't exist yet
336
            pass
337
        else:
338
            self.fail('test_obj2 was not supposed to exist yet')
339
340
        InstrumentedReplacer(scope=globals(), name='test_obj2',
341
                             factory=factory)
342
343
        self.assertEqual(InstrumentedReplacer,
344
                         object.__getattribute__(test_obj2, '__class__'))
345
        # This is technically not allowed, but we don't have a way to
346
        # test it until later.
347
        test_obj3 = test_obj2
348
        self.assertEqual(InstrumentedReplacer,
349
                         object.__getattribute__(test_obj2, '__class__'))
350
        self.assertEqual(InstrumentedReplacer,
351
                         object.__getattribute__(test_obj3, '__class__'))
352
        
353
        # The first use of the alternate variable causes test_obj2 to
354
        # be replaced.
355
        self.assertEqual('foo', test_obj3.foo(1))
356
        # test_obj2 has been replaced, but the ScopeReplacer has no
357
        # idea of test_obj3
358
        self.assertEqual(TestClass,
359
                         object.__getattribute__(test_obj2, '__class__'))
360
        self.assertEqual(InstrumentedReplacer,
361
                         object.__getattribute__(test_obj3, '__class__'))
362
        # We should be able to access test_obj2 attributes normally
363
        self.assertEqual('foo', test_obj2.foo(2))
364
        self.assertEqual('foo', test_obj2.foo(3))
365
366
        # However, the next access on test_obj3 should raise an error
367
        # because only now are we able to detect the problem.
368
        self.assertRaises(errors.IllegalUseOfScopeReplacer,
369
                          getattr, test_obj3, 'foo')
370
        
2399.1.11 by John Arbash Meinel
Update lazy_import with tests for the new '_should_proxy' variable.
371
        self.assertEqual([('__getattribute__', 'foo'),
372
                          '_replace',
373
                          'factory',
374
                          'init',
375
                          ('foo', 1),
376
                          ('foo', 2),
377
                          ('foo', 3),
378
                          ('__getattribute__', 'foo'),
379
                          '_replace',
380
                         ], actions)
381
382
    def test_enable_proxying(self):
383
        """Test that we can allow ScopeReplacer to proxy."""
384
        actions = []
385
        InstrumentedReplacer.use_actions(actions)
386
        TestClass.use_actions(actions)
387
388
        def factory(replacer, scope, name):
389
            actions.append('factory')
390
            return TestClass()
391
392
        try:
393
            test_obj4
394
        except NameError:
395
            # test_obj4 shouldn't exist yet
396
            pass
397
        else:
398
            self.fail('test_obj4 was not supposed to exist yet')
399
400
        lazy_import.ScopeReplacer._should_proxy = True
401
        InstrumentedReplacer(scope=globals(), name='test_obj4',
402
                             factory=factory)
403
404
        self.assertEqual(InstrumentedReplacer,
405
                         object.__getattribute__(test_obj4, '__class__'))
406
        test_obj5 = test_obj4
407
        self.assertEqual(InstrumentedReplacer,
408
                         object.__getattribute__(test_obj4, '__class__'))
409
        self.assertEqual(InstrumentedReplacer,
410
                         object.__getattribute__(test_obj5, '__class__'))
411
412
        # The first use of the alternate variable causes test_obj2 to
413
        # be replaced.
414
        self.assertEqual('foo', test_obj4.foo(1))
415
        self.assertEqual(TestClass,
416
                         object.__getattribute__(test_obj4, '__class__'))
417
        self.assertEqual(InstrumentedReplacer,
418
                         object.__getattribute__(test_obj5, '__class__'))
419
        # We should be able to access test_obj4 attributes normally
420
        self.assertEqual('foo', test_obj4.foo(2))
421
        # because we enabled proxying, test_obj5 can access its members as well
422
        self.assertEqual('foo', test_obj5.foo(3))
423
        self.assertEqual('foo', test_obj5.foo(4))
424
425
        # However, it cannot be replaced by the ScopeReplacer
426
        self.assertEqual(InstrumentedReplacer,
427
                         object.__getattribute__(test_obj5, '__class__'))
428
429
        self.assertEqual([('__getattribute__', 'foo'),
430
                          '_replace',
431
                          'factory',
432
                          'init',
433
                          ('foo', 1),
434
                          ('foo', 2),
435
                          ('__getattribute__', 'foo'),
436
                          ('foo', 3),
437
                          ('__getattribute__', 'foo'),
438
                          ('foo', 4),
1996.1.16 by John Arbash Meinel
Raise an exception when ScopeReplacer has been misused
439
                         ], actions)
440
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
441
1996.1.8 by John Arbash Meinel
Split up TestImportReplacer into a helper class
442
class ImportReplacerHelper(TestCaseInTempDir):
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
443
    """Test the ability to have a lazily imported module or object"""
444
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
445
    def setUp(self):
446
        TestCaseInTempDir.setUp(self)
447
        self.create_modules()
448
        base_path = self.test_dir + '/base'
449
1996.1.3 by John Arbash Meinel
Basic single-level imports work
450
        self.actions = []
451
        InstrumentedImportReplacer.use_actions(self.actions)
452
453
        original_import = __import__
454
        def instrumented_import(mod, scope1, scope2, fromlist):
455
            self.actions.append(('import', mod, fromlist))
456
            return original_import(mod, scope1, scope2, fromlist)
457
458
        def cleanup():
459
            if base_path in sys.path:
460
                sys.path.remove(base_path)
461
            __builtins__['__import__'] = original_import
462
        self.addCleanup(cleanup)
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
463
        sys.path.append(base_path)
1996.1.3 by John Arbash Meinel
Basic single-level imports work
464
        __builtins__['__import__'] = instrumented_import
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
465
466
    def create_modules(self):
467
        """Create some random modules to be imported.
468
469
        Each entry has a random suffix, and the full names are saved
470
471
        These are setup as follows:
472
         base/ <= used to ensure not in default search path
473
            root-XXX/
474
                __init__.py <= This will contain var1, func1
475
                mod-XXX.py <= This will contain var2, func2
476
                sub-XXX/
477
                    __init__.py <= Contains var3, func3
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
478
                    submoda-XXX.py <= contains var4, func4
479
                    submodb-XXX.py <= containse var5, func5
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
480
        """
481
        rand_suffix = osutils.rand_chars(4)
482
        root_name = 'root_' + rand_suffix
483
        mod_name = 'mod_' + rand_suffix
484
        sub_name = 'sub_' + rand_suffix
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
485
        submoda_name = 'submoda_' + rand_suffix
486
        submodb_name = 'submodb_' + rand_suffix
487
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
488
        os.mkdir('base')
489
        root_path = osutils.pathjoin('base', root_name)
490
        os.mkdir(root_path)
491
        root_init = osutils.pathjoin(root_path, '__init__.py')
492
        f = open(osutils.pathjoin(root_path, '__init__.py'), 'wb')
493
        try:
494
            f.write('var1 = 1\ndef func1(a):\n  return a\n')
495
        finally:
496
            f.close()
497
        mod_path = osutils.pathjoin(root_path, mod_name + '.py')
498
        f = open(mod_path, 'wb')
499
        try:
500
            f.write('var2 = 2\ndef func2(a):\n  return a\n')
501
        finally:
502
            f.close()
503
504
        sub_path = osutils.pathjoin(root_path, sub_name)
505
        os.mkdir(sub_path)
506
        f = open(osutils.pathjoin(sub_path, '__init__.py'), 'wb')
507
        try:
508
            f.write('var3 = 3\ndef func3(a):\n  return a\n')
509
        finally:
510
            f.close()
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
511
        submoda_path = osutils.pathjoin(sub_path, submoda_name + '.py')
512
        f = open(submoda_path, 'wb')
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
513
        try:
514
            f.write('var4 = 4\ndef func4(a):\n  return a\n')
515
        finally:
516
            f.close()
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
517
        submodb_path = osutils.pathjoin(sub_path, submodb_name + '.py')
518
        f = open(submodb_path, 'wb')
519
        try:
520
            f.write('var5 = 5\ndef func5(a):\n  return a\n')
521
        finally:
522
            f.close()
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
523
        self.root_name = root_name
524
        self.mod_name = mod_name
525
        self.sub_name = sub_name
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
526
        self.submoda_name = submoda_name
527
        self.submodb_name = submodb_name
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
528
1996.1.8 by John Arbash Meinel
Split up TestImportReplacer into a helper class
529
530
class TestImportReplacerHelper(ImportReplacerHelper):
531
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
532
    def test_basic_import(self):
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
533
        """Test that a real import of these modules works"""
1996.1.3 by John Arbash Meinel
Basic single-level imports work
534
        sub_mod_path = '.'.join([self.root_name, self.sub_name,
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
535
                                  self.submoda_name])
1996.1.3 by John Arbash Meinel
Basic single-level imports work
536
        root = __import__(sub_mod_path, globals(), locals(), [])
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
537
        self.assertEqual(1, root.var1)
538
        self.assertEqual(3, getattr(root, self.sub_name).var3)
539
        self.assertEqual(4, getattr(getattr(root, self.sub_name),
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
540
                                    self.submoda_name).var4)
1996.1.3 by John Arbash Meinel
Basic single-level imports work
541
542
        mod_path = '.'.join([self.root_name, self.mod_name])
543
        root = __import__(mod_path, globals(), locals(), [])
544
        self.assertEqual(2, getattr(root, self.mod_name).var2)
545
546
        self.assertEqual([('import', sub_mod_path, []),
547
                          ('import', mod_path, []),
548
                         ], self.actions)
549
1996.1.8 by John Arbash Meinel
Split up TestImportReplacer into a helper class
550
551
class TestImportReplacer(ImportReplacerHelper):
552
1996.1.3 by John Arbash Meinel
Basic single-level imports work
553
    def test_import_root(self):
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
554
        """Test 'import root-XXX as root1'"""
1996.1.3 by John Arbash Meinel
Basic single-level imports work
555
        try:
556
            root1
557
        except NameError:
558
            # root1 shouldn't exist yet
559
            pass
560
        else:
561
            self.fail('root1 was not supposed to exist yet')
562
563
        # This should replicate 'import root-xxyyzz as root1'
564
        InstrumentedImportReplacer(scope=globals(), name='root1',
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
565
                                   module_path=[self.root_name],
1996.1.15 by John Arbash Meinel
Everything is now hooked up
566
                                   member=None, children={})
1996.1.3 by John Arbash Meinel
Basic single-level imports work
567
568
        self.assertEqual(InstrumentedImportReplacer,
569
                         object.__getattribute__(root1, '__class__'))
570
        self.assertEqual(1, root1.var1)
571
        self.assertEqual('x', root1.func1('x'))
572
573
        self.assertEqual([('__getattribute__', 'var1'),
574
                          '_replace',
575
                          ('_import', 'root1'),
576
                          ('import', self.root_name, []),
577
                         ], self.actions)
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
578
579
    def test_import_mod(self):
580
        """Test 'import root-XXX.mod-XXX as mod2'"""
581
        try:
582
            mod1
583
        except NameError:
584
            # mod1 shouldn't exist yet
585
            pass
586
        else:
587
            self.fail('mod1 was not supposed to exist yet')
588
589
        mod_path = self.root_name + '.' + self.mod_name
590
        InstrumentedImportReplacer(scope=globals(), name='mod1',
591
                                   module_path=[self.root_name, self.mod_name],
1996.1.15 by John Arbash Meinel
Everything is now hooked up
592
                                   member=None, children={})
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
593
594
        self.assertEqual(InstrumentedImportReplacer,
595
                         object.__getattribute__(mod1, '__class__'))
596
        self.assertEqual(2, mod1.var2)
597
        self.assertEqual('y', mod1.func2('y'))
598
599
        self.assertEqual([('__getattribute__', 'var2'),
600
                          '_replace',
601
                          ('_import', 'mod1'),
602
                          ('import', mod_path, []),
603
                         ], self.actions)
604
605
    def test_import_mod_from_root(self):
606
        """Test 'from root-XXX import mod-XXX as mod2'"""
607
        try:
608
            mod2
609
        except NameError:
610
            # mod2 shouldn't exist yet
611
            pass
612
        else:
613
            self.fail('mod2 was not supposed to exist yet')
614
615
        InstrumentedImportReplacer(scope=globals(), name='mod2',
616
                                   module_path=[self.root_name],
1996.1.15 by John Arbash Meinel
Everything is now hooked up
617
                                   member=self.mod_name, children={})
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
618
619
        self.assertEqual(InstrumentedImportReplacer,
620
                         object.__getattribute__(mod2, '__class__'))
621
        self.assertEqual(2, mod2.var2)
622
        self.assertEqual('y', mod2.func2('y'))
623
624
        self.assertEqual([('__getattribute__', 'var2'),
625
                          '_replace',
626
                          ('_import', 'mod2'),
627
                          ('import', self.root_name, [self.mod_name]),
628
                         ], self.actions)
1996.1.5 by John Arbash Meinel
Test that we can lazy import a module, and its children
629
630
    def test_import_root_and_mod(self):
631
        """Test 'import root-XXX.mod-XXX' remapping both to root3.mod3"""
632
        try:
633
            root3
634
        except NameError:
635
            # root3 shouldn't exist yet
636
            pass
637
        else:
638
            self.fail('root3 was not supposed to exist yet')
639
640
        InstrumentedImportReplacer(scope=globals(),
641
            name='root3', module_path=[self.root_name], member=None,
1996.1.15 by John Arbash Meinel
Everything is now hooked up
642
            children={'mod3':([self.root_name, self.mod_name], None, {})})
1996.1.5 by John Arbash Meinel
Test that we can lazy import a module, and its children
643
644
        # So 'root3' should be a lazy import
645
        # and once it is imported, mod3 should also be lazy until
646
        # actually accessed.
647
        self.assertEqual(InstrumentedImportReplacer,
648
                         object.__getattribute__(root3, '__class__'))
649
        self.assertEqual(1, root3.var1)
650
651
        # There is a mod3 member, but it is also lazy
652
        self.assertEqual(InstrumentedImportReplacer,
653
                         object.__getattribute__(root3.mod3, '__class__'))
654
        self.assertEqual(2, root3.mod3.var2)
655
656
        mod_path = self.root_name + '.' + self.mod_name
657
        self.assertEqual([('__getattribute__', 'var1'),
658
                          '_replace',
659
                          ('_import', 'root3'),
660
                          ('import', self.root_name, []),
661
                          ('__getattribute__', 'var2'),
662
                          '_replace',
663
                          ('_import', 'mod3'),
664
                          ('import', mod_path, []),
665
                         ], self.actions)
1996.1.6 by John Arbash Meinel
Test that we can add more children to the existing lazy object
666
667
    def test_import_root_and_root_mod(self):
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
668
        """Test that 'import root, root.mod' can be done.
669
670
        The second import should re-use the first one, and just add
671
        children to be imported.
672
        """
1996.1.6 by John Arbash Meinel
Test that we can add more children to the existing lazy object
673
        try:
674
            root4
675
        except NameError:
676
            # root4 shouldn't exist yet
677
            pass
678
        else:
679
            self.fail('root4 was not supposed to exist yet')
680
681
        InstrumentedImportReplacer(scope=globals(),
682
            name='root4', module_path=[self.root_name], member=None,
1996.1.15 by John Arbash Meinel
Everything is now hooked up
683
            children={})
1996.1.6 by John Arbash Meinel
Test that we can add more children to the existing lazy object
684
685
        # So 'root4' should be a lazy import
686
        self.assertEqual(InstrumentedImportReplacer,
687
                         object.__getattribute__(root4, '__class__'))
688
689
        # Lets add a new child to be imported on demand
690
        # This syntax of using object.__getattribute__ is the correct method
691
        # for accessing the _import_replacer_children member
692
        children = object.__getattribute__(root4, '_import_replacer_children')
1996.1.15 by John Arbash Meinel
Everything is now hooked up
693
        children['mod4'] = ([self.root_name, self.mod_name], None, {})
1996.1.6 by John Arbash Meinel
Test that we can add more children to the existing lazy object
694
695
        # Accessing root4.mod4 should import root, but mod should stay lazy
696
        self.assertEqual(InstrumentedImportReplacer,
697
                         object.__getattribute__(root4.mod4, '__class__'))
698
        self.assertEqual(2, root4.mod4.var2)
699
700
        mod_path = self.root_name + '.' + self.mod_name
701
        self.assertEqual([('__getattribute__', 'mod4'),
702
                          '_replace',
703
                          ('_import', 'root4'),
704
                          ('import', self.root_name, []),
705
                          ('__getattribute__', 'var2'),
706
                          '_replace',
707
                          ('_import', 'mod4'),
708
                          ('import', mod_path, []),
709
                         ], self.actions)
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
710
711
    def test_import_root_sub_submod(self):
712
        """Test import root.mod, root.sub.submoda, root.sub.submodb
713
        root should be a lazy import, with multiple children, who also
714
        have children to be imported.
715
        And when root is imported, the children should be lazy, and
716
        reuse the intermediate lazy object.
717
        """
718
        try:
719
            root5
720
        except NameError:
1996.1.15 by John Arbash Meinel
Everything is now hooked up
721
            # root5 shouldn't exist yet
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
722
            pass
723
        else:
724
            self.fail('root5 was not supposed to exist yet')
725
726
        InstrumentedImportReplacer(scope=globals(),
727
            name='root5', module_path=[self.root_name], member=None,
1996.1.15 by John Arbash Meinel
Everything is now hooked up
728
            children={'mod5':([self.root_name, self.mod_name], None, {}),
729
                      'sub5':([self.root_name, self.sub_name], None,
730
                            {'submoda5':([self.root_name, self.sub_name,
731
                                         self.submoda_name], None, {}),
732
                             'submodb5':([self.root_name, self.sub_name,
733
                                          self.submodb_name], None, {})
734
                            }),
735
                     })
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
736
737
        # So 'root5' should be a lazy import
738
        self.assertEqual(InstrumentedImportReplacer,
739
                         object.__getattribute__(root5, '__class__'))
740
741
        # Accessing root5.mod5 should import root, but mod should stay lazy
742
        self.assertEqual(InstrumentedImportReplacer,
743
                         object.__getattribute__(root5.mod5, '__class__'))
744
        # root5.sub5 should still be lazy, but not re-import root5
745
        self.assertEqual(InstrumentedImportReplacer,
746
                         object.__getattribute__(root5.sub5, '__class__'))
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
747
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
748
        # Accessing root5.sub5.submoda5 should import sub5, but not either
749
        # of the sub objects (they should be available as lazy objects
750
        self.assertEqual(InstrumentedImportReplacer,
751
                     object.__getattribute__(root5.sub5.submoda5, '__class__'))
752
        self.assertEqual(InstrumentedImportReplacer,
753
                     object.__getattribute__(root5.sub5.submodb5, '__class__'))
754
755
        # This should import mod5
756
        self.assertEqual(2, root5.mod5.var2)
757
        # These should import submoda5 and submodb5
758
        self.assertEqual(4, root5.sub5.submoda5.var4)
759
        self.assertEqual(5, root5.sub5.submodb5.var5)
760
761
        mod_path = self.root_name + '.' + self.mod_name
762
        sub_path = self.root_name + '.' + self.sub_name
763
        submoda_path = sub_path + '.' + self.submoda_name
764
        submodb_path = sub_path + '.' + self.submodb_name
765
766
        self.assertEqual([('__getattribute__', 'mod5'),
767
                          '_replace',
768
                          ('_import', 'root5'),
769
                          ('import', self.root_name, []),
770
                          ('__getattribute__', 'submoda5'),
771
                          '_replace',
772
                          ('_import', 'sub5'),
773
                          ('import', sub_path, []),
774
                          ('__getattribute__', 'var2'),
775
                          '_replace',
776
                          ('_import', 'mod5'),
777
                          ('import', mod_path, []),
778
                          ('__getattribute__', 'var4'),
779
                          '_replace',
780
                          ('_import', 'submoda5'),
781
                          ('import', submoda_path, []),
782
                          ('__getattribute__', 'var5'),
783
                          '_replace',
784
                          ('_import', 'submodb5'),
785
                          ('import', submodb_path, []),
786
                         ], self.actions)
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
787
788
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
789
class TestConvertImportToMap(TestCase):
790
    """Directly test the conversion from import strings to maps"""
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
791
1996.1.13 by John Arbash Meinel
small test updates
792
    def check(self, expected, import_strings):
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
793
        proc = lazy_import.ImportProcessor()
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
794
        for import_str in import_strings:
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
795
            proc._convert_import_str(import_str)
796
        self.assertEqual(expected, proc.imports,
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
797
                         'Import of %r was not converted correctly'
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
798
                         ' %s != %s' % (import_strings, expected,
799
                                        proc.imports))
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
800
801
    def test_import_one(self):
1996.1.13 by John Arbash Meinel
small test updates
802
        self.check({'one':(['one'], None, {}),
803
                   }, ['import one'])
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
804
805
    def test_import_one_two(self):
806
        one_two_map = {'one':(['one'], None,
807
                              {'two':(['one', 'two'], None, {}),
808
                              }),
809
                      }
1996.1.13 by John Arbash Meinel
small test updates
810
        self.check(one_two_map, ['import one.two'])
811
        self.check(one_two_map, ['import one, one.two'])
812
        self.check(one_two_map, ['import one', 'import one.two'])
813
        self.check(one_two_map, ['import one.two', 'import one'])
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
814
815
    def test_import_one_two_three(self):
816
        one_two_three_map = {
817
            'one':(['one'], None,
818
                   {'two':(['one', 'two'], None,
819
                           {'three':(['one', 'two', 'three'], None, {}),
820
                           }),
821
                   }),
822
        }
1996.1.13 by John Arbash Meinel
small test updates
823
        self.check(one_two_three_map, ['import one.two.three'])
824
        self.check(one_two_three_map, ['import one, one.two.three'])
825
        self.check(one_two_three_map, ['import one',
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
826
                                              'import one.two.three'])
1996.1.13 by John Arbash Meinel
small test updates
827
        self.check(one_two_three_map, ['import one.two.three',
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
828
                                              'import one'])
829
830
    def test_import_one_as_x(self):
1996.1.13 by John Arbash Meinel
small test updates
831
        self.check({'x':(['one'], None, {}),
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
832
                          }, ['import one as x'])
833
834
    def test_import_one_two_as_x(self):
1996.1.13 by John Arbash Meinel
small test updates
835
        self.check({'x':(['one', 'two'], None, {}),
836
                   }, ['import one.two as x'])
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
837
838
    def test_import_mixed(self):
839
        mixed = {'x':(['one', 'two'], None, {}),
840
                 'one':(['one'], None,
841
                       {'two':(['one', 'two'], None, {}),
842
                       }),
843
                }
1996.1.13 by John Arbash Meinel
small test updates
844
        self.check(mixed, ['import one.two as x, one.two'])
845
        self.check(mixed, ['import one.two as x', 'import one.two'])
846
        self.check(mixed, ['import one.two', 'import one.two as x'])
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
847
848
    def test_import_with_as(self):
1996.1.13 by John Arbash Meinel
small test updates
849
        self.check({'fast':(['fast'], None, {})}, ['import fast'])
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
850
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
851
852
class TestFromToMap(TestCase):
853
    """Directly test the conversion of 'from foo import bar' syntax"""
854
855
    def check_result(self, expected, from_strings):
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
856
        proc = lazy_import.ImportProcessor()
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
857
        for from_str in from_strings:
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
858
            proc._convert_from_str(from_str)
859
        self.assertEqual(expected, proc.imports,
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
860
                         'Import of %r was not converted correctly'
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
861
                         ' %s != %s' % (from_strings, expected, proc.imports))
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
862
863
    def test_from_one_import_two(self):
864
        self.check_result({'two':(['one'], 'two', {})},
865
                          ['from one import two'])
866
867
    def test_from_one_import_two_as_three(self):
868
        self.check_result({'three':(['one'], 'two', {})},
869
                          ['from one import two as three'])
870
871
    def test_from_one_import_two_three(self):
872
        two_three_map = {'two':(['one'], 'two', {}),
873
                         'three':(['one'], 'three', {}),
874
                        }
875
        self.check_result(two_three_map,
876
                          ['from one import two, three'])
877
        self.check_result(two_three_map,
878
                          ['from one import two',
879
                           'from one import three'])
880
881
    def test_from_one_two_import_three(self):
882
        self.check_result({'three':(['one', 'two'], 'three', {})},
883
                          ['from one.two import three'])
884
1996.1.11 by John Arbash Meinel
Test the ability to take a bunch of import lines and canonicalize them
885
886
class TestCanonicalize(TestCase):
887
    """Test that we can canonicalize import texts"""
888
889
    def check(self, expected, text):
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
890
        proc = lazy_import.ImportProcessor()
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
891
        parsed = proc._canonicalize_import_text(text)
1996.1.11 by John Arbash Meinel
Test the ability to take a bunch of import lines and canonicalize them
892
        self.assertEqual(expected, parsed,
893
                         'Incorrect parsing of text:\n%s\n%s\n!=\n%s'
894
                         % (text, expected, parsed))
895
896
    def test_import_one(self):
897
        self.check(['import one'], 'import one')
898
        self.check(['import one'], '\nimport one\n\n')
899
900
    def test_import_one_two(self):
901
        self.check(['import one, two'], 'import one, two')
902
        self.check(['import one, two'], '\nimport one, two\n\n')
903
904
    def test_import_one_as_two_as(self):
905
        self.check(['import one as x, two as y'], 'import one as x, two as y')
906
        self.check(['import one as x, two as y'],
907
                   '\nimport one as x, two as y\n')
908
909
    def test_from_one_import_two(self):
910
        self.check(['from one import two'], 'from one import two')
911
        self.check(['from one import two'], '\nfrom one import two\n\n')
912
        self.check(['from one import two'], '\nfrom one import (two)\n')
913
        self.check(['from one import  two '], '\nfrom one import (\n\ttwo\n)\n')
1996.1.13 by John Arbash Meinel
small test updates
914
915
    def test_multiple(self):
916
        self.check(['import one', 'import two, three', 'from one import four'],
917
                   'import one\nimport two, three\nfrom one import four')
918
        self.check(['import one', 'import two, three', 'from one import four'],
919
                   'import one\nimport (two, three)\nfrom one import four')
1996.1.15 by John Arbash Meinel
Everything is now hooked up
920
        self.check(['import one', 'import two, three', 'from one import four'],
1996.1.13 by John Arbash Meinel
small test updates
921
                   'import one\n'
1996.1.15 by John Arbash Meinel
Everything is now hooked up
922
                   'import two, three\n'
1996.1.13 by John Arbash Meinel
small test updates
923
                   'from one import four')
1996.1.15 by John Arbash Meinel
Everything is now hooked up
924
        self.check(['import one',
925
                    'import two, three', 'from one import  four, '],
926
                   'import one\n'
927
                   'import two, three\n'
1996.1.13 by John Arbash Meinel
small test updates
928
                   'from one import (\n'
929
                   '    four,\n'
930
                   '    )\n'
931
                   )
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
932
1996.1.18 by John Arbash Meinel
Add more structured error handling
933
    def test_missing_trailing(self):
934
        proc = lazy_import.ImportProcessor()
935
        self.assertRaises(errors.InvalidImportLine,
936
                          proc._canonicalize_import_text,
937
                          "from foo import (\n  bar\n")
938
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
939
940
class TestImportProcessor(TestCase):
941
    """Test that ImportProcessor can turn import texts into lazy imports"""
942
943
    def check(self, expected, text):
944
        proc = lazy_import.ImportProcessor()
945
        proc._build_map(text)
946
        self.assertEqual(expected, proc.imports,
947
                         'Incorrect processing of:\n%s\n%s\n!=\n%s'
948
                         % (text, expected, proc.imports))
949
950
    def test_import_one(self):
951
        exp = {'one':(['one'], None, {})}
952
        self.check(exp, 'import one')
953
        self.check(exp, '\nimport one\n')
954
955
    def test_import_one_two(self):
956
        exp = {'one':(['one'], None,
957
                      {'two':(['one', 'two'], None, {}),
958
                      }),
959
              }
960
        self.check(exp, 'import one.two')
961
        self.check(exp, 'import one, one.two')
962
        self.check(exp, 'import one\nimport one.two')
963
964
    def test_import_as(self):
965
        exp = {'two':(['one'], None, {})}
966
        self.check(exp, 'import one as two')
967
968
    def test_import_many(self):
969
        exp = {'one':(['one'], None,
970
                      {'two':(['one', 'two'], None,
971
                              {'three':(['one', 'two', 'three'], None, {}),
972
                              }),
973
                       'four':(['one', 'four'], None, {}),
974
                      }),
975
               'five':(['one', 'five'], None, {}),
976
              }
977
        self.check(exp, 'import one.two.three, one.four, one.five as five')
978
        self.check(exp, 'import one.five as five\n'
979
                        'import one\n'
980
                        'import one.two.three\n'
981
                        'import one.four\n')
982
983
    def test_from_one_import_two(self):
984
        exp = {'two':(['one'], 'two', {})}
985
        self.check(exp, 'from one import two\n')
986
        self.check(exp, 'from one import (\n'
987
                        '    two,\n'
988
                        '    )\n')
989
990
    def test_from_one_import_two(self):
991
        exp = {'two':(['one'], 'two', {})}
992
        self.check(exp, 'from one import two\n')
993
        self.check(exp, 'from one import (two)\n')
994
        self.check(exp, 'from one import (two,)\n')
995
        self.check(exp, 'from one import two as two\n')
996
        self.check(exp, 'from one import (\n'
997
                        '    two,\n'
998
                        '    )\n')
999
1000
    def test_from_many(self):
1001
        exp = {'two':(['one'], 'two', {}),
1002
               'three':(['one', 'two'], 'three', {}),
1003
               'five':(['one', 'two'], 'four', {}),
1004
              }
1005
        self.check(exp, 'from one import two\n'
1006
                        'from one.two import three, four as five\n')
1007
        self.check(exp, 'from one import two\n'
1008
                        'from one.two import (\n'
1009
                        '    three,\n'
1010
                        '    four as five,\n'
1011
                        '    )\n')
1012
1013
    def test_mixed(self):
1014
        exp = {'two':(['one'], 'two', {}),
1015
               'three':(['one', 'two'], 'three', {}),
1016
               'five':(['one', 'two'], 'four', {}),
1017
               'one':(['one'], None,
1018
                      {'two':(['one', 'two'], None, {}),
1019
                      }),
1020
              }
1021
        self.check(exp, 'from one import two\n'
1022
                        'from one.two import three, four as five\n'
1023
                        'import one.two')
1024
        self.check(exp, 'from one import two\n'
1025
                        'from one.two import (\n'
1026
                        '    three,\n'
1027
                        '    four as five,\n'
1028
                        '    )\n'
1029
                        'import one\n'
1030
                        'import one.two\n')
1996.1.15 by John Arbash Meinel
Everything is now hooked up
1031
1996.1.18 by John Arbash Meinel
Add more structured error handling
1032
    def test_incorrect_line(self):
1033
        proc = lazy_import.ImportProcessor()
1034
        self.assertRaises(errors.InvalidImportLine,
1035
                          proc._build_map, 'foo bar baz')
1036
        self.assertRaises(errors.InvalidImportLine,
1037
                          proc._build_map, 'improt foo')
1038
        self.assertRaises(errors.InvalidImportLine,
1039
                          proc._build_map, 'importfoo')
1040
        self.assertRaises(errors.InvalidImportLine,
1041
                          proc._build_map, 'fromimport')
1042
1043
    def test_name_collision(self):
1044
        proc = lazy_import.ImportProcessor()
1045
        proc._build_map('import foo')
1046
1047
        # All of these would try to create an object with the
1048
        # same name as an existing object.
1049
        self.assertRaises(errors.ImportNameCollision,
1050
                          proc._build_map, 'import bar as foo')
1051
        self.assertRaises(errors.ImportNameCollision,
1052
                          proc._build_map, 'from foo import bar as foo')
1053
        self.assertRaises(errors.ImportNameCollision,
1054
                          proc._build_map, 'from bar import foo')
1055
1996.1.15 by John Arbash Meinel
Everything is now hooked up
1056
1057
class TestLazyImportProcessor(ImportReplacerHelper):
1058
1059
    def test_root(self):
1060
        try:
1061
            root6
1062
        except NameError:
1063
            pass # root6 should not be defined yet
1064
        else:
1065
            self.fail('root6 was not supposed to exist yet')
1066
1067
        text = 'import %s as root6' % (self.root_name,)
1068
        proc = lazy_import.ImportProcessor(InstrumentedImportReplacer)
1996.1.19 by John Arbash Meinel
Write a simple wrapper function to make lazy imports easy.
1069
        proc.lazy_import(scope=globals(), text=text)
1996.1.15 by John Arbash Meinel
Everything is now hooked up
1070
1071
        # So 'root6' should be a lazy import
1072
        self.assertEqual(InstrumentedImportReplacer,
1073
                         object.__getattribute__(root6, '__class__'))
1074
1075
        self.assertEqual(1, root6.var1)
1076
        self.assertEqual('x', root6.func1('x'))
1077
1078
        self.assertEqual([('__getattribute__', 'var1'),
1079
                          '_replace',
1080
                          ('_import', 'root6'),
1081
                          ('import', self.root_name, []),
1082
                         ], self.actions)
1083
1084
    def test_import_deep(self):
1085
        """Test import root.mod, root.sub.submoda, root.sub.submodb
1086
        root should be a lazy import, with multiple children, who also
1087
        have children to be imported.
1088
        And when root is imported, the children should be lazy, and
1089
        reuse the intermediate lazy object.
1090
        """
1091
        try:
1092
            submoda7
1093
        except NameError:
1094
            pass # submoda7 should not be defined yet
1095
        else:
1096
            self.fail('submoda7 was not supposed to exist yet')
1097
1098
        text = """\
1099
import %(root_name)s.%(sub_name)s.%(submoda_name)s as submoda7
1100
""" % self.__dict__
1101
        proc = lazy_import.ImportProcessor(InstrumentedImportReplacer)
1996.1.19 by John Arbash Meinel
Write a simple wrapper function to make lazy imports easy.
1102
        proc.lazy_import(scope=globals(), text=text)
1996.1.15 by John Arbash Meinel
Everything is now hooked up
1103
1104
        # So 'submoda7' should be a lazy import
1105
        self.assertEqual(InstrumentedImportReplacer,
1106
                         object.__getattribute__(submoda7, '__class__'))
1107
1108
        # This should import submoda7
1109
        self.assertEqual(4, submoda7.var4)
1110
1111
        sub_path = self.root_name + '.' + self.sub_name
1112
        submoda_path = sub_path + '.' + self.submoda_name
1113
1114
        self.assertEqual([('__getattribute__', 'var4'),
1115
                          '_replace',
1116
                          ('_import', 'submoda7'),
1117
                          ('import', submoda_path, []),
1118
                         ], self.actions)
1996.1.19 by John Arbash Meinel
Write a simple wrapper function to make lazy imports easy.
1119
1120
    def test_lazy_import(self):
1121
        """Smoke test that lazy_import() does the right thing"""
1122
        try:
1123
            root8
1124
        except NameError:
1125
            pass # root8 should not be defined yet
1126
        else:
1127
            self.fail('root8 was not supposed to exist yet')
1128
        lazy_import.lazy_import(globals(),
1129
                                'import %s as root8' % (self.root_name,),
1130
                                lazy_import_class=InstrumentedImportReplacer)
1131
1132
        self.assertEqual(InstrumentedImportReplacer,
1133
                         object.__getattribute__(root8, '__class__'))
1134
1135
        self.assertEqual(1, root8.var1)
1136
        self.assertEqual(1, root8.var1)
1137
        self.assertEqual(1, root8.func1(1))
1138
1139
        self.assertEqual([('__getattribute__', 'var1'),
1140
                          '_replace',
1141
                          ('_import', 'root8'),
1142
                          ('import', self.root_name, []),
1143
                         ], self.actions)