/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
7143.15.2 by Jelmer Vernooij
Run autopep8.
101
2399.1.11 by John Arbash Meinel
Update lazy_import with tests for the new '_should_proxy' variable.
102
        def restore():
103
            lazy_import.ScopeReplacer._should_proxy = orig_proxy
104
        lazy_import.ScopeReplacer._should_proxy = False
105
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
106
    def test_object(self):
1996.1.27 by John Arbash Meinel
Add a test for side-effects from using ScopeReplacer
107
        """ScopeReplacer can create an instance in local scope.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
108
1996.1.27 by John Arbash Meinel
Add a test for side-effects from using ScopeReplacer
109
        An object should appear in globals() by constructing a ScopeReplacer,
110
        and it will be replaced with the real object upon the first request.
111
        """
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
112
        actions = []
113
        InstrumentedReplacer.use_actions(actions)
114
        TestClass.use_actions(actions)
115
116
        def factory(replacer, scope, name):
117
            actions.append('factory')
118
            return TestClass()
119
120
        try:
121
            test_obj1
122
        except NameError:
123
            # test_obj1 shouldn't exist yet
124
            pass
125
        else:
126
            self.fail('test_obj1 was not supposed to exist yet')
127
128
        InstrumentedReplacer(scope=globals(), name='test_obj1',
129
                             factory=factory)
130
131
        # We can't use isinstance() because that uses test_obj1.__class__
132
        # and that goes through __getattribute__ which would activate
133
        # the replacement
134
        self.assertEqual(InstrumentedReplacer,
135
                         object.__getattribute__(test_obj1, '__class__'))
136
        self.assertEqual('foo', test_obj1.foo(1))
137
        self.assertIsInstance(test_obj1, TestClass)
138
        self.assertEqual('foo', test_obj1.foo(2))
139
        self.assertEqual([('__getattribute__', 'foo'),
140
                          'factory',
141
                          'init',
142
                          ('foo', 1),
143
                          ('foo', 2),
7143.15.2 by Jelmer Vernooij
Run autopep8.
144
                          ], actions)
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
145
1551.18.22 by Aaron Bentley
Fix exception when ScopeReplacer is assigned to before retrieving any members
146
    def test_setattr_replaces(self):
147
        """ScopeReplacer can create an instance in local scope.
148
149
        An object should appear in globals() by constructing a ScopeReplacer,
150
        and it will be replaced with the real object upon the first request.
151
        """
3302.5.2 by Vincent Ladeuil
Fix test bug revealed when run alone.
152
        actions = []
153
        TestClass.use_actions(actions)
7143.15.2 by Jelmer Vernooij
Run autopep8.
154
1551.18.22 by Aaron Bentley
Fix exception when ScopeReplacer is assigned to before retrieving any members
155
        def factory(replacer, scope, name):
156
            return TestClass()
157
        try:
158
            test_obj6
159
        except NameError:
160
            # test_obj6 shouldn't exist yet
161
            pass
162
        else:
163
            self.fail('test_obj6 was not supposed to exist yet')
164
165
        lazy_import.ScopeReplacer(scope=globals(), name='test_obj6',
166
                                  factory=factory)
167
168
        # We can't use isinstance() because that uses test_obj6.__class__
169
        # and that goes through __getattribute__ which would activate
170
        # the replacement
171
        self.assertEqual(lazy_import.ScopeReplacer,
172
                         object.__getattribute__(test_obj6, '__class__'))
173
        test_obj6.bar = 'test'
174
        self.assertNotEqual(lazy_import.ScopeReplacer,
175
                            object.__getattribute__(test_obj6, '__class__'))
1551.18.23 by Aaron Bentley
Ensure that the attribute is actually set
176
        self.assertEqual('test', test_obj6.bar)
1551.18.22 by Aaron Bentley
Fix exception when ScopeReplacer is assigned to before retrieving any members
177
1996.1.27 by John Arbash Meinel
Add a test for side-effects from using ScopeReplacer
178
    def test_replace_side_effects(self):
179
        """Creating a new object should only create one entry in globals.
180
181
        And only that entry even after replacement.
182
        """
183
        try:
184
            test_scope1
185
        except NameError:
186
            # test_scope1 shouldn't exist yet
187
            pass
188
        else:
189
            self.fail('test_scope1 was not supposed to exist yet')
190
191
        # ignore the logged actions
192
        TestClass.use_actions([])
193
194
        def factory(replacer, scope, name):
195
            return TestClass()
196
197
        orig_globals = set(globals().keys())
198
199
        lazy_import.ScopeReplacer(scope=globals(), name='test_scope1',
200
                                  factory=factory)
201
202
        new_globals = set(globals().keys())
203
204
        self.assertEqual(lazy_import.ScopeReplacer,
205
                         object.__getattribute__(test_scope1, '__class__'))
206
        self.assertEqual('foo', test_scope1.foo(1))
207
        self.assertIsInstance(test_scope1, TestClass)
208
209
        final_globals = set(globals().keys())
210
6619.3.12 by Jelmer Vernooij
Use 2to3 set_literal fixer.
211
        self.assertEqual({'test_scope1'}, new_globals - orig_globals)
1996.1.27 by John Arbash Meinel
Add a test for side-effects from using ScopeReplacer
212
        self.assertEqual(set(), orig_globals - new_globals)
213
        self.assertEqual(set(), final_globals - new_globals)
214
        self.assertEqual(set(), new_globals - final_globals)
215
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
216
    def test_class(self):
217
        actions = []
218
        InstrumentedReplacer.use_actions(actions)
