/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
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
469
        def instrumented_import(mod, scope1, scope2, fromlist, level):
470
            self.actions.append(('import', mod, fromlist, level))
7011.1.1 by Martin
Make replacement for lazy import tests less dodgy
471
            return __import__(mod, scope1, scope2, fromlist, level=level)
472
        self.addCleanup(setattr, lazy_import, '_builtin_import', __import__)
473
        lazy_import._builtin_import = instrumented_import
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
474
475
    def create_modules(self):
476
        """Create some random modules to be imported.
477
478
        Each entry has a random suffix, and the full names are saved
479
480
        These are setup as follows:
481
         base/ <= used to ensure not in default search path
482
            root-XXX/
483
                __init__.py <= This will contain var1, func1
484
                mod-XXX.py <= This will contain var2, func2
485
                sub-XXX/
486
                    __init__.py <= Contains var3, func3
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
487
                    submoda-XXX.py <= contains var4, func4
488
                    submodb-XXX.py <= containse var5, func5
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
489
        """
490
        rand_suffix = osutils.rand_chars(4)
491
        root_name = 'root_' + rand_suffix
492
        mod_name = 'mod_' + rand_suffix
493
        sub_name = 'sub_' + rand_suffix
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
494
        submoda_name = 'submoda_' + rand_suffix
495
        submodb_name = 'submodb_' + rand_suffix
496
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
497
        os.mkdir('base')
498
        root_path = osutils.pathjoin('base', root_name)
499
        os.mkdir(root_path)
500
        root_init = osutils.pathjoin(root_path, '__init__.py')
6754.7.1 by Martin
Fix lazy import test file handling on Python 3
501
        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
502
            f.write('var1 = 1\ndef func1(a):\n  return a\n')
503
        mod_path = osutils.pathjoin(root_path, mod_name + '.py')
6754.7.1 by Martin
Fix lazy import test file handling on Python 3
504
        with open(mod_path, 'w') as f:
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
505
            f.write('var2 = 2\ndef func2(a):\n  return a\n')
506
507
        sub_path = osutils.pathjoin(root_path, sub_name)
508
        os.mkdir(sub_path)
6754.7.1 by Martin
Fix lazy import test file handling on Python 3
509
        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
510
            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
511
        submoda_path = osutils.pathjoin(sub_path, submoda_name + '.py')
6754.7.1 by Martin
Fix lazy import test file handling on Python 3
512
        with open(submoda_path, 'w') as f:
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
513
            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
514
        submodb_path = osutils.pathjoin(sub_path, submodb_name + '.py')
6754.7.1 by Martin
Fix lazy import test file handling on Python 3
515
        with open(submodb_path, 'w') as f:
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
516
            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
517
        self.root_name = root_name
518
        self.mod_name = mod_name
519
        self.sub_name = sub_name
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
520
        self.submoda_name = submoda_name
521
        self.submodb_name = submodb_name
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
522
1996.1.8 by John Arbash Meinel
Split up TestImportReplacer into a helper class
523
524
class TestImportReplacerHelper(ImportReplacerHelper):
525
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
526
    def test_basic_import(self):
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
527
        """Test that a real import of these modules works"""
1996.1.3 by John Arbash Meinel
Basic single-level imports work
528
        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
529
                                  self.submoda_name])
7011.1.1 by Martin
Make replacement for lazy import tests less dodgy
530
        root = lazy_import._builtin_import(sub_mod_path, {}, {}, [], 0)
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
531
        self.assertEqual(1, root.var1)
532
        self.assertEqual(3, getattr(root, self.sub_name).var3)
533
        self.assertEqual(4, getattr(getattr(root, self.sub_name),
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
534
                                    self.submoda_name).var4)
1996.1.3 by John Arbash Meinel
Basic single-level imports work
535
536
        mod_path = '.'.join([self.root_name, self.mod_name])
7011.1.1 by Martin
Make replacement for lazy import tests less dodgy
537
        root = lazy_import._builtin_import(mod_path, {}, {}, [], 0)
1996.1.3 by John Arbash Meinel
Basic single-level imports work
538
        self.assertEqual(2, getattr(root, self.mod_name).var2)
539
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
540
        self.assertEqual([('import', sub_mod_path, [], 0),
541
                          ('import', mod_path, [], 0),
1996.1.3 by John Arbash Meinel
Basic single-level imports work
542
                         ], self.actions)
543
1996.1.8 by John Arbash Meinel
Split up TestImportReplacer into a helper class
544
545
class TestImportReplacer(ImportReplacerHelper):
546
1996.1.3 by John Arbash Meinel
Basic single-level imports work
547
    def test_import_root(self):
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
548
        """Test 'import root-XXX as root1'"""
