34
39
def use_actions(actions):
35
40
InstrumentedReplacer.actions = actions
38
InstrumentedReplacer.actions.append('_replace')
39
return lazy_import.ScopeReplacer._replace(self)
41
42
def __getattribute__(self, attr):
42
43
InstrumentedReplacer.actions.append(('__getattribute__', attr))
43
44
return lazy_import.ScopeReplacer.__getattribute__(self, attr)
57
58
InstrumentedImportReplacer.actions.append(('_import', name))
58
59
return lazy_import.ImportReplacer._import(self, scope, name)
61
InstrumentedImportReplacer.actions.append('_replace')
62
return lazy_import.ScopeReplacer._replace(self)
64
61
def __getattribute__(self, attr):
65
62
InstrumentedImportReplacer.actions.append(('__getattribute__', attr))
66
63
return lazy_import.ScopeReplacer.__getattribute__(self, attr)
128
125
self.fail('test_obj1 was not supposed to exist yet')
130
orig_globals = set(globals().keys())
132
127
InstrumentedReplacer(scope=globals(), name='test_obj1',
135
new_globals = set(globals().keys())
137
130
# We can't use isinstance() because that uses test_obj1.__class__
138
131
# and that goes through __getattribute__ which would activate
139
132
# the replacement
169
161
self.fail('test_obj6 was not supposed to exist yet')
171
orig_globals = set(globals().keys())
173
163
lazy_import.ScopeReplacer(scope=globals(), name='test_obj6',
176
new_globals = set(globals().keys())
178
166
# We can't use isinstance() because that uses test_obj6.__class__
179
167
# and that goes through __getattribute__ which would activate
180
168
# the replacement
270
257
InstrumentedReplacer(scope=globals(), name='test_class2',
273
self.failIf(test_class2 is TestClass)
260
self.assertFalse(test_class2 is TestClass)
274
261
obj = test_class2()
275
262
self.assertIs(test_class2, TestClass)
276
263
self.assertIsInstance(obj, TestClass)
277
264
self.assertEqual('class_member', obj.class_member)
278
265
self.assertEqual([('__call__', (), {}),
303
289
InstrumentedReplacer(scope=globals(), name='test_func1',
306
self.failIf(test_func1 is func)
292
self.assertFalse(test_func1 is func)
307
293
val = test_func1(1, 2, c='3')
308
294
self.assertIs(test_func1, func)
310
296
self.assertEqual((1,2,'3'), val)
311
297
self.assertEqual([('__call__', (1,2), {'c':'3'}),
425
def test_replacing_from_own_scope_fails(self):
426
"""If a ScopeReplacer tries to replace itself a nice error is given"""
428
InstrumentedReplacer.use_actions(actions)
429
TestClass.use_actions(actions)
431
def factory(replacer, scope, name):
432
actions.append('factory')
433
# return the name in given scope, which is currently the replacer
439
# test_obj7 shouldn't exist yet
442
self.fail('test_obj7 was not supposed to exist yet')
444
InstrumentedReplacer(scope=globals(), name='test_obj7',
447
self.assertEqual(InstrumentedReplacer,
448
object.__getattribute__(test_obj7, '__class__'))
449
e = self.assertRaises(errors.IllegalUseOfScopeReplacer, test_obj7)
450
self.assertIn("replace itself", e.msg)
451
self.assertEqual([('__call__', (), {}),
444
455
class ImportReplacerHelper(TestCaseInTempDir):
445
456
"""Test the ability to have a lazily imported module or object"""
448
TestCaseInTempDir.setUp(self)
459
super(ImportReplacerHelper, self).setUp()
449
460
self.create_modules()
450
461
base_path = self.test_dir + '/base'
456
467
self.addCleanup(sys.path.remove, base_path)
458
469
original_import = __import__
459
def instrumented_import(mod, scope1, scope2, fromlist):
460
self.actions.append(('import', mod, fromlist))
461
return original_import(mod, scope1, scope2, fromlist)
470
def instrumented_import(mod, scope1, scope2, fromlist, level):
471
self.actions.append(('import', mod, fromlist, level))
472
return original_import(mod, scope1, scope2, fromlist, level)
463
474
__builtins__['__import__'] = original_import
464
475
self.addCleanup(cleanup)
534
545
"""Test that a real import of these modules works"""
535
546
sub_mod_path = '.'.join([self.root_name, self.sub_name,
536
547
self.submoda_name])
537
root = __import__(sub_mod_path, globals(), locals(), [])
548
root = __import__(sub_mod_path, globals(), locals(), [], 0)
538
549
self.assertEqual(1, root.var1)
539
550
self.assertEqual(3, getattr(root, self.sub_name).var3)
540
551
self.assertEqual(4, getattr(getattr(root, self.sub_name),
541
552
self.submoda_name).var4)
543
554
mod_path = '.'.join([self.root_name, self.mod_name])
544
root = __import__(mod_path, globals(), locals(), [])
555
root = __import__(mod_path, globals(), locals(), [], 0)
545
556
self.assertEqual(2, getattr(root, self.mod_name).var2)
547
self.assertEqual([('import', sub_mod_path, []),
548
('import', mod_path, []),
558
self.assertEqual([('import', sub_mod_path, [], 0),
559
('import', mod_path, [], 0),
572
583
self.assertEqual('x', root1.func1('x'))
574
585
self.assertEqual([('__getattribute__', 'var1'),
576
586
('_import', 'root1'),
577
('import', self.root_name, []),
587
('import', self.root_name, [], 0),
580
590
def test_import_mod(self):
598
608
self.assertEqual('y', mod1.func2('y'))
600
610
self.assertEqual([('__getattribute__', 'var2'),
602
611
('_import', 'mod1'),
603
('import', mod_path, []),
612
('import', mod_path, [], 0),
606
615
def test_import_mod_from_root(self):
623
632
self.assertEqual('y', mod2.func2('y'))
625
634
self.assertEqual([('__getattribute__', 'var2'),
627
635
('_import', 'mod2'),
628
('import', self.root_name, [self.mod_name]),
636
('import', self.root_name, [self.mod_name], 0),
631
639
def test_import_root_and_mod(self):
657
665
mod_path = self.root_name + '.' + self.mod_name
658
666
self.assertEqual([('__getattribute__', 'var1'),
660
667
('_import', 'root3'),
661
('import', self.root_name, []),
668
('import', self.root_name, [], 0),
662
669
('__getattribute__', 'var2'),
664
670
('_import', 'mod3'),
665
('import', mod_path, []),
671
('import', mod_path, [], 0),
668
674
def test_import_root_and_root_mod(self):
701
707
mod_path = self.root_name + '.' + self.mod_name
702
708
self.assertEqual([('__getattribute__', 'mod4'),
704
709
('_import', 'root4'),
705
('import', self.root_name, []),
710
('import', self.root_name, [], 0),
706
711
('__getattribute__', 'var2'),
708
712
('_import', 'mod4'),
709
('import', mod_path, []),
713
('import', mod_path, [], 0),
712
716
def test_import_root_sub_submod(self):
765
769
submodb_path = sub_path + '.' + self.submodb_name
767
771
self.assertEqual([('__getattribute__', 'mod5'),
769
772
('_import', 'root5'),
770
('import', self.root_name, []),
773
('import', self.root_name, [], 0),
771
774
('__getattribute__', 'submoda5'),
773
775
('_import', 'sub5'),
774
('import', sub_path, []),
776
('import', sub_path, [], 0),
775
777
('__getattribute__', 'var2'),
777
778
('_import', 'mod5'),
778
('import', mod_path, []),
779
('import', mod_path, [], 0),
779
780
('__getattribute__', 'var4'),
781
781
('_import', 'submoda5'),
782
('import', submoda_path, []),
782
('import', submoda_path, [], 0),
783
783
('__getattribute__', 'var5'),
785
784
('_import', 'submodb5'),
786
('import', submodb_path, []),
785
('import', submodb_path, [], 0),
1077
1076
self.assertEqual('x', root6.func1('x'))
1079
1078
self.assertEqual([('__getattribute__', 'var1'),
1081
1079
('_import', 'root6'),
1082
('import', self.root_name, []),
1080
('import', self.root_name, [], 0),
1083
1081
], self.actions)
1085
1083
def test_import_deep(self):
1113
1111
submoda_path = sub_path + '.' + self.submoda_name
1115
1113
self.assertEqual([('__getattribute__', 'var4'),
1117
1114
('_import', 'submoda7'),
1118
('import', submoda_path, []),
1115
('import', submoda_path, [], 0),
1119
1116
], self.actions)
1121
1118
def test_lazy_import(self):
1138
1135
self.assertEqual(1, root8.func1(1))
1140
1137
self.assertEqual([('__getattribute__', 'var1'),
1142
1138
('_import', 'root8'),
1143
('import', self.root_name, []),
1139
('import', self.root_name, [], 0),
1144
1140
], self.actions)
1143
class TestScopeReplacerReentrance(TestCase):
1144
"""The ScopeReplacer should be reentrant.
1146
Invoking a replacer while an invocation was already on-going leads to a
1147
race to see which invocation will be the first to call _replace.
1148
The losing caller used to see an exception (bugs 396819 and 702914).
1150
These tests set up a tracer that stops at a suitable moment (upon
1151
entry of a specified method) and starts another call to the
1152
functionality in question (__call__, __getattribute__, __setattr_)
1153
in order to win the race, setting up the original caller to lose.
1156
def tracer(self, frame, event, arg):
1159
# Grab the name of the file that contains the code being executed.
1161
filename = code.co_filename
1162
# Convert ".pyc" and ".pyo" file names to their ".py" equivalent.
1163
filename = re.sub(r'\.py[co]$', '.py', filename)
1164
function_name = code.co_name
1165
# If we're executing a line of code from the right module...
1166
if (filename.endswith('lazy_import.py') and
1167
function_name == self.method_to_trace):
1168
# We don't need to trace any more.
1170
# Run another racer. This one will "win" the race.
1174
def run_race(self, racer, method_to_trace='_resolve'):
1175
self.overrideAttr(lazy_import.ScopeReplacer, '_should_proxy', True)
1177
self.method_to_trace = method_to_trace
1178
sys.settrace(self.tracer)
1179
self.racer() # Should not raise any exception
1180
# Make sure the tracer actually found the code it was
1181
# looking for. If not, maybe the code was refactored in
1182
# such a way that these tests aren't needed any more.
1183
self.assertEqual(None, sys.gettrace())
1185
def test_call(self):
1188
replacer = lazy_import.ScopeReplacer({}, factory, 'name')
1189
self.run_race(replacer)
1191
def test_setattr(self):
1198
replacer = lazy_import.ScopeReplacer({}, factory, 'name')
1203
self.run_race(racer)
1205
def test_getattribute(self):
1212
replacer = lazy_import.ScopeReplacer({}, factory, 'name')
1217
self.run_race(racer)