219
        TestClass.use_actions(actions)
220
221
        def factory(replacer, scope, name):
222
            actions.append('factory')
223
            return TestClass
224
225
        try:
226
            test_class1
227
        except NameError:
228
            # test_class2 shouldn't exist yet
229
            pass
230
        else:
231
            self.fail('test_class1 was not supposed to exist yet')
232
233
        InstrumentedReplacer(scope=globals(), name='test_class1',
234
                             factory=factory)
235
236
        self.assertEqual('class_member', test_class1.class_member)
237
        self.assertEqual(test_class1, TestClass)
238
        self.assertEqual([('__getattribute__', 'class_member'),
239
                          'factory',
7143.15.2 by Jelmer Vernooij
Run autopep8.
240
                          ], actions)
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
241
242
    def test_call_class(self):
243
        actions = []
244
        InstrumentedReplacer.use_actions(actions)
245
        TestClass.use_actions(actions)
246
247
        def factory(replacer, scope, name):
248
            actions.append('factory')
249
            return TestClass
250
251
        try:
252
            test_class2
253
        except NameError:
254
            # test_class2 shouldn't exist yet
255
            pass
256
        else:
257
            self.fail('test_class2 was not supposed to exist yet')
258
259
        InstrumentedReplacer(scope=globals(), name='test_class2',
260
                             factory=factory)
261
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
262
        self.assertFalse(test_class2 is TestClass)
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
263
        obj = test_class2()
264
        self.assertIs(test_class2, TestClass)
265
        self.assertIsInstance(obj, TestClass)
266
        self.assertEqual('class_member', obj.class_member)
267
        self.assertEqual([('__call__', (), {}),
268
                          'factory',
269
                          'init',
7143.15.2 by Jelmer Vernooij
Run autopep8.
270
                          ], actions)
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
271
272
    def test_call_func(self):
273
        actions = []
274
        InstrumentedReplacer.use_actions(actions)
275
276
        def func(a, b, c=None):
277
            actions.append('func')
278
            return (a, b, c)
279
280
        def factory(replacer, scope, name):
281
            actions.append('factory')
282
            return func
283
284
        try:
285
            test_func1
286
        except NameError:
287
            # test_func1 shouldn't exist yet
288
            pass
289
        else:
290
            self.fail('test_func1 was not supposed to exist yet')
291
        InstrumentedReplacer(scope=globals(), name='test_func1',
292
                             factory=factory)
293
5784.1.1 by Martin Pool
Stop using failIf, failUnless, etc
294
        self.assertFalse(test_func1 is func)
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
295
        val = test_func1(1, 2, c='3')
296
        self.assertIs(test_func1, func)
297
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
298
        self.assertEqual((1, 2, '3'), val)
7143.15.2 by Jelmer Vernooij
Run autopep8.
299
        self.assertEqual([('__call__', (1, 2), {'c': '3'}),
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
300
                          'factory',
301
                          'func',
7143.15.2 by Jelmer Vernooij
Run autopep8.
302
                          ], actions)
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
303
1996.1.16 by John Arbash Meinel
Raise an exception when ScopeReplacer has been misused
304
    def test_other_variable(self):
305
        """Test when a ScopeReplacer is assigned to another variable.
306
307
        This test could be updated if we find a way to trap '=' rather
308
        than just giving a belated exception.
309
        ScopeReplacer only knows about the variable it was created as,
310
        so until the object is replaced, it is illegal to pass it to
311
        another variable. (Though discovering this may take a while)
312
        """
313
        actions = []
314
        InstrumentedReplacer.use_actions(actions)
315
        TestClass.use_actions(actions)
316
317
        def factory(replacer, scope, name):
318
            actions.append('factory')
319
            return TestClass()
320
321
        try:
322
            test_obj2
323
        except NameError:
324
            # test_obj2 shouldn't exist yet
325
            pass
326
        else:
327
            self.fail('test_obj2 was not supposed to exist yet')
328
329
        InstrumentedReplacer(scope=globals(), name='test_obj2',
330
                             factory=factory)
331
332
        self.assertEqual(InstrumentedReplacer,
333
                         object.__getattribute__(test_obj2, '__class__'))
334
        # This is technically not allowed, but we don't have a way to
335
        # test it until later.
336
        test_obj3 = test_obj2
337
        self.assertEqual(InstrumentedReplacer,
338
                         object.__getattribute__(test_obj2, '__class__'))
339
        self.assertEqual(InstrumentedReplacer,
340
                         object.__getattribute__(test_obj3, '__class__'))
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
341
1996.1.16 by John Arbash Meinel
Raise an exception when ScopeReplacer has been misused
342
        # The first use of the alternate variable causes test_obj2 to
343
        # be replaced.
344
        self.assertEqual('foo', test_obj3.foo(1))
345
        # test_obj2 has been replaced, but the ScopeReplacer has no
346
        # idea of test_obj3
347
        self.assertEqual(TestClass,
348
                         object.__getattribute__(test_obj2, '__class__'))
349
        self.assertEqual(InstrumentedReplacer,
350
                         object.__getattribute__(test_obj3, '__class__'))
351
        # We should be able to access test_obj2 attributes normally
352
        self.assertEqual('foo', test_obj2.foo(2))
353
        self.assertEqual('foo', test_obj2.foo(3))
354
355
        # However, the next access on test_obj3 should raise an error
356
        # because only now are we able to detect the problem.
