~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_lazy_import.py

  • Committer: Patch Queue Manager
  • Date: 2016-01-15 09:21:49 UTC
  • mfrom: (6606.2.1 autodoc-unicode)
  • Revision ID: pqm@pqm.ubuntu.com-20160115092149-z5f4sfq3jvaz0enb
(vila) Fix autodoc runner when LANG=C. (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 Canonical Ltd
 
1
# Copyright (C) 2006-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
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 (
24
26
    lazy_import,
25
27
    osutils,
26
28
    )
27
 
from bzrlib.tests import TestCase, TestCaseInTempDir
 
29
from bzrlib.tests import (
 
30
    TestCase,
 
31
    TestCaseInTempDir,
 
32
    )
28
33
 
29
34
 
30
35
class InstrumentedReplacer(lazy_import.ScopeReplacer):
34
39
    def use_actions(actions):
35
40
        InstrumentedReplacer.actions = actions
36
41
 
37
 
    def _replace(self):
38
 
        InstrumentedReplacer.actions.append('_replace')
39
 
        return lazy_import.ScopeReplacer._replace(self)
40
 
 
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)
59
60
 
60
 
    def _replace(self):
61
 
        InstrumentedImportReplacer.actions.append('_replace')
62
 
        return lazy_import.ScopeReplacer._replace(self)
63
 
 
64
61
    def __getattribute__(self, attr):
65
62
        InstrumentedImportReplacer.actions.append(('__getattribute__', attr))
66
63
        return lazy_import.ScopeReplacer.__getattribute__(self, attr)
