1from .. import util
2import importlib._bootstrap
3import sys
4from types import MethodType
5import unittest
6import warnings
7
8
9class CallingOrder:
10
11    """Calls to the importers on sys.meta_path happen in order that they are
12    specified in the sequence, starting with the first importer
13    [first called], and then continuing on down until one is found that doesn't
14    return None [continuing]."""
15
16
17    def test_first_called(self):
18        # [first called]
19        mod = 'top_level'
20        with util.mock_spec(mod) as first, util.mock_spec(mod) as second:
21            with util.import_state(meta_path=[first, second]):
22                self.assertIs(self.__import__(mod), first.modules[mod])
23
24    def test_continuing(self):
25        # [continuing]
26        mod_name = 'for_real'
27        with util.mock_spec('nonexistent') as first, \
28             util.mock_spec(mod_name) as second:
29            first.find_spec = lambda self, fullname, path=None, parent=None: None
30            with util.import_state(meta_path=[first, second]):
31                self.assertIs(self.__import__(mod_name), second.modules[mod_name])
32
33    def test_empty(self):
34        # Raise an ImportWarning if sys.meta_path is empty.
35        module_name = 'nothing'
36        try:
37            del sys.modules[module_name]
38        except KeyError:
39            pass
40        with util.import_state(meta_path=[]):
41            with warnings.catch_warnings(record=True) as w:
42                warnings.simplefilter('always')
43                self.assertIsNone(importlib._bootstrap._find_spec('nothing',
44                                                                  None))
45                self.assertEqual(len(w), 1)
46                self.assertTrue(issubclass(w[-1].category, ImportWarning))
47
48
49(Frozen_CallingOrder,
50 Source_CallingOrder
51 ) = util.test_both(CallingOrder, __import__=util.__import__)
52
53
54class CallSignature:
55
56    """If there is no __path__ entry on the parent module, then 'path' is None
57    [no path]. Otherwise, the value for __path__ is passed in for the 'path'
58    argument [path set]."""
59
60    def log_finder(self, importer):
61        fxn = getattr(importer, self.finder_name)
62        log = []
63        def wrapper(self, *args, **kwargs):
64            log.append([args, kwargs])
65            return fxn(*args, **kwargs)
66        return log, wrapper
67
68    def test_no_path(self):
69        # [no path]
70        mod_name = 'top_level'
71        assert '.' not in mod_name
72        with self.mock_modules(mod_name) as importer:
73            log, wrapped_call = self.log_finder(importer)
74            setattr(importer, self.finder_name, MethodType(wrapped_call, importer))
75            with util.import_state(meta_path=[importer]):
76                self.__import__(mod_name)
77                assert len(log) == 1
78                args = log[0][0]
79                # Assuming all arguments are positional.
80                self.assertEqual(args[0], mod_name)
81                self.assertIsNone(args[1])
82
83    def test_with_path(self):
84        # [path set]
85        pkg_name = 'pkg'
86        mod_name = pkg_name + '.module'
87        path = [42]
88        assert '.' in mod_name
89        with self.mock_modules(pkg_name+'.__init__', mod_name) as importer:
90            importer.modules[pkg_name].__path__ = path
91            log, wrapped_call = self.log_finder(importer)
92            setattr(importer, self.finder_name, MethodType(wrapped_call, importer))
93            with util.import_state(meta_path=[importer]):
94                self.__import__(mod_name)
95                assert len(log) == 2
96                args = log[1][0]
97                kwargs = log[1][1]
98                # Assuming all arguments are positional.
99                self.assertFalse(kwargs)
100                self.assertEqual(args[0], mod_name)
101                self.assertIs(args[1], path)
102
103class CallSignoreSuppressImportWarning(CallSignature):
104
105    def test_no_path(self):
106        with warnings.catch_warnings():
107            warnings.simplefilter("ignore", ImportWarning)
108            super().test_no_path()
109
110    def test_with_path(self):
111        with warnings.catch_warnings():
112            warnings.simplefilter("ignore", ImportWarning)
113            super().test_no_path()
114
115
116class CallSignaturePEP302(CallSignoreSuppressImportWarning):
117    mock_modules = util.mock_modules
118    finder_name = 'find_module'
119
120
121(Frozen_CallSignaturePEP302,
122 Source_CallSignaturePEP302
123 ) = util.test_both(CallSignaturePEP302, __import__=util.__import__)
124
125
126class CallSignaturePEP451(CallSignature):
127    mock_modules = util.mock_spec
128    finder_name = 'find_spec'
129
130
131(Frozen_CallSignaturePEP451,
132 Source_CallSignaturePEP451
133 ) = util.test_both(CallSignaturePEP451, __import__=util.__import__)
134
135
136if __name__ == '__main__':
137    unittest.main()
138