7413.8.11 by Jelmer Vernooij
Don't lazy-import errors.
357
        self.assertRaises(lazy_import.IllegalUseOfScopeReplacer,
1996.1.16 by John Arbash Meinel
Raise an exception when ScopeReplacer has been misused
358
                          getattr, test_obj3, 'foo')
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
359
2399.1.11 by John Arbash Meinel
Update lazy_import with tests for the new '_should_proxy' variable.
360
        self.assertEqual([('__getattribute__', 'foo'),
361
                          'factory',
362
                          'init',
363
                          ('foo', 1),
364
                          ('foo', 2),
365
                          ('foo', 3),
366
                          ('__getattribute__', 'foo'),
7143.15.2 by Jelmer Vernooij
Run autopep8.
367
                          ], actions)
2399.1.11 by John Arbash Meinel
Update lazy_import with tests for the new '_should_proxy' variable.
368
369
    def test_enable_proxying(self):
370
        """Test that we can allow ScopeReplacer to proxy."""
371
        actions = []
372
        InstrumentedReplacer.use_actions(actions)
373
        TestClass.use_actions(actions)
374
375
        def factory(replacer, scope, name):
376
            actions.append('factory')
377
            return TestClass()
378
379
        try:
380
            test_obj4
381
        except NameError:
382
            # test_obj4 shouldn't exist yet
383
            pass
384
        else:
385
            self.fail('test_obj4 was not supposed to exist yet')
386
387
        lazy_import.ScopeReplacer._should_proxy = True
388
        InstrumentedReplacer(scope=globals(), name='test_obj4',
389
                             factory=factory)
390
391
        self.assertEqual(InstrumentedReplacer,
392
                         object.__getattribute__(test_obj4, '__class__'))
393
        test_obj5 = test_obj4
394
        self.assertEqual(InstrumentedReplacer,
395
                         object.__getattribute__(test_obj4, '__class__'))
396
        self.assertEqual(InstrumentedReplacer,
397
                         object.__getattribute__(test_obj5, '__class__'))
398
399
        # The first use of the alternate variable causes test_obj2 to
400
        # be replaced.
401
        self.assertEqual('foo', test_obj4.foo(1))
402
        self.assertEqual(TestClass,
403
                         object.__getattribute__(test_obj4, '__class__'))
404
        self.assertEqual(InstrumentedReplacer,
405
                         object.__getattribute__(test_obj5, '__class__'))
406
        # We should be able to access test_obj4 attributes normally
407
        self.assertEqual('foo', test_obj4.foo(2))
408
        # because we enabled proxying, test_obj5 can access its members as well
409
        self.assertEqual('foo', test_obj5.foo(3))
410
        self.assertEqual('foo', test_obj5.foo(4))
411
412
        # However, it cannot be replaced by the ScopeReplacer
413
        self.assertEqual(InstrumentedReplacer,
414
                         object.__getattribute__(test_obj5, '__class__'))
415
416
        self.assertEqual([('__getattribute__', 'foo'),
417
                          'factory',
418
                          'init',
419
                          ('foo', 1),
420
                          ('foo', 2),
421
                          ('__getattribute__', 'foo'),
422
                          ('foo', 3),
423
                          ('__getattribute__', 'foo'),
424
                          ('foo', 4),
7143.15.2 by Jelmer Vernooij
Run autopep8.
425
                          ], actions)
1996.1.16 by John Arbash Meinel
Raise an exception when ScopeReplacer has been misused
426
5409.2.1 by Martin
Add check in lazy import ScopeReplacer to ensure it's not trying to replace itself
427
    def test_replacing_from_own_scope_fails(self):
428
        """If a ScopeReplacer tries to replace itself a nice error is given"""
429
        actions = []
430
        InstrumentedReplacer.use_actions(actions)
431
        TestClass.use_actions(actions)
432
433
        def factory(replacer, scope, name):
434
            actions.append('factory')
435
            # return the name in given scope, which is currently the replacer
436
            return scope[name]
437
438
        try:
439
            test_obj7
440
        except NameError:
441
            # test_obj7 shouldn't exist yet
442
            pass
443
        else:
444
            self.fail('test_obj7 was not supposed to exist yet')
445
446
        InstrumentedReplacer(scope=globals(), name='test_obj7',
447
                             factory=factory)
448
449
        self.assertEqual(InstrumentedReplacer,
450
                         object.__getattribute__(test_obj7, '__class__'))
7413.8.11 by Jelmer Vernooij
Don't lazy-import errors.
451
        e = self.assertRaises(lazy_import.IllegalUseOfScopeReplacer, test_obj7)
5409.2.1 by Martin
Add check in lazy import ScopeReplacer to ensure it's not trying to replace itself
452
        self.assertIn("replace itself", e.msg)
453
        self.assertEqual([('__call__', (), {}),
454
                          'factory'], actions)
455
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
456
1996.1.8 by John Arbash Meinel
Split up TestImportReplacer into a helper class
457
class ImportReplacerHelper(TestCaseInTempDir):
1996.1.1 by John Arbash Meinel
Adding a ScopeReplacer class, which can replace itself on demand
458
    """Test the ability to have a lazily imported module or object"""
459
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
460
    def setUp(self):
6552.1.4 by Vincent Ladeuil
Remaining tests matching setup(self) that can be rewritten with super().
461
        super(ImportReplacerHelper, self).setUp()
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
462
        self.create_modules()
463
        base_path = self.test_dir + '/base'
464
1996.1.3 by John Arbash Meinel
Basic single-level imports work
465
        self.actions = []
466
        InstrumentedImportReplacer.use_actions(self.actions)
467
4985.2.1 by Vincent Ladeuil
Deploy addAttrCleanup on the whole test suite.
468
        sys.path.append(base_path)
