1"""Test relative imports (PEP 328)."""
2from .. import util
3import unittest
4import warnings
5
6
7class RelativeImports:
8
9    """PEP 328 introduced relative imports. This allows for imports to occur
10    from within a package without having to specify the actual package name.
11
12    A simple example is to import another module within the same package
13    [module from module]::
14
15      # From pkg.mod1 with pkg.mod2 being a module.
16      from . import mod2
17
18    This also works for getting an attribute from a module that is specified
19    in a relative fashion [attr from module]::
20
21      # From pkg.mod1.
22      from .mod2 import attr
23
24    But this is in no way restricted to working between modules; it works
25    from [package to module],::
26
27      # From pkg, importing pkg.module which is a module.
28      from . import module
29
30    [module to package],::
31
32      # Pull attr from pkg, called from pkg.module which is a module.
33      from . import attr
34
35    and [package to package]::
36
37      # From pkg.subpkg1 (both pkg.subpkg[1,2] are packages).
38      from .. import subpkg2
39
40    The number of dots used is in no way restricted [deep import]::
41
42      # Import pkg.attr from pkg.pkg1.pkg2.pkg3.pkg4.pkg5.
43      from ...... import attr
44
45    To prevent someone from accessing code that is outside of a package, one
46    cannot reach the location containing the root package itself::
47
48      # From pkg.__init__ [too high from package]
49      from .. import top_level
50
51      # From pkg.module [too high from module]
52      from .. import top_level
53
54     Relative imports are the only type of import that allow for an empty
55     module name for an import [empty name].
56
57    """
58
59    def relative_import_test(self, create, globals_, callback):
60        """Abstract out boilerplace for setting up for an import test."""
61        uncache_names = []
62        for name in create:
63            if not name.endswith('.__init__'):
64                uncache_names.append(name)
65            else:
66                uncache_names.append(name[:-len('.__init__')])
67        with util.mock_spec(*create) as importer:
68            with util.import_state(meta_path=[importer]):
69                with warnings.catch_warnings():
70                    warnings.simplefilter("ignore")
71                    for global_ in globals_:
72                        with util.uncache(*uncache_names):
73                            callback(global_)
74
75
76    def test_module_from_module(self):
77        # [module from module]
78        create = 'pkg.__init__', 'pkg.mod2'
79        globals_ = {'__package__': 'pkg'}, {'__name__': 'pkg.mod1'}
80        def callback(global_):
81            self.__import__('pkg')  # For __import__().
82            module = self.__import__('', global_, fromlist=['mod2'], level=1)
83            self.assertEqual(module.__name__, 'pkg')
84            self.assertTrue(hasattr(module, 'mod2'))
85            self.assertEqual(module.mod2.attr, 'pkg.mod2')
86        self.relative_import_test(create, globals_, callback)
87
88    def test_attr_from_module(self):
89        # [attr from module]
90        create = 'pkg.__init__', 'pkg.mod2'
91        globals_ = {'__package__': 'pkg'}, {'__name__': 'pkg.mod1'}
92        def callback(global_):
93            self.__import__('pkg')  # For __import__().
94            module = self.__import__('mod2', global_, fromlist=['attr'],
95                                            level=1)
96            self.assertEqual(module.__name__, 'pkg.mod2')
97            self.assertEqual(module.attr, 'pkg.mod2')
98        self.relative_import_test(create, globals_, callback)
99
100    def test_package_to_module(self):
101        # [package to module]
102        create = 'pkg.__init__', 'pkg.module'
103        globals_ = ({'__package__': 'pkg'},
104                    {'__name__': 'pkg', '__path__': ['blah']})
105        def callback(global_):
106            self.__import__('pkg')  # For __import__().
107            module = self.__import__('', global_, fromlist=['module'],
108                             level=1)
109            self.assertEqual(module.__name__, 'pkg')
110            self.assertTrue(hasattr(module, 'module'))
111            self.assertEqual(module.module.attr, 'pkg.module')
112        self.relative_import_test(create, globals_, callback)
113
114    def test_module_to_package(self):
115        # [module to package]
116        create = 'pkg.__init__', 'pkg.module'
117        globals_ = {'__package__': 'pkg'}, {'__name__': 'pkg.module'}
118        def callback(global_):
119            self.__import__('pkg')  # For __import__().
120            module = self.__import__('', global_, fromlist=['attr'], level=1)
121            self.assertEqual(module.__name__, 'pkg')
122        self.relative_import_test(create, globals_, callback)
123
124    def test_package_to_package(self):
125        # [package to package]
126        create = ('pkg.__init__', 'pkg.subpkg1.__init__',
127                    'pkg.subpkg2.__init__')
128        globals_ =  ({'__package__': 'pkg.subpkg1'},
129                     {'__name__': 'pkg.subpkg1', '__path__': ['blah']})
130        def callback(global_):
131            module = self.__import__('', global_, fromlist=['subpkg2'],
132                                            level=2)
133            self.assertEqual(module.__name__, 'pkg')
134            self.assertTrue(hasattr(module, 'subpkg2'))
135            self.assertEqual(module.subpkg2.attr, 'pkg.subpkg2.__init__')
136
137    def test_deep_import(self):
138        # [deep import]
139        create = ['pkg.__init__']
140        for count in range(1,6):
141            create.append('{0}.pkg{1}.__init__'.format(
142                            create[-1][:-len('.__init__')], count))
143        globals_ = ({'__package__': 'pkg.pkg1.pkg2.pkg3.pkg4.pkg5'},
144                    {'__name__': 'pkg.pkg1.pkg2.pkg3.pkg4.pkg5',
145                        '__path__': ['blah']})
146        def callback(global_):
147            self.__import__(globals_[0]['__package__'])
148            module = self.__import__('', global_, fromlist=['attr'], level=6)
149            self.assertEqual(module.__name__, 'pkg')
150        self.relative_import_test(create, globals_, callback)
151
152    def test_too_high_from_package(self):
153        # [too high from package]
154        create = ['top_level', 'pkg.__init__']
155        globals_ = ({'__package__': 'pkg'},
156                    {'__name__': 'pkg', '__path__': ['blah']})
157        def callback(global_):
158            self.__import__('pkg')
159            with self.assertRaises(ValueError):
160                self.__import__('', global_, fromlist=['top_level'],
161                                    level=2)
162        self.relative_import_test(create, globals_, callback)
163
164    def test_too_high_from_module(self):
165        # [too high from module]
166        create = ['top_level', 'pkg.__init__', 'pkg.module']
167        globals_ = {'__package__': 'pkg'}, {'__name__': 'pkg.module'}
168        def callback(global_):
169            self.__import__('pkg')
170            with self.assertRaises(ValueError):
171                self.__import__('', global_, fromlist=['top_level'],
172                                    level=2)
173        self.relative_import_test(create, globals_, callback)
174
175    def test_empty_name_w_level_0(self):
176        # [empty name]
177        with self.assertRaises(ValueError):
178            self.__import__('')
179
180    def test_import_from_different_package(self):
181        # Test importing from a different package than the caller.
182        # in pkg.subpkg1.mod
183        # from ..subpkg2 import mod
184        create = ['__runpy_pkg__.__init__',
185                    '__runpy_pkg__.__runpy_pkg__.__init__',
186                    '__runpy_pkg__.uncle.__init__',
187                    '__runpy_pkg__.uncle.cousin.__init__',
188                    '__runpy_pkg__.uncle.cousin.nephew']
189        globals_ = {'__package__': '__runpy_pkg__.__runpy_pkg__'}
190        def callback(global_):
191            self.__import__('__runpy_pkg__.__runpy_pkg__')
192            module = self.__import__('uncle.cousin', globals_, {},
193                                    fromlist=['nephew'],
194                                level=2)
195            self.assertEqual(module.__name__, '__runpy_pkg__.uncle.cousin')
196        self.relative_import_test(create, globals_, callback)
197
198    def test_import_relative_import_no_fromlist(self):
199        # Import a relative module w/ no fromlist.
200        create = ['crash.__init__', 'crash.mod']
201        globals_ = [{'__package__': 'crash', '__name__': 'crash'}]
202        def callback(global_):
203            self.__import__('crash')
204            mod = self.__import__('mod', global_, {}, [], 1)
205            self.assertEqual(mod.__name__, 'crash.mod')
206        self.relative_import_test(create, globals_, callback)
207
208    def test_relative_import_no_globals(self):
209        # No globals for a relative import is an error.
210        with warnings.catch_warnings():
211            warnings.simplefilter("ignore")
212            with self.assertRaises(KeyError):
213                self.__import__('sys', level=1)
214
215    def test_relative_import_no_package(self):
216        with self.assertRaises(ImportError):
217            self.__import__('a', {'__package__': '', '__spec__': None},
218                            level=1)
219
220    def test_relative_import_no_package_exists_absolute(self):
221        with self.assertRaises(ImportError):
222            self.__import__('sys', {'__package__': '', '__spec__': None},
223                            level=1)
224
225
226(Frozen_RelativeImports,
227 Source_RelativeImports
228 ) = util.test_both(RelativeImports, __import__=util.__import__)
229
230
231if __name__ == '__main__':
232    unittest.main()
233