/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5557.1.15 by John Arbash Meinel
Merge bzr.dev 5597 to resolve NEWS, aka bzr-2.3.txt
1
# Copyright (C) 2006-2011 Canonical Ltd
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
2
#
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.
7
#
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.
12
#
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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
16
17
"""Test the lazy_import functionality."""
18
6082.4.1 by Benji York
fix bug 702914
19
import linecache
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
20
import os
6082.4.1 by Benji York
fix bug 702914
21
import re
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
22
import sys
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
23
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
24
from .. import (
1996.1.16 by John Arbash Meinel
Raise an exception when ScopeReplacer has been misused
25
    errors,
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
26
    lazy_import,
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
27
    osutils,
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
28
    )
6624 by Jelmer Vernooij
Merge Python3 porting work ('py3 pokes')
29
from . import (
5579.3.1 by Jelmer Vernooij
Remove unused imports.
30
    TestCase,
31
    TestCaseInTempDir,
32
    )
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
33
34
35
class InstrumentedReplacer(lazy_import.ScopeReplacer):
36
    """Track what actions are done"""
37
38
    @staticmethod
39
    def use_actions(actions):
40
        InstrumentedReplacer.actions = actions
41
42
    def __getattribute__(self, attr):
43
        InstrumentedReplacer.actions.append(('__getattribute__', attr))
44
        return lazy_import.ScopeReplacer.__getattribute__(self, attr)
45
46
    def __call__(self, *args, **kwargs):
47
        InstrumentedReplacer.actions.append(('__call__', args, kwargs))
48
        return lazy_import.ScopeReplacer.__call__(self, *args, **kwargs)
49
50
1996.1.3 by John Arbash Meinel
Basic single-level imports work
51
class InstrumentedImportReplacer(lazy_import.ImportReplacer):
52
53
    @staticmethod
54
    def use_actions(actions):
55
        InstrumentedImportReplacer.actions = actions
56
57
    def _import(self, scope, name):
58
        InstrumentedImportReplacer.actions.append(('_import', name))
59
        return lazy_import.ImportReplacer._import(self, scope, name)
60
61
    def __getattribute__(self, attr):
62
        InstrumentedImportReplacer.actions.append(('__getattribute__', attr))
63
        return lazy_import.ScopeReplacer.__getattribute__(self, attr)
64
65
    def __call__(self, *args, **kwargs):
66
        InstrumentedImportReplacer.actions.append(('__call__', args, kwargs))
67
        return lazy_import.ScopeReplacer.__call__(self, *args, **kwargs)
68
69
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
70
class TestClass(object):
71
    """Just a simple test class instrumented for the test cases"""
72
73
    class_member = 'class_member'
74
75
    @staticmethod
76
    def use_actions(actions):
77
        TestClass.actions = actions
78
79
    def __init__(self):
80
        TestClass.actions.append('init')
81
82
    def foo(self, x):
83
        TestClass.actions.append(('foo', x))
84
        return 'foo'
85
86
87
class TestScopeReplacer(TestCase):
88
    """Test the ability of the replacer to put itself into the correct scope.
89
90
    In these tests we use the global scope, because we cannot replace
91
    variables in the local scope. This means that we need to be careful
92
    and not have the replacing objects use the same name, or we would
93
    get collisions.
94
    """
95
2399.1.11 by John Arbash Meinel
Update lazy_import with tests for the new '_should_proxy' variable.
96
    def setUp(self):
6552.1.4 by Vincent Ladeuil
Remaining tests matching setup(self) that can be rewritten with super().
97
        super(TestScopeReplacer, self).setUp()
2399.1.11 by John Arbash Meinel
Update lazy_import with tests for the new '_should_proxy' variable.
98
        # These tests assume we will not be proxying, so make sure proxying is
99
        # disabled.
100
        orig_proxy = lazy_import.ScopeReplacer._should_proxy
101
        def restore():
102
            lazy_import.ScopeReplacer._should_proxy = orig_proxy
103
        lazy_import.ScopeReplacer._should_proxy = False
104
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
105
    def test_object(self):
1996.1.27 by John Arbash Meinel
Add a test for side-effects from using ScopeReplacer
106
        """ScopeReplacer can create an instance in local scope.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
107
1996.1.27 by John Arbash Meinel
Add a test for side-effects from using ScopeReplacer
108
        An object should appear in globals() by constructing a ScopeReplacer,
109
        and it will be replaced with the real object upon the first request.
110
        """
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
111
        actions = []
112
        InstrumentedReplacer.use_actions(actions)
113
        TestClass.use_actions(actions)
114
115
        def factory(replacer, scope, name):
116
            actions.append('factory')
117
            return TestClass()
118
119
        try:
120
            test_obj1
121
        except NameError:
122
            # test_obj1 shouldn't exist yet
123
            pass
124
        else:
125
            self.fail('test_obj1 was not supposed to exist yet')
126
127
        InstrumentedReplacer(scope=globals(), name='test_obj1',
128
                             factory=factory)
129
130
        # We can't use isinstance() because that uses test_obj1.__class__
131
        # and that goes through __getattribute__ which would activate
132
        # the replacement
133
        self.assertEqual(InstrumentedReplacer,
134
                         object.__getattribute__(test_obj1, '__class__'))
135
        self.assertEqual('foo', test_obj1.foo(1))
136
        self.assertIsInstance(test_obj1, TestClass)
137
        self.assertEqual('foo', test_obj1.foo(2))
138
        self.assertEqual([('__getattribute__', 'foo'),
139
                          'factory',
140
                          'init',
141
                          ('foo', 1),
142
                          ('foo', 2),
143
                         ], actions)
144
1551.18.22 by Aaron Bentley
Fix exception when ScopeReplacer is assigned to before retrieving any members
145
    def test_setattr_replaces(self):
146
        """ScopeReplacer can create an instance in local scope.
147
148
        An object should appear in globals() by constructing a ScopeReplacer,
149
        and it will be replaced with the real object upon the first request.
150
        """
3302.5.2 by Vincent Ladeuil
Fix test bug revealed when run alone.
151
        actions = []
152
        TestClass.use_actions(actions)
1551.18.22 by Aaron Bentley
Fix exception when ScopeReplacer is assigned to before retrieving any members
153
        def factory(replacer, scope, name):
154
            return TestClass()
155
        try:
156
            test_obj6
157
        except NameError:
158
            # test_obj6 shouldn't exist yet
159
            pass
160
        else:
161
            self.fail('test_obj6 was not supposed to exist yet')
162
163
        lazy_import.ScopeReplacer(scope=globals(), name='test_obj6',
164
                                  factory=factory)
165
166
        # We can't use isinstance() because that uses test_obj6.__class__
167
        # and that goes through __getattribute__ which would activate
168
        # the replacement
169
        self.assertEqual(lazy_import.ScopeReplacer,
170
                         object.__getattribute__(test_obj6, '__class__'))
171
        test_obj6.bar = 'test'
