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