• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""PEP 366 ("Main module explicit relative imports") specifies the
2semantics for the __package__ attribute on modules. This attribute is
3used, when available, to detect which package a module belongs to (instead
4of using the typical __path__/__name__ test).
5
6"""
7import unittest
8import warnings
9from .. import util
10
11
12class Using__package__:
13
14    """Use of __package__ supersedes the use of __name__/__path__ to calculate
15    what package a module belongs to. The basic algorithm is [__package__]::
16
17      def resolve_name(name, package, level):
18          level -= 1
19          base = package.rsplit('.', level)[0]
20          return '{0}.{1}'.format(base, name)
21
22    But since there is no guarantee that __package__ has been set (or not been
23    set to None [None]), there has to be a way to calculate the attribute's value
24    [__name__]::
25
26      def calc_package(caller_name, has___path__):
27          if has__path__:
28              return caller_name
29          else:
30              return caller_name.rsplit('.', 1)[0]
31
32    Then the normal algorithm for relative name imports can proceed as if
33    __package__ had been set.
34
35    """
36
37    def import_module(self, globals_):
38        with self.mock_modules('pkg.__init__', 'pkg.fake') as importer:
39            with util.import_state(meta_path=[importer]):
40                self.__import__('pkg.fake')
41                module = self.__import__('',
42                                         globals=globals_,
43                                         fromlist=['attr'], level=2)
44        return module
45
46    def test_using___package__(self):
47        # [__package__]
48        module = self.import_module({'__package__': 'pkg.fake'})
49        self.assertEqual(module.__name__, 'pkg')
50
51    def test_using___name__(self):
52        # [__name__]
53        with warnings.catch_warnings():
54            warnings.simplefilter("ignore")
55            module = self.import_module({'__name__': 'pkg.fake',
56                                         '__path__': []})
57        self.assertEqual(module.__name__, 'pkg')
58
59    def test_warn_when_using___name__(self):
60        with self.assertWarns(ImportWarning):
61            self.import_module({'__name__': 'pkg.fake', '__path__': []})
62
63    def test_None_as___package__(self):
64        # [None]
65        with warnings.catch_warnings():
66            warnings.simplefilter("ignore")
67            module = self.import_module({
68                '__name__': 'pkg.fake', '__path__': [], '__package__': None })
69        self.assertEqual(module.__name__, 'pkg')
70
71    def test_spec_fallback(self):
72        # If __package__ isn't defined, fall back on __spec__.parent.
73        module = self.import_module({'__spec__': FakeSpec('pkg.fake')})
74        self.assertEqual(module.__name__, 'pkg')
75
76    def test_warn_when_package_and_spec_disagree(self):
77        # Raise an ImportWarning if __package__ != __spec__.parent.
78        with self.assertWarns(ImportWarning):
79            self.import_module({'__package__': 'pkg.fake',
80                                '__spec__': FakeSpec('pkg.fakefake')})
81
82    def test_bad__package__(self):
83        globals = {'__package__': '<not real>'}
84        with self.assertRaises(ModuleNotFoundError):
85            self.__import__('', globals, {}, ['relimport'], 1)
86
87    def test_bunk__package__(self):
88        globals = {'__package__': 42}
89        with self.assertRaises(TypeError):
90            self.__import__('', globals, {}, ['relimport'], 1)
91
92
93class FakeSpec:
94    def __init__(self, parent):
95        self.parent = parent
96
97
98class Using__package__PEP302(Using__package__):
99    mock_modules = util.mock_modules
100
101
102(Frozen_UsingPackagePEP302,
103 Source_UsingPackagePEP302
104 ) = util.test_both(Using__package__PEP302, __import__=util.__import__)
105
106
107class Using__package__PEP451(Using__package__):
108    mock_modules = util.mock_spec
109
110
111(Frozen_UsingPackagePEP451,
112 Source_UsingPackagePEP451
113 ) = util.test_both(Using__package__PEP451, __import__=util.__import__)
114
115
116class Setting__package__:
117
118    """Because __package__ is a new feature, it is not always set by a loader.
119    Import will set it as needed to help with the transition to relying on
120    __package__.
121
122    For a top-level module, __package__ is set to None [top-level]. For a
123    package __name__ is used for __package__ [package]. For submodules the
124    value is __name__.rsplit('.', 1)[0] [submodule].
125
126    """
127
128    __import__ = util.__import__['Source']
129
130    # [top-level]
131    def test_top_level(self):
132        with self.mock_modules('top_level') as mock:
133            with util.import_state(meta_path=[mock]):
134                del mock['top_level'].__package__
135                module = self.__import__('top_level')
136                self.assertEqual(module.__package__, '')
137
138    # [package]
139    def test_package(self):
140        with self.mock_modules('pkg.__init__') as mock:
141            with util.import_state(meta_path=[mock]):
142                del mock['pkg'].__package__
143                module = self.__import__('pkg')
144                self.assertEqual(module.__package__, 'pkg')
145
146    # [submodule]
147    def test_submodule(self):
148        with self.mock_modules('pkg.__init__', 'pkg.mod') as mock:
149            with util.import_state(meta_path=[mock]):
150                del mock['pkg.mod'].__package__
151                pkg = self.__import__('pkg.mod')
152                module = getattr(pkg, 'mod')
153                self.assertEqual(module.__package__, 'pkg')
154
155class Setting__package__PEP302(Setting__package__, unittest.TestCase):
156    mock_modules = util.mock_modules
157
158class Setting__package__PEP451(Setting__package__, unittest.TestCase):
159    mock_modules = util.mock_spec
160
161
162if __name__ == '__main__':
163    unittest.main()
164