• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from .. import util
2
3importlib = util.import_importlib('importlib')
4machinery = util.import_importlib('importlib.machinery')
5
6import os
7import sys
8import tempfile
9from types import ModuleType
10import unittest
11import warnings
12import zipimport
13
14
15class FinderTests:
16
17    """Tests for PathFinder."""
18
19    find = None
20    check_found = None
21
22    def test_failure(self):
23        # Test None returned upon not finding a suitable loader.
24        module = '<test module>'
25        with util.import_state():
26            self.assertIsNone(self.find(module))
27
28    def test_sys_path(self):
29        # Test that sys.path is used when 'path' is None.
30        # Implicitly tests that sys.path_importer_cache is used.
31        module = '<test module>'
32        path = '<test path>'
33        importer = util.mock_spec(module)
34        with util.import_state(path_importer_cache={path: importer},
35                               path=[path]):
36            found = self.find(module)
37            self.check_found(found, importer)
38
39    def test_path(self):
40        # Test that 'path' is used when set.
41        # Implicitly tests that sys.path_importer_cache is used.
42        module = '<test module>'
43        path = '<test path>'
44        importer = util.mock_spec(module)
45        with util.import_state(path_importer_cache={path: importer}):
46            found = self.find(module, [path])
47            self.check_found(found, importer)
48
49    def test_empty_list(self):
50        # An empty list should not count as asking for sys.path.
51        module = 'module'
52        path = '<test path>'
53        importer = util.mock_spec(module)
54        with util.import_state(path_importer_cache={path: importer},
55                               path=[path]):
56            self.assertIsNone(self.find('module', []))
57
58    def test_path_hooks(self):
59        # Test that sys.path_hooks is used.
60        # Test that sys.path_importer_cache is set.
61        module = '<test module>'
62        path = '<test path>'
63        importer = util.mock_spec(module)
64        hook = util.mock_path_hook(path, importer=importer)
65        with util.import_state(path_hooks=[hook]):
66            found = self.find(module, [path])
67            self.check_found(found, importer)
68            self.assertIn(path, sys.path_importer_cache)
69            self.assertIs(sys.path_importer_cache[path], importer)
70
71    def test_empty_path_hooks(self):
72        # Test that if sys.path_hooks is empty a warning is raised,
73        # sys.path_importer_cache gets None set, and PathFinder returns None.
74        path_entry = 'bogus_path'
75        with util.import_state(path_importer_cache={}, path_hooks=[],
76                               path=[path_entry]):
77            with warnings.catch_warnings(record=True) as w:
78                warnings.simplefilter('always')
79                self.assertIsNone(self.find('os'))
80                self.assertIsNone(sys.path_importer_cache[path_entry])
81                self.assertEqual(len(w), 1)
82                self.assertTrue(issubclass(w[-1].category, ImportWarning))
83
84    def test_path_importer_cache_empty_string(self):
85        # The empty string should create a finder using the cwd.
86        path = ''
87        module = '<test module>'
88        importer = util.mock_spec(module)
89        hook = util.mock_path_hook(os.getcwd(), importer=importer)
90        with util.import_state(path=[path], path_hooks=[hook]):
91            found = self.find(module)
92            self.check_found(found, importer)
93            self.assertIn(os.getcwd(), sys.path_importer_cache)
94
95    def test_None_on_sys_path(self):
96        # Putting None in sys.path[0] caused an import regression from Python
97        # 3.2: http://bugs.python.org/issue16514
98        new_path = sys.path[:]
99        new_path.insert(0, None)
100        new_path_importer_cache = sys.path_importer_cache.copy()
101        new_path_importer_cache.pop(None, None)
102        new_path_hooks = [zipimport.zipimporter,
103                          self.machinery.FileFinder.path_hook(
104                              *self.importlib._bootstrap_external._get_supported_file_loaders())]
105        missing = object()
106        email = sys.modules.pop('email', missing)
107        try:
108            with util.import_state(meta_path=sys.meta_path[:],
109                                   path=new_path,
110                                   path_importer_cache=new_path_importer_cache,
111                                   path_hooks=new_path_hooks):
112                module = self.importlib.import_module('email')
113                self.assertIsInstance(module, ModuleType)
114        finally:
115            if email is not missing:
116                sys.modules['email'] = email
117
118    def test_finder_with_find_module(self):
119        class TestFinder:
120            def find_module(self, fullname):
121                return self.to_return
122        failing_finder = TestFinder()
123        failing_finder.to_return = None
124        path = 'testing path'
125        with util.import_state(path_importer_cache={path: failing_finder}):
126            self.assertIsNone(
127                    self.machinery.PathFinder.find_spec('whatever', [path]))
128        success_finder = TestFinder()
129        success_finder.to_return = __loader__
130        with util.import_state(path_importer_cache={path: success_finder}):
131            spec = self.machinery.PathFinder.find_spec('whatever', [path])
132        self.assertEqual(spec.loader, __loader__)
133
134    def test_finder_with_find_loader(self):
135        class TestFinder:
136            loader = None
137            portions = []
138            def find_loader(self, fullname):
139                return self.loader, self.portions
140        path = 'testing path'
141        with util.import_state(path_importer_cache={path: TestFinder()}):
142            self.assertIsNone(
143                    self.machinery.PathFinder.find_spec('whatever', [path]))
144        success_finder = TestFinder()
145        success_finder.loader = __loader__
146        with util.import_state(path_importer_cache={path: success_finder}):
147            spec = self.machinery.PathFinder.find_spec('whatever', [path])
148        self.assertEqual(spec.loader, __loader__)
149
150    def test_finder_with_find_spec(self):
151        class TestFinder:
152            spec = None
153            def find_spec(self, fullname, target=None):
154                return self.spec
155        path = 'testing path'
156        with util.import_state(path_importer_cache={path: TestFinder()}):
157            self.assertIsNone(
158                    self.machinery.PathFinder.find_spec('whatever', [path]))
159        success_finder = TestFinder()
160        success_finder.spec = self.machinery.ModuleSpec('whatever', __loader__)
161        with util.import_state(path_importer_cache={path: success_finder}):
162            got = self.machinery.PathFinder.find_spec('whatever', [path])
163        self.assertEqual(got, success_finder.spec)
164
165    def test_deleted_cwd(self):
166        # Issue #22834
167        old_dir = os.getcwd()
168        self.addCleanup(os.chdir, old_dir)
169        new_dir = tempfile.mkdtemp()
170        try:
171            os.chdir(new_dir)
172            try:
173                os.rmdir(new_dir)
174            except OSError:
175                # EINVAL on Solaris, EBUSY on AIX, ENOTEMPTY on Windows
176                self.skipTest("platform does not allow "
177                              "the deletion of the cwd")
178        except:
179            os.chdir(old_dir)
180            os.rmdir(new_dir)
181            raise
182
183        with util.import_state(path=['']):
184            # Do not want FileNotFoundError raised.
185            self.assertIsNone(self.machinery.PathFinder.find_spec('whatever'))
186
187    def test_invalidate_caches_finders(self):
188        # Finders with an invalidate_caches() method have it called.
189        class FakeFinder:
190            def __init__(self):
191                self.called = False
192
193            def invalidate_caches(self):
194                self.called = True
195
196        cache = {'leave_alone': object(), 'finder_to_invalidate': FakeFinder()}
197        with util.import_state(path_importer_cache=cache):
198            self.machinery.PathFinder.invalidate_caches()
199        self.assertTrue(cache['finder_to_invalidate'].called)
200
201    def test_invalidate_caches_clear_out_None(self):
202        # Clear out None in sys.path_importer_cache() when invalidating caches.
203        cache = {'clear_out': None}
204        with util.import_state(path_importer_cache=cache):
205            self.machinery.PathFinder.invalidate_caches()
206        self.assertEqual(len(cache), 0)
207
208
209class FindModuleTests(FinderTests):
210    def find(self, *args, **kwargs):
211        return self.machinery.PathFinder.find_module(*args, **kwargs)
212    def check_found(self, found, importer):
213        self.assertIs(found, importer)
214
215
216(Frozen_FindModuleTests,
217 Source_FindModuleTests
218) = util.test_both(FindModuleTests, importlib=importlib, machinery=machinery)
219
220
221class FindSpecTests(FinderTests):
222    def find(self, *args, **kwargs):
223        return self.machinery.PathFinder.find_spec(*args, **kwargs)
224    def check_found(self, found, importer):
225        self.assertIs(found.loader, importer)
226
227
228(Frozen_FindSpecTests,
229 Source_FindSpecTests
230 ) = util.test_both(FindSpecTests, importlib=importlib, machinery=machinery)
231
232
233class PathEntryFinderTests:
234
235    def test_finder_with_failing_find_spec(self):
236        # PathEntryFinder with find_module() defined should work.
237        # Issue #20763.
238        class Finder:
239            path_location = 'test_finder_with_find_module'
240            def __init__(self, path):
241                if path != self.path_location:
242                    raise ImportError
243
244            @staticmethod
245            def find_module(fullname):
246                return None
247
248
249        with util.import_state(path=[Finder.path_location]+sys.path[:],
250                               path_hooks=[Finder]):
251            self.machinery.PathFinder.find_spec('importlib')
252
253    def test_finder_with_failing_find_module(self):
254        # PathEntryFinder with find_module() defined should work.
255        # Issue #20763.
256        class Finder:
257            path_location = 'test_finder_with_find_module'
258            def __init__(self, path):
259                if path != self.path_location:
260                    raise ImportError
261
262            @staticmethod
263            def find_module(fullname):
264                return None
265
266
267        with util.import_state(path=[Finder.path_location]+sys.path[:],
268                               path_hooks=[Finder]):
269            self.machinery.PathFinder.find_module('importlib')
270
271
272(Frozen_PEFTests,
273 Source_PEFTests
274 ) = util.test_both(PathEntryFinderTests, machinery=machinery)
275
276
277if __name__ == '__main__':
278    unittest.main()
279