97
94
    """
98
95
 
99
96
    def setUp(self):
100
 
        TestCase.setUp(self)
 
97
        super(TestScopeReplacer, self).setUp()
101
98
        # These tests assume we will not be proxying, so make sure proxying is
102
99
        # disabled.
103
100
        orig_proxy = lazy_import.ScopeReplacer._should_proxy
127
124
        else:
128
125
            self.fail('test_obj1 was not supposed to exist yet')
129
126
 
130
 
        orig_globals = set(globals().keys())
131
 
 
132
127
        InstrumentedReplacer(scope=globals(), name='test_obj1',
133
128
                             factory=factory)
134
129
 
135
 
        new_globals = set(globals().keys())
136
 
 
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
143
136
        self.assertIsInstance(test_obj1, TestClass)
144
137
        self.assertEqual('foo', test_obj1.foo(2))
145
138
        self.assertEqual([('__getattribute__', 'foo'),
146
 
                          '_replace',
147
139
                          'factory',
148
140
                          'init',
149
141
                          ('foo', 1),
168
160
        else:
169
161
            self.fail('test_obj6 was not supposed to exist yet')
170
162
 
171
 
        orig_globals = set(globals().keys())
172
 
 
173
163
        lazy_import.ScopeReplacer(scope=globals(), name='test_obj6',
174
164
                                  factory=factory)
175
165
 
176
 
        new_globals = set(globals().keys())
177
 
 
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
246
234
        self.assertEqual('class_member', test_class1.class_member)
247
235
        self.assertEqual(test_class1, TestClass)
248
236
        self.assertEqual([('__getattribute__', 'class_member'),
249
 
                          '_replace',
250
237
                          'factory',
251
238
                         ], actions)
252
239
 
270
257
        InstrumentedReplacer(scope=globals(), name='test_class2',
271
258
                             factory=factory)
272
259
 
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__', (), {}),
279
 
                          '_replace',
280
266
                          'factory',
281
267
                          'init',
282
268
                         ], actions)
303
289
        InstrumentedReplacer(scope=globals(), name='test_func1',
304
290
                             factory=factory)
305
291
 
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)
309
295
 
310
296
        self.assertEqual((1,2,'3'), val)
311
297
        self.assertEqual([('__call__', (1,2), {'c':'3'}),
312
 
                          '_replace',
313
298
                          'factory',
314
299
                          'func',
315
300
                         ], actions)
371
356
                          getattr, test_obj3, 'foo')
372
357
 
373
358
        self.assertEqual([('__getattribute__', 'foo'),
374
 
                          '_replace',
375
359
                          'factory',
376
360
                          'init',
377
361
                          ('foo', 1),
378
362
                          ('foo', 2),
379
363
                          ('foo', 3),
380
364
                          ('__getattribute__', 'foo'),
381
 
                          '_replace',
382
365
                         ], actions)
383
366
 
384
367
    def test_enable_proxying(self):
429
412
                         object.__getattribute__(test_obj5, '__class__'))
430
413
 
431
414
        self.assertEqual([('__getattribute__', 'foo'),
432
 
                          '_replace',
433
415
                          'factory',
434
416
                          'init',
435
417
                          ('foo', 1),
440
422
                          ('foo', 4),
441
423
                         ], actions)
442
424
 
 
425
    def test_replacing_from_own_scope_fails(self):
 
426
        """If a ScopeReplacer tries to replace itself a nice error is given"""
 
427
        actions = []
 
428
        InstrumentedReplacer.use_actions(actions)
 
429
        TestClass.use_actions(actions)
 
430
 
 
431
        def factory(replacer, scope, name):
 
432
            actions.append('factory')
 
433
            # return the name in given scope, which is currently the replacer
 
434
            return scope[name]
 
435
 
 
436
        try:
 
437
            test_obj7
 
438
        except NameError:
 
439
            # test_obj7 shouldn't exist yet
 
440
            pass
 
441
        else:
 
442
            self.fail('test_obj7 was not supposed to exist yet')
 
443
 
 
444
        InstrumentedReplacer(scope=globals(), name='test_obj7',
 
445
                             factory=factory)
 
446
 
 
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__', (), {}),
 
452
                          'factory'], actions)
 
453
 
443
454
 
444
455
class ImportReplacerHelper(TestCaseInTempDir):
445
456
    """Test the ability to have a lazily imported module or object"""
446
457
 
447
458
    def setUp(self):
448
 
        TestCaseInTempDir.setUp(self)
 
459
        super(ImportReplacerHelper, self).setUp()
449
460
        self.create_modules()
450
461
        base_path = self.test_dir + '/base'
451
462
 
456
467
        self.addCleanup(sys.path.remove, base_path)
457
468
 
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)
462
473
        def cleanup():
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)
542
553
 
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)
546
557
 
547
 
        self.assertEqual([('import', sub_mod_path, []),
548
 
                          ('import', mod_path, []),
 
558
        self.assertEqual([('import', sub_mod_path, [], 0),
 
559
                          ('import', mod_path, [], 0),
549
560
                         ], self.actions)
550
561
 
551
562
 
572
583
        self.assertEqual('x', root1.func1('x'))
573
584
 
574
585
        self.assertEqual([('__getattribute__', 'var1'),
575
 
                          '_replace',
576
586
                          ('_import', 'root1'),
577
 
                          ('import', self.root_name, []),
 
587
                          ('import', self.root_name, [], 0),
578
588
                         ], self.actions)
579
589
 
580
590
    def test_import_mod(self):
598
608
        self.assertEqual('y', mod1.func2('y'))
599
609
 
600
610
        self.assertEqual([('__getattribute__', 'var2'),
601
 
                          '_replace',
602
611
                          ('_import', 'mod1'),
603
 
                          ('import', mod_path, []),
 
612
                          ('import', mod_path, [], 0),
604
613
                         ], self.actions)
605
614
 
606
615
    def test_import_mod_from_root(self):
623
632
        self.assertEqual('y', mod2.func2('y'))
624
633
 
625
634
        self.assertEqual([('__getattribute__', 'var2'),
626
 
                          '_replace',
627
635
                          ('_import', 'mod2'),
628
 
                          ('import', self.root_name, [self.mod_name]),
 
636
                          ('import', self.root_name, [self.mod_name], 0),
629
637
                         ], self.actions)
630
638
 
631
639
    def test_import_root_and_mod(self):
656
664
 
657
665
        mod_path = self.root_name + '.' + self.mod_name
658
666
        self.assertEqual([('__getattribute__', 'var1'),
659
 
                          '_replace',
660
667
                          ('_import', 'root3'),
661
 
                          ('import', self.root_name, []),
 
668
                          ('import', self.root_name, [], 0),
662
669
                          ('__getattribute__', 'var2'),
663
 
                          '_replace',
664
670
                          ('_import', 'mod3'),
665
 
                          ('import', mod_path, []),
 
671
                          ('import', mod_path, [], 0),
666
672
                         ], self.actions)
667
673
 
668
674
    def test_import_root_and_root_mod(self):
700
706
 
701
707
        mod_path = self.root_name + '.' + self.mod_name
702
708
        self.assertEqual([('__getattribute__', 'mod4'),
703
 
                          '_replace',
704
709
                          ('_import', 'root4'),
705
 
                          ('import', self.root_name, []),
 
710
                          ('import', self.root_name, [], 0),
706
711
                          ('__getattribute__', 'var2'),
707
 
                          '_replace',
708
712
                          ('_import', 'mod4'),
709
 
                          ('import', mod_path, []),
 
713
                          ('import', mod_path, [], 0),
710
714
                         ], self.actions)
711
715
 
712
716
    def test_import_root_sub_submod(self):
765
769
        submodb_path = sub_path + '.' + self.submodb_name
766
770
 
767
771
        self.assertEqual([('__getattribute__', 'mod5'),
768
 
                          '_replace',
769
772
                          ('_import', 'root5'),
770
 
                          ('import', self.root_name, []),
 
773
                          ('import', self.root_name, [], 0),
771
774
                          ('__getattribute__', 'submoda5'),
772
 
                          '_replace',
773
775
                          ('_import', 'sub5'),
774
 
                          ('import', sub_path, []),
 
776
                          ('import', sub_path, [], 0),
775
777
                          ('__getattribute__', 'var2'),
776
 
                          '_replace',
777
778
                          ('_import', 'mod5'),
778
 
                          ('import', mod_path, []),
 
779
                          ('import', mod_path, [], 0),
779
780
                          ('__getattribute__', 'var4'),
780
 
                          '_replace',
781
781
                          ('_import', 'submoda5'),
782
 
                          ('import', submoda_path, []),
 
782
                          ('import', submoda_path, [], 0),
783
783
                          ('__getattribute__', 'var5'),
784
 
                          '_replace',
785
784
                          ('_import', 'submodb5'),
786
 
                          ('import', submodb_path, []),
 
785
                          ('import', submodb_path, [], 0),
787
786
                         ], self.actions)
788
787
 
789
788
 
1077
1076
        self.assertEqual('x', root6.func1('x'))
1078
1077
 
1079
1078
        self.assertEqual([('__getattribute__', 'var1'),
1080
 
                          '_replace',
1081
1079
                          ('_import', 'root6'),
1082
 
                          ('import', self.root_name, []),
 
1080
                          ('import', self.root_name, [], 0),
1083
1081
                         ], self.actions)
1084
1082
 
1085
1083
    def test_import_deep(self):
1113
1111
        submoda_path = sub_path + '.' + self.submoda_name
1114
1112
 
1115
1113
        self.assertEqual([('__getattribute__', 'var4'),
1116
 
                          '_replace',
1117
1114
                          ('_import', 'submoda7'),
1118
 
                          ('import', submoda_path, []),
 
1115
                          ('import', submoda_path, [], 0),
1119
1116
                         ], self.actions)
1120
1117
 
1121
1118
    def test_lazy_import(self):
1138
1135
        self.assertEqual(1, root8.func1(1))
1139
1136
 
1140
1137
        self.assertEqual([('__getattribute__', 'var1'),
1141
 
                          '_replace',
1142
1138
                          ('_import', 'root8'),
1143
 
                          ('import', self.root_name, []),
 
1139
                          ('import', self.root_name, [], 0),
1144
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)