469
        self.addCleanup(sys.path.remove, base_path)
470
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
471
        def instrumented_import(mod, scope1, scope2, fromlist, level):
472
            self.actions.append(('import', mod, fromlist, level))
7011.1.1 by Martin
Make replacement for lazy import tests less dodgy
473
            return __import__(mod, scope1, scope2, fromlist, level=level)
474
        self.addCleanup(setattr, lazy_import, '_builtin_import', __import__)
475
        lazy_import._builtin_import = instrumented_import
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
476
477
    def create_modules(self):
478
        """Create some random modules to be imported.
479
480
        Each entry has a random suffix, and the full names are saved
481
482
        These are setup as follows:
483
         base/ <= used to ensure not in default search path
484
            root-XXX/
485
                __init__.py <= This will contain var1, func1
486
                mod-XXX.py <= This will contain var2, func2
487
                sub-XXX/
488
                    __init__.py <= Contains var3, func3
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
489
                    submoda-XXX.py <= contains var4, func4
490
                    submodb-XXX.py <= containse var5, func5
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
491
        """
492
        rand_suffix = osutils.rand_chars(4)
493
        root_name = 'root_' + rand_suffix
494
        mod_name = 'mod_' + rand_suffix
495
        sub_name = 'sub_' + rand_suffix
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
496
        submoda_name = 'submoda_' + rand_suffix
497
        submodb_name = 'submodb_' + rand_suffix
498
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
499
        os.mkdir('base')
500
        root_path = osutils.pathjoin('base', root_name)
501
        os.mkdir(root_path)
502
        root_init = osutils.pathjoin(root_path, '__init__.py')
6754.7.1 by Martin
Fix lazy import test file handling on Python 3
503
        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
504
            f.write('var1 = 1\ndef func1(a):\n  return a\n')
505
        mod_path = osutils.pathjoin(root_path, mod_name + '.py')
6754.7.1 by Martin
Fix lazy import test file handling on Python 3
506
        with open(mod_path, 'w') as f:
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
507
            f.write('var2 = 2\ndef func2(a):\n  return a\n')
508
509
        sub_path = osutils.pathjoin(root_path, sub_name)
510
        os.mkdir(sub_path)
6754.7.1 by Martin
Fix lazy import test file handling on Python 3
511
        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
512
            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
513
        submoda_path = osutils.pathjoin(sub_path, submoda_name + '.py')
6754.7.1 by Martin
Fix lazy import test file handling on Python 3
514
        with open(submoda_path, 'w') as f:
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
515
            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
516
        submodb_path = osutils.pathjoin(sub_path, submodb_name + '.py')
6754.7.1 by Martin
Fix lazy import test file handling on Python 3
517
        with open(submodb_path, 'w') as f:
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
518
            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
519
        self.root_name = root_name
520
        self.mod_name = mod_name
521
        self.sub_name = sub_name
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
522
        self.submoda_name = submoda_name
523
        self.submodb_name = submodb_name
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
524
1996.1.8 by John Arbash Meinel
Split up TestImportReplacer into a helper class
525
526
class TestImportReplacerHelper(ImportReplacerHelper):
527
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
528
    def test_basic_import(self):
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
529
        """Test that a real import of these modules works"""
1996.1.3 by John Arbash Meinel
Basic single-level imports work
530
        sub_mod_path = '.'.join([self.root_name, self.sub_name,
7143.15.2 by Jelmer Vernooij
Run autopep8.
531
                                 self.submoda_name])
7011.1.1 by Martin
Make replacement for lazy import tests less dodgy
532
        root = lazy_import._builtin_import(sub_mod_path, {}, {}, [], 0)
1996.1.2 by John Arbash Meinel
start working on some lazy importing code
533
        self.assertEqual(1, root.var1)
534
        self.assertEqual(3, getattr(root, self.sub_name).var3)
535
        self.assertEqual(4, getattr(getattr(root, self.sub_name),
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
536
                                    self.submoda_name).var4)
1996.1.3 by John Arbash Meinel
Basic single-level imports work
537
538
        mod_path = '.'.join([self.root_name, self.mod_name])
7011.1.1 by Martin
Make replacement for lazy import tests less dodgy
539
        root = lazy_import._builtin_import(mod_path, {}, {}, [], 0)
1996.1.3 by John Arbash Meinel
Basic single-level imports work
540
        self.assertEqual(2, getattr(root, self.mod_name).var2)
541
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
542
        self.assertEqual([('import', sub_mod_path, [], 0),
543
                          ('import', mod_path, [], 0),
7143.15.2 by Jelmer Vernooij
Run autopep8.
544
                          ], self.actions)
1996.1.3 by John Arbash Meinel
Basic single-level imports work
545
1996.1.8 by John Arbash Meinel
Split up TestImportReplacer into a helper class
546
547
class TestImportReplacer(ImportReplacerHelper):
548
1996.1.3 by John Arbash Meinel
Basic single-level imports work
549
    def test_import_root(self):
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
550
        """Test 'import root-XXX as root1'"""
1996.1.3 by John Arbash Meinel
Basic single-level imports work
551
        try:
552
            root1
553
        except NameError:
554
            # root1 shouldn't exist yet
555
            pass
556
        else:
557
            self.fail('root1 was not supposed to exist yet')
558
559
        # This should replicate 'import root-xxyyzz as root1'
560
        InstrumentedImportReplacer(scope=globals(), name='root1',
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
561
                                   module_path=[self.root_name],
1996.1.15 by John Arbash Meinel
Everything is now hooked up
562
                                   member=None, children={})