172
        self.assertNotEqual(lazy_import.ScopeReplacer,
173
                            object.__getattribute__(test_obj6, '__class__'))
1551.18.23 by Aaron Bentley
Ensure that the attribute is actually set
174
        self.assertEqual('test', test_obj6.bar)
1551.18.22 by Aaron Bentley
Fix exception when ScopeReplacer is assigned to before retrieving any members
175
1996.1.27 by John Arbash Meinel
Add a test for side-effects from using ScopeReplacer
176
    def test_replace_side_effects(self):
177
        """Creating a new object should only create one entry in globals.
178
179
        And only that entry even after replacement.
180
        """
181
        try:
182
            test_scope1
183
        except NameError:
184
            # test_scope1 shouldn't exist yet
185
            pass
186
        else:
187
            self.fail('test_scope1 was not supposed to exist yet')
188
189
        # ignore the logged actions
190
        TestClass.use_actions([])
191
192
        def factory(replacer, scope, name):
193
            return TestClass()
194
195
        orig_globals = set(globals().keys())
196
197
        lazy_import.ScopeReplacer(scope=globals(), name='test_scope1',
198
                                  factory=factory)
199
200
        new_globals = set(globals().keys())
201
202
        self.assertEqual(lazy_import.ScopeReplacer,
203
                         object.__getattribute__(test_scope1, '__class__'))
204
        self.assertEqual('foo', test_scope1.foo(1))
205
        self.assertIsInstance(test_scope1, TestClass)
206
207
        final_globals = set(globals().keys())
208
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
209
        self.assertEqual({'test_scope1'}, new_globals - orig_globals)
1996.1.27 by John Arbash Meinel
Add a test for side-effects from using ScopeReplacer
210
        self.assertEqual(set(), orig_globals - new_globals)
211
        self.assertEqual(set(), final_globals - new_globals)
212
        self.assertEqual(set(), new_globals - final_globals)
213
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
214
    def test_class(self):
215
        actions = []
216
        InstrumentedReplacer.use_actions(actions)
217
        TestClass.use_actions(actions)
218
219
        def factory(replacer, scope, name):
220
            actions.append('factory')
221
            return TestClass
222
223
        try:
224
            test_class1
225
        except NameError:
226
            # test_class2 shouldn't exist yet
227
            pass
228
        else:
229
            self.fail('test_class1 was not supposed to exist yet')
230
231
        InstrumentedReplacer(scope=globals(), name='test_class1',
232
                             factory=factory)
233
234
        self.assertEqual('class_member', test_class1.class_member)
235
        self.assertEqual(test_class1, TestClass)
236
        self.assertEqual([('__getattribute__', 'class_member'),
237
                          'factory',
238
                         ], actions)
239
240
    def test_call_class(self):
241
        actions = []
242
        InstrumentedReplacer.use_actions(actions)
243
        TestClass.use_actions(actions)
244
245
        def factory(replacer, scope, name):
246
            actions.append('factory')
247
            return TestClass
248
249
        try:
250
            test_class2
251
        except NameError:
252
            # test_class2 shouldn't exist yet
253
            pass
254
        else:
255
            self.fail('test_class2 was not supposed to exist yet')
256
257
        InstrumentedReplacer(scope=globals(), name='test_class2',
258
                             factory=factory)
259
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
260
        self.assertFalse(test_class2 is TestClass)
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
261
        obj = test_class2()
262
        self.assertIs(test_class2, TestClass)
263
        self.assertIsInstance(obj, TestClass)
264
        self.assertEqual('class_member', obj.class_member)
265
        self.assertEqual([('__call__', (), {}),
266
                          'factory',
267
                          'init',
268
                         ], actions)
269
270
    def test_call_func(self):
271
        actions = []
272
        InstrumentedReplacer.use_actions(actions)
273
274
        def func(a, b, c=None):
275
            actions.append('func')
276
            return (a, b, c)
277
278
        def factory(replacer, scope, name):
279
            actions.append('factory')
280
            return func
281
282
        try:
283
            test_func1
284
        except NameError:
285
            # test_func1 shouldn't exist yet
286
            pass
287
        else:
288
            self.fail('test_func1 was not supposed to exist yet')
289
        InstrumentedReplacer(scope=globals(), name='test_func1',
290
                             factory=factory)
291
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
292
        self.assertFalse(test_func1 is func)
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
293
        val = test_func1(1, 2, c='3')
294
        self.assertIs(test_func1, func)
295
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
296
        self.assertEqual((1, 2, '3'), val)
297
        self.assertEqual([('__call__', (1, 2), {'c':'3'}),
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
298
                          'factory',
299
                          'func',
300
                         ], actions)
301
1996.1.16 by John Arbash Meinel
Raise an exception when ScopeReplacer has been misused
302
    def test_other_variable(self):
303
        """Test when a ScopeReplacer is assigned to another variable.
304
305
        This test could be updated if we find a way to trap '=' rather
306
        than just giving a belated exception.
307
        ScopeReplacer only knows about the variable it was created as,
308
        so until the object is replaced, it is illegal to pass it to
309
        another variable. (Though discovering this may take a while)
310
        """
311
        actions = []
312
        InstrumentedReplacer.use_actions(actions)
313
        TestClass.use_actions(actions)
314
315
        def factory(replacer, scope, name):
316
            actions.append('factory')
317
            return TestClass()
318
319
        try:
320
            test_obj2
321
        except NameError:
322
            # test_obj2 shouldn't exist yet
323
            pass
324
        else:
325
            self.fail('test_obj2 was not supposed to exist yet')
326
327
        InstrumentedReplacer(scope=globals(), name='test_obj2',
328
                             factory=factory)
329
330
        self.assertEqual(InstrumentedReplacer,
331
                         object.__getattribute__(test_obj2, '__class__'))
332
        # This is technically not allowed, but we don't have a way to
333
        # test it until later.
334
        test_obj3 = test_obj2
335
        self.assertEqual(InstrumentedReplacer,
336
                         object.__getattribute__(test_obj2, '__class__'))
337
        self.assertEqual(InstrumentedReplacer,
338
                         object.__getattribute__(test_obj3, '__class__'))
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
339
1996.1.16 by John Arbash Meinel
Raise an exception when ScopeReplacer has been misused
340
        # The first use of the alternate variable causes test_obj2 to
341
        # be replaced.
342
        self.assertEqual('foo', test_obj3.foo(1))
343
        # test_obj2 has been replaced, but the ScopeReplacer has no
344
        # idea of test_obj3
345
        self.assertEqual(TestClass,
346
                         object.__getattribute__(test_obj2, '__class__'))
347
        self.assertEqual(InstrumentedReplacer,
348
                         object.__getattribute__(test_obj3, '__class__'))
349
        # We should be able to access test_obj2 attributes normally
350
        self.assertEqual('foo', test_obj2.foo(2))
351
        self.assertEqual('foo', test_obj2.foo(3))
