1
# Copyright (C) 2006 by Canonical Ltd
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.
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.
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
17
"""Test the lazy_import functionality."""
27
from bzrlib.tests import TestCase, TestCaseInTempDir
30
class InstrumentedReplacer(lazy_import.ScopeReplacer):
31
"""Track what actions are done"""
34
def use_actions(actions):
35
InstrumentedReplacer.actions = actions
38
InstrumentedReplacer.actions.append('_replace')
39
return lazy_import.ScopeReplacer._replace(self)
41
def __getattribute__(self, attr):
42
InstrumentedReplacer.actions.append(('__getattribute__', attr))
43
return lazy_import.ScopeReplacer.__getattribute__(self, attr)
45
def __call__(self, *args, **kwargs):
46
InstrumentedReplacer.actions.append(('__call__', args, kwargs))
47
return lazy_import.ScopeReplacer.__call__(self, *args, **kwargs)
50
class InstrumentedImportReplacer(lazy_import.ImportReplacer):
53
def use_actions(actions):
54
InstrumentedImportReplacer.actions = actions
56
def _import(self, scope, name):
57
InstrumentedImportReplacer.actions.append(('_import', name))
58
return lazy_import.ImportReplacer._import(self, scope, name)
61
InstrumentedImportReplacer.actions.append('_replace')
62
return lazy_import.ScopeReplacer._replace(self)
64
def __getattribute__(self, attr):
65
InstrumentedImportReplacer.actions.append(('__getattribute__', attr))
66
return lazy_import.ScopeReplacer.__getattribute__(self, attr)
68
def __call__(self, *args, **kwargs):
69
InstrumentedImportReplacer.actions.append(('__call__', args, kwargs))
70
return lazy_import.ScopeReplacer.__call__(self, *args, **kwargs)
73
class TestClass(object):
74
"""Just a simple test class instrumented for the test cases"""
76
class_member = 'class_member'
79
def use_actions(actions):
80
TestClass.actions = actions
83
TestClass.actions.append('init')
86
TestClass.actions.append(('foo', x))
90
class TestScopeReplacer(TestCase):
91
"""Test the ability of the replacer to put itself into the correct scope.
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
99
def test_object(self):
100
"""ScopeReplacer can create an instance in local scope.
102
An object should appear in globals() by constructing a ScopeReplacer,
103
and it will be replaced with the real object upon the first request.
106
InstrumentedReplacer.use_actions(actions)
107
TestClass.use_actions(actions)
109
def factory(replacer, scope, name):
110
actions.append('factory')
116
# test_obj1 shouldn't exist yet
119
self.fail('test_obj1 was not supposed to exist yet')
121
orig_globals = set(globals().keys())
123
InstrumentedReplacer(scope=globals(), name='test_obj1',
126
new_globals = set(globals().keys())
128
# We can't use isinstance() because that uses test_obj1.__class__
129
# and that goes through __getattribute__ which would activate
131
self.assertEqual(InstrumentedReplacer,
132
object.__getattribute__(test_obj1, '__class__'))
133
self.assertEqual('foo', test_obj1.foo(1))
134
self.assertIsInstance(test_obj1, TestClass)
135
self.assertEqual('foo', test_obj1.foo(2))
136
self.assertEqual([('__getattribute__', 'foo'),
144
def test_replace_side_effects(self):
145
"""Creating a new object should only create one entry in globals.
147
And only that entry even after replacement.
152
# test_scope1 shouldn't exist yet
155
self.fail('test_scope1 was not supposed to exist yet')
157
# ignore the logged actions
158
TestClass.use_actions([])
160
def factory(replacer, scope, name):
163
orig_globals = set(globals().keys())
165
lazy_import.ScopeReplacer(scope=globals(), name='test_scope1',
168
new_globals = set(globals().keys())
170
self.assertEqual(lazy_import.ScopeReplacer,
171
object.__getattribute__(test_scope1, '__class__'))
172
self.assertEqual('foo', test_scope1.foo(1))
173
self.assertIsInstance(test_scope1, TestClass)
175
final_globals = set(globals().keys())
177
self.assertEqual(set(['test_scope1']), new_globals - orig_globals)
178
self.assertEqual(set(), orig_globals - new_globals)
179
self.assertEqual(set(), final_globals - new_globals)
180
self.assertEqual(set(), new_globals - final_globals)
182
def test_class(self):
184
InstrumentedReplacer.use_actions(actions)
185
TestClass.use_actions(actions)
187
def factory(replacer, scope, name):
188
actions.append('factory')
194
# test_class2 shouldn't exist yet
197
self.fail('test_class1 was not supposed to exist yet')
199
InstrumentedReplacer(scope=globals(), name='test_class1',
202
self.assertEqual('class_member', test_class1.class_member)
203
self.assertEqual(test_class1, TestClass)
204
self.assertEqual([('__getattribute__', 'class_member'),
209
def test_call_class(self):
211
InstrumentedReplacer.use_actions(actions)
212
TestClass.use_actions(actions)
214
def factory(replacer, scope, name):
215
actions.append('factory')
221
# test_class2 shouldn't exist yet
224
self.fail('test_class2 was not supposed to exist yet')
226
InstrumentedReplacer(scope=globals(), name='test_class2',
229
self.failIf(test_class2 is TestClass)
231
self.assertIs(test_class2, TestClass)
232
self.assertIsInstance(obj, TestClass)
233
self.assertEqual('class_member', obj.class_member)
234
self.assertEqual([('__call__', (), {}),
240
def test_call_func(self):
242
InstrumentedReplacer.use_actions(actions)
244
def func(a, b, c=None):
245
actions.append('func')
248
def factory(replacer, scope, name):
249
actions.append('factory')
255
# test_func1 shouldn't exist yet
258
self.fail('test_func1 was not supposed to exist yet')
259
InstrumentedReplacer(scope=globals(), name='test_func1',
262
self.failIf(test_func1 is func)
263
val = test_func1(1, 2, c='3')
264
self.assertIs(test_func1, func)
266
self.assertEqual((1,2,'3'), val)
267
self.assertEqual([('__call__', (1,2), {'c':'3'}),
273
def test_other_variable(self):
274
"""Test when a ScopeReplacer is assigned to another variable.
276
This test could be updated if we find a way to trap '=' rather
277
than just giving a belated exception.
278
ScopeReplacer only knows about the variable it was created as,
279
so until the object is replaced, it is illegal to pass it to
280
another variable. (Though discovering this may take a while)
283
InstrumentedReplacer.use_actions(actions)
284
TestClass.use_actions(actions)
286
def factory(replacer, scope, name):
287
actions.append('factory')
293
# test_obj2 shouldn't exist yet
296
self.fail('test_obj2 was not supposed to exist yet')
298
InstrumentedReplacer(scope=globals(), name='test_obj2',
301
self.assertEqual(InstrumentedReplacer,
302
object.__getattribute__(test_obj2, '__class__'))
303
# This is technically not allowed, but we don't have a way to
304
# test it until later.
305
test_obj3 = test_obj2
306
self.assertEqual(InstrumentedReplacer,
307
object.__getattribute__(test_obj2, '__class__'))
308
self.assertEqual(InstrumentedReplacer,
309
object.__getattribute__(test_obj3, '__class__'))
311
# The first use of the alternate variable causes test_obj2 to
313
self.assertEqual('foo', test_obj3.foo(1))
314
# test_obj2 has been replaced, but the ScopeReplacer has no
316
self.assertEqual(TestClass,
317
object.__getattribute__(test_obj2, '__class__'))
318
self.assertEqual(InstrumentedReplacer,
319
object.__getattribute__(test_obj3, '__class__'))
320
# We should be able to access test_obj2 attributes normally
321
self.assertEqual('foo', test_obj2.foo(2))
322
self.assertEqual('foo', test_obj2.foo(3))
324
# However, the next access on test_obj3 should raise an error
325
# because only now are we able to detect the problem.
326
self.assertRaises(errors.IllegalUseOfScopeReplacer,
327
getattr, test_obj3, 'foo')
330
self.assertEqual([('__getattribute__', 'foo'),
337
('__getattribute__', 'foo'),
342
class ImportReplacerHelper(TestCaseInTempDir):
343
"""Test the ability to have a lazily imported module or object"""
346
TestCaseInTempDir.setUp(self)
347
self.create_modules()
348
base_path = self.test_dir + '/base'
351
InstrumentedImportReplacer.use_actions(self.actions)
353
original_import = __import__
354
def instrumented_import(mod, scope1, scope2, fromlist):
355
self.actions.append(('import', mod, fromlist))
356
return original_import(mod, scope1, scope2, fromlist)
359
if base_path in sys.path:
360
sys.path.remove(base_path)
361
__builtins__['__import__'] = original_import
362
self.addCleanup(cleanup)
363
sys.path.append(base_path)
364
__builtins__['__import__'] = instrumented_import
366
def create_modules(self):
367
"""Create some random modules to be imported.
369
Each entry has a random suffix, and the full names are saved
371
These are setup as follows:
372
base/ <= used to ensure not in default search path
374
__init__.py <= This will contain var1, func1
375
mod-XXX.py <= This will contain var2, func2
377
__init__.py <= Contains var3, func3
378
submoda-XXX.py <= contains var4, func4
379
submodb-XXX.py <= containse var5, func5
381
rand_suffix = osutils.rand_chars(4)
382
root_name = 'root_' + rand_suffix
383
mod_name = 'mod_' + rand_suffix
384
sub_name = 'sub_' + rand_suffix
385
submoda_name = 'submoda_' + rand_suffix
386
submodb_name = 'submodb_' + rand_suffix
389
root_path = osutils.pathjoin('base', root_name)
391
root_init = osutils.pathjoin(root_path, '__init__.py')
392
f = open(osutils.pathjoin(root_path, '__init__.py'), 'wb')
394
f.write('var1 = 1\ndef func1(a):\n return a\n')
397
mod_path = osutils.pathjoin(root_path, mod_name + '.py')
398
f = open(mod_path, 'wb')
400
f.write('var2 = 2\ndef func2(a):\n return a\n')
404
sub_path = osutils.pathjoin(root_path, sub_name)
406
f = open(osutils.pathjoin(sub_path, '__init__.py'), 'wb')
408
f.write('var3 = 3\ndef func3(a):\n return a\n')
411
submoda_path = osutils.pathjoin(sub_path, submoda_name + '.py')
412
f = open(submoda_path, 'wb')
414
f.write('var4 = 4\ndef func4(a):\n return a\n')
417
submodb_path = osutils.pathjoin(sub_path, submodb_name + '.py')
418
f = open(submodb_path, 'wb')
420
f.write('var5 = 5\ndef func5(a):\n return a\n')
423
self.root_name = root_name
424
self.mod_name = mod_name
425
self.sub_name = sub_name
426
self.submoda_name = submoda_name
427
self.submodb_name = submodb_name
430
class TestImportReplacerHelper(ImportReplacerHelper):
432
def test_basic_import(self):
433
"""Test that a real import of these modules works"""
434
sub_mod_path = '.'.join([self.root_name, self.sub_name,
436
root = __import__(sub_mod_path, globals(), locals(), [])
437
self.assertEqual(1, root.var1)
438
self.assertEqual(3, getattr(root, self.sub_name).var3)
439
self.assertEqual(4, getattr(getattr(root, self.sub_name),
440
self.submoda_name).var4)
442
mod_path = '.'.join([self.root_name, self.mod_name])
443
root = __import__(mod_path, globals(), locals(), [])
444
self.assertEqual(2, getattr(root, self.mod_name).var2)
446
self.assertEqual([('import', sub_mod_path, []),
447
('import', mod_path, []),
451
class TestImportReplacer(ImportReplacerHelper):
453
def test_import_root(self):
454
"""Test 'import root-XXX as root1'"""
458
# root1 shouldn't exist yet
461
self.fail('root1 was not supposed to exist yet')
463
# This should replicate 'import root-xxyyzz as root1'
464
InstrumentedImportReplacer(scope=globals(), name='root1',
465
module_path=[self.root_name],
466
member=None, children={})
468
self.assertEqual(InstrumentedImportReplacer,
469
object.__getattribute__(root1, '__class__'))
470
self.assertEqual(1, root1.var1)
471
self.assertEqual('x', root1.func1('x'))
473
self.assertEqual([('__getattribute__', 'var1'),
475
('_import', 'root1'),
476
('import', self.root_name, []),
479
def test_import_mod(self):
480
"""Test 'import root-XXX.mod-XXX as mod2'"""
484
# mod1 shouldn't exist yet
487
self.fail('mod1 was not supposed to exist yet')
489
mod_path = self.root_name + '.' + self.mod_name
490
InstrumentedImportReplacer(scope=globals(), name='mod1',
491
module_path=[self.root_name, self.mod_name],
492
member=None, children={})
494
self.assertEqual(InstrumentedImportReplacer,
495
object.__getattribute__(mod1, '__class__'))
496
self.assertEqual(2, mod1.var2)
497
self.assertEqual('y', mod1.func2('y'))
499
self.assertEqual([('__getattribute__', 'var2'),
502
('import', mod_path, []),
505
def test_import_mod_from_root(self):
506
"""Test 'from root-XXX import mod-XXX as mod2'"""
510
# mod2 shouldn't exist yet
513
self.fail('mod2 was not supposed to exist yet')
515
InstrumentedImportReplacer(scope=globals(), name='mod2',
516
module_path=[self.root_name],
517
member=self.mod_name, children={})
519
self.assertEqual(InstrumentedImportReplacer,
520
object.__getattribute__(mod2, '__class__'))
521
self.assertEqual(2, mod2.var2)
522
self.assertEqual('y', mod2.func2('y'))
524
self.assertEqual([('__getattribute__', 'var2'),
527
('import', self.root_name, [self.mod_name]),
530
def test_import_root_and_mod(self):
531
"""Test 'import root-XXX.mod-XXX' remapping both to root3.mod3"""
535
# root3 shouldn't exist yet
538
self.fail('root3 was not supposed to exist yet')
540
InstrumentedImportReplacer(scope=globals(),
541
name='root3', module_path=[self.root_name], member=None,
542
children={'mod3':([self.root_name, self.mod_name], None, {})})
544
# So 'root3' should be a lazy import
545
# and once it is imported, mod3 should also be lazy until
547
self.assertEqual(InstrumentedImportReplacer,
548
object.__getattribute__(root3, '__class__'))
549
self.assertEqual(1, root3.var1)
551
# There is a mod3 member, but it is also lazy
552
self.assertEqual(InstrumentedImportReplacer,
553
object.__getattribute__(root3.mod3, '__class__'))
554
self.assertEqual(2, root3.mod3.var2)
556
mod_path = self.root_name + '.' + self.mod_name
557
self.assertEqual([('__getattribute__', 'var1'),
559
('_import', 'root3'),
560
('import', self.root_name, []),
561
('__getattribute__', 'var2'),
564
('import', mod_path, []),
567
def test_import_root_and_root_mod(self):
568
"""Test that 'import root, root.mod' can be done.
570
The second import should re-use the first one, and just add
571
children to be imported.
576
# root4 shouldn't exist yet
579
self.fail('root4 was not supposed to exist yet')
581
InstrumentedImportReplacer(scope=globals(),
582
name='root4', module_path=[self.root_name], member=None,
585
# So 'root4' should be a lazy import
586
self.assertEqual(InstrumentedImportReplacer,
587
object.__getattribute__(root4, '__class__'))
589
# Lets add a new child to be imported on demand
590
# This syntax of using object.__getattribute__ is the correct method
591
# for accessing the _import_replacer_children member
592
children = object.__getattribute__(root4, '_import_replacer_children')
593
children['mod4'] = ([self.root_name, self.mod_name], None, {})
595
# Accessing root4.mod4 should import root, but mod should stay lazy
596
self.assertEqual(InstrumentedImportReplacer,
597
object.__getattribute__(root4.mod4, '__class__'))
598
self.assertEqual(2, root4.mod4.var2)
600
mod_path = self.root_name + '.' + self.mod_name
601
self.assertEqual([('__getattribute__', 'mod4'),
603
('_import', 'root4'),
604
('import', self.root_name, []),
605
('__getattribute__', 'var2'),
608
('import', mod_path, []),
611
def test_import_root_sub_submod(self):
612
"""Test import root.mod, root.sub.submoda, root.sub.submodb
613
root should be a lazy import, with multiple children, who also
614
have children to be imported.
615
And when root is imported, the children should be lazy, and
616
reuse the intermediate lazy object.
621
# root5 shouldn't exist yet
624
self.fail('root5 was not supposed to exist yet')
626
InstrumentedImportReplacer(scope=globals(),
627
name='root5', module_path=[self.root_name], member=None,
628
children={'mod5':([self.root_name, self.mod_name], None, {}),
629
'sub5':([self.root_name, self.sub_name], None,
630
{'submoda5':([self.root_name, self.sub_name,
631
self.submoda_name], None, {}),
632
'submodb5':([self.root_name, self.sub_name,
633
self.submodb_name], None, {})
637
# So 'root5' should be a lazy import
638
self.assertEqual(InstrumentedImportReplacer,
639
object.__getattribute__(root5, '__class__'))
641
# Accessing root5.mod5 should import root, but mod should stay lazy
642
self.assertEqual(InstrumentedImportReplacer,
643
object.__getattribute__(root5.mod5, '__class__'))
644
# root5.sub5 should still be lazy, but not re-import root5
645
self.assertEqual(InstrumentedImportReplacer,
646
object.__getattribute__(root5.sub5, '__class__'))
648
# Accessing root5.sub5.submoda5 should import sub5, but not either
649
# of the sub objects (they should be available as lazy objects
650
self.assertEqual(InstrumentedImportReplacer,
651
object.__getattribute__(root5.sub5.submoda5, '__class__'))
652
self.assertEqual(InstrumentedImportReplacer,
653
object.__getattribute__(root5.sub5.submodb5, '__class__'))
655
# This should import mod5
656
self.assertEqual(2, root5.mod5.var2)
657
# These should import submoda5 and submodb5
658
self.assertEqual(4, root5.sub5.submoda5.var4)
659
self.assertEqual(5, root5.sub5.submodb5.var5)
661
mod_path = self.root_name + '.' + self.mod_name
662
sub_path = self.root_name + '.' + self.sub_name
663
submoda_path = sub_path + '.' + self.submoda_name
664
submodb_path = sub_path + '.' + self.submodb_name
666
self.assertEqual([('__getattribute__', 'mod5'),
668
('_import', 'root5'),
669
('import', self.root_name, []),
670
('__getattribute__', 'submoda5'),
673
('import', sub_path, []),
674
('__getattribute__', 'var2'),
677
('import', mod_path, []),
678
('__getattribute__', 'var4'),
680
('_import', 'submoda5'),
681
('import', submoda_path, []),
682
('__getattribute__', 'var5'),
684
('_import', 'submodb5'),
685
('import', submodb_path, []),
689
class TestConvertImportToMap(TestCase):
690
"""Directly test the conversion from import strings to maps"""
692
def check(self, expected, import_strings):
693
proc = lazy_import.ImportProcessor()
694
for import_str in import_strings:
695
proc._convert_import_str(import_str)
696
self.assertEqual(expected, proc.imports,
697
'Import of %r was not converted correctly'
698
' %s != %s' % (import_strings, expected,
701
def test_import_one(self):
702
self.check({'one':(['one'], None, {}),
705
def test_import_one_two(self):
706
one_two_map = {'one':(['one'], None,
707
{'two':(['one', 'two'], None, {}),
710
self.check(one_two_map, ['import one.two'])
711
self.check(one_two_map, ['import one, one.two'])
712
self.check(one_two_map, ['import one', 'import one.two'])
713
self.check(one_two_map, ['import one.two', 'import one'])
715
def test_import_one_two_three(self):
716
one_two_three_map = {
717
'one':(['one'], None,
718
{'two':(['one', 'two'], None,
719
{'three':(['one', 'two', 'three'], None, {}),
723
self.check(one_two_three_map, ['import one.two.three'])
724
self.check(one_two_three_map, ['import one, one.two.three'])
725
self.check(one_two_three_map, ['import one',
726
'import one.two.three'])
727
self.check(one_two_three_map, ['import one.two.three',
730
def test_import_one_as_x(self):
731
self.check({'x':(['one'], None, {}),
732
}, ['import one as x'])
734
def test_import_one_two_as_x(self):
735
self.check({'x':(['one', 'two'], None, {}),
736
}, ['import one.two as x'])
738
def test_import_mixed(self):
739
mixed = {'x':(['one', 'two'], None, {}),
740
'one':(['one'], None,
741
{'two':(['one', 'two'], None, {}),
744
self.check(mixed, ['import one.two as x, one.two'])
745
self.check(mixed, ['import one.two as x', 'import one.two'])
746
self.check(mixed, ['import one.two', 'import one.two as x'])
748
def test_import_with_as(self):
749
self.check({'fast':(['fast'], None, {})}, ['import fast'])
752
class TestFromToMap(TestCase):
753
"""Directly test the conversion of 'from foo import bar' syntax"""
755
def check_result(self, expected, from_strings):
756
proc = lazy_import.ImportProcessor()
757
for from_str in from_strings:
758
proc._convert_from_str(from_str)
759
self.assertEqual(expected, proc.imports,
760
'Import of %r was not converted correctly'
761
' %s != %s' % (from_strings, expected, proc.imports))
763
def test_from_one_import_two(self):
764
self.check_result({'two':(['one'], 'two', {})},
765
['from one import two'])
767
def test_from_one_import_two_as_three(self):
768
self.check_result({'three':(['one'], 'two', {})},
769
['from one import two as three'])
771
def test_from_one_import_two_three(self):
772
two_three_map = {'two':(['one'], 'two', {}),
773
'three':(['one'], 'three', {}),
775
self.check_result(two_three_map,
776
['from one import two, three'])
777
self.check_result(two_three_map,
778
['from one import two',
779
'from one import three'])
781
def test_from_one_two_import_three(self):
782
self.check_result({'three':(['one', 'two'], 'three', {})},
783
['from one.two import three'])
786
class TestCanonicalize(TestCase):
787
"""Test that we can canonicalize import texts"""
789
def check(self, expected, text):
790
proc = lazy_import.ImportProcessor()
791
parsed = proc._canonicalize_import_text(text)
792
self.assertEqual(expected, parsed,
793
'Incorrect parsing of text:\n%s\n%s\n!=\n%s'
794
% (text, expected, parsed))
796
def test_import_one(self):
797
self.check(['import one'], 'import one')
798
self.check(['import one'], '\nimport one\n\n')
800
def test_import_one_two(self):
801
self.check(['import one, two'], 'import one, two')
802
self.check(['import one, two'], '\nimport one, two\n\n')
804
def test_import_one_as_two_as(self):
805
self.check(['import one as x, two as y'], 'import one as x, two as y')
806
self.check(['import one as x, two as y'],
807
'\nimport one as x, two as y\n')
809
def test_from_one_import_two(self):
810
self.check(['from one import two'], 'from one import two')
811
self.check(['from one import two'], '\nfrom one import two\n\n')
812
self.check(['from one import two'], '\nfrom one import (two)\n')
813
self.check(['from one import two '], '\nfrom one import (\n\ttwo\n)\n')
815
def test_multiple(self):
816
self.check(['import one', 'import two, three', 'from one import four'],
817
'import one\nimport two, three\nfrom one import four')
818
self.check(['import one', 'import two, three', 'from one import four'],
819
'import one\nimport (two, three)\nfrom one import four')
820
self.check(['import one', 'import two, three', 'from one import four'],
822
'import two, three\n'
823
'from one import four')
824
self.check(['import one',
825
'import two, three', 'from one import four, '],
827
'import two, three\n'
828
'from one import (\n'
833
def test_missing_trailing(self):
834
proc = lazy_import.ImportProcessor()
835
self.assertRaises(errors.InvalidImportLine,
836
proc._canonicalize_import_text,
837
"from foo import (\n bar\n")
840
class TestImportProcessor(TestCase):
841
"""Test that ImportProcessor can turn import texts into lazy imports"""
843
def check(self, expected, text):
844
proc = lazy_import.ImportProcessor()
845
proc._build_map(text)
846
self.assertEqual(expected, proc.imports,
847
'Incorrect processing of:\n%s\n%s\n!=\n%s'
848
% (text, expected, proc.imports))
850
def test_import_one(self):
851
exp = {'one':(['one'], None, {})}
852
self.check(exp, 'import one')
853
self.check(exp, '\nimport one\n')
855
def test_import_one_two(self):
856
exp = {'one':(['one'], None,
857
{'two':(['one', 'two'], None, {}),
860
self.check(exp, 'import one.two')
861
self.check(exp, 'import one, one.two')
862
self.check(exp, 'import one\nimport one.two')
864
def test_import_as(self):
865
exp = {'two':(['one'], None, {})}
866
self.check(exp, 'import one as two')
868
def test_import_many(self):
869
exp = {'one':(['one'], None,
870
{'two':(['one', 'two'], None,
871
{'three':(['one', 'two', 'three'], None, {}),
873
'four':(['one', 'four'], None, {}),
875
'five':(['one', 'five'], None, {}),
877
self.check(exp, 'import one.two.three, one.four, one.five as five')
878
self.check(exp, 'import one.five as five\n'
880
'import one.two.three\n'
883
def test_from_one_import_two(self):
884
exp = {'two':(['one'], 'two', {})}
885
self.check(exp, 'from one import two\n')
886
self.check(exp, 'from one import (\n'
890
def test_from_one_import_two(self):
891
exp = {'two':(['one'], 'two', {})}
892
self.check(exp, 'from one import two\n')
893
self.check(exp, 'from one import (two)\n')
894
self.check(exp, 'from one import (two,)\n')
895
self.check(exp, 'from one import two as two\n')
896
self.check(exp, 'from one import (\n'
900
def test_from_many(self):
901
exp = {'two':(['one'], 'two', {}),
902
'three':(['one', 'two'], 'three', {}),
903
'five':(['one', 'two'], 'four', {}),
905
self.check(exp, 'from one import two\n'
906
'from one.two import three, four as five\n')
907
self.check(exp, 'from one import two\n'
908
'from one.two import (\n'
913
def test_mixed(self):
914
exp = {'two':(['one'], 'two', {}),
915
'three':(['one', 'two'], 'three', {}),
916
'five':(['one', 'two'], 'four', {}),
917
'one':(['one'], None,
918
{'two':(['one', 'two'], None, {}),
921
self.check(exp, 'from one import two\n'
922
'from one.two import three, four as five\n'
924
self.check(exp, 'from one import two\n'
925
'from one.two import (\n'
932
def test_incorrect_line(self):
933
proc = lazy_import.ImportProcessor()
934
self.assertRaises(errors.InvalidImportLine,
935
proc._build_map, 'foo bar baz')
936
self.assertRaises(errors.InvalidImportLine,
937
proc._build_map, 'improt foo')
938
self.assertRaises(errors.InvalidImportLine,
939
proc._build_map, 'importfoo')
940
self.assertRaises(errors.InvalidImportLine,
941
proc._build_map, 'fromimport')
943
def test_name_collision(self):
944
proc = lazy_import.ImportProcessor()
945
proc._build_map('import foo')
947
# All of these would try to create an object with the
948
# same name as an existing object.
949
self.assertRaises(errors.ImportNameCollision,
950
proc._build_map, 'import bar as foo')
951
self.assertRaises(errors.ImportNameCollision,
952
proc._build_map, 'from foo import bar as foo')
953
self.assertRaises(errors.ImportNameCollision,
954
proc._build_map, 'from bar import foo')
957
class TestLazyImportProcessor(ImportReplacerHelper):
963
pass # root6 should not be defined yet
965
self.fail('root6 was not supposed to exist yet')
967
text = 'import %s as root6' % (self.root_name,)
968
proc = lazy_import.ImportProcessor(InstrumentedImportReplacer)
969
proc.lazy_import(scope=globals(), text=text)
971
# So 'root6' should be a lazy import
972
self.assertEqual(InstrumentedImportReplacer,
973
object.__getattribute__(root6, '__class__'))
975
self.assertEqual(1, root6.var1)
976
self.assertEqual('x', root6.func1('x'))
978
self.assertEqual([('__getattribute__', 'var1'),
980
('_import', 'root6'),
981
('import', self.root_name, []),
984
def test_import_deep(self):
985
"""Test import root.mod, root.sub.submoda, root.sub.submodb
986
root should be a lazy import, with multiple children, who also
987
have children to be imported.
988
And when root is imported, the children should be lazy, and
989
reuse the intermediate lazy object.
994
pass # submoda7 should not be defined yet
996
self.fail('submoda7 was not supposed to exist yet')
999
import %(root_name)s.%(sub_name)s.%(submoda_name)s as submoda7
1001
proc = lazy_import.ImportProcessor(InstrumentedImportReplacer)
1002
proc.lazy_import(scope=globals(), text=text)
1004
# So 'submoda7' should be a lazy import
1005
self.assertEqual(InstrumentedImportReplacer,
1006
object.__getattribute__(submoda7, '__class__'))
1008
# This should import submoda7
1009
self.assertEqual(4, submoda7.var4)
1011
sub_path = self.root_name + '.' + self.sub_name
1012
submoda_path = sub_path + '.' + self.submoda_name
1014
self.assertEqual([('__getattribute__', 'var4'),
1016
('_import', 'submoda7'),
1017
('import', submoda_path, []),
1020
def test_lazy_import(self):
1021
"""Smoke test that lazy_import() does the right thing"""
1025
pass # root8 should not be defined yet
1027
self.fail('root8 was not supposed to exist yet')
1028
lazy_import.lazy_import(globals(),
1029
'import %s as root8' % (self.root_name,),
1030
lazy_import_class=InstrumentedImportReplacer)
1032
self.assertEqual(InstrumentedImportReplacer,
1033
object.__getattribute__(root8, '__class__'))
1035
self.assertEqual(1, root8.var1)
1036
self.assertEqual(1, root8.var1)
1037
self.assertEqual(1, root8.func1(1))
1039
self.assertEqual([('__getattribute__', 'var1'),
1041
('_import', 'root8'),
1042
('import', self.root_name, []),