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