352
353
        # However, the next access on test_obj3 should raise an error
354
        # because only now are we able to detect the problem.
355
        self.assertRaises(errors.IllegalUseOfScopeReplacer,
356
                          getattr, test_obj3, 'foo')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
357
2399.1.11 by John Arbash Meinel
Update lazy_import with tests for the new '_should_proxy' variable.
358
        self.assertEqual([('__getattribute__', 'foo'),
359
                          'factory',
360
                          'init',
361
                          ('foo', 1),
362
                          ('foo', 2),
363
                          ('foo', 3),
364
                          ('__getattribute__', 'foo'),
365
                         ], actions)
366
367
    def test_enable_proxying(self):
368
        """Test that we can allow ScopeReplacer to proxy."""
369
        actions = []
370
        InstrumentedReplacer.use_actions(actions)
371
        TestClass.use_actions(actions)
372
373
        def factory(replacer, scope, name):
374
            actions.append('factory')
375
            return TestClass()
376
377
        try:
378
            test_obj4
379
        except NameError:
380
            # test_obj4 shouldn't exist yet
381
            pass
382
        else:
383
            self.fail('test_obj4 was not supposed to exist yet')
384
385
        lazy_import.ScopeReplacer._should_proxy = True
386
        InstrumentedReplacer(scope=globals(), name='test_obj4',
387
                             factory=factory)
388
389
        self.assertEqual(InstrumentedReplacer,
390
                         object.__getattribute__(test_obj4, '__class__'))
391
        test_obj5 = test_obj4
392
        self.assertEqual(InstrumentedReplacer,
393
                         object.__getattribute__(test_obj4, '__class__'))
394
        self.assertEqual(InstrumentedReplacer,
395
                         object.__getattribute__(test_obj5, '__class__'))
396
397
        # The first use of the alternate variable causes test_obj2 to
398
        # be replaced.
399
        self.assertEqual('foo', test_obj4.foo(1))
400
        self.assertEqual(TestClass,
401
                         object.__getattribute__(test_obj4, '__class__'))
402
        self.assertEqual(InstrumentedReplacer,
403
                         object.__getattribute__(test_obj5, '__class__'))
404
        # We should be able to access test_obj4 attributes normally
405
        self.assertEqual('foo', test_obj4.foo(2))
406
        # because we enabled proxying, test_obj5 can access its members as well
407
        self.assertEqual('foo', test_obj5.foo(3))
408
        self.assertEqual('foo', test_obj5.foo(4))
409
410
        # However, it cannot be replaced by the ScopeReplacer
411
        self.assertEqual(InstrumentedReplacer,
412
                         object.__getattribute__(test_obj5, '__class__'))
413
414
        self.assertEqual([('__getattribute__', 'foo'),
415
                          'factory',
416
                          'init',
417
                          ('foo', 1),
418
                          ('foo', 2),
419
                          ('__getattribute__', 'foo'),
420
                          ('foo', 3),
421
                          ('__getattribute__', 'foo'),
422
                          ('foo', 4),
1996.1.16 by John Arbash Meinel
Raise an exception when ScopeReplacer has been misused
423
                         ], actions)
424
5409.2.1 by Martin
Add check in lazy import ScopeReplacer to ensure it's not trying to replace itself
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
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
454
1996.1.8 by John Arbash Meinel
Split up TestImportReplacer into a helper class
455
class ImportReplacerHelper(TestCaseInTempDir):
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
456
    """Test the ability to have a lazily imported module or object"""
457
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
458
    def setUp(self):
6552.1.4 by Vincent Ladeuil
Remaining tests matching setup(self) that can be rewritten with super().
459
        super(ImportReplacerHelper, self).setUp()
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
460
        self.create_modules()
461
        base_path = self.test_dir + '/base'
462
1996.1.3 by John Arbash Meinel
Basic single-level imports work
463
        self.actions = []
464
        InstrumentedImportReplacer.use_actions(self.actions)
465
4985.2.1 by Vincent Ladeuil
Deploy addAttrCleanup on the whole test suite.
466
        sys.path.append(base_path)
467
        self.addCleanup(sys.path.remove, base_path)
468
1996.1.3 by John Arbash Meinel
Basic single-level imports work
469
        original_import = __import__
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
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)
1996.1.3 by John Arbash Meinel
Basic single-level imports work
473
        def cleanup():
474
            __builtins__['__import__'] = original_import
475
        self.addCleanup(cleanup)
476
        __builtins__['__import__'] = instrumented_import
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
477
478
    def create_modules(self):
479
        """Create some random modules to be imported.
480
481
        Each entry has a random suffix, and the full names are saved
482
483
        These are setup as follows:
484
         base/ <= used to ensure not in default search path
485
            root-XXX/
486
                __init__.py <= This will contain var1, func1
487
                mod-XXX.py <= This will contain var2, func2
488
                sub-XXX/
489
                    __init__.py <= Contains var3, func3
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
490
                    submoda-XXX.py <= contains var4, func4
491
                    submodb-XXX.py <= containse var5, func5
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
492
        """
493
        rand_suffix = osutils.rand_chars(4)
494
        root_name = 'root_' + rand_suffix
495
        mod_name = 'mod_' + rand_suffix
496
        sub_name = 'sub_' + rand_suffix
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
497
        submoda_name = 'submoda_' + rand_suffix
498
        submodb_name = 'submodb_' + rand_suffix
499
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
500
        os.mkdir('base')
501
        root_path = osutils.pathjoin('base', root_name)
502
        os.mkdir(root_path)
503
        root_init = osutils.pathjoin(root_path, '__init__.py')
6754.7.1 by Martin
Fix lazy import test file handling on Python 3
504
        with open(osutils.pathjoin(root_path, '__init__.py'), 'w') as f:
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
505
            f.write('var1 = 1\ndef func1(a):\n  return a\n')
506
        mod_path = osutils.pathjoin(root_path, mod_name + '.py')
6754.7.1 by Martin
Fix lazy import test file handling on Python 3
507
        with open(mod_path, 'w') as f:
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
508
            f.write('var2 = 2\ndef func2(a):\n  return a\n')
509
510
        sub_path = osutils.pathjoin(root_path, sub_name)
511
        os.mkdir(sub_path)
6754.7.1 by Martin
Fix lazy import test file handling on Python 3
512
        with open(osutils.pathjoin(sub_path, '__init__.py'), 'w') as f:
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
513
            f.write('var3 = 3\ndef func3(a):\n  return a\n')
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
514
        submoda_path = osutils.pathjoin(sub_path, submoda_name + '.py')
6754.7.1 by Martin
Fix lazy import test file handling on Python 3
515
        with open(submoda_path, 'w') as f:
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
516
            f.write('var4 = 4\ndef func4(a):\n  return a\n')
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
517
        submodb_path = osutils.pathjoin(sub_path, submodb_name + '.py')
