~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_lazy_import.py

  • Committer: Jelmer Vernooij
  • Date: 2012-02-20 12:19:29 UTC
  • mfrom: (6437.23.11 2.5)
  • mto: (6581.1.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 6582.
  • Revision ID: jelmer@samba.org-20120220121929-7ni2psvjoatm1yp4
Merge bzr/2.5.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""Test the lazy_import functionality."""
18
18
 
 
19
import linecache
19
20
import os
 
21
import re
20
22
import sys
21
23
 
22
24
from bzrlib import (
37
39
    def use_actions(actions):
38
40
        InstrumentedReplacer.actions = actions
39
41
 
40
 
    def _replace(self):
41
 
        InstrumentedReplacer.actions.append('_replace')
42
 
        return lazy_import.ScopeReplacer._replace(self)
43
 
 
44
42
    def __getattribute__(self, attr):
45
43
        InstrumentedReplacer.actions.append(('__getattribute__', attr))
46
44
        return lazy_import.ScopeReplacer.__getattribute__(self, attr)
60
58
        InstrumentedImportReplacer.actions.append(('_import', name))
61
59
        return lazy_import.ImportReplacer._import(self, scope, name)
62
60
 
63
 
    def _replace(self):
64
 
        InstrumentedImportReplacer.actions.append('_replace')
65
 
        return lazy_import.ScopeReplacer._replace(self)
66
 
 
67
61
    def __getattribute__(self, attr):
68
62
        InstrumentedImportReplacer.actions.append(('__getattribute__', attr))
69
63
        return lazy_import.ScopeReplacer.__getattribute__(self, attr)
142
136
        self.assertIsInstance(test_obj1, TestClass)
143
137
        self.assertEqual('foo', test_obj1.foo(2))
144
138
        self.assertEqual([('__getattribute__', 'foo'),
145
 
                          '_replace',
146
139
                          'factory',
147
140
                          'init',
148
141
                          ('foo', 1),
241
234
        self.assertEqual('class_member', test_class1.class_member)
242
235
        self.assertEqual(test_class1, TestClass)
243
236
        self.assertEqual([('__getattribute__', 'class_member'),
244
 
                          '_replace',
245
237
                          'factory',
246
238
                         ], actions)
247
239
 
271
263
        self.assertIsInstance(obj, TestClass)
272
264
        self.assertEqual('class_member', obj.class_member)
273
265
        self.assertEqual([('__call__', (), {}),
274
 
                          '_replace',
275
266
                          'factory',
276
267
                          'init',
277
268
                         ], actions)
304
295
 
305
296
        self.assertEqual((1,2,'3'), val)
306
297
        self.assertEqual([('__call__', (1,2), {'c':'3'}),
307
 
                          '_replace',
308
298
                          'factory',
309
299
                          'func',
310
300
                         ], actions)
366
356
                          getattr, test_obj3, 'foo')
367
357
 
368
358
        self.assertEqual([('__getattribute__', 'foo'),
369
 
                          '_replace',
370
359
                          'factory',
371
360
                          'init',
372
361
                          ('foo', 1),
373
362
                          ('foo', 2),
374
363
                          ('foo', 3),
375
364
                          ('__getattribute__', 'foo'),
376
 
                          '_replace',
377
365
                         ], actions)
378
366
 
379
367
    def test_enable_proxying(self):
424
412
                         object.__getattribute__(test_obj5, '__class__'))
425
413
 
426
414
        self.assertEqual([('__getattribute__', 'foo'),
427
 
                          '_replace',
428
415
                          'factory',
429
416
                          'init',
430
417
                          ('foo', 1),
462
449
        e = self.assertRaises(errors.IllegalUseOfScopeReplacer, test_obj7)
463
450
        self.assertIn("replace itself", e.msg)
464
451
        self.assertEqual([('__call__', (), {}),
465
 
                          '_replace',
466
452
                          'factory'], actions)
467
453
 
468
454
 
481
467
        self.addCleanup(sys.path.remove, base_path)
482
468
 
483
469
        original_import = __import__
484
 
        def instrumented_import(mod, scope1, scope2, fromlist):
485
 
            self.actions.append(('import', mod, fromlist))
486
 
            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)
487
473
        def cleanup():
488
474
            __builtins__['__import__'] = original_import
489
475
        self.addCleanup(cleanup)
559
545
        """Test that a real import of these modules works"""
560
546
        sub_mod_path = '.'.join([self.root_name, self.sub_name,
561
547
                                  self.submoda_name])
562
 
        root = __import__(sub_mod_path, globals(), locals(), [])
 
548
        root = __import__(sub_mod_path, globals(), locals(), [], 0)
563
549
        self.assertEqual(1, root.var1)
564
550
        self.assertEqual(3, getattr(root, self.sub_name).var3)
565
551
        self.assertEqual(4, getattr(getattr(root, self.sub_name),
566
552
                                    self.submoda_name).var4)
567
553
 
568
554
        mod_path = '.'.join([self.root_name, self.mod_name])
569
 
        root = __import__(mod_path, globals(), locals(), [])
 
555
        root = __import__(mod_path, globals(), locals(), [], 0)
570
556
        self.assertEqual(2, getattr(root, self.mod_name).var2)
571
557
 
572
 
        self.assertEqual([('import', sub_mod_path, []),
573
 
                          ('import', mod_path, []),
 
558
        self.assertEqual([('import', sub_mod_path, [], 0),
 
559
                          ('import', mod_path, [], 0),
574
560
                         ], self.actions)
575
561
 
576
562
 
597
583
        self.assertEqual('x', root1.func1('x'))
598
584
 
599
585
        self.assertEqual([('__getattribute__', 'var1'),
600
 
                          '_replace',
601
586
                          ('_import', 'root1'),
602
 
                          ('import', self.root_name, []),
 
587
                          ('import', self.root_name, [], 0),
603
588
                         ], self.actions)
604
589
 
605
590
    def test_import_mod(self):
623
608
        self.assertEqual('y', mod1.func2('y'))
624
609
 
625
610
        self.assertEqual([('__getattribute__', 'var2'),
626
 
                          '_replace',
627
611
                          ('_import', 'mod1'),
628
 
                          ('import', mod_path, []),
 
612
                          ('import', mod_path, [], 0),
629
613
                         ], self.actions)
630
614
 
631
615
    def test_import_mod_from_root(self):
648
632
        self.assertEqual('y', mod2.func2('y'))
649
633
 
650
634
        self.assertEqual([('__getattribute__', 'var2'),
651
 
                          '_replace',
652
635
                          ('_import', 'mod2'),
653
 
                          ('import', self.root_name, [self.mod_name]),
 
636
                          ('import', self.root_name, [self.mod_name], 0),
654
637
                         ], self.actions)
655
638
 
656
639
    def test_import_root_and_mod(self):
681
664
 
682
665
        mod_path = self.root_name + '.' + self.mod_name
683
666
        self.assertEqual([('__getattribute__', 'var1'),
684
 
                          '_replace',
685
667
                          ('_import', 'root3'),
686
 
                          ('import', self.root_name, []),
 
668
                          ('import', self.root_name, [], 0),
687
669
                          ('__getattribute__', 'var2'),
688
 
                          '_replace',
689
670
                          ('_import', 'mod3'),
690
 
                          ('import', mod_path, []),
 
671
                          ('import', mod_path, [], 0),
691
672
                         ], self.actions)
692
673
 
693
674
    def test_import_root_and_root_mod(self):
725
706
 
726
707
        mod_path = self.root_name + '.' + self.mod_name
727
708
        self.assertEqual([('__getattribute__', 'mod4'),
728
 
                          '_replace',
729
709
                          ('_import', 'root4'),
730
 
                          ('import', self.root_name, []),
 
710
                          ('import', self.root_name, [], 0),
731
711
                          ('__getattribute__', 'var2'),
732
 
                          '_replace',
733
712
                          ('_import', 'mod4'),
734
 
                          ('import', mod_path, []),
 
713
                          ('import', mod_path, [], 0),
735
714
                         ], self.actions)
736
715
 
737
716
    def test_import_root_sub_submod(self):
790
769
        submodb_path = sub_path + '.' + self.submodb_name
791
770
 
792
771
        self.assertEqual([('__getattribute__', 'mod5'),
793
 
                          '_replace',
794
772
                          ('_import', 'root5'),
795
 
                          ('import', self.root_name, []),
 
773
                          ('import', self.root_name, [], 0),
796
774
                          ('__getattribute__', 'submoda5'),
797
 
                          '_replace',
798
775
                          ('_import', 'sub5'),
799
 
                          ('import', sub_path, []),
 
776
                          ('import', sub_path, [], 0),
800
777
                          ('__getattribute__', 'var2'),
801
 
                          '_replace',
802
778
                          ('_import', 'mod5'),
803
 
                          ('import', mod_path, []),
 
779
                          ('import', mod_path, [], 0),
804
780
                          ('__getattribute__', 'var4'),
805
 
                          '_replace',
806
781
                          ('_import', 'submoda5'),
807
 
                          ('import', submoda_path, []),
 
782
                          ('import', submoda_path, [], 0),
808
783
                          ('__getattribute__', 'var5'),
809
 
                          '_replace',
810
784
                          ('_import', 'submodb5'),
811
 
                          ('import', submodb_path, []),
 
785
                          ('import', submodb_path, [], 0),
812
786
                         ], self.actions)
813
787
 
814
788
 
1102
1076
        self.assertEqual('x', root6.func1('x'))
1103
1077
 
1104
1078
        self.assertEqual([('__getattribute__', 'var1'),
1105
 
                          '_replace',
1106
1079
                          ('_import', 'root6'),
1107
 
                          ('import', self.root_name, []),
 
1080
                          ('import', self.root_name, [], 0),
1108
1081
                         ], self.actions)
1109
1082
 
1110
1083
    def test_import_deep(self):
1138
1111
        submoda_path = sub_path + '.' + self.submoda_name
1139
1112
 
1140
1113
        self.assertEqual([('__getattribute__', 'var4'),
1141
 
                          '_replace',
1142
1114
                          ('_import', 'submoda7'),
1143
 
                          ('import', submoda_path, []),
 
1115
                          ('import', submoda_path, [], 0),
1144
1116
                         ], self.actions)
1145
1117
 
1146
1118
    def test_lazy_import(self):
1163
1135
        self.assertEqual(1, root8.func1(1))
1164
1136
 
1165
1137
        self.assertEqual([('__getattribute__', 'var1'),
1166
 
                          '_replace',
1167
1138
                          ('_import', 'root8'),
1168
 
                          ('import', self.root_name, []),
 
1139
                          ('import', self.root_name, [], 0),
1169
1140
                         ], self.actions)
 
1141
 
 
1142
 
 
1143
class TestScopeReplacerReentrance(TestCase):
 
1144
    """The ScopeReplacer should be reentrant.
 
1145
 
 
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).
 
