• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from . import util as test_util
2
3init = test_util.import_importlib('importlib')
4util = test_util.import_importlib('importlib.util')
5machinery = test_util.import_importlib('importlib.machinery')
6
7import os.path
8import sys
9from test import support
10from test.support import import_helper
11from test.support import os_helper
12import types
13import unittest
14import warnings
15
16
17class ImportModuleTests:
18
19    """Test importlib.import_module."""
20
21    def test_module_import(self):
22        # Test importing a top-level module.
23        with test_util.mock_spec('top_level') as mock:
24            with test_util.import_state(meta_path=[mock]):
25                module = self.init.import_module('top_level')
26                self.assertEqual(module.__name__, 'top_level')
27
28    def test_absolute_package_import(self):
29        # Test importing a module from a package with an absolute name.
30        pkg_name = 'pkg'
31        pkg_long_name = '{0}.__init__'.format(pkg_name)
32        name = '{0}.mod'.format(pkg_name)
33        with test_util.mock_spec(pkg_long_name, name) as mock:
34            with test_util.import_state(meta_path=[mock]):
35                module = self.init.import_module(name)
36                self.assertEqual(module.__name__, name)
37
38    def test_shallow_relative_package_import(self):
39        # Test importing a module from a package through a relative import.
40        pkg_name = 'pkg'
41        pkg_long_name = '{0}.__init__'.format(pkg_name)
42        module_name = 'mod'
43        absolute_name = '{0}.{1}'.format(pkg_name, module_name)
44        relative_name = '.{0}'.format(module_name)
45        with test_util.mock_spec(pkg_long_name, absolute_name) as mock:
46            with test_util.import_state(meta_path=[mock]):
47                self.init.import_module(pkg_name)
48                module = self.init.import_module(relative_name, pkg_name)
49                self.assertEqual(module.__name__, absolute_name)
50
51    def test_deep_relative_package_import(self):
52        modules = ['a.__init__', 'a.b.__init__', 'a.c']
53        with test_util.mock_spec(*modules) as mock:
54            with test_util.import_state(meta_path=[mock]):
55                self.init.import_module('a')
56                self.init.import_module('a.b')
57                module = self.init.import_module('..c', 'a.b')
58                self.assertEqual(module.__name__, 'a.c')
59
60    def test_absolute_import_with_package(self):
61        # Test importing a module from a package with an absolute name with
62        # the 'package' argument given.
63        pkg_name = 'pkg'
64        pkg_long_name = '{0}.__init__'.format(pkg_name)
65        name = '{0}.mod'.format(pkg_name)
66        with test_util.mock_spec(pkg_long_name, name) as mock:
67            with test_util.import_state(meta_path=[mock]):
68                self.init.import_module(pkg_name)
69                module = self.init.import_module(name, pkg_name)
70                self.assertEqual(module.__name__, name)
71
72    def test_relative_import_wo_package(self):
73        # Relative imports cannot happen without the 'package' argument being
74        # set.
75        with self.assertRaises(TypeError):
76            self.init.import_module('.support')
77
78
79    def test_loaded_once(self):
80        # Issue #13591: Modules should only be loaded once when
81        # initializing the parent package attempts to import the
82        # module currently being imported.
83        b_load_count = 0
84        def load_a():
85            self.init.import_module('a.b')
86        def load_b():
87            nonlocal b_load_count
88            b_load_count += 1
89        code = {'a': load_a, 'a.b': load_b}
90        modules = ['a.__init__', 'a.b']
91        with test_util.mock_spec(*modules, module_code=code) as mock:
92            with test_util.import_state(meta_path=[mock]):
93                self.init.import_module('a.b')
94        self.assertEqual(b_load_count, 1)
95
96
97(Frozen_ImportModuleTests,
98 Source_ImportModuleTests
99 ) = test_util.test_both(ImportModuleTests, init=init)
100
101
102class FindLoaderTests:
103
104    FakeMetaFinder = None
105
106    def test_sys_modules(self):
107        # If a module with __loader__ is in sys.modules, then return it.
108        name = 'some_mod'
109        with test_util.uncache(name):
110            module = types.ModuleType(name)
111            loader = 'a loader!'
112            module.__loader__ = loader
113            sys.modules[name] = module
114            with warnings.catch_warnings():
115                warnings.simplefilter('ignore', DeprecationWarning)
116                found = self.init.find_loader(name)
117            self.assertEqual(loader, found)
118
119    def test_sys_modules_loader_is_None(self):
120        # If sys.modules[name].__loader__ is None, raise ValueError.
121        name = 'some_mod'
122        with test_util.uncache(name):
123            module = types.ModuleType(name)
124            module.__loader__ = None
125            sys.modules[name] = module
126            with self.assertRaises(ValueError):
127                with warnings.catch_warnings():
128                    warnings.simplefilter('ignore', DeprecationWarning)
129                    self.init.find_loader(name)
130
131    def test_sys_modules_loader_is_not_set(self):
132        # Should raise ValueError
133        # Issue #17099
134        name = 'some_mod'
135        with test_util.uncache(name):
136            module = types.ModuleType(name)
137            try:
138                del module.__loader__
139            except AttributeError:
140                pass
141            sys.modules[name] = module
142            with self.assertRaises(ValueError):
143                with warnings.catch_warnings():
144                    warnings.simplefilter('ignore', DeprecationWarning)
145                    self.init.find_loader(name)
146
147    def test_success(self):
148        # Return the loader found on sys.meta_path.
149        name = 'some_mod'
150        with test_util.uncache(name):
151            with test_util.import_state(meta_path=[self.FakeMetaFinder]):
152                with warnings.catch_warnings():
153                    warnings.simplefilter('ignore', DeprecationWarning)
154                    warnings.simplefilter('ignore', ImportWarning)
155                    self.assertEqual((name, None), self.init.find_loader(name))
156
157    def test_success_path(self):
158        # Searching on a path should work.
159        name = 'some_mod'
160        path = 'path to some place'
161        with test_util.uncache(name):
162            with test_util.import_state(meta_path=[self.FakeMetaFinder]):
163                with warnings.catch_warnings():
164                    warnings.simplefilter('ignore', DeprecationWarning)
165                    warnings.simplefilter('ignore', ImportWarning)
166                    self.assertEqual((name, path),
167                                     self.init.find_loader(name, path))
168
169    def test_nothing(self):
170        # None is returned upon failure to find a loader.
171        with warnings.catch_warnings():
172            warnings.simplefilter('ignore', DeprecationWarning)
173            self.assertIsNone(self.init.find_loader('nevergoingtofindthismodule'))
174
175
176class FindLoaderPEP451Tests(FindLoaderTests):
177
178    class FakeMetaFinder:
179        @staticmethod
180        def find_spec(name, path=None, target=None):
181            return machinery['Source'].ModuleSpec(name, (name, path))
182
183
184(Frozen_FindLoaderPEP451Tests,
185 Source_FindLoaderPEP451Tests
186 ) = test_util.test_both(FindLoaderPEP451Tests, init=init)
187
188
189class FindLoaderPEP302Tests(FindLoaderTests):
190
191    class FakeMetaFinder:
192        @staticmethod
193        def find_module(name, path=None):
194            return name, path
195
196
197(Frozen_FindLoaderPEP302Tests,
198 Source_FindLoaderPEP302Tests
199 ) = test_util.test_both(FindLoaderPEP302Tests, init=init)
200
201
202class ReloadTests:
203
204    def test_reload_modules(self):
205        for mod in ('tokenize', 'time', 'marshal'):
206            with self.subTest(module=mod):
207                with import_helper.CleanImport(mod):
208                    module = self.init.import_module(mod)
209                    self.init.reload(module)
210
211    def test_module_replaced(self):
212        def code():
213            import sys
214            module = type(sys)('top_level')
215            module.spam = 3
216            sys.modules['top_level'] = module
217        mock = test_util.mock_spec('top_level',
218                                   module_code={'top_level': code})
219        with mock:
220            with test_util.import_state(meta_path=[mock]):
221                module = self.init.import_module('top_level')
222                reloaded = self.init.reload(module)
223                actual = sys.modules['top_level']
224                self.assertEqual(actual.spam, 3)
225                self.assertEqual(reloaded.spam, 3)
226
227    def test_reload_missing_loader(self):
228        with import_helper.CleanImport('types'):
229            import types
230            loader = types.__loader__
231            del types.__loader__
232            reloaded = self.init.reload(types)
233
234            self.assertIs(reloaded, types)
235            self.assertIs(sys.modules['types'], types)
236            self.assertEqual(reloaded.__loader__.path, loader.path)
237
238    def test_reload_loader_replaced(self):
239        with import_helper.CleanImport('types'):
240            import types
241            types.__loader__ = None
242            self.init.invalidate_caches()
243            reloaded = self.init.reload(types)
244
245            self.assertIsNot(reloaded.__loader__, None)
246            self.assertIs(reloaded, types)
247            self.assertIs(sys.modules['types'], types)
248
249    def test_reload_location_changed(self):
250        name = 'spam'
251        with os_helper.temp_cwd(None) as cwd:
252            with test_util.uncache('spam'):
253                with import_helper.DirsOnSysPath(cwd):
254                    # Start as a plain module.
255                    self.init.invalidate_caches()
256                    path = os.path.join(cwd, name + '.py')
257                    cached = self.util.cache_from_source(path)
258                    expected = {'__name__': name,
259                                '__package__': '',
260                                '__file__': path,
261                                '__cached__': cached,
262                                '__doc__': None,
263                                }
264                    os_helper.create_empty_file(path)
265                    module = self.init.import_module(name)
266                    ns = vars(module).copy()
267                    loader = ns.pop('__loader__')
268                    spec = ns.pop('__spec__')
269                    ns.pop('__builtins__', None)  # An implementation detail.
270                    self.assertEqual(spec.name, name)
271                    self.assertEqual(spec.loader, loader)
272                    self.assertEqual(loader.path, path)
273                    self.assertEqual(ns, expected)
274
275                    # Change to a package.
276                    self.init.invalidate_caches()
277                    init_path = os.path.join(cwd, name, '__init__.py')
278                    cached = self.util.cache_from_source(init_path)
279                    expected = {'__name__': name,
280                                '__package__': name,
281                                '__file__': init_path,
282                                '__cached__': cached,
283                                '__path__': [os.path.dirname(init_path)],
284                                '__doc__': None,
285                                }
286                    os.mkdir(name)
287                    os.rename(path, init_path)
288                    reloaded = self.init.reload(module)
289                    ns = vars(reloaded).copy()
290                    loader = ns.pop('__loader__')
291                    spec = ns.pop('__spec__')
292                    ns.pop('__builtins__', None)  # An implementation detail.
293                    self.assertEqual(spec.name, name)
294                    self.assertEqual(spec.loader, loader)
295                    self.assertIs(reloaded, module)
296                    self.assertEqual(loader.path, init_path)
297                    self.maxDiff = None
298                    self.assertEqual(ns, expected)
299
300    def test_reload_namespace_changed(self):
301        name = 'spam'
302        with os_helper.temp_cwd(None) as cwd:
303            with test_util.uncache('spam'):
304                with import_helper.DirsOnSysPath(cwd):
305                    # Start as a namespace package.
306                    self.init.invalidate_caches()
307                    bad_path = os.path.join(cwd, name, '__init.py')
308                    cached = self.util.cache_from_source(bad_path)
309                    expected = {'__name__': name,
310                                '__package__': name,
311                                '__doc__': None,
312                                '__file__': None,
313                                }
314                    os.mkdir(name)
315                    with open(bad_path, 'w', encoding='utf-8') as init_file:
316                        init_file.write('eggs = None')
317                    module = self.init.import_module(name)
318                    ns = vars(module).copy()
319                    loader = ns.pop('__loader__')
320                    path = ns.pop('__path__')
321                    spec = ns.pop('__spec__')
322                    ns.pop('__builtins__', None)  # An implementation detail.
323                    self.assertEqual(spec.name, name)
324                    self.assertIsNotNone(spec.loader)
325                    self.assertIsNotNone(loader)
326                    self.assertEqual(spec.loader, loader)
327                    self.assertEqual(set(path),
328                                     set([os.path.dirname(bad_path)]))
329                    with self.assertRaises(AttributeError):
330                        # a NamespaceLoader
331                        loader.path
332                    self.assertEqual(ns, expected)
333
334                    # Change to a regular package.
335                    self.init.invalidate_caches()
336                    init_path = os.path.join(cwd, name, '__init__.py')
337                    cached = self.util.cache_from_source(init_path)
338                    expected = {'__name__': name,
339                                '__package__': name,
340                                '__file__': init_path,
341                                '__cached__': cached,
342                                '__path__': [os.path.dirname(init_path)],
343                                '__doc__': None,
344                                'eggs': None,
345                                }
346                    os.rename(bad_path, init_path)
347                    reloaded = self.init.reload(module)
348                    ns = vars(reloaded).copy()
349                    loader = ns.pop('__loader__')
350                    spec = ns.pop('__spec__')
351                    ns.pop('__builtins__', None)  # An implementation detail.
352                    self.assertEqual(spec.name, name)
353                    self.assertEqual(spec.loader, loader)
354                    self.assertIs(reloaded, module)
355                    self.assertEqual(loader.path, init_path)
356                    self.assertEqual(ns, expected)
357
358    def test_reload_submodule(self):
359        # See #19851.
360        name = 'spam'
361        subname = 'ham'
362        with test_util.temp_module(name, pkg=True) as pkg_dir:
363            fullname, _ = test_util.submodule(name, subname, pkg_dir)
364            ham = self.init.import_module(fullname)
365            reloaded = self.init.reload(ham)
366            self.assertIs(reloaded, ham)
367
368    def test_module_missing_spec(self):
369        #Test that reload() throws ModuleNotFounderror when reloading
370        # a module whose missing a spec. (bpo-29851)
371        name = 'spam'
372        with test_util.uncache(name):
373            module = sys.modules[name] = types.ModuleType(name)
374            # Sanity check by attempting an import.
375            module = self.init.import_module(name)
376            self.assertIsNone(module.__spec__)
377            with self.assertRaises(ModuleNotFoundError):
378                self.init.reload(module)
379
380
381(Frozen_ReloadTests,
382 Source_ReloadTests
383 ) = test_util.test_both(ReloadTests, init=init, util=util)
384
385
386class InvalidateCacheTests:
387
388    def test_method_called(self):
389        # If defined the method should be called.
390        class InvalidatingNullFinder:
391            def __init__(self, *ignored):
392                self.called = False
393            def find_module(self, *args):
394                return None
395            def invalidate_caches(self):
396                self.called = True
397
398        key = 'gobledeegook'
399        meta_ins = InvalidatingNullFinder()
400        path_ins = InvalidatingNullFinder()
401        sys.meta_path.insert(0, meta_ins)
402        self.addCleanup(lambda: sys.path_importer_cache.__delitem__(key))
403        sys.path_importer_cache[key] = path_ins
404        self.addCleanup(lambda: sys.meta_path.remove(meta_ins))
405        self.init.invalidate_caches()
406        self.assertTrue(meta_ins.called)
407        self.assertTrue(path_ins.called)
408
409    def test_method_lacking(self):
410        # There should be no issues if the method is not defined.
411        key = 'gobbledeegook'
412        sys.path_importer_cache[key] = None
413        self.addCleanup(lambda: sys.path_importer_cache.pop(key, None))
414        self.init.invalidate_caches()  # Shouldn't trigger an exception.
415
416
417(Frozen_InvalidateCacheTests,
418 Source_InvalidateCacheTests
419 ) = test_util.test_both(InvalidateCacheTests, init=init)
420
421
422class FrozenImportlibTests(unittest.TestCase):
423
424    def test_no_frozen_importlib(self):
425        # Should be able to import w/o _frozen_importlib being defined.
426        # Can't do an isinstance() check since separate copies of importlib
427        # may have been used for import, so just check the name is not for the
428        # frozen loader.
429        source_init = init['Source']
430        self.assertNotEqual(source_init.__loader__.__class__.__name__,
431                            'FrozenImporter')
432
433
434class StartupTests:
435
436    def test_everyone_has___loader__(self):
437        # Issue #17098: all modules should have __loader__ defined.
438        for name, module in sys.modules.items():
439            if isinstance(module, types.ModuleType):
440                with self.subTest(name=name):
441                    self.assertTrue(hasattr(module, '__loader__'),
442                                    '{!r} lacks a __loader__ attribute'.format(name))
443                    if self.machinery.BuiltinImporter.find_spec(name):
444                        self.assertIsNot(module.__loader__, None)
445                    elif self.machinery.FrozenImporter.find_spec(name):
446                        self.assertIsNot(module.__loader__, None)
447
448    def test_everyone_has___spec__(self):
449        for name, module in sys.modules.items():
450            if isinstance(module, types.ModuleType):
451                with self.subTest(name=name):
452                    self.assertTrue(hasattr(module, '__spec__'))
453                    if self.machinery.BuiltinImporter.find_spec(name):
454                        self.assertIsNot(module.__spec__, None)
455                    elif self.machinery.FrozenImporter.find_spec(name):
456                        self.assertIsNot(module.__spec__, None)
457
458
459(Frozen_StartupTests,
460 Source_StartupTests
461 ) = test_util.test_both(StartupTests, machinery=machinery)
462
463
464if __name__ == '__main__':
465    unittest.main()
466