6754.7.1 by Martin
Fix lazy import test file handling on Python 3
518
        with open(submodb_path, 'w') as f:
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
519
            f.write('var5 = 5\ndef func5(a):\n  return a\n')
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
520
        self.root_name = root_name
521
        self.mod_name = mod_name
522
        self.sub_name = sub_name
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
523
        self.submoda_name = submoda_name
524
        self.submodb_name = submodb_name
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
525
1996.1.8 by John Arbash Meinel
Split up TestImportReplacer into a helper class
526
527
class TestImportReplacerHelper(ImportReplacerHelper):
528
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
529
    def test_basic_import(self):
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
530
        """Test that a real import of these modules works"""
1996.1.3 by John Arbash Meinel
Basic single-level imports work
531
        sub_mod_path = '.'.join([self.root_name, self.sub_name,
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
532
                                  self.submoda_name])
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
533
        root = __import__(sub_mod_path, globals(), locals(), [], 0)
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
534
        self.assertEqual(1, root.var1)
535
        self.assertEqual(3, getattr(root, self.sub_name).var3)
536
        self.assertEqual(4, getattr(getattr(root, self.sub_name),
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
537
                                    self.submoda_name).var4)
1996.1.3 by John Arbash Meinel
Basic single-level imports work
538
539
        mod_path = '.'.join([self.root_name, self.mod_name])
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
540
        root = __import__(mod_path, globals(), locals(), [], 0)
1996.1.3 by John Arbash Meinel
Basic single-level imports work
541
        self.assertEqual(2, getattr(root, self.mod_name).var2)
542
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
543
        self.assertEqual([('import', sub_mod_path, [], 0),
544
                          ('import', mod_path, [], 0),
1996.1.3 by John Arbash Meinel
Basic single-level imports work
545
                         ], self.actions)
546
1996.1.8 by John Arbash Meinel
Split up TestImportReplacer into a helper class
547
548
class TestImportReplacer(ImportReplacerHelper):
549
1996.1.3 by John Arbash Meinel
Basic single-level imports work
550
    def test_import_root(self):
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
551
        """Test 'import root-XXX as root1'"""
1996.1.3 by John Arbash Meinel
Basic single-level imports work
552
        try:
553
            root1
554
        except NameError:
555
            # root1 shouldn't exist yet
556
            pass
557
        else:
558
            self.fail('root1 was not supposed to exist yet')
559
560
        # This should replicate 'import root-xxyyzz as root1'
561
        InstrumentedImportReplacer(scope=globals(), name='root1',
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
562
                                   module_path=[self.root_name],
1996.1.15 by John Arbash Meinel
Everything is now hooked up
563
                                   member=None, children={})
1996.1.3 by John Arbash Meinel
Basic single-level imports work
564
565
        self.assertEqual(InstrumentedImportReplacer,
566
                         object.__getattribute__(root1, '__class__'))
567
        self.assertEqual(1, root1.var1)
568
        self.assertEqual('x', root1.func1('x'))
