1"""Test relative imports (PEP 328)."""
2from test.test_importlib 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        self.relative_import_test(create, globals_, callback)
137
138    def test_deep_import(self):
139        # [deep import]
140        create = ['pkg.__init__']
141        for count in range(1,6):
142            create.append('{0}.pkg{1}.__init__'.format(
143                            create[-1][:-len('.__init__')], count))
144        globals_ = ({'__package__': 'pkg.pkg1.pkg2.pkg3.pkg4.pkg5'},
145                    {'__name__': 'pkg.pkg1.pkg2.pkg3.pkg4.pkg5',
146                        '__path__': ['blah']})
147        def callback(global_):
148            self.__import__(globals_[0]['__package__'])
149            module = self.__import__('', global_, fromlist=['attr'], level=6)
150            self.assertEqual(module.__name__, 'pkg')
151        self.relative_import_test(create, globals_, callback)
152
153    def test_too_high_from_package(self):
154        # [too high from package]
155        create = ['top_level', 'pkg.__init__']
156        globals_ = ({'__package__': 'pkg'},
157                    {'__name__': 'pkg', '__path__': ['blah']})
158        def callback(global_):
159            self.__import__('pkg')
160            with self.assertRaises(ImportError):
161                self.__import__('', global_, fromlist=['top_level'],
162                                    level=2)
163        self.relative_import_test(create, globals_, callback)
164
165    def test_too_high_from_module(self):
166        # [too high from module]
167        create = ['top_level', 'pkg.__init__', 'pkg.module']
168        globals_ = {'__package__': 'pkg'}, {'__name__': 'pkg.module'}
169        def callback(global_):
170            self.__import__('pkg')
171            with self.assertRaises(ImportError):
172                self.__import__('', global_, fromlist=['top_level'],
173                                    level=2)
174        self.relative_import_test(create, globals_, callback)
175
176    def test_empty_name_w_level_0(self):
177        # [empty name]
178        with self.assertRaises(ValueError):
179            self.__import__('')
180
181    def test_import_from_different_package(self):
182        # Test importing from a different package than the caller.
183        # in pkg.subpkg1.mod
184        # from ..subpkg2 import mod
185        create = ['__runpy_pkg__.__init__',
186                    '__runpy_pkg__.__runpy_pkg__.__init__',
187                    '__runpy_pkg__.uncle.__init__',
188                    '__runpy_pkg__.uncle.cousin.__init__',
189                    '__runpy_pkg__.uncle.cousin.nephew']
190        globals_ = {'__package__': '__runpy_pkg__.__runpy_pkg__'}
191        def callback(global_):
192            self.__import__('__runpy_pkg__.__runpy_pkg__')
193            module = self.__import__('uncle.cousin', globals_, {},
194                                    fromlist=['nephew'],
195                                level=2)
196            self.assertEqual(module.__name__, '__runpy_pkg__.uncle.cousin')
197        self.relative_import_test(create, globals_, callback)
198
199    def test_import_relative_import_no_fromlist(self):
200        # Import a relative module w/ no fromlist.
201        create = ['crash.__init__', 'crash.mod']
202        globals_ = [{'__package__': 'crash', '__name__': 'crash'}]
203        def callback(global_):
204            self.__import__('crash')
205            mod = self.__import__('mod', global_, {}, [], 1)
206            self.assertEqual(mod.__name__, 'crash.mod')
207        self.relative_import_test(create, globals_, callback)
208
209    def test_relative_import_no_globals(self):
210        # No globals for a relative import is an error.
211        with warnings.catch_warnings():
212            warnings.simplefilter("ignore")
213            with self.assertRaises(KeyError):
214                self.__import__('sys', level=1)
215
216    def test_relative_import_no_package(self):
217        with self.assertRaises(ImportError):
218            self.__import__('a', {'__package__': '', '__spec__': None},
219                            level=1)
220
221    def test_relative_import_no_package_exists_absolute(self):
222        with self.assertRaises(ImportError):
223            self.__import__('sys', {'__package__': '', '__spec__': None},
224                            level=1)
225
226
227(Frozen_RelativeImports,
228 Source_RelativeImports
229 ) = util.test_both(RelativeImports, __import__=util.__import__)
230
231
232if __name__ == '__main__':
233    unittest.main()
234