1149
 
 
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.
 
1154
    """
 
1155
 
 
1156
    def tracer(self, frame, event, arg):
 
1157
        if event != 'call':
 
1158
            return self.tracer
 
1159
        # Grab the name of the file that contains the code being executed.
 
1160
        code = frame.f_code
 
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.
 
1169
            sys.settrace(None)
 
1170
            # Run another racer.  This one will "win" the race.
 
1171
            self.racer()
 
1172
        return self.tracer
 
1173
 
 
1174
    def run_race(self, racer, method_to_trace='_resolve'):
 
1175
        self.overrideAttr(lazy_import.ScopeReplacer, '_should_proxy', True)
 
1176
        self.racer = racer
 
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())
 
1184
 
 
1185
    def test_call(self):
 
1186
        def factory(*args):
 
1187
            return factory
 
1188
        replacer = lazy_import.ScopeReplacer({}, factory, 'name')
 
1189
        self.run_race(replacer)
 
1190
 
 
1191
    def test_setattr(self):
 
1192
        class Replaced:
 
1193
            pass
 
1194
 
 
1195
        def factory(*args):
 
1196
            return Replaced()
 
1197
 
 
1198
        replacer = lazy_import.ScopeReplacer({}, factory, 'name')
 
1199
 
 
1200
        def racer():
 
1201
            replacer.foo = 42
 
1202
 
 
1203
        self.run_race(racer)
 
1204
 
 
1205
    def test_getattribute(self):
 
1206
        class Replaced:
 
1207
            foo = 'bar'
 
1208
 
 
1209
        def factory(*args):
 
1210
            return Replaced()
 
1211
 
 
1212
        replacer = lazy_import.ScopeReplacer({}, factory, 'name')
 
1213
 
 
1214
        def racer():
 
1215
            replacer.foo
 
1216
 
 
1217
        self.run_race(racer)