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