1from test.test_importlib import abc, util 2 3machinery = util.import_importlib('importlib.machinery') 4 5import errno 6import os 7import py_compile 8import stat 9import sys 10import tempfile 11from test.support.import_helper import make_legacy_pyc 12import unittest 13 14 15class FinderTests(abc.FinderTests): 16 17 """For a top-level module, it should just be found directly in the 18 directory being searched. This is true for a directory with source 19 [top-level source], bytecode [top-level bc], or both [top-level both]. 20 There is also the possibility that it is a package [top-level package], in 21 which case there will be a directory with the module name and an 22 __init__.py file. If there is a directory without an __init__.py an 23 ImportWarning is returned [empty dir]. 24 25 For sub-modules and sub-packages, the same happens as above but only use 26 the tail end of the name [sub module] [sub package] [sub empty]. 27 28 When there is a conflict between a package and module having the same name 29 in the same directory, the package wins out [package over module]. This is 30 so that imports of modules within the package can occur rather than trigger 31 an import error. 32 33 When there is a package and module with the same name, always pick the 34 package over the module [package over module]. This is so that imports from 35 the package have the possibility of succeeding. 36 37 """ 38 39 def get_finder(self, root): 40 loader_details = [(self.machinery.SourceFileLoader, 41 self.machinery.SOURCE_SUFFIXES), 42 (self.machinery.SourcelessFileLoader, 43 self.machinery.BYTECODE_SUFFIXES)] 44 return self.machinery.FileFinder(root, *loader_details) 45 46 def import_(self, root, module): 47 finder = self.get_finder(root) 48 return self._find(finder, module, loader_only=True) 49 50 def run_test(self, test, create=None, *, compile_=None, unlink=None): 51 """Test the finding of 'test' with the creation of modules listed in 52 'create'. 53 54 Any names listed in 'compile_' are byte-compiled. Modules 55 listed in 'unlink' have their source files deleted. 56 57 """ 58 if create is None: 59 create = {test} 60 with util.create_modules(*create) as mapping: 61 if compile_: 62 for name in compile_: 63 py_compile.compile(mapping[name]) 64 if unlink: 65 for name in unlink: 66 os.unlink(mapping[name]) 67 try: 68 make_legacy_pyc(mapping[name]) 69 except OSError as error: 70 # Some tests do not set compile_=True so the source 71 # module will not get compiled and there will be no 72 # PEP 3147 pyc file to rename. 73 if error.errno != errno.ENOENT: 74 raise 75 loader = self.import_(mapping['.root'], test) 76 self.assertTrue(hasattr(loader, 'load_module')) 77 return loader 78 79 def test_module(self): 80 # [top-level source] 81 self.run_test('top_level') 82 # [top-level bc] 83 self.run_test('top_level', compile_={'top_level'}, 84 unlink={'top_level'}) 85 # [top-level both] 86 self.run_test('top_level', compile_={'top_level'}) 87 88 # [top-level package] 89 def test_package(self): 90 # Source. 91 self.run_test('pkg', {'pkg.__init__'}) 92 # Bytecode. 93 self.run_test('pkg', {'pkg.__init__'}, compile_={'pkg.__init__'}, 94 unlink={'pkg.__init__'}) 95 # Both. 96 self.run_test('pkg', {'pkg.__init__'}, compile_={'pkg.__init__'}) 97 98 # [sub module] 99 def test_module_in_package(self): 100 with util.create_modules('pkg.__init__', 'pkg.sub') as mapping: 101 pkg_dir = os.path.dirname(mapping['pkg.__init__']) 102 loader = self.import_(pkg_dir, 'pkg.sub') 103 self.assertTrue(hasattr(loader, 'load_module')) 104 105 # [sub package] 106 def test_package_in_package(self): 107 context = util.create_modules('pkg.__init__', 'pkg.sub.__init__') 108 with context as mapping: 109 pkg_dir = os.path.dirname(mapping['pkg.__init__']) 110 loader = self.import_(pkg_dir, 'pkg.sub') 111 self.assertTrue(hasattr(loader, 'load_module')) 112 113 # [package over modules] 114 def test_package_over_module(self): 115 name = '_temp' 116 loader = self.run_test(name, {'{0}.__init__'.format(name), name}) 117 self.assertIn('__init__', loader.get_filename(name)) 118 119 def test_failure(self): 120 with util.create_modules('blah') as mapping: 121 nothing = self.import_(mapping['.root'], 'sdfsadsadf') 122 self.assertEqual(nothing, self.NOT_FOUND) 123 124 def test_empty_string_for_dir(self): 125 # The empty string from sys.path means to search in the cwd. 126 finder = self.machinery.FileFinder('', (self.machinery.SourceFileLoader, 127 self.machinery.SOURCE_SUFFIXES)) 128 with open('mod.py', 'w', encoding='utf-8') as file: 129 file.write("# test file for importlib") 130 try: 131 loader = self._find(finder, 'mod', loader_only=True) 132 self.assertTrue(hasattr(loader, 'load_module')) 133 finally: 134 os.unlink('mod.py') 135 136 def test_invalidate_caches(self): 137 # invalidate_caches() should reset the mtime. 138 finder = self.machinery.FileFinder('', (self.machinery.SourceFileLoader, 139 self.machinery.SOURCE_SUFFIXES)) 140 finder._path_mtime = 42 141 finder.invalidate_caches() 142 self.assertEqual(finder._path_mtime, -1) 143 144 # Regression test for http://bugs.python.org/issue14846 145 def test_dir_removal_handling(self): 146 mod = 'mod' 147 with util.create_modules(mod) as mapping: 148 finder = self.get_finder(mapping['.root']) 149 found = self._find(finder, 'mod', loader_only=True) 150 self.assertIsNotNone(found) 151 found = self._find(finder, 'mod', loader_only=True) 152 self.assertEqual(found, self.NOT_FOUND) 153 154 @unittest.skipUnless(sys.platform != 'win32', 155 'os.chmod() does not support the needed arguments under Windows') 156 def test_no_read_directory(self): 157 # Issue #16730 158 tempdir = tempfile.TemporaryDirectory() 159 self.enterContext(tempdir) 160 # Since we muck with the permissions, we want to set them back to 161 # their original values to make sure the directory can be properly 162 # cleaned up. 163 original_mode = os.stat(tempdir.name).st_mode 164 self.addCleanup(os.chmod, tempdir.name, original_mode) 165 os.chmod(tempdir.name, stat.S_IWUSR | stat.S_IXUSR) 166 finder = self.get_finder(tempdir.name) 167 found = self._find(finder, 'doesnotexist') 168 self.assertEqual(found, self.NOT_FOUND) 169 170 def test_ignore_file(self): 171 # If a directory got changed to a file from underneath us, then don't 172 # worry about looking for submodules. 173 with tempfile.NamedTemporaryFile() as file_obj: 174 finder = self.get_finder(file_obj.name) 175 found = self._find(finder, 'doesnotexist') 176 self.assertEqual(found, self.NOT_FOUND) 177 178 179class FinderTestsPEP451(FinderTests): 180 181 NOT_FOUND = None 182 183 def _find(self, finder, name, loader_only=False): 184 spec = finder.find_spec(name) 185 return spec.loader if spec is not None else spec 186 187 188(Frozen_FinderTestsPEP451, 189 Source_FinderTestsPEP451 190 ) = util.test_both(FinderTestsPEP451, machinery=machinery) 191 192 193class FinderTestsPEP420(FinderTests): 194 195 NOT_FOUND = (None, []) 196 197 def _find(self, finder, name, loader_only=False): 198 spec = finder.find_spec(name) 199 if spec is None: 200 return self.NOT_FOUND 201 if loader_only: 202 return spec.loader 203 return spec.loader, spec.submodule_search_locations 204 205 206(Frozen_FinderTestsPEP420, 207 Source_FinderTestsPEP420 208 ) = util.test_both(FinderTestsPEP420, machinery=machinery) 209 210 211if __name__ == '__main__': 212 unittest.main() 213