13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
"""Test the lazy_import functionality."""
24
22
from bzrlib import (
39
34
def use_actions(actions):
40
35
InstrumentedReplacer.actions = actions
38
InstrumentedReplacer.actions.append('_replace')
39
return lazy_import.ScopeReplacer._replace(self)
42
41
def __getattribute__(self, attr):
43
42
InstrumentedReplacer.actions.append(('__getattribute__', attr))
44
43
return lazy_import.ScopeReplacer.__getattribute__(self, attr)
58
57
InstrumentedImportReplacer.actions.append(('_import', name))
59
58
return lazy_import.ImportReplacer._import(self, scope, name)
61
InstrumentedImportReplacer.actions.append('_replace')
62
return lazy_import.ScopeReplacer._replace(self)
61
64
def __getattribute__(self, attr):
62
65
InstrumentedImportReplacer.actions.append(('__getattribute__', attr))
63
66
return lazy_import.ScopeReplacer.__getattribute__(self, attr)
125
128
self.fail('test_obj1 was not supposed to exist yet')
130
orig_globals = set(globals().keys())
127
132
InstrumentedReplacer(scope=globals(), name='test_obj1',
135
new_globals = set(globals().keys())
130
137
# We can't use isinstance() because that uses test_obj1.__class__
131
138
# and that goes through __getattribute__ which would activate
132
139
# the replacement
161
169
self.fail('test_obj6 was not supposed to exist yet')
171
orig_globals = set(globals().keys())
163
173
lazy_import.ScopeReplacer(scope=globals(), name='test_obj6',
176
new_globals = set(globals().keys())
166
178
# We can't use isinstance() because that uses test_obj6.__class__
167
179
# and that goes through __getattribute__ which would activate
168
180
# the replacement
257
270
InstrumentedReplacer(scope=globals(), name='test_class2',
260
self.assertFalse(test_class2 is TestClass)
273
self.failIf(test_class2 is TestClass)
261
274
obj = test_class2()
262
275
self.assertIs(test_class2, TestClass)
263
276
self.assertIsInstance(obj, TestClass)
264
277
self.assertEqual('class_member', obj.class_member)
265
278
self.assertEqual([('__call__', (), {}),
289
303
InstrumentedReplacer(scope=globals(), name='test_func1',
292
self.assertFalse(test_func1 is func)
306
self.failIf(test_func1 is func)
293
307
val = test_func1(1, 2, c='3')
294
308
self.assertIs(test_func1, func)
296
310
self.assertEqual((1,2,'3'), val)
297
311
self.assertEqual([('__call__', (1,2), {'c':'3'}),
354
369
# because only now are we able to detect the problem.
355
370
self.assertRaises(errors.IllegalUseOfScopeReplacer,
356
371
getattr, test_obj3, 'foo')
358
373
self.assertEqual([('__getattribute__', 'foo'),
364
380
('__getattribute__', 'foo'),
367
384
def test_enable_proxying(self):
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__', (), {}),
455
444
class ImportReplacerHelper(TestCaseInTempDir):
456
445
"""Test the ability to have a lazily imported module or object"""
459
super(ImportReplacerHelper, self).setUp()
448
TestCaseInTempDir.setUp(self)
460
449
self.create_modules()
461
450
base_path = self.test_dir + '/base'
463
452
self.actions = []
464
453
InstrumentedImportReplacer.use_actions(self.actions)
466
sys.path.append(base_path)
467
self.addCleanup(sys.path.remove, base_path)
469
455
original_import = __import__
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)
456
def instrumented_import(mod, scope1, scope2, fromlist):
457
self.actions.append(('import', mod, fromlist))
458
return original_import(mod, scope1, scope2, fromlist)
461
if base_path in sys.path:
462
sys.path.remove(base_path)
474
463
__builtins__['__import__'] = original_import
475
464
self.addCleanup(cleanup)
465
sys.path.append(base_path)
476
466
__builtins__['__import__'] = instrumented_import
478
468
def create_modules(self):
545
535
"""Test that a real import of these modules works"""
546
536
sub_mod_path = '.'.join([self.root_name, self.sub_name,
547
537
self.submoda_name])
548
root = __import__(sub_mod_path, globals(), locals(), [], 0)
538
root = __import__(sub_mod_path, globals(), locals(), [])
549
539
self.assertEqual(1, root.var1)
550
540
self.assertEqual(3, getattr(root, self.sub_name).var3)
551
541
self.assertEqual(4, getattr(getattr(root, self.sub_name),
552
542
self.submoda_name).var4)
554
544
mod_path = '.'.join([self.root_name, self.mod_name])
555
root = __import__(mod_path, globals(), locals(), [], 0)
545
root = __import__(mod_path, globals(), locals(), [])
556
546
self.assertEqual(2, getattr(root, self.mod_name).var2)
558
self.assertEqual([('import', sub_mod_path, [], 0),
559
('import', mod_path, [], 0),
548
self.assertEqual([('import', sub_mod_path, []),
549
('import', mod_path, []),
583
573
self.assertEqual('x', root1.func1('x'))
585
575
self.assertEqual([('__getattribute__', 'var1'),
586
577
('_import', 'root1'),
587
('import', self.root_name, [], 0),
578
('import', self.root_name, []),
590
581
def test_import_mod(self):
608
599
self.assertEqual('y', mod1.func2('y'))
610
601
self.assertEqual([('__getattribute__', 'var2'),
611
603
('_import', 'mod1'),
612
('import', mod_path, [], 0),
604
('import', mod_path, []),
615
607
def test_import_mod_from_root(self):
632
624
self.assertEqual('y', mod2.func2('y'))
634
626
self.assertEqual([('__getattribute__', 'var2'),
635
628
('_import', 'mod2'),
636
('import', self.root_name, [self.mod_name], 0),
629
('import', self.root_name, [self.mod_name]),
639
632
def test_import_root_and_mod(self):
665
658
mod_path = self.root_name + '.' + self.mod_name
666
659
self.assertEqual([('__getattribute__', 'var1'),
667
661
('_import', 'root3'),
668
('import', self.root_name, [], 0),
662
('import', self.root_name, []),
669
663
('__getattribute__', 'var2'),
670
665
('_import', 'mod3'),
671
('import', mod_path, [], 0),
666
('import', mod_path, []),
674
669
def test_import_root_and_root_mod(self):
707
702
mod_path = self.root_name + '.' + self.mod_name
708
703
self.assertEqual([('__getattribute__', 'mod4'),
709
705
('_import', 'root4'),
710
('import', self.root_name, [], 0),
706
('import', self.root_name, []),
711
707
('__getattribute__', 'var2'),
712
709
('_import', 'mod4'),
713
('import', mod_path, [], 0),
710
('import', mod_path, []),
716
713
def test_import_root_sub_submod(self):
769
766
submodb_path = sub_path + '.' + self.submodb_name
771
768
self.assertEqual([('__getattribute__', 'mod5'),
772
770
('_import', 'root5'),
773
('import', self.root_name, [], 0),
771
('import', self.root_name, []),
774
772
('__getattribute__', 'submoda5'),
775
774
('_import', 'sub5'),
776
('import', sub_path, [], 0),
775
('import', sub_path, []),
777
776
('__getattribute__', 'var2'),
778
778
('_import', 'mod5'),
779
('import', mod_path, [], 0),
779
('import', mod_path, []),
780
780
('__getattribute__', 'var4'),
781
782
('_import', 'submoda5'),
782
('import', submoda_path, [], 0),
783
('import', submoda_path, []),
783
784
('__getattribute__', 'var5'),
784
786
('_import', 'submodb5'),
785
('import', submodb_path, [], 0),
787
('import', submodb_path, []),
1076
1078
self.assertEqual('x', root6.func1('x'))
1078
1080
self.assertEqual([('__getattribute__', 'var1'),
1079
1082
('_import', 'root6'),
1080
('import', self.root_name, [], 0),
1083
('import', self.root_name, []),
1081
1084
], self.actions)
1083
1086
def test_import_deep(self):
1111
1114
submoda_path = sub_path + '.' + self.submoda_name
1113
1116
self.assertEqual([('__getattribute__', 'var4'),
1114
1118
('_import', 'submoda7'),
1115
('import', submoda_path, [], 0),
1119
('import', submoda_path, []),
1116
1120
], self.actions)
1118
1122
def test_lazy_import(self):
1135
1139
self.assertEqual(1, root8.func1(1))
1137
1141
self.assertEqual([('__getattribute__', 'var1'),
1138
1143
('_import', 'root8'),
1139
('import', self.root_name, [], 0),
1144
('import', self.root_name, []),
1140
1145
], 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)