• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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