569
570
        self.assertEqual([('__getattribute__', 'var1'),
571
                          ('_import', 'root1'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
572
                          ('import', self.root_name, [], 0),
1996.1.3 by John Arbash Meinel
Basic single-level imports work
573
                         ], self.actions)
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
574
575
    def test_import_mod(self):
576
        """Test 'import root-XXX.mod-XXX as mod2'"""
577
        try:
578
            mod1
579
        except NameError:
580
            # mod1 shouldn't exist yet
581
            pass
582
        else:
583
            self.fail('mod1 was not supposed to exist yet')
584
585
        mod_path = self.root_name + '.' + self.mod_name
586
        InstrumentedImportReplacer(scope=globals(), name='mod1',
587
                                   module_path=[self.root_name, self.mod_name],
1996.1.15 by John Arbash Meinel
Everything is now hooked up
588
                                   member=None, children={})
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
589
590
        self.assertEqual(InstrumentedImportReplacer,
591
                         object.__getattribute__(mod1, '__class__'))
592
        self.assertEqual(2, mod1.var2)
593
        self.assertEqual('y', mod1.func2('y'))
594
595
        self.assertEqual([('__getattribute__', 'var2'),
596
                          ('_import', 'mod1'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
597
                          ('import', mod_path, [], 0),
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
598
                         ], self.actions)
599
600
    def test_import_mod_from_root(self):
601
        """Test 'from root-XXX import mod-XXX as mod2'"""
602
        try:
603
            mod2
604
        except NameError:
605
            # mod2 shouldn't exist yet
606
            pass
607
        else:
608
            self.fail('mod2 was not supposed to exist yet')
609
610
        InstrumentedImportReplacer(scope=globals(), name='mod2',
611
                                   module_path=[self.root_name],
1996.1.15 by John Arbash Meinel
Everything is now hooked up
612
                                   member=self.mod_name, children={})
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
613
614
        self.assertEqual(InstrumentedImportReplacer,
615
                         object.__getattribute__(mod2, '__class__'))
616
        self.assertEqual(2, mod2.var2)
617
        self.assertEqual('y', mod2.func2('y'))
618
619
        self.assertEqual([('__getattribute__', 'var2'),
620
                          ('_import', 'mod2'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
621
                          ('import', self.root_name, [self.mod_name], 0),
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
622
                         ], self.actions)
1996.1.5 by John Arbash Meinel
Test that we can lazy import a module, and its children
623
624
    def test_import_root_and_mod(self):
625
        """Test 'import root-XXX.mod-XXX' remapping both to root3.mod3"""
626
        try:
627
            root3
628
        except NameError:
629
            # root3 shouldn't exist yet
630
            pass
631
        else:
632
            self.fail('root3 was not supposed to exist yet')
633
634
        InstrumentedImportReplacer(scope=globals(),
635
            name='root3', module_path=[self.root_name], member=None,
1996.1.15 by John Arbash Meinel
Everything is now hooked up
636
            children={'mod3':([self.root_name, self.mod_name], None, {})})
1996.1.5 by John Arbash Meinel
Test that we can lazy import a module, and its children
637
638
        # So 'root3' should be a lazy import
639
        # and once it is imported, mod3 should also be lazy until
640
        # actually accessed.
641
        self.assertEqual(InstrumentedImportReplacer,
642
                         object.__getattribute__(root3, '__class__'))
643
        self.assertEqual(1, root3.var1)
644
645
        # There is a mod3 member, but it is also lazy
646
        self.assertEqual(InstrumentedImportReplacer,
647
                         object.__getattribute__(root3.mod3, '__class__'))
648
        self.assertEqual(2, root3.mod3.var2)
649
650
        mod_path = self.root_name + '.' + self.mod_name
651
        self.assertEqual([('__getattribute__', 'var1'),
652
                          ('_import', 'root3'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
653
                          ('import', self.root_name, [], 0),
1996.1.5 by John Arbash Meinel
Test that we can lazy import a module, and its children
654
                          ('__getattribute__', 'var2'),
655
                          ('_import', 'mod3'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
656
                          ('import', mod_path, [], 0),
1996.1.5 by John Arbash Meinel
Test that we can lazy import a module, and its children
657
                         ], self.actions)
1996.1.6 by John Arbash Meinel
Test that we can add more children to the existing lazy object
658
659
    def test_import_root_and_root_mod(self):
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
660
        """Test that 'import root, root.mod' can be done.
661
662
        The second import should re-use the first one, and just add
663
        children to be imported.
664
        """
1996.1.6 by John Arbash Meinel
Test that we can add more children to the existing lazy object
665
        try:
666
            root4
667
        except NameError:
668
            # root4 shouldn't exist yet
669
            pass
670
        else:
671
            self.fail('root4 was not supposed to exist yet')
672
673
        InstrumentedImportReplacer(scope=globals(),
674
            name='root4', module_path=[self.root_name], member=None,
1996.1.15 by John Arbash Meinel
Everything is now hooked up
675
            children={})
1996.1.6 by John Arbash Meinel
Test that we can add more children to the existing lazy object
676
677
        # So 'root4' should be a lazy import
678
        self.assertEqual(InstrumentedImportReplacer,
679
                         object.__getattribute__(root4, '__class__'))
680
681
        # Lets add a new child to be imported on demand
682
        # This syntax of using object.__getattribute__ is the correct method
683
        # for accessing the _import_replacer_children member
684
        children = object.__getattribute__(root4, '_import_replacer_children')
1996.1.15 by John Arbash Meinel
Everything is now hooked up
685
        children['mod4'] = ([self.root_name, self.mod_name], None, {})
1996.1.6 by John Arbash Meinel
Test that we can add more children to the existing lazy object
686
687
        # Accessing root4.mod4 should import root, but mod should stay lazy
688
        self.assertEqual(InstrumentedImportReplacer,
689
                         object.__getattribute__(root4.mod4, '__class__'))
690
        self.assertEqual(2, root4.mod4.var2)
691
692
        mod_path = self.root_name + '.' + self.mod_name
693
        self.assertEqual([('__getattribute__', 'mod4'),
694
                          ('_import', 'root4'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
695
                          ('import', self.root_name, [], 0),
1996.1.6 by John Arbash Meinel
Test that we can add more children to the existing lazy object
696
                          ('__getattribute__', 'var2'),
697
                          ('_import', 'mod4'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
698
                          ('import', mod_path, [], 0),
1996.1.6 by John Arbash Meinel
Test that we can add more children to the existing lazy object
699
                         ], self.actions)
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
700
701
    def test_import_root_sub_submod(self):
702
        """Test import root.mod, root.sub.submoda, root.sub.submodb
703
        root should be a lazy import, with multiple children, who also
704
        have children to be imported.
705
        And when root is imported, the children should be lazy, and
706
        reuse the intermediate lazy object.
707
        """
708
        try:
709
            root5
710
        except NameError:
1996.1.15 by John Arbash Meinel
Everything is now hooked up
711
            # root5 shouldn't exist yet
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
712
            pass
713
        else:
714
            self.fail('root5 was not supposed to exist yet')
715
716
        InstrumentedImportReplacer(scope=globals(),
717
            name='root5', module_path=[self.root_name], member=None,
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
718
            children={'mod5': ([self.root_name, self.mod_name], None, {}),
719
                      'sub5': ([self.root_name, self.sub_name], None,
1996.1.15 by John Arbash Meinel
Everything is now hooked up
720
                            {'submoda5':([self.root_name, self.sub_name,
721
                                         self.submoda_name], None, {}),
722
                             'submodb5':([self.root_name, self.sub_name,
723
                                          self.submodb_name], None, {})
724
                            }),
725
                     })
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
726
727
        # So 'root5' should be a lazy import
728
        self.assertEqual(InstrumentedImportReplacer,
729
                         object.__getattribute__(root5, '__class__'))
730
731
        # Accessing root5.mod5 should import root, but mod should stay lazy
732
        self.assertEqual(InstrumentedImportReplacer,
733
                         object.__getattribute__(root5.mod5, '__class__'))
734
        # root5.sub5 should still be lazy, but not re-import root5
735
        self.assertEqual(InstrumentedImportReplacer,
736
                         object.__getattribute__(root5.sub5, '__class__'))
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
737
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
738
        # Accessing root5.sub5.submoda5 should import sub5, but not either
739
        # of the sub objects (they should be available as lazy objects
740
        self.assertEqual(InstrumentedImportReplacer,
741
                     object.__getattribute__(root5.sub5.submoda5, '__class__'))
742
        self.assertEqual(InstrumentedImportReplacer,
743
                     object.__getattribute__(root5.sub5.submodb5, '__class__'))
744
745
        # This should import mod5
746
        self.assertEqual(2, root5.mod5.var2)
747
        # These should import submoda5 and submodb5
748
        self.assertEqual(4, root5.sub5.submoda5.var4)
749
        self.assertEqual(5, root5.sub5.submodb5.var5)
750
751
        mod_path = self.root_name + '.' + self.mod_name
752
        sub_path = self.root_name + '.' + self.sub_name
753
        submoda_path = sub_path + '.' + self.submoda_name
754
        submodb_path = sub_path + '.' + self.submodb_name
755
756
        self.assertEqual([('__getattribute__', 'mod5'),
757
                          ('_import', 'root5'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
758
                          ('import', self.root_name, [], 0),
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
759
                          ('__getattribute__', 'submoda5'),
760
                          ('_import', 'sub5'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
761
                          ('import', sub_path, [], 0),
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
762
                          ('__getattribute__', 'var2'),
763
                          ('_import', 'mod5'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
764
                          ('import', mod_path, [], 0),
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
765
                          ('__getattribute__', 'var4'),
766
                          ('_import', 'submoda5'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
767
                          ('import', submoda_path, [], 0),
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
768
                          ('__getattribute__', 'var5'),
769
                          ('_import', 'submodb5'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
770
                          ('import', submodb_path, [], 0),
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
771
                         ], self.actions)
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
772
773
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
774
class TestConvertImportToMap(TestCase):
775
    """Directly test the conversion from import strings to maps"""
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
776
1996.1.13 by John Arbash Meinel
small test updates
777
    def check(self, expected, import_strings):
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
778
        proc = lazy_import.ImportProcessor()
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
779
        for import_str in import_strings:
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
780
            proc._convert_import_str(import_str)
781
        self.assertEqual(expected, proc.imports,
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
782
                         'Import of %r was not converted correctly'
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
783
                         ' %s != %s' % (import_strings, expected,
784
                                        proc.imports))
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
785
786
    def test_import_one(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
787
        self.check({'one': (['one'], None, {}),
1996.1.13 by John Arbash Meinel
small test updates
788
                   }, ['import one'])
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
789
790
    def test_import_one_two(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
791
        one_two_map = {'one': (['one'], None,
792
                              {'two': (['one', 'two'], None, {}),
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
793
                              }),
794
                      }
1996.1.13 by John Arbash Meinel
small test updates
795
        self.check(one_two_map, ['import one.two'])
796
        self.check(one_two_map, ['import one, one.two'])
797
        self.check(one_two_map, ['import one', 'import one.two'])
798
        self.check(one_two_map, ['import one.two', 'import one'])
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
799
800
    def test_import_one_two_three(self):
801
        one_two_three_map = {
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
802
            'one': (['one'], None,
803
                   {'two': (['one', 'two'], None,
804
                           {'three': (['one', 'two', 'three'], None, {}),
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
805
                           }),
806
                   }),
807
        }
1996.1.13 by John Arbash Meinel
small test updates
808
        self.check(one_two_three_map, ['import one.two.three'])
809
        self.check(one_two_three_map, ['import one, one.two.three'])
810
        self.check(one_two_three_map, ['import one',
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
811
                                              'import one.two.three'])
1996.1.13 by John Arbash Meinel
small test updates
812
        self.check(one_two_three_map, ['import one.two.three',
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
813
                                              'import one'])
814
815
    def test_import_one_as_x(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
816
        self.check({'x': (['one'], None, {}),
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
817
                          }, ['import one as x'])
818
819
    def test_import_one_two_as_x(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
820
        self.check({'x': (['one', 'two'], None, {}),
1996.1.13 by John Arbash Meinel
small test updates
821
                   }, ['import one.two as x'])
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
822
823
    def test_import_mixed(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
824
        mixed = {'x': (['one', 'two'], None, {}),
825
                 'one': (['one'], None,
826
                       {'two': (['one', 'two'], None, {}),
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
827
                       }),
828
                }
1996.1.13 by John Arbash Meinel
small test updates
829
        self.check(mixed, ['import one.two as x, one.two'])
830
        self.check(mixed, ['import one.two as x', 'import one.two'])
831
        self.check(mixed, ['import one.two', 'import one.two as x'])
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
832
833
    def test_import_with_as(self):
1996.1.13 by John Arbash Meinel
small test updates
834
        self.check({'fast':(['fast'], None, {})}, ['import fast'])
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
835
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
836
837
class TestFromToMap(TestCase):
838
    """Directly test the conversion of 'from foo import bar' syntax"""
839
840
    def check_result(self, expected, from_strings):
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
841
        proc = lazy_import.ImportProcessor()
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
842
        for from_str in from_strings:
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
843
            proc._convert_from_str(from_str)
844
        self.assertEqual(expected, proc.imports,
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
845
                         'Import of %r was not converted correctly'
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
846
                         ' %s != %s' % (from_strings, expected, proc.imports))
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
847
848
    def test_from_one_import_two(self):
849
        self.check_result({'two':(['one'], 'two', {})},
850
                          ['from one import two'])
851
852
    def test_from_one_import_two_as_three(self):
853
        self.check_result({'three':(['one'], 'two', {})},
854
                          ['from one import two as three'])
855
856
    def test_from_one_import_two_three(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
857
        two_three_map = {'two': (['one'], 'two', {}),
858
                         'three': (['one'], 'three', {}),
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
859
                        }
860
        self.check_result(two_three_map,
861
                          ['from one import two, three'])
862
        self.check_result(two_three_map,
863
                          ['from one import two',
864
                           'from one import three'])
865
866
    def test_from_one_two_import_three(self):
867
        self.check_result({'three':(['one', 'two'], 'three', {})},
868
                          ['from one.two import three'])
869
1996.1.11 by John Arbash Meinel
Test the ability to take a bunch of import lines and canonicalize them
870
871
class TestCanonicalize(TestCase):
872
    """Test that we can canonicalize import texts"""
873
874
    def check(self, expected, text):
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
875
        proc = lazy_import.ImportProcessor()
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
876
        parsed = proc._canonicalize_import_text(text)
1996.1.11 by John Arbash Meinel
Test the ability to take a bunch of import lines and canonicalize them
877
        self.assertEqual(expected, parsed,
878
                         'Incorrect parsing of text:\n%s\n%s\n!=\n%s'
879
                         % (text, expected, parsed))
880
881
    def test_import_one(self):
882
        self.check(['import one'], 'import one')
883
        self.check(['import one'], '\nimport one\n\n')
884
885
    def test_import_one_two(self):
886
        self.check(['import one, two'], 'import one, two')
887
        self.check(['import one, two'], '\nimport one, two\n\n')
888
889
    def test_import_one_as_two_as(self):
890
        self.check(['import one as x, two as y'], 'import one as x, two as y')
891
        self.check(['import one as x, two as y'],
892
                   '\nimport one as x, two as y\n')
893
894
    def test_from_one_import_two(self):
895
        self.check(['from one import two'], 'from one import two')
896
        self.check(['from one import two'], '\nfrom one import two\n\n')
897
        self.check(['from one import two'], '\nfrom one import (two)\n')
898
        self.check(['from one import  two '], '\nfrom one import (\n\ttwo\n)\n')
1996.1.13 by John Arbash Meinel
small test updates
899
900
    def test_multiple(self):
901
        self.check(['import one', 'import two, three', 'from one import four'],
902
                   'import one\nimport two, three\nfrom one import four')
903
        self.check(['import one', 'import two, three', 'from one import four'],
904
                   'import one\nimport (two, three)\nfrom one import four')
1996.1.15 by John Arbash Meinel
Everything is now hooked up
905
        self.check(['import one', 'import two, three', 'from one import four'],
1996.1.13 by John Arbash Meinel
small test updates
906
                   'import one\n'
1996.1.15 by John Arbash Meinel
Everything is now hooked up
907
                   'import two, three\n'
1996.1.13 by John Arbash Meinel
small test updates
908
                   'from one import four')
1996.1.15 by John Arbash Meinel
Everything is now hooked up
909
        self.check(['import one',
910
                    'import two, three', 'from one import  four, '],
911
                   'import one\n'
912
                   'import two, three\n'
1996.1.13 by John Arbash Meinel
small test updates
913
                   'from one import (\n'
914
                   '    four,\n'
915
                   '    )\n'
916
                   )
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
917
1996.1.18 by John Arbash Meinel
Add more structured error handling
918
    def test_missing_trailing(self):
919
        proc = lazy_import.ImportProcessor()
920
        self.assertRaises(errors.InvalidImportLine,
921
                          proc._canonicalize_import_text,
922
                          "from foo import (\n  bar\n")
923
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
924
925
class TestImportProcessor(TestCase):
926
    """Test that ImportProcessor can turn import texts into lazy imports"""
927
928
    def check(self, expected, text):
929
        proc = lazy_import.ImportProcessor()
930
        proc._build_map(text)
931
        self.assertEqual(expected, proc.imports,
932
                         'Incorrect processing of:\n%s\n%s\n!=\n%s'
933
                         % (text, expected, proc.imports))
934
935
    def test_import_one(self):
936
        exp = {'one':(['one'], None, {})}
937
        self.check(exp, 'import one')
938
        self.check(exp, '\nimport one\n')
939
940
    def test_import_one_two(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
941
        exp = {'one': (['one'], None,
942
                      {'two': (['one', 'two'], None, {}),
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
943
                      }),
944
              }
945
        self.check(exp, 'import one.two')
946
        self.check(exp, 'import one, one.two')
947
        self.check(exp, 'import one\nimport one.two')
948
949
    def test_import_as(self):
950
        exp = {'two':(['one'], None, {})}
951
        self.check(exp, 'import one as two')
952
953
    def test_import_many(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
954
        exp = {'one': (['one'], None,
955
                      {'two': (['one', 'two'], None,
956
                              {'three': (['one', 'two', 'three'], None, {}),
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
957
                              }),
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
958
                       'four': (['one', 'four'], None, {}),
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
959
                      }),
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
960
               'five': (['one', 'five'], None, {}),
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
961
              }
962
        self.check(exp, 'import one.two.three, one.four, one.five as five')
963
        self.check(exp, 'import one.five as five\n'
964
                        'import one\n'
965
                        'import one.two.three\n'
966
                        'import one.four\n')
967
968
    def test_from_one_import_two(self):
969
        exp = {'two':(['one'], 'two', {})}
970
        self.check(exp, 'from one import two\n')
971
        self.check(exp, 'from one import (\n'
972
                        '    two,\n'
973
                        '    )\n')
974
975
    def test_from_one_import_two(self):
976
        exp = {'two':(['one'], 'two', {})}
977
        self.check(exp, 'from one import two\n')
978
        self.check(exp, 'from one import (two)\n')
979
        self.check(exp, 'from one import (two,)\n')
980
        self.check(exp, 'from one import two as two\n')
981
        self.check(exp, 'from one import (\n'
982
                        '    two,\n'
983
                        '    )\n')
984
985
    def test_from_many(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
986
        exp = {'two': (['one'], 'two', {}),
987
               'three': (['one', 'two'], 'three', {}),
988
               'five': (['one', 'two'], 'four', {}),
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
989
              }
990
        self.check(exp, 'from one import two\n'
991
                        'from one.two import three, four as five\n')
992
        self.check(exp, 'from one import two\n'
993
                        'from one.two import (\n'
994
                        '    three,\n'
995
                        '    four as five,\n'
996
                        '    )\n')
997
998
    def test_mixed(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
999
        exp = {'two': (['one'], 'two', {}),
1000
               'three': (['one', 'two'], 'three', {}),
1001
               'five': (['one', 'two'], 'four', {}),
1002
               'one': (['one'], None,
1003
                      {'two': (['one', 'two'], None, {}),
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
1004
                      }),
1005
              }
1006
        self.check(exp, 'from one import two\n'
1007
                        'from one.two import three, four as five\n'
1008
                        'import one.two')
1009
        self.check(exp, 'from one import two\n'
1010
                        'from one.two import (\n'
1011
                        '    three,\n'
1012
                        '    four as five,\n'
1013
                        '    )\n'
1014
                        'import one\n'
1015
                        'import one.two\n')
1996.1.15 by John Arbash Meinel
Everything is now hooked up
1016
1996.1.18 by John Arbash Meinel
Add more structured error handling
1017
    def test_incorrect_line(self):
1018
        proc = lazy_import.ImportProcessor()
1019
        self.assertRaises(errors.InvalidImportLine,
1020
                          proc._build_map, 'foo bar baz')
1021
        self.assertRaises(errors.InvalidImportLine,
1022
                          proc._build_map, 'improt foo')
1023
        self.assertRaises(errors.InvalidImportLine,
1024
                          proc._build_map, 'importfoo')
1025
        self.assertRaises(errors.InvalidImportLine,
1026
                          proc._build_map, 'fromimport')
1027
1028
    def test_name_collision(self):
1029
        proc = lazy_import.ImportProcessor()
1030
        proc._build_map('import foo')
1031
1032
        # All of these would try to create an object with the
1033
        # same name as an existing object.
1034
        self.assertRaises(errors.ImportNameCollision,
1035
                          proc._build_map, 'import bar as foo')
1036
        self.assertRaises(errors.ImportNameCollision,
1037
                          proc._build_map, 'from foo import bar as foo')
1038
        self.assertRaises(errors.ImportNameCollision,
1039
                          proc._build_map, 'from bar import foo')
1040
6621.26.1 by Martin
Prevent lazy imports misunderstanding relative import syntax
1041
    def test_relative_imports(self):
1042
        proc = lazy_import.ImportProcessor()
1043
        self.assertRaises(ImportError,
1044
                          proc._build_map, 'import .bar as foo')
1045
        self.assertRaises(ImportError,
1046
                          proc._build_map, 'from .foo import bar as foo')
1047
        self.assertRaises(ImportError,
1048
                          proc._build_map, 'from .bar import foo')
1049
1996.1.15 by John Arbash Meinel
Everything is now hooked up
1050
1051
class TestLazyImportProcessor(ImportReplacerHelper):
1052
1053
    def test_root(self):
1054
        try:
1055
            root6
1056
        except NameError:
1057
            pass # root6 should not be defined yet
1058
        else:
1059
            self.fail('root6 was not supposed to exist yet')
1060
1061
        text = 'import %s as root6' % (self.root_name,)
1062
        proc = lazy_import.ImportProcessor(InstrumentedImportReplacer)
1996.1.19 by John Arbash Meinel
Write a simple wrapper function to make lazy imports easy.
1063
        proc.lazy_import(scope=globals(), text=text)
1996.1.15 by John Arbash Meinel
Everything is now hooked up
1064
1065
        # So 'root6' should be a lazy import
1066
        self.assertEqual(InstrumentedImportReplacer,
1067
                         object.__getattribute__(root6, '__class__'))
1068
1069
        self.assertEqual(1, root6.var1)
1070
        self.assertEqual('x', root6.func1('x'))
1071
1072
        self.assertEqual([('__getattribute__', 'var1'),
1073
                          ('_import', 'root6'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
1074
                          ('import', self.root_name, [], 0),
1996.1.15 by John Arbash Meinel
Everything is now hooked up
1075
                         ], self.actions)
1076
1077
    def test_import_deep(self):
1078
        """Test import root.mod, root.sub.submoda, root.sub.submodb
1079
        root should be a lazy import, with multiple children, who also
1080
        have children to be imported.
1081
        And when root is imported, the children should be lazy, and
1082
        reuse the intermediate lazy object.
1083
        """
1084
        try:
1085
            submoda7
1086
        except NameError:
1087
            pass # submoda7 should not be defined yet
1088
        else:
1089
            self.fail('submoda7 was not supposed to exist yet')
1090
1091
        text = """\
1092
import %(root_name)s.%(sub_name)s.%(submoda_name)s as submoda7
1093
""" % self.__dict__
1094
        proc = lazy_import.ImportProcessor(InstrumentedImportReplacer)
1996.1.19 by John Arbash Meinel
Write a simple wrapper function to make lazy imports easy.
1095
        proc.lazy_import(scope=globals(), text=text)
1996.1.15 by John Arbash Meinel
Everything is now hooked up
1096
1097
        # So 'submoda7' should be a lazy import
1098
        self.assertEqual(InstrumentedImportReplacer,
1099
                         object.__getattribute__(submoda7, '__class__'))
1100
1101
        # This should import submoda7
1102
        self.assertEqual(4, submoda7.var4)
1103
1104
        sub_path = self.root_name + '.' + self.sub_name
1105
        submoda_path = sub_path + '.' + self.submoda_name
1106
1107
        self.assertEqual([('__getattribute__', 'var4'),
1108
                          ('_import', 'submoda7'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
1109
                          ('import', submoda_path, [], 0),
1996.1.15 by John Arbash Meinel
Everything is now hooked up
1110
                         ], self.actions)
1996.1.19 by John Arbash Meinel
Write a simple wrapper function to make lazy imports easy.
1111
1112
    def test_lazy_import(self):
1113
        """Smoke test that lazy_import() does the right thing"""
1114
        try:
1115
            root8
1116
        except NameError:
1117
            pass # root8 should not be defined yet
1118
        else:
1119
            self.fail('root8 was not supposed to exist yet')
1120
        lazy_import.lazy_import(globals(),
1121
                                'import %s as root8' % (self.root_name,),
1122
                                lazy_import_class=InstrumentedImportReplacer)
1123
1124
        self.assertEqual(InstrumentedImportReplacer,
1125
                         object.__getattribute__(root8, '__class__'))
1126
1127
        self.assertEqual(1, root8.var1)
1128
        self.assertEqual(1, root8.var1)
1129
        self.assertEqual(1, root8.func1(1))
1130
1131
        self.assertEqual([('__getattribute__', 'var1'),
1132
                          ('_import', 'root8'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
1133
                          ('import', self.root_name, [], 0),
1996.1.19 by John Arbash Meinel
Write a simple wrapper function to make lazy imports easy.
1134
                         ], self.actions)
6082.4.1 by Benji York
fix bug 702914
1135
6082.4.2 by Benji York
fix whitespace
1136
6082.4.1 by Benji York
fix bug 702914
1137
class TestScopeReplacerReentrance(TestCase):
1138
    """The ScopeReplacer should be reentrant.
1139
1140
    Invoking a replacer while an invocation was already on-going leads to a
6111.3.1 by Martin von Gagern
Make lazy imports (at least more) thread-safe.
1141
    race to see which invocation will be the first to call _replace.
6111.3.9 by Martin Packman
Fix loose formatting and spelling
1142
    The losing caller used to see an exception (bugs 396819 and 702914).
6082.4.1 by Benji York
fix bug 702914
1143
6111.3.1 by Martin von Gagern
Make lazy imports (at least more) thread-safe.
1144
    These tests set up a tracer that stops at a suitable moment (upon
1145
    entry of a specified method) and starts another call to the
1146
    functionality in question (__call__, __getattribute__, __setattr_)
6111.3.10 by Martin Packman
Fix docstring typo noted by vila in review
1147
    in order to win the race, setting up the original caller to lose.
6082.4.1 by Benji York
fix bug 702914
1148
    """
1149
1150
    def tracer(self, frame, event, arg):
6111.3.1 by Martin von Gagern
Make lazy imports (at least more) thread-safe.
1151
        if event != 'call':
1152
            return self.tracer
6082.4.1 by Benji York
fix bug 702914
1153
        # Grab the name of the file that contains the code being executed.
6111.3.1 by Martin von Gagern
Make lazy imports (at least more) thread-safe.
1154
        code = frame.f_code
1155
        filename = code.co_filename
6082.4.1 by Benji York
fix bug 702914
1156
        # Convert ".pyc" and ".pyo" file names to their ".py" equivalent.
1157
        filename = re.sub(r'\.py[co]$', '.py', filename)
6111.3.1 by Martin von Gagern
Make lazy imports (at least more) thread-safe.
1158
        function_name = code.co_name
6082.4.1 by Benji York
fix bug 702914
1159
        # If we're executing a line of code from the right module...
6111.3.1 by Martin von Gagern
Make lazy imports (at least more) thread-safe.
1160
        if (filename.endswith('lazy_import.py') and
1161
            function_name == self.method_to_trace):
1162
            # We don't need to trace any more.
1163
            sys.settrace(None)
1164
            # Run another racer.  This one will "win" the race.
1165
            self.racer()
6082.4.1 by Benji York
fix bug 702914
1166
        return self.tracer
1167
6111.3.1 by Martin von Gagern
Make lazy imports (at least more) thread-safe.
1168
    def run_race(self, racer, method_to_trace='_resolve'):
6111.3.4 by Martin Packman
Correct overriding of ScopeReplacer._should_proxy in lazy_import tests
1169
        self.overrideAttr(lazy_import.ScopeReplacer, '_should_proxy', True)
6111.3.2 by Martin von Gagern
Allow subclasses to override _should_proxy.
1170
        self.racer = racer
1171
        self.method_to_trace = method_to_trace
1172
        sys.settrace(self.tracer)
1173
        self.racer() # Should not raise any exception
1174
        # Make sure the tracer actually found the code it was
1175
        # looking for.  If not, maybe the code was refactored in
1176
        # such a way that these tests aren't needed any more.
1177
        self.assertEqual(None, sys.gettrace())
6082.4.1 by Benji York
fix bug 702914
1178
1179
    def test_call(self):
1180
        def factory(*args):
1181
            return factory
6111.3.4 by Martin Packman
Correct overriding of ScopeReplacer._should_proxy in lazy_import tests
1182
        replacer = lazy_import.ScopeReplacer({}, factory, 'name')
6082.4.1 by Benji York
fix bug 702914
1183
        self.run_race(replacer)
1184
1185
    def test_setattr(self):
1186
        class Replaced:
1187
            pass
1188
1189
        def factory(*args):
1190
            return Replaced()
1191
6111.3.4 by Martin Packman
Correct overriding of ScopeReplacer._should_proxy in lazy_import tests
1192
        replacer = lazy_import.ScopeReplacer({}, factory, 'name')
6082.4.1 by Benji York
fix bug 702914
1193
1194
        def racer():
1195
            replacer.foo = 42
1196
1197
        self.run_race(racer)
1198
1199
    def test_getattribute(self):
1200
        class Replaced:
1201
            foo = 'bar'
1202
1203
        def factory(*args):
1204
            return Replaced()
1205
6111.3.4 by Martin Packman
Correct overriding of ScopeReplacer._should_proxy in lazy_import tests
1206
        replacer = lazy_import.ScopeReplacer({}, factory, 'name')
6082.4.1 by Benji York
fix bug 702914
1207
1208
        def racer():
1209
            replacer.foo
1210
1211
        self.run_race(racer)