1996.1.3 by John Arbash Meinel
Basic single-level imports work
549
        try:
550
            root1
551
        except NameError:
552
            # root1 shouldn't exist yet
553
            pass
554
        else:
555
            self.fail('root1 was not supposed to exist yet')
556
557
        # This should replicate 'import root-xxyyzz as root1'
558
        InstrumentedImportReplacer(scope=globals(), name='root1',
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
559
                                   module_path=[self.root_name],
1996.1.15 by John Arbash Meinel
Everything is now hooked up
560
                                   member=None, children={})
1996.1.3 by John Arbash Meinel
Basic single-level imports work
561
562
        self.assertEqual(InstrumentedImportReplacer,
563
                         object.__getattribute__(root1, '__class__'))
564
        self.assertEqual(1, root1.var1)
565
        self.assertEqual('x', root1.func1('x'))
566
567
        self.assertEqual([('__getattribute__', 'var1'),
568
                          ('_import', 'root1'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
569
                          ('import', self.root_name, [], 0),
1996.1.3 by John Arbash Meinel
Basic single-level imports work
570
                         ], self.actions)
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
571
572
    def test_import_mod(self):
573
        """Test 'import root-XXX.mod-XXX as mod2'"""
574
        try:
575
            mod1
576
        except NameError:
577
            # mod1 shouldn't exist yet
578
            pass
579
        else:
580
            self.fail('mod1 was not supposed to exist yet')
581
582
        mod_path = self.root_name + '.' + self.mod_name
583
        InstrumentedImportReplacer(scope=globals(), name='mod1',
584
                                   module_path=[self.root_name, self.mod_name],
1996.1.15 by John Arbash Meinel
Everything is now hooked up
585
                                   member=None, children={})
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
586
587
        self.assertEqual(InstrumentedImportReplacer,
588
                         object.__getattribute__(mod1, '__class__'))
589
        self.assertEqual(2, mod1.var2)
590
        self.assertEqual('y', mod1.func2('y'))
591
592
        self.assertEqual([('__getattribute__', 'var2'),
593
                          ('_import', 'mod1'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
594
                          ('import', mod_path, [], 0),
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
595
                         ], self.actions)
596
597
    def test_import_mod_from_root(self):
598
        """Test 'from root-XXX import mod-XXX as mod2'"""
599
        try:
600
            mod2
601
        except NameError:
602
            # mod2 shouldn't exist yet
603
            pass
604
        else:
605
            self.fail('mod2 was not supposed to exist yet')
606
607
        InstrumentedImportReplacer(scope=globals(), name='mod2',
608
                                   module_path=[self.root_name],
1996.1.15 by John Arbash Meinel
Everything is now hooked up
609
                                   member=self.mod_name, children={})
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
610
611
        self.assertEqual(InstrumentedImportReplacer,
612
                         object.__getattribute__(mod2, '__class__'))
613
        self.assertEqual(2, mod2.var2)
614
        self.assertEqual('y', mod2.func2('y'))
615
616
        self.assertEqual([('__getattribute__', 'var2'),
617
                          ('_import', 'mod2'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
618
                          ('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'
619
                         ], self.actions)
1996.1.5 by John Arbash Meinel
Test that we can lazy import a module, and its children
620
621
    def test_import_root_and_mod(self):
622
        """Test 'import root-XXX.mod-XXX' remapping both to root3.mod3"""
623
        try:
624
            root3
625
        except NameError:
626
            # root3 shouldn't exist yet
627
            pass
628
        else:
629
            self.fail('root3 was not supposed to exist yet')
630
631
        InstrumentedImportReplacer(scope=globals(),
632
            name='root3', module_path=[self.root_name], member=None,
1996.1.15 by John Arbash Meinel
Everything is now hooked up
633
            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
634
635
        # So 'root3' should be a lazy import
636
        # and once it is imported, mod3 should also be lazy until
637
        # actually accessed.
638
        self.assertEqual(InstrumentedImportReplacer,
639
                         object.__getattribute__(root3, '__class__'))
640
        self.assertEqual(1, root3.var1)
641
642
        # There is a mod3 member, but it is also lazy
643
        self.assertEqual(InstrumentedImportReplacer,
644
                         object.__getattribute__(root3.mod3, '__class__'))
645
        self.assertEqual(2, root3.mod3.var2)
646
647
        mod_path = self.root_name + '.' + self.mod_name
648
        self.assertEqual([('__getattribute__', 'var1'),
649
                          ('_import', 'root3'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
650
                          ('import', self.root_name, [], 0),
1996.1.5 by John Arbash Meinel
Test that we can lazy import a module, and its children
651
                          ('__getattribute__', 'var2'),
652
                          ('_import', 'mod3'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
653
                          ('import', mod_path, [], 0),
1996.1.5 by John Arbash Meinel
Test that we can lazy import a module, and its children
654
                         ], self.actions)
1996.1.6 by John Arbash Meinel
Test that we can add more children to the existing lazy object
655
656
    def test_import_root_and_root_mod(self):
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
657
        """Test that 'import root, root.mod' can be done.
658
659
        The second import should re-use the first one, and just add
660
        children to be imported.
661
        """
1996.1.6 by John Arbash Meinel
Test that we can add more children to the existing lazy object
662
        try:
663
            root4
664
        except NameError:
665
            # root4 shouldn't exist yet
666
            pass
667
        else:
668
            self.fail('root4 was not supposed to exist yet')
669
670
        InstrumentedImportReplacer(scope=globals(),
671
            name='root4', module_path=[self.root_name], member=None,
1996.1.15 by John Arbash Meinel
Everything is now hooked up
672
            children={})
1996.1.6 by John Arbash Meinel
Test that we can add more children to the existing lazy object
673
674
        # So 'root4' should be a lazy import
675
        self.assertEqual(InstrumentedImportReplacer,
676
                         object.__getattribute__(root4, '__class__'))
677
678
        # Lets add a new child to be imported on demand
679
        # This syntax of using object.__getattribute__ is the correct method
680
        # for accessing the _import_replacer_children member
681
        children = object.__getattribute__(root4, '_import_replacer_children')
1996.1.15 by John Arbash Meinel
Everything is now hooked up
682
        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
683
684
        # Accessing root4.mod4 should import root, but mod should stay lazy
685
        self.assertEqual(InstrumentedImportReplacer,
686
                         object.__getattribute__(root4.mod4, '__class__'))
687
        self.assertEqual(2, root4.mod4.var2)
688
689
        mod_path = self.root_name + '.' + self.mod_name
690
        self.assertEqual([('__getattribute__', 'mod4'),
691
                          ('_import', 'root4'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
692
                          ('import', self.root_name, [], 0),
1996.1.6 by John Arbash Meinel
Test that we can add more children to the existing lazy object
693
                          ('__getattribute__', 'var2'),
694
                          ('_import', 'mod4'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
695
                          ('import', mod_path, [], 0),
1996.1.6 by John Arbash Meinel
Test that we can add more children to the existing lazy object
696
                         ], self.actions)
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
697
698
    def test_import_root_sub_submod(self):
699
        """Test import root.mod, root.sub.submoda, root.sub.submodb
700
        root should be a lazy import, with multiple children, who also
701
        have children to be imported.
702
        And when root is imported, the children should be lazy, and
703
        reuse the intermediate lazy object.
704
        """
705
        try:
706
            root5
707
        except NameError:
1996.1.15 by John Arbash Meinel
Everything is now hooked up
708
            # root5 shouldn't exist yet
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
709
            pass
710
        else:
711
            self.fail('root5 was not supposed to exist yet')
712
713
        InstrumentedImportReplacer(scope=globals(),
714
            name='root5', module_path=[self.root_name], member=None,
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
715
            children={'mod5': ([self.root_name, self.mod_name], None, {}),
716
                      'sub5': ([self.root_name, self.sub_name], None,
1996.1.15 by John Arbash Meinel
Everything is now hooked up
717
                            {'submoda5':([self.root_name, self.sub_name,
718
                                         self.submoda_name], None, {}),
719
                             'submodb5':([self.root_name, self.sub_name,
720
                                          self.submodb_name], None, {})
721
                            }),
722
                     })
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
723
724
        # So 'root5' should be a lazy import
725
        self.assertEqual(InstrumentedImportReplacer,
726
                         object.__getattribute__(root5, '__class__'))
727
728
        # Accessing root5.mod5 should import root, but mod should stay lazy
729
        self.assertEqual(InstrumentedImportReplacer,
730
                         object.__getattribute__(root5.mod5, '__class__'))
731
        # root5.sub5 should still be lazy, but not re-import root5
732
        self.assertEqual(InstrumentedImportReplacer,
733
                         object.__getattribute__(root5.sub5, '__class__'))
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
734
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
735
        # Accessing root5.sub5.submoda5 should import sub5, but not either
736
        # of the sub objects (they should be available as lazy objects
737
        self.assertEqual(InstrumentedImportReplacer,
738
                     object.__getattribute__(root5.sub5.submoda5, '__class__'))
739
        self.assertEqual(InstrumentedImportReplacer,
740
                     object.__getattribute__(root5.sub5.submodb5, '__class__'))
741
742
        # This should import mod5
743
        self.assertEqual(2, root5.mod5.var2)
744
        # These should import submoda5 and submodb5
745
        self.assertEqual(4, root5.sub5.submoda5.var4)
746
        self.assertEqual(5, root5.sub5.submodb5.var5)
747
748
        mod_path = self.root_name + '.' + self.mod_name
749
        sub_path = self.root_name + '.' + self.sub_name
750
        submoda_path = sub_path + '.' + self.submoda_name
751
        submodb_path = sub_path + '.' + self.submodb_name
752
753
        self.assertEqual([('__getattribute__', 'mod5'),
754
                          ('_import', 'root5'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
755
                          ('import', self.root_name, [], 0),
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
756
                          ('__getattribute__', 'submoda5'),
757
                          ('_import', 'sub5'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
758
                          ('import', sub_path, [], 0),
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
759
                          ('__getattribute__', 'var2'),
760
                          ('_import', 'mod5'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
761
                          ('import', mod_path, [], 0),
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
762
                          ('__getattribute__', 'var4'),
763
                          ('_import', 'submoda5'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
764
                          ('import', submoda_path, [], 0),
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
765
                          ('__getattribute__', 'var5'),
766
                          ('_import', 'submodb5'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
767
                          ('import', submodb_path, [], 0),
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
768
                         ], self.actions)
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
769
770
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
771
class TestConvertImportToMap(TestCase):
772
    """Directly test the conversion from import strings to maps"""
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
773
1996.1.13 by John Arbash Meinel
small test updates
774
    def check(self, expected, import_strings):
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
775
        proc = lazy_import.ImportProcessor()
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
776
        for import_str in import_strings:
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
777
            proc._convert_import_str(import_str)
778
        self.assertEqual(expected, proc.imports,
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
779
                         'Import of %r was not converted correctly'
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
780
                         ' %s != %s' % (import_strings, expected,
781
                                        proc.imports))
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
782
783
    def test_import_one(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
784
        self.check({'one': (['one'], None, {}),
1996.1.13 by John Arbash Meinel
small test updates
785
                   }, ['import one'])
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
786
787
    def test_import_one_two(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
788
        one_two_map = {'one': (['one'], None,
789
                              {'two': (['one', 'two'], None, {}),
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
790
                              }),
791
                      }
1996.1.13 by John Arbash Meinel
small test updates
792
        self.check(one_two_map, ['import one.two'])
793
        self.check(one_two_map, ['import one, one.two'])
794
        self.check(one_two_map, ['import one', 'import one.two'])
795
        self.check(one_two_map, ['import one.two', 'import one'])
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
796
797
    def test_import_one_two_three(self):
798
        one_two_three_map = {
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
799
            'one': (['one'], None,
800
                   {'two': (['one', 'two'], None,
801
                           {'three': (['one', 'two', 'three'], None, {}),
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
802
                           }),
803
                   }),
804
        }
1996.1.13 by John Arbash Meinel
small test updates
805
        self.check(one_two_three_map, ['import one.two.three'])
806
        self.check(one_two_three_map, ['import one, one.two.three'])
807
        self.check(one_two_three_map, ['import one',
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
808
                                              'import one.two.three'])
1996.1.13 by John Arbash Meinel
small test updates
809
        self.check(one_two_three_map, ['import one.two.three',
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
810
                                              'import one'])
811
812
    def test_import_one_as_x(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
813
        self.check({'x': (['one'], None, {}),
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
814
                          }, ['import one as x'])
815
816
    def test_import_one_two_as_x(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
817
        self.check({'x': (['one', 'two'], None, {}),
1996.1.13 by John Arbash Meinel
small test updates
818
                   }, ['import one.two as x'])
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
819
820
    def test_import_mixed(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
821
        mixed = {'x': (['one', 'two'], None, {}),
822
                 'one': (['one'], None,
823
                       {'two': (['one', 'two'], None, {}),
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
824
                       }),
825
                }
1996.1.13 by John Arbash Meinel
small test updates
826
        self.check(mixed, ['import one.two as x, one.two'])
827
        self.check(mixed, ['import one.two as x', 'import one.two'])
828
        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.
829
830
    def test_import_with_as(self):
1996.1.13 by John Arbash Meinel
small test updates
831
        self.check({'fast':(['fast'], None, {})}, ['import fast'])
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
832
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
833
834
class TestFromToMap(TestCase):
835
    """Directly test the conversion of 'from foo import bar' syntax"""
836
837
    def check_result(self, expected, from_strings):
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
838
        proc = lazy_import.ImportProcessor()
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
839
        for from_str in from_strings:
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
840
            proc._convert_from_str(from_str)
841
        self.assertEqual(expected, proc.imports,
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
842
                         'Import of %r was not converted correctly'
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
843
                         ' %s != %s' % (from_strings, expected, proc.imports))
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
844
845
    def test_from_one_import_two(self):
846
        self.check_result({'two':(['one'], 'two', {})},
847
                          ['from one import two'])
848
849
    def test_from_one_import_two_as_three(self):
850
        self.check_result({'three':(['one'], 'two', {})},
851
                          ['from one import two as three'])
852
853
    def test_from_one_import_two_three(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
854
        two_three_map = {'two': (['one'], 'two', {}),
855
                         'three': (['one'], 'three', {}),
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
856
                        }
857
        self.check_result(two_three_map,
858
                          ['from one import two, three'])
859
        self.check_result(two_three_map,
860
                          ['from one import two',
861
                           'from one import three'])
862
863
    def test_from_one_two_import_three(self):
864
        self.check_result({'three':(['one', 'two'], 'three', {})},
865
                          ['from one.two import three'])
866
1996.1.11 by John Arbash Meinel
Test the ability to take a bunch of import lines and canonicalize them
867
868
class TestCanonicalize(TestCase):
869
    """Test that we can canonicalize import texts"""
870
871
    def check(self, expected, text):
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
872
        proc = lazy_import.ImportProcessor()
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
873
        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
874
        self.assertEqual(expected, parsed,
875
                         'Incorrect parsing of text:\n%s\n%s\n!=\n%s'
876
                         % (text, expected, parsed))
877
878
    def test_import_one(self):
879
        self.check(['import one'], 'import one')
880
        self.check(['import one'], '\nimport one\n\n')
881
882
    def test_import_one_two(self):
883
        self.check(['import one, two'], 'import one, two')
884
        self.check(['import one, two'], '\nimport one, two\n\n')
885
886
    def test_import_one_as_two_as(self):
887
        self.check(['import one as x, two as y'], 'import one as x, two as y')
888
        self.check(['import one as x, two as y'],
889
                   '\nimport one as x, two as y\n')
890
891
    def test_from_one_import_two(self):
892
        self.check(['from one import two'], 'from one import two')
893
        self.check(['from one import two'], '\nfrom one import two\n\n')
894
        self.check(['from one import two'], '\nfrom one import (two)\n')
895
        self.check(['from one import  two '], '\nfrom one import (\n\ttwo\n)\n')
1996.1.13 by John Arbash Meinel
small test updates
896
897
    def test_multiple(self):
898
        self.check(['import one', 'import two, three', 'from one import four'],
899
                   'import one\nimport two, three\nfrom one import four')
900
        self.check(['import one', 'import two, three', 'from one import four'],
901
                   'import one\nimport (two, three)\nfrom one import four')
1996.1.15 by John Arbash Meinel
Everything is now hooked up
902
        self.check(['import one', 'import two, three', 'from one import four'],
1996.1.13 by John Arbash Meinel
small test updates
903
                   'import one\n'
1996.1.15 by John Arbash Meinel
Everything is now hooked up
904
                   'import two, three\n'
1996.1.13 by John Arbash Meinel
small test updates
905
                   'from one import four')
1996.1.15 by John Arbash Meinel
Everything is now hooked up
906
        self.check(['import one',
907
                    'import two, three', 'from one import  four, '],
908
                   'import one\n'
909
                   'import two, three\n'
1996.1.13 by John Arbash Meinel
small test updates
910
                   'from one import (\n'
911
                   '    four,\n'
912
                   '    )\n'
913
                   )
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
914
1996.1.18 by John Arbash Meinel
Add more structured error handling
915
    def test_missing_trailing(self):
916
        proc = lazy_import.ImportProcessor()
917
        self.assertRaises(errors.InvalidImportLine,
918
                          proc._canonicalize_import_text,
919
                          "from foo import (\n  bar\n")
920
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
921
922
class TestImportProcessor(TestCase):
923
    """Test that ImportProcessor can turn import texts into lazy imports"""
924
925
    def check(self, expected, text):
926
        proc = lazy_import.ImportProcessor()
927
        proc._build_map(text)
928
        self.assertEqual(expected, proc.imports,
929
                         'Incorrect processing of:\n%s\n%s\n!=\n%s'
930
                         % (text, expected, proc.imports))
931
932
    def test_import_one(self):
933
        exp = {'one':(['one'], None, {})}
934
        self.check(exp, 'import one')
935
        self.check(exp, '\nimport one\n')
936
937
    def test_import_one_two(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
938
        exp = {'one': (['one'], None,
939
                      {'two': (['one', 'two'], None, {}),
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
940
                      }),
941
              }
942
        self.check(exp, 'import one.two')
943
        self.check(exp, 'import one, one.two')
944
        self.check(exp, 'import one\nimport one.two')
945
946
    def test_import_as(self):
947
        exp = {'two':(['one'], None, {})}
948
        self.check(exp, 'import one as two')
949
950
    def test_import_many(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
951
        exp = {'one': (['one'], None,
952
                      {'two': (['one', 'two'], None,
953
                              {'three': (['one', 'two', 'three'], None, {}),
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
954
                              }),
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
955
                       'four': (['one', 'four'], None, {}),
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
956
                      }),
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
957
               'five': (['one', 'five'], None, {}),
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
958
              }
959
        self.check(exp, 'import one.two.three, one.four, one.five as five')
960
        self.check(exp, 'import one.five as five\n'
961
                        'import one\n'
962
                        'import one.two.three\n'
963
                        'import one.four\n')
964
965
    def test_from_one_import_two(self):
966
        exp = {'two':(['one'], 'two', {})}
967
        self.check(exp, 'from one import two\n')
968
        self.check(exp, 'from one import (\n'
969
                        '    two,\n'
970
                        '    )\n')
971
972
    def test_from_one_import_two(self):
973
        exp = {'two':(['one'], 'two', {})}
974
        self.check(exp, 'from one import two\n')
975
        self.check(exp, 'from one import (two)\n')
976
        self.check(exp, 'from one import (two,)\n')
977
        self.check(exp, 'from one import two as two\n')
978
        self.check(exp, 'from one import (\n'
979
                        '    two,\n'
980
                        '    )\n')
981
982
    def test_from_many(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
983
        exp = {'two': (['one'], 'two', {}),
984
               'three': (['one', 'two'], 'three', {}),
985
               'five': (['one', 'two'], 'four', {}),
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
986
              }
987
        self.check(exp, 'from one import two\n'
988
                        'from one.two import three, four as five\n')
989
        self.check(exp, 'from one import two\n'
990
                        'from one.two import (\n'
991
                        '    three,\n'
992
                        '    four as five,\n'
993
                        '    )\n')
994
995
    def test_mixed(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
996
        exp = {'two': (['one'], 'two', {}),
997
               'three': (['one', 'two'], 'three', {}),
998
               'five': (['one', 'two'], 'four', {}),
999
               'one': (['one'], None,
1000
                      {'two': (['one', 'two'], None, {}),
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
1001
                      }),
1002
              }
1003
        self.check(exp, 'from one import two\n'
1004
                        'from one.two import three, four as five\n'
1005
                        'import one.two')
1006
        self.check(exp, 'from one import two\n'
1007
                        'from one.two import (\n'
1008
                        '    three,\n'
1009
                        '    four as five,\n'
1010
                        '    )\n'
1011
                        'import one\n'
1012
                        'import one.two\n')
1996.1.15 by John Arbash Meinel
Everything is now hooked up
1013
1996.1.18 by John Arbash Meinel
Add more structured error handling
1014
    def test_incorrect_line(self):
1015
        proc = lazy_import.ImportProcessor()
1016
        self.assertRaises(errors.InvalidImportLine,
1017
                          proc._build_map, 'foo bar baz')
1018
        self.assertRaises(errors.InvalidImportLine,
1019
                          proc._build_map, 'improt foo')
1020
        self.assertRaises(errors.InvalidImportLine,
1021
                          proc._build_map, 'importfoo')
1022
        self.assertRaises(errors.InvalidImportLine,
1023
                          proc._build_map, 'fromimport')
1024
1025
    def test_name_collision(self):
1026
        proc = lazy_import.ImportProcessor()
1027
        proc._build_map('import foo')
1028
1029
        # All of these would try to create an object with the
1030
        # same name as an existing object.
1031
        self.assertRaises(errors.ImportNameCollision,
1032
                          proc._build_map, 'import bar as foo')
1033
        self.assertRaises(errors.ImportNameCollision,
1034
                          proc._build_map, 'from foo import bar as foo')
1035
        self.assertRaises(errors.ImportNameCollision,
1036
                          proc._build_map, 'from bar import foo')
1037
6621.26.1 by Martin
Prevent lazy imports misunderstanding relative import syntax
1038
    def test_relative_imports(self):
1039
        proc = lazy_import.ImportProcessor()
1040
        self.assertRaises(ImportError,
1041
                          proc._build_map, 'import .bar as foo')
1042
        self.assertRaises(ImportError,
1043
                          proc._build_map, 'from .foo import bar as foo')
1044
        self.assertRaises(ImportError,
1045
                          proc._build_map, 'from .bar import foo')
1046
1996.1.15 by John Arbash Meinel
Everything is now hooked up
1047
1048
class TestLazyImportProcessor(ImportReplacerHelper):
1049
1050
    def test_root(self):
1051
        try:
1052
            root6
1053
        except NameError:
1054
            pass # root6 should not be defined yet
1055
        else:
1056
            self.fail('root6 was not supposed to exist yet')
1057
1058
        text = 'import %s as root6' % (self.root_name,)
1059
        proc = lazy_import.ImportProcessor(InstrumentedImportReplacer)
1996.1.19 by John Arbash Meinel
Write a simple wrapper function to make lazy imports easy.
1060
        proc.lazy_import(scope=globals(), text=text)
1996.1.15 by John Arbash Meinel
Everything is now hooked up
1061
1062
        # So 'root6' should be a lazy import
1063
        self.assertEqual(InstrumentedImportReplacer,
1064
                         object.__getattribute__(root6, '__class__'))
1065
1066
        self.assertEqual(1, root6.var1)
1067
        self.assertEqual('x', root6.func1('x'))
1068
1069
        self.assertEqual([('__getattribute__', 'var1'),
1070
                          ('_import', 'root6'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
1071
                          ('import', self.root_name, [], 0),
1996.1.15 by John Arbash Meinel
Everything is now hooked up
1072
                         ], self.actions)
1073
1074
    def test_import_deep(self):
1075
        """Test import root.mod, root.sub.submoda, root.sub.submodb
1076
        root should be a lazy import, with multiple children, who also
1077
        have children to be imported.
1078
        And when root is imported, the children should be lazy, and
1079
        reuse the intermediate lazy object.
1080
        """
1081
        try:
1082
            submoda7
1083
        except NameError:
1084
            pass # submoda7 should not be defined yet
1085
        else:
1086
            self.fail('submoda7 was not supposed to exist yet')
1087
1088
        text = """\
1089
import %(root_name)s.%(sub_name)s.%(submoda_name)s as submoda7
1090
""" % self.__dict__
1091
        proc = lazy_import.ImportProcessor(InstrumentedImportReplacer)
1996.1.19 by John Arbash Meinel
Write a simple wrapper function to make lazy imports easy.
1092
        proc.lazy_import(scope=globals(), text=text)
1996.1.15 by John Arbash Meinel
Everything is now hooked up
1093
1094
        # So 'submoda7' should be a lazy import
1095
        self.assertEqual(InstrumentedImportReplacer,
1096
                         object.__getattribute__(submoda7, '__class__'))
1097
1098
        # This should import submoda7
1099
        self.assertEqual(4, submoda7.var4)
1100
1101
        sub_path = self.root_name + '.' + self.sub_name
1102
        submoda_path = sub_path + '.' + self.submoda_name
1103
1104
        self.assertEqual([('__getattribute__', 'var4'),
1105
                          ('_import', 'submoda7'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
1106
                          ('import', submoda_path, [], 0),
1996.1.15 by John Arbash Meinel
Everything is now hooked up
1107
                         ], self.actions)
1996.1.19 by John Arbash Meinel
Write a simple wrapper function to make lazy imports easy.
1108
1109
    def test_lazy_import(self):
1110
        """Smoke test that lazy_import() does the right thing"""
1111
        try:
1112
            root8
1113
        except NameError:
1114
            pass # root8 should not be defined yet
1115
        else:
1116
            self.fail('root8 was not supposed to exist yet')
1117
        lazy_import.lazy_import(globals(),
1118
                                'import %s as root8' % (self.root_name,),
1119
                                lazy_import_class=InstrumentedImportReplacer)
1120
1121
        self.assertEqual(InstrumentedImportReplacer,
1122
                         object.__getattribute__(root8, '__class__'))
1123
1124
        self.assertEqual(1, root8.var1)
1125
        self.assertEqual(1, root8.var1)
1126
        self.assertEqual(1, root8.func1(1))
1127
1128
        self.assertEqual([('__getattribute__', 'var1'),
1129
                          ('_import', 'root8'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
1130
                          ('import', self.root_name, [], 0),
1996.1.19 by John Arbash Meinel
Write a simple wrapper function to make lazy imports easy.
1131
                         ], self.actions)
6082.4.1 by Benji York
fix bug 702914
1132
6082.4.2 by Benji York
fix whitespace
1133
6082.4.1 by Benji York
fix bug 702914
1134
class TestScopeReplacerReentrance(TestCase):
1135
    """The ScopeReplacer should be reentrant.
1136
1137
    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.
1138
    race to see which invocation will be the first to call _replace.
6111.3.9 by Martin Packman
Fix loose formatting and spelling
1139
    The losing caller used to see an exception (bugs 396819 and 702914).
6082.4.1 by Benji York
fix bug 702914
1140
6111.3.1 by Martin von Gagern
Make lazy imports (at least more) thread-safe.
1141
    These tests set up a tracer that stops at a suitable moment (upon
1142
    entry of a specified method) and starts another call to the
1143
    functionality in question (__call__, __getattribute__, __setattr_)
6111.3.10 by Martin Packman
Fix docstring typo noted by vila in review
1144
    in order to win the race, setting up the original caller to lose.
6082.4.1 by Benji York
fix bug 702914
1145
    """
1146
1147
    def tracer(self, frame, event, arg):
6111.3.1 by Martin von Gagern
Make lazy imports (at least more) thread-safe.
1148
        if event != 'call':
1149
            return self.tracer
6082.4.1 by Benji York
fix bug 702914
1150
        # 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.
1151
        code = frame.f_code
1152
        filename = code.co_filename
6082.4.1 by Benji York
fix bug 702914
1153
        # Convert ".pyc" and ".pyo" file names to their ".py" equivalent.
1154
        filename = re.sub(r'\.py[co]$', '.py', filename)
6111.3.1 by Martin von Gagern
Make lazy imports (at least more) thread-safe.
1155
        function_name = code.co_name
6082.4.1 by Benji York
fix bug 702914
1156
        # 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.
1157
        if (filename.endswith('lazy_import.py') and
1158
            function_name == self.method_to_trace):
1159
            # We don't need to trace any more.
1160
            sys.settrace(None)
1161
            # Run another racer.  This one will "win" the race.
1162
            self.racer()
6082.4.1 by Benji York
fix bug 702914
1163
        return self.tracer
1164
6111.3.1 by Martin von Gagern
Make lazy imports (at least more) thread-safe.
1165
    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
1166
        self.overrideAttr(lazy_import.ScopeReplacer, '_should_proxy', True)
6111.3.2 by Martin von Gagern
Allow subclasses to override _should_proxy.
1167
        self.racer = racer
1168
        self.method_to_trace = method_to_trace
1169
        sys.settrace(self.tracer)
1170
        self.racer() # Should not raise any exception
1171
        # Make sure the tracer actually found the code it was
1172
        # looking for.  If not, maybe the code was refactored in
1173
        # such a way that these tests aren't needed any more.
1174
        self.assertEqual(None, sys.gettrace())
6082.4.1 by Benji York
fix bug 702914
1175
1176
    def test_call(self):
1177
        def factory(*args):
1178
            return factory
6111.3.4 by Martin Packman
Correct overriding of ScopeReplacer._should_proxy in lazy_import tests
1179
        replacer = lazy_import.ScopeReplacer({}, factory, 'name')
6082.4.1 by Benji York
fix bug 702914
1180
        self.run_race(replacer)
1181
1182
    def test_setattr(self):
1183
        class Replaced:
1184
            pass
1185
1186
        def factory(*args):
1187
            return Replaced()
1188
6111.3.4 by Martin Packman
Correct overriding of ScopeReplacer._should_proxy in lazy_import tests
1189
        replacer = lazy_import.ScopeReplacer({}, factory, 'name')
6082.4.1 by Benji York
fix bug 702914
1190
1191
        def racer():
1192
            replacer.foo = 42
1193
1194
        self.run_race(racer)
1195
1196
    def test_getattribute(self):
1197
        class Replaced:
1198
            foo = 'bar'
1199
1200
        def factory(*args):
1201
            return Replaced()
1202
6111.3.4 by Martin Packman
Correct overriding of ScopeReplacer._should_proxy in lazy_import tests
1203
        replacer = lazy_import.ScopeReplacer({}, factory, 'name')
6082.4.1 by Benji York
fix bug 702914
1204
1205
        def racer():
1206
            replacer.foo
1207
1208
        self.run_race(racer)