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