1996.1.3 by John Arbash Meinel
Basic single-level imports work
563
564
        self.assertEqual(InstrumentedImportReplacer,
565
                         object.__getattribute__(root1, '__class__'))
566
        self.assertEqual(1, root1.var1)
567
        self.assertEqual('x', root1.func1('x'))
568
569
        self.assertEqual([('__getattribute__', 'var1'),
570
                          ('_import', 'root1'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
571
                          ('import', self.root_name, [], 0),
7143.15.2 by Jelmer Vernooij
Run autopep8.
572
                          ], self.actions)
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
573
574
    def test_import_mod(self):
575
        """Test 'import root-XXX.mod-XXX as mod2'"""
576
        try:
577
            mod1
578
        except NameError:
579
            # mod1 shouldn't exist yet
580
            pass
581
        else:
582
            self.fail('mod1 was not supposed to exist yet')
583
584
        mod_path = self.root_name + '.' + self.mod_name
585
        InstrumentedImportReplacer(scope=globals(), name='mod1',
586
                                   module_path=[self.root_name, self.mod_name],
1996.1.15 by John Arbash Meinel
Everything is now hooked up
587
                                   member=None, children={})
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
588
589
        self.assertEqual(InstrumentedImportReplacer,
590
                         object.__getattribute__(mod1, '__class__'))
591
        self.assertEqual(2, mod1.var2)
592
        self.assertEqual('y', mod1.func2('y'))
593
594
        self.assertEqual([('__getattribute__', 'var2'),
595
                          ('_import', 'mod1'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
596
                          ('import', mod_path, [], 0),
7143.15.2 by Jelmer Vernooij
Run autopep8.
597
                          ], self.actions)
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
598
599
    def test_import_mod_from_root(self):
600
        """Test 'from root-XXX import mod-XXX as mod2'"""
601
        try:
602
            mod2
603
        except NameError:
604
            # mod2 shouldn't exist yet
605
            pass
606
        else:
607
            self.fail('mod2 was not supposed to exist yet')
608
609
        InstrumentedImportReplacer(scope=globals(), name='mod2',
610
                                   module_path=[self.root_name],
1996.1.15 by John Arbash Meinel
Everything is now hooked up
611
                                   member=self.mod_name, children={})
1996.1.4 by John Arbash Meinel
Change how parameters are passed to support 'import root1.mod1 as mod1'
612
613
        self.assertEqual(InstrumentedImportReplacer,
614
                         object.__getattribute__(mod2, '__class__'))
615
        self.assertEqual(2, mod2.var2)
616
        self.assertEqual('y', mod2.func2('y'))
617
618
        self.assertEqual([('__getattribute__', 'var2'),
619
                          ('_import', 'mod2'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
620
                          ('import', self.root_name, [self.mod_name], 0),
7143.15.2 by Jelmer Vernooij
Run autopep8.
621
                          ], self.actions)
1996.1.5 by John Arbash Meinel
Test that we can lazy import a module, and its children
622
623
    def test_import_root_and_mod(self):
624
        """Test 'import root-XXX.mod-XXX' remapping both to root3.mod3"""
625
        try:
626
            root3
627
        except NameError:
628
            # root3 shouldn't exist yet
629
            pass
630
        else:
631
            self.fail('root3 was not supposed to exist yet')
632
633
        InstrumentedImportReplacer(scope=globals(),
7143.15.2 by Jelmer Vernooij
Run autopep8.
634
                                   name='root3', module_path=[self.root_name], member=None,
635
                                   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
636
637
        # So 'root3' should be a lazy import
638
        # and once it is imported, mod3 should also be lazy until
639
        # actually accessed.
640
        self.assertEqual(InstrumentedImportReplacer,
641
                         object.__getattribute__(root3, '__class__'))
642
        self.assertEqual(1, root3.var1)
643
644
        # There is a mod3 member, but it is also lazy
645
        self.assertEqual(InstrumentedImportReplacer,
646
                         object.__getattribute__(root3.mod3, '__class__'))
647
        self.assertEqual(2, root3.mod3.var2)
648
649
        mod_path = self.root_name + '.' + self.mod_name
650
        self.assertEqual([('__getattribute__', 'var1'),
651
                          ('_import', 'root3'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
652
                          ('import', self.root_name, [], 0),
1996.1.5 by John Arbash Meinel
Test that we can lazy import a module, and its children
653
                          ('__getattribute__', 'var2'),
654
                          ('_import', 'mod3'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
655
                          ('import', mod_path, [], 0),
7143.15.2 by Jelmer Vernooij
Run autopep8.
656
                          ], self.actions)
1996.1.6 by John Arbash Meinel
Test that we can add more children to the existing lazy object
657
658
    def test_import_root_and_root_mod(self):
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
659
        """Test that 'import root, root.mod' can be done.
660
661
        The second import should re-use the first one, and just add
662
        children to be imported.
663
        """
1996.1.6 by John Arbash Meinel
Test that we can add more children to the existing lazy object
664
        try:
665
            root4
666
        except NameError:
667
            # root4 shouldn't exist yet
668
            pass
669
        else:
670
            self.fail('root4 was not supposed to exist yet')
671
672
        InstrumentedImportReplacer(scope=globals(),
7143.15.2 by Jelmer Vernooij
Run autopep8.
673
                                   name='root4', module_path=[self.root_name], member=None,
674
                                   children={})
1996.1.6 by John Arbash Meinel
Test that we can add more children to the existing lazy object
675
676
        # So 'root4' should be a lazy import
677
        self.assertEqual(InstrumentedImportReplacer,
678
                         object.__getattribute__(root4, '__class__'))
679
680
        # Lets add a new child to be imported on demand
681
        # This syntax of using object.__getattribute__ is the correct method
682
        # for accessing the _import_replacer_children member
683
        children = object.__getattribute__(root4, '_import_replacer_children')
1996.1.15 by John Arbash Meinel
Everything is now hooked up
684
        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
685
686
        # Accessing root4.mod4 should import root, but mod should stay lazy
687
        self.assertEqual(InstrumentedImportReplacer,
688
                         object.__getattribute__(root4.mod4, '__class__'))
689
        self.assertEqual(2, root4.mod4.var2)
690
691
        mod_path = self.root_name + '.' + self.mod_name
692
        self.assertEqual([('__getattribute__', 'mod4'),
693
                          ('_import', 'root4'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
694
                          ('import', self.root_name, [], 0),
1996.1.6 by John Arbash Meinel
Test that we can add more children to the existing lazy object
695
                          ('__getattribute__', 'var2'),
696
                          ('_import', 'mod4'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
697
                          ('import', mod_path, [], 0),
7143.15.2 by Jelmer Vernooij
Run autopep8.
698
                          ], self.actions)
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
699
700
    def test_import_root_sub_submod(self):
701
        """Test import root.mod, root.sub.submoda, root.sub.submodb
702
        root should be a lazy import, with multiple children, who also
703
        have children to be imported.
704
        And when root is imported, the children should be lazy, and
705
        reuse the intermediate lazy object.
706
        """
707
        try:
708
            root5
709
        except NameError:
1996.1.15 by John Arbash Meinel
Everything is now hooked up
710
            # root5 shouldn't exist yet
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
711
            pass
712
        else:
713
            self.fail('root5 was not supposed to exist yet')
714
715
        InstrumentedImportReplacer(scope=globals(),
7143.15.2 by Jelmer Vernooij
Run autopep8.
716
                                   name='root5', module_path=[self.root_name], member=None,
717
                                   children={'mod5': ([self.root_name, self.mod_name], None, {}),
718
                                             'sub5': ([self.root_name, self.sub_name], None,
719
                                                      {'submoda5': ([self.root_name, self.sub_name,
720
                                                                     self.submoda_name], None, {}),
721
                                                       'submodb5': ([self.root_name, self.sub_name,
722
                                                                     self.submodb_name], None, {})
723
                                                       }),
724
                                             })
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
725
726
        # So 'root5' should be a lazy import
727
        self.assertEqual(InstrumentedImportReplacer,
728
                         object.__getattribute__(root5, '__class__'))
729
730
        # Accessing root5.mod5 should import root, but mod should stay lazy
731
        self.assertEqual(InstrumentedImportReplacer,
732
                         object.__getattribute__(root5.mod5, '__class__'))
733
        # root5.sub5 should still be lazy, but not re-import root5
734
        self.assertEqual(InstrumentedImportReplacer,
735
                         object.__getattribute__(root5.sub5, '__class__'))
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
736
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
737
        # Accessing root5.sub5.submoda5 should import sub5, but not either
738
        # of the sub objects (they should be available as lazy objects
739
        self.assertEqual(InstrumentedImportReplacer,
7143.15.2 by Jelmer Vernooij
Run autopep8.
740
                         object.__getattribute__(root5.sub5.submoda5, '__class__'))
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
741
        self.assertEqual(InstrumentedImportReplacer,
7143.15.2 by Jelmer Vernooij
Run autopep8.
742
                         object.__getattribute__(root5.sub5.submodb5, '__class__'))
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
743
744
        # This should import mod5
745
        self.assertEqual(2, root5.mod5.var2)
746
        # These should import submoda5 and submodb5
747
        self.assertEqual(4, root5.sub5.submoda5.var4)
748
        self.assertEqual(5, root5.sub5.submodb5.var5)
749
750
        mod_path = self.root_name + '.' + self.mod_name
751
        sub_path = self.root_name + '.' + self.sub_name
752
        submoda_path = sub_path + '.' + self.submoda_name
753
        submodb_path = sub_path + '.' + self.submodb_name
754
755
        self.assertEqual([('__getattribute__', 'mod5'),
756
                          ('_import', 'root5'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
757
                          ('import', self.root_name, [], 0),
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
758
                          ('__getattribute__', 'submoda5'),
759
                          ('_import', 'sub5'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
760
                          ('import', sub_path, [], 0),
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
761
                          ('__getattribute__', 'var2'),
762
                          ('_import', 'mod5'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
763
                          ('import', mod_path, [], 0),
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
764
                          ('__getattribute__', 'var4'),
765
                          ('_import', 'submoda5'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
766
                          ('import', submoda_path, [], 0),
1996.1.7 by John Arbash Meinel
Test a nested import with multiple deep children
767
                          ('__getattribute__', 'var5'),
768
                          ('_import', 'submodb5'),
6355.2.4 by Jelmer Vernooij
Fix lazy import tests.
769
                          ('import', submodb_path, [], 0),
7143.15.2 by Jelmer Vernooij
Run autopep8.
770
                          ], self.actions)
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
771
772
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
773
class TestConvertImportToMap(TestCase):
774
    """Directly test the conversion from import strings to maps"""
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
775
1996.1.13 by John Arbash Meinel
small test updates
776
    def check(self, expected, import_strings):
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
777
        proc = lazy_import.ImportProcessor()
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
778
        for import_str in import_strings:
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
779
            proc._convert_import_str(import_str)
780
        self.assertEqual(expected, proc.imports,
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
781
                         'Import of %r was not converted correctly'
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
782
                         ' %s != %s' % (import_strings, expected,
783
                                        proc.imports))
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
784
785
    def test_import_one(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
786
        self.check({'one': (['one'], None, {}),
7143.15.2 by Jelmer Vernooij
Run autopep8.
787
                    }, ['import one'])
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
788
789
    def test_import_one_two(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
790
        one_two_map = {'one': (['one'], None,
7143.15.2 by Jelmer Vernooij
Run autopep8.
791
                               {'two': (['one', 'two'], None, {}),
792
                                }),
793
                       }
1996.1.13 by John Arbash Meinel
small test updates
794
        self.check(one_two_map, ['import one.two'])
795
        self.check(one_two_map, ['import one, one.two'])
796
        self.check(one_two_map, ['import one', 'import one.two'])
797
        self.check(one_two_map, ['import one.two', 'import one'])
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
798
799
    def test_import_one_two_three(self):
800
        one_two_three_map = {
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
801
            'one': (['one'], None,
7143.15.2 by Jelmer Vernooij
Run autopep8.
802
                    {'two': (['one', 'two'], None,
803
                             {'three': (['one', 'two', 'three'], None, {}),
804
                              }),
805
                     }),
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
806
        }
1996.1.13 by John Arbash Meinel
small test updates
807
        self.check(one_two_three_map, ['import one.two.three'])
808
        self.check(one_two_three_map, ['import one, one.two.three'])
809
        self.check(one_two_three_map, ['import one',
7143.15.2 by Jelmer Vernooij
Run autopep8.
810
                                       'import one.two.three'])
1996.1.13 by John Arbash Meinel
small test updates
811
        self.check(one_two_three_map, ['import one.two.three',
7143.15.2 by Jelmer Vernooij
Run autopep8.
812
                                       'import one'])
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
813
814
    def test_import_one_as_x(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
815
        self.check({'x': (['one'], None, {}),
7143.15.2 by Jelmer Vernooij
Run autopep8.
816
                    }, ['import one as x'])
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
817
818
    def test_import_one_two_as_x(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
819
        self.check({'x': (['one', 'two'], None, {}),
7143.15.2 by Jelmer Vernooij
Run autopep8.
820
                    }, ['import one.two as x'])
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
821
822
    def test_import_mixed(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
823
        mixed = {'x': (['one', 'two'], None, {}),
824
                 'one': (['one'], None,
7143.15.2 by Jelmer Vernooij
Run autopep8.
825
                         {'two': (['one', 'two'], None, {}),
826
                          }),
827
                 }
1996.1.13 by John Arbash Meinel
small test updates
828
        self.check(mixed, ['import one.two as x, one.two'])
829
        self.check(mixed, ['import one.two as x', 'import one.two'])
830
        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.
831
832
    def test_import_with_as(self):
7143.15.2 by Jelmer Vernooij
Run autopep8.
833
        self.check({'fast': (['fast'], None, {})}, ['import fast'])
1996.1.9 by John Arbash Meinel
Create a method for handling 'import *' syntax.
834
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
835
836
class TestFromToMap(TestCase):
837
    """Directly test the conversion of 'from foo import bar' syntax"""
838
839
    def check_result(self, expected, from_strings):
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
840
        proc = lazy_import.ImportProcessor()
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
841
        for from_str in from_strings:
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
842
            proc._convert_from_str(from_str)
843
        self.assertEqual(expected, proc.imports,
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
844
                         'Import of %r was not converted correctly'
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
845
                         ' %s != %s' % (from_strings, expected, proc.imports))
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
846
847
    def test_from_one_import_two(self):
7143.15.2 by Jelmer Vernooij
Run autopep8.
848
        self.check_result({'two': (['one'], 'two', {})},
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
849
                          ['from one import two'])
850
851
    def test_from_one_import_two_as_three(self):
7143.15.2 by Jelmer Vernooij
Run autopep8.
852
        self.check_result({'three': (['one'], 'two', {})},
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
853
                          ['from one import two as three'])
854
855
    def test_from_one_import_two_three(self):
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
856
        two_three_map = {'two': (['one'], 'two', {}),
857
                         'three': (['one'], 'three', {}),
7143.15.2 by Jelmer Vernooij
Run autopep8.
858
                         }
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
859
        self.check_result(two_three_map,
860
                          ['from one import two, three'])
861
        self.check_result(two_three_map,
862
                          ['from one import two',
863
                           'from one import three'])
864
865
    def test_from_one_two_import_three(self):
7143.15.2 by Jelmer Vernooij
Run autopep8.
866
        self.check_result({'three': (['one', 'two'], 'three', {})},
1996.1.10 by John Arbash Meinel
Handle 'from foo import bar' syntax
867
                          ['from one.two import three'])
868
1996.1.11 by John Arbash Meinel
Test the ability to take a bunch of import lines and canonicalize them
869
870
class TestCanonicalize(TestCase):
871
    """Test that we can canonicalize import texts"""
872
873
    def check(self, expected, text):
1996.1.12 by John Arbash Meinel
Switch from individual functions to a class
874
        proc = lazy_import.ImportProcessor()
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
875
        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
876
        self.assertEqual(expected, parsed,
877
                         'Incorrect parsing of text:\n%s\n%s\n!=\n%s'
878
                         % (text, expected, parsed))
879
880
    def test_import_one(self):
881
        self.check(['import one'], 'import one')
882
        self.check(['import one'], '\nimport one\n\n')
883
884
    def test_import_one_two(self):
885
        self.check(['import one, two'], 'import one, two')
886
        self.check(['import one, two'], '\nimport one, two\n\n')
887
888
    def test_import_one_as_two_as(self):
889
        self.check(['import one as x, two as y'], 'import one as x, two as y')
890
        self.check(['import one as x, two as y'],
891
                   '\nimport one as x, two as y\n')
892
893
    def test_from_one_import_two(self):
894
        self.check(['from one import two'], 'from one import two')
895
        self.check(['from one import two'], '\nfrom one import two\n\n')
896
        self.check(['from one import two'], '\nfrom one import (two)\n')
7143.15.2 by Jelmer Vernooij
Run autopep8.
897
        self.check(['from one import  two '],
898
                   '\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()
7413.8.11 by Jelmer Vernooij
Don't lazy-import errors.
920
        self.assertRaises(lazy_import.InvalidImportLine,
1996.1.18 by John Arbash Meinel
Add more structured error handling
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):
7143.15.2 by Jelmer Vernooij
Run autopep8.
936
        exp = {'one': (['one'], None, {})}
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
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,
7143.15.2 by Jelmer Vernooij
Run autopep8.
942
                       {'two': (['one', 'two'], None, {}),
943
                        }),
944
               }
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
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):
7143.15.2 by Jelmer Vernooij
Run autopep8.
950
        exp = {'two': (['one'], None, {})}
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
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,
7143.15.2 by Jelmer Vernooij
Run autopep8.
955
                       {'two': (['one', 'two'], None,
956
                                {'three': (['one', 'two', 'three'], None, {}),
957
                                 }),
958
                        'four': (['one', 'four'], None, {}),
959
                        }),
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
960
               'five': (['one', 'five'], None, {}),
7143.15.2 by Jelmer Vernooij
Run autopep8.
961
               }
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
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):
7143.15.2 by Jelmer Vernooij
Run autopep8.
969
        exp = {'two': (['one'], 'two', {})}
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
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):
7143.15.2 by Jelmer Vernooij
Run autopep8.
976
        exp = {'two': (['one'], 'two', {})}
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
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', {}),
7143.15.2 by Jelmer Vernooij
Run autopep8.
989
               }
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
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,
7143.15.2 by Jelmer Vernooij
Run autopep8.
1003
                       {'two': (['one', 'two'], None, {}),
1004
                        }),
1005
               }
1996.1.14 by John Arbash Meinel
Add tests for converting from a string to the final map
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()
7413.8.11 by Jelmer Vernooij
Don't lazy-import errors.
1019
        self.assertRaises(lazy_import.InvalidImportLine,
1996.1.18 by John Arbash Meinel
Add more structured error handling
1020
                          proc._build_map, 'foo bar baz')
7413.8.11 by Jelmer Vernooij
Don't lazy-import errors.
1021
        self.assertRaises(lazy_import.InvalidImportLine,
1996.1.18 by John Arbash Meinel
Add more structured error handling
1022
                          proc._build_map, 'improt foo')
7413.8.11 by Jelmer Vernooij
Don't lazy-import errors.
1023
        self.assertRaises(lazy_import.InvalidImportLine,
1996.1.18 by John Arbash Meinel
Add more structured error handling
1024
                          proc._build_map, 'importfoo')
7413.8.11 by Jelmer Vernooij
Don't lazy-import errors.
1025
        self.assertRaises(lazy_import.InvalidImportLine,
1996.1.18 by John Arbash Meinel
Add more structured error handling
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.
7413.8.11 by Jelmer Vernooij
Don't lazy-import errors.
1034
        self.assertRaises(lazy_import.ImportNameCollision,
1996.1.18 by John Arbash Meinel
Add more structured error handling
1035
                          proc._build_map, 'import bar as foo')
7413.8.11 by Jelmer Vernooij
Don't lazy-import errors.
1036
        self.assertRaises(lazy_import.ImportNameCollision,
1996.1.18 by John Arbash Meinel
Add more structured error handling
1037
                          proc._build_map, 'from foo import bar as foo')
7413.8.11 by Jelmer Vernooij
Don't lazy-import errors.
1038
        self.assertRaises(lazy_import.ImportNameCollision,
1996.1.18 by John Arbash Meinel
Add more structured error handling
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:
7143.15.2 by Jelmer Vernooij
Run autopep8.
1057
            pass  # root6 should not be defined yet
1996.1.15 by John Arbash Meinel
Everything is now hooked up
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),
7143.15.2 by Jelmer Vernooij
Run autopep8.
1075
                          ], self.actions)
1996.1.15 by John Arbash Meinel
Everything is now hooked up
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:
7143.15.2 by Jelmer Vernooij
Run autopep8.
1087
            pass  # submoda7 should not be defined yet
1996.1.15 by John Arbash Meinel
Everything is now hooked up
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),
7143.15.2 by Jelmer Vernooij
Run autopep8.
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:
7143.15.2 by Jelmer Vernooij
Run autopep8.
1117
            pass  # root8 should not be defined yet
1996.1.19 by John Arbash Meinel
Write a simple wrapper function to make lazy imports easy.
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),
7143.15.2 by Jelmer Vernooij
Run autopep8.
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...
7143.15.2 by Jelmer Vernooij
Run autopep8.
1160
        if (filename.endswith('lazy_import.py')
1161
                and function_name == self.method_to_trace):
6111.3.1 by Martin von Gagern
Make lazy imports (at least more) thread-safe.
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)
7143.15.2 by Jelmer Vernooij
Run autopep8.
1173
        self.racer()  # Should not raise any exception
6111.3.2 by Martin von Gagern
Allow subclasses to override _should_proxy.
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)