diff options
Diffstat (limited to 'Lib/test/test_importlib/test_abc.py')
-rw-r--r-- | Lib/test/test_importlib/test_abc.py | 988 |
1 files changed, 947 insertions, 41 deletions
diff --git a/Lib/test/test_importlib/test_abc.py b/Lib/test/test_importlib/test_abc.py index c620c37..979a481 100644 --- a/Lib/test/test_importlib/test_abc.py +++ b/Lib/test/test_importlib/test_abc.py @@ -1,9 +1,21 @@ -from importlib import abc -from importlib import machinery +import contextlib import inspect +import io +import marshal +import os +import sys +from test import support +import types import unittest +from unittest import mock +from . import util +frozen_init, source_init = util.import_importlib('importlib') +frozen_abc, source_abc = util.import_importlib('importlib.abc') +frozen_util, source_util = util.import_importlib('importlib.util') + +##### Inheritance ############################################################## class InheritanceTests: """Test that the specified class is a subclass/superclass of the expected @@ -14,8 +26,19 @@ class InheritanceTests: def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.superclasses = [getattr(self.abc, class_name) + for class_name in self.superclass_names] + if hasattr(self, 'subclass_names'): + # Because test.support.import_fresh_module() creates a new + # importlib._bootstrap per module, inheritance checks fail when + # checking across module boundaries (i.e. the _bootstrap in abc is + # not the same as the one in machinery). That means stealing one of + # the modules from the other to make sure the same instance is used. + self.subclasses = [getattr(self.abc.machinery, class_name) + for class_name in self.subclass_names] assert self.subclasses or self.superclasses, self.__class__ - self.__test = getattr(abc, self.__class__.__name__) + testing = self.__class__.__name__.partition('_')[2] + self.__test = getattr(self.abc, testing) def test_subclasses(self): # Test that the expected subclasses inherit. @@ -29,75 +52,958 @@ class InheritanceTests: self.assertTrue(issubclass(self.__test, superclass), "{0} is not a superclass of {1}".format(superclass, self.__test)) +def create_inheritance_tests(base_class): + def set_frozen(ns): + ns['abc'] = frozen_abc + def set_source(ns): + ns['abc'] = source_abc -class MetaPathFinder(InheritanceTests, unittest.TestCase): + classes = [] + for prefix, ns_set in [('Frozen', set_frozen), ('Source', set_source)]: + classes.append(types.new_class('_'.join([prefix, base_class.__name__]), + (base_class, unittest.TestCase), + exec_body=ns_set)) + return classes - superclasses = [abc.Finder] - subclasses = [machinery.BuiltinImporter, machinery.FrozenImporter, - machinery.PathFinder, machinery.WindowsRegistryFinder] +class MetaPathFinder(InheritanceTests): + superclass_names = ['Finder'] + subclass_names = ['BuiltinImporter', 'FrozenImporter', 'PathFinder', + 'WindowsRegistryFinder'] -class PathEntryFinder(InheritanceTests, unittest.TestCase): +tests = create_inheritance_tests(MetaPathFinder) +Frozen_MetaPathFinderInheritanceTests, Source_MetaPathFinderInheritanceTests = tests - superclasses = [abc.Finder] - subclasses = [machinery.FileFinder] +class PathEntryFinder(InheritanceTests): + superclass_names = ['Finder'] + subclass_names = ['FileFinder'] -class Loader(InheritanceTests, unittest.TestCase): +tests = create_inheritance_tests(PathEntryFinder) +Frozen_PathEntryFinderInheritanceTests, Source_PathEntryFinderInheritanceTests = tests - subclasses = [abc.PyLoader] +class ResourceLoader(InheritanceTests): + superclass_names = ['Loader'] -class ResourceLoader(InheritanceTests, unittest.TestCase): +tests = create_inheritance_tests(ResourceLoader) +Frozen_ResourceLoaderInheritanceTests, Source_ResourceLoaderInheritanceTests = tests - superclasses = [abc.Loader] +class InspectLoader(InheritanceTests): + superclass_names = ['Loader'] + subclass_names = ['BuiltinImporter', 'FrozenImporter', 'ExtensionFileLoader'] -class InspectLoader(InheritanceTests, unittest.TestCase): +tests = create_inheritance_tests(InspectLoader) +Frozen_InspectLoaderInheritanceTests, Source_InspectLoaderInheritanceTests = tests - superclasses = [abc.Loader] - subclasses = [abc.PyLoader, machinery.BuiltinImporter, - machinery.FrozenImporter, machinery.ExtensionFileLoader] +class ExecutionLoader(InheritanceTests): + superclass_names = ['InspectLoader'] + subclass_names = ['ExtensionFileLoader'] -class ExecutionLoader(InheritanceTests, unittest.TestCase): +tests = create_inheritance_tests(ExecutionLoader) +Frozen_ExecutionLoaderInheritanceTests, Source_ExecutionLoaderInheritanceTests = tests - superclasses = [abc.InspectLoader] - subclasses = [abc.PyLoader] +class FileLoader(InheritanceTests): + superclass_names = ['ResourceLoader', 'ExecutionLoader'] + subclass_names = ['SourceFileLoader', 'SourcelessFileLoader'] -class FileLoader(InheritanceTests, unittest.TestCase): +tests = create_inheritance_tests(FileLoader) +Frozen_FileLoaderInheritanceTests, Source_FileLoaderInheritanceTests = tests - superclasses = [abc.ResourceLoader, abc.ExecutionLoader] - subclasses = [machinery.SourceFileLoader, machinery.SourcelessFileLoader] +class SourceLoader(InheritanceTests): + superclass_names = ['ResourceLoader', 'ExecutionLoader'] + subclass_names = ['SourceFileLoader'] -class SourceLoader(InheritanceTests, unittest.TestCase): +tests = create_inheritance_tests(SourceLoader) +Frozen_SourceLoaderInheritanceTests, Source_SourceLoaderInheritanceTests = tests - superclasses = [abc.ResourceLoader, abc.ExecutionLoader] - subclasses = [machinery.SourceFileLoader] +##### Default return values #################################################### +def make_abc_subclasses(base_class): + classes = [] + for kind, abc in [('Frozen', frozen_abc), ('Source', source_abc)]: + name = '_'.join([kind, base_class.__name__]) + base_classes = base_class, getattr(abc, base_class.__name__) + classes.append(types.new_class(name, base_classes)) + return classes +def make_return_value_tests(base_class, test_class): + frozen_class, source_class = make_abc_subclasses(base_class) + tests = [] + for prefix, class_in_test in [('Frozen', frozen_class), ('Source', source_class)]: + def set_ns(ns): + ns['ins'] = class_in_test() + tests.append(types.new_class('_'.join([prefix, test_class.__name__]), + (test_class, unittest.TestCase), + exec_body=set_ns)) + return tests -class PyLoader(InheritanceTests, unittest.TestCase): - superclasses = [abc.Loader, abc.ResourceLoader, abc.ExecutionLoader] +class MetaPathFinder: + def find_module(self, fullname, path): + return super().find_module(fullname, path) -class PyPycLoader(InheritanceTests, unittest.TestCase): +Frozen_MPF, Source_MPF = make_abc_subclasses(MetaPathFinder) - superclasses = [abc.PyLoader] +class MetaPathFinderDefaultsTests: + + def test_find_module(self): + # Default should return None. + self.assertIsNone(self.ins.find_module('something', None)) + + def test_invalidate_caches(self): + # Calling the method is a no-op. + self.ins.invalidate_caches() + + +tests = make_return_value_tests(MetaPathFinder, MetaPathFinderDefaultsTests) +Frozen_MPFDefaultTests, Source_MPFDefaultTests = tests + + +class PathEntryFinder: + + def find_loader(self, fullname): + return super().find_loader(fullname) + +Frozen_PEF, Source_PEF = make_abc_subclasses(PathEntryFinder) + + +class PathEntryFinderDefaultsTests: + + def test_find_loader(self): + self.assertEqual((None, []), self.ins.find_loader('something')) + + def find_module(self): + self.assertEqual(None, self.ins.find_module('something')) + + def test_invalidate_caches(self): + # Should be a no-op. + self.ins.invalidate_caches() + + +tests = make_return_value_tests(PathEntryFinder, PathEntryFinderDefaultsTests) +Frozen_PEFDefaultTests, Source_PEFDefaultTests = tests + + +class Loader: + + def load_module(self, fullname): + return super().load_module(fullname) + + +Frozen_L, Source_L = make_abc_subclasses(Loader) + + +class LoaderDefaultsTests: + + def test_load_module(self): + with self.assertRaises(ImportError): + self.ins.load_module('something') + + def test_module_repr(self): + mod = types.ModuleType('blah') + with self.assertRaises(NotImplementedError): + self.ins.module_repr(mod) + original_repr = repr(mod) + mod.__loader__ = self.ins + # Should still return a proper repr. + self.assertTrue(repr(mod)) + + +tests = make_return_value_tests(Loader, LoaderDefaultsTests) +Frozen_LDefaultTests, SourceLDefaultTests = tests + + +class ResourceLoader(Loader): + + def get_data(self, path): + return super().get_data(path) + + +Frozen_RL, Source_RL = make_abc_subclasses(ResourceLoader) + + +class ResourceLoaderDefaultsTests: + + def test_get_data(self): + with self.assertRaises(IOError): + self.ins.get_data('/some/path') + + +tests = make_return_value_tests(ResourceLoader, ResourceLoaderDefaultsTests) +Frozen_RLDefaultTests, Source_RLDefaultTests = tests + + +class InspectLoader(Loader): + + def is_package(self, fullname): + return super().is_package(fullname) + + def get_source(self, fullname): + return super().get_source(fullname) + + +Frozen_IL, Source_IL = make_abc_subclasses(InspectLoader) + + +class InspectLoaderDefaultsTests: + + def test_is_package(self): + with self.assertRaises(ImportError): + self.ins.is_package('blah') + + def test_get_source(self): + with self.assertRaises(ImportError): + self.ins.get_source('blah') + + +tests = make_return_value_tests(InspectLoader, InspectLoaderDefaultsTests) +Frozen_ILDefaultTests, Source_ILDefaultTests = tests + + +class ExecutionLoader(InspectLoader): + + def get_filename(self, fullname): + return super().get_filename(fullname) + +Frozen_EL, Source_EL = make_abc_subclasses(ExecutionLoader) + + +class ExecutionLoaderDefaultsTests: + + def test_get_filename(self): + with self.assertRaises(ImportError): + self.ins.get_filename('blah') + + +tests = make_return_value_tests(ExecutionLoader, InspectLoaderDefaultsTests) +Frozen_ELDefaultTests, Source_ELDefaultsTests = tests + +##### Loader concrete methods ################################################## +class LoaderConcreteMethodTests: + + def test_init_module_attrs(self): + loader = self.LoaderSubclass() + module = types.ModuleType('blah') + loader.init_module_attrs(module) + self.assertEqual(module.__loader__, loader) + + +class Frozen_LoaderConcreateMethodTests(LoaderConcreteMethodTests, unittest.TestCase): + LoaderSubclass = Frozen_L + +class Source_LoaderConcreateMethodTests(LoaderConcreteMethodTests, unittest.TestCase): + LoaderSubclass = Source_L + + +##### InspectLoader concrete methods ########################################### +class InspectLoaderSourceToCodeTests: + + def source_to_module(self, data, path=None): + """Help with source_to_code() tests.""" + module = types.ModuleType('blah') + loader = self.InspectLoaderSubclass() + if path is None: + code = loader.source_to_code(data) + else: + code = loader.source_to_code(data, path) + exec(code, module.__dict__) + return module + + def test_source_to_code_source(self): + # Since compile() can handle strings, so should source_to_code(). + source = 'attr = 42' + module = self.source_to_module(source) + self.assertTrue(hasattr(module, 'attr')) + self.assertEqual(module.attr, 42) + + def test_source_to_code_bytes(self): + # Since compile() can handle bytes, so should source_to_code(). + source = b'attr = 42' + module = self.source_to_module(source) + self.assertTrue(hasattr(module, 'attr')) + self.assertEqual(module.attr, 42) + + def test_source_to_code_path(self): + # Specifying a path should set it for the code object. + path = 'path/to/somewhere' + loader = self.InspectLoaderSubclass() + code = loader.source_to_code('', path) + self.assertEqual(code.co_filename, path) + + def test_source_to_code_no_path(self): + # Not setting a path should still work and be set to <string> since that + # is a pre-existing practice as a default to compile(). + loader = self.InspectLoaderSubclass() + code = loader.source_to_code('') + self.assertEqual(code.co_filename, '<string>') + + +class Frozen_ILSourceToCodeTests(InspectLoaderSourceToCodeTests, unittest.TestCase): + InspectLoaderSubclass = Frozen_IL + +class Source_ILSourceToCodeTests(InspectLoaderSourceToCodeTests, unittest.TestCase): + InspectLoaderSubclass = Source_IL + + +class InspectLoaderGetCodeTests: + + def test_get_code(self): + # Test success. + module = types.ModuleType('blah') + with mock.patch.object(self.InspectLoaderSubclass, 'get_source') as mocked: + mocked.return_value = 'attr = 42' + loader = self.InspectLoaderSubclass() + code = loader.get_code('blah') + exec(code, module.__dict__) + self.assertEqual(module.attr, 42) + + def test_get_code_source_is_None(self): + # If get_source() is None then this should be None. + with mock.patch.object(self.InspectLoaderSubclass, 'get_source') as mocked: + mocked.return_value = None + loader = self.InspectLoaderSubclass() + code = loader.get_code('blah') + self.assertIsNone(code) + + def test_get_code_source_not_found(self): + # If there is no source then there is no code object. + loader = self.InspectLoaderSubclass() + with self.assertRaises(ImportError): + loader.get_code('blah') + + +class Frozen_ILGetCodeTests(InspectLoaderGetCodeTests, unittest.TestCase): + InspectLoaderSubclass = Frozen_IL + +class Source_ILGetCodeTests(InspectLoaderGetCodeTests, unittest.TestCase): + InspectLoaderSubclass = Source_IL + + +class InspectLoaderInitModuleTests: + + def mock_is_package(self, return_value): + return mock.patch.object(self.InspectLoaderSubclass, 'is_package', + return_value=return_value) + + def init_module_attrs(self, name): + loader = self.InspectLoaderSubclass() + module = types.ModuleType(name) + loader.init_module_attrs(module) + self.assertEqual(module.__loader__, loader) + return module + + def test_package(self): + # If a package, then __package__ == __name__, __path__ == [] + with self.mock_is_package(True): + name = 'blah' + module = self.init_module_attrs(name) + self.assertEqual(module.__package__, name) + self.assertEqual(module.__path__, []) + + def test_toplevel(self): + # If a module is top-level, __package__ == '' + with self.mock_is_package(False): + name = 'blah' + module = self.init_module_attrs(name) + self.assertEqual(module.__package__, '') + + def test_submodule(self): + # If a module is contained within a package then set __package__ to the + # package name. + with self.mock_is_package(False): + name = 'pkg.mod' + module = self.init_module_attrs(name) + self.assertEqual(module.__package__, 'pkg') + + def test_is_package_ImportError(self): + # If is_package() raises ImportError, __package__ should be None and + # __path__ should not be set. + with self.mock_is_package(False) as mocked_method: + mocked_method.side_effect = ImportError + name = 'mod' + module = self.init_module_attrs(name) + self.assertIsNone(module.__package__) + self.assertFalse(hasattr(module, '__path__')) + + +class Frozen_ILInitModuleTests(InspectLoaderInitModuleTests, unittest.TestCase): + InspectLoaderSubclass = Frozen_IL + +class Source_ILInitModuleTests(InspectLoaderInitModuleTests, unittest.TestCase): + InspectLoaderSubclass = Source_IL + + +class InspectLoaderLoadModuleTests: + + """Test InspectLoader.load_module().""" + + module_name = 'blah' + + def setUp(self): + support.unload(self.module_name) + self.addCleanup(support.unload, self.module_name) + + def mock_get_code(self): + return mock.patch.object(self.InspectLoaderSubclass, 'get_code') + + def test_get_code_ImportError(self): + # If get_code() raises ImportError, it should propagate. + with self.mock_get_code() as mocked_get_code: + mocked_get_code.side_effect = ImportError + with self.assertRaises(ImportError): + loader = self.InspectLoaderSubclass() + loader.load_module(self.module_name) + + def test_get_code_None(self): + # If get_code() returns None, raise ImportError. + with self.mock_get_code() as mocked_get_code: + mocked_get_code.return_value = None + with self.assertRaises(ImportError): + loader = self.InspectLoaderSubclass() + loader.load_module(self.module_name) + + def test_module_returned(self): + # The loaded module should be returned. + code = compile('attr = 42', '<string>', 'exec') + with self.mock_get_code() as mocked_get_code: + mocked_get_code.return_value = code + loader = self.InspectLoaderSubclass() + module = loader.load_module(self.module_name) + self.assertEqual(module, sys.modules[self.module_name]) + + +class Frozen_ILLoadModuleTests(InspectLoaderLoadModuleTests, unittest.TestCase): + InspectLoaderSubclass = Frozen_IL + +class Source_ILLoadModuleTests(InspectLoaderLoadModuleTests, unittest.TestCase): + InspectLoaderSubclass = Source_IL + + +##### ExecutionLoader concrete methods ######################################### +class ExecutionLoaderGetCodeTests: + + def mock_methods(self, *, get_source=False, get_filename=False): + source_mock_context, filename_mock_context = None, None + if get_source: + source_mock_context = mock.patch.object(self.ExecutionLoaderSubclass, + 'get_source') + if get_filename: + filename_mock_context = mock.patch.object(self.ExecutionLoaderSubclass, + 'get_filename') + return source_mock_context, filename_mock_context + + def test_get_code(self): + path = 'blah.py' + source_mock_context, filename_mock_context = self.mock_methods( + get_source=True, get_filename=True) + with source_mock_context as source_mock, filename_mock_context as name_mock: + source_mock.return_value = 'attr = 42' + name_mock.return_value = path + loader = self.ExecutionLoaderSubclass() + code = loader.get_code('blah') + self.assertEqual(code.co_filename, path) + module = types.ModuleType('blah') + exec(code, module.__dict__) + self.assertEqual(module.attr, 42) + + def test_get_code_source_is_None(self): + # If get_source() is None then this should be None. + source_mock_context, _ = self.mock_methods(get_source=True) + with source_mock_context as mocked: + mocked.return_value = None + loader = self.ExecutionLoaderSubclass() + code = loader.get_code('blah') + self.assertIsNone(code) + + def test_get_code_source_not_found(self): + # If there is no source then there is no code object. + loader = self.ExecutionLoaderSubclass() + with self.assertRaises(ImportError): + loader.get_code('blah') + + def test_get_code_no_path(self): + # If get_filename() raises ImportError then simply skip setting the path + # on the code object. + source_mock_context, filename_mock_context = self.mock_methods( + get_source=True, get_filename=True) + with source_mock_context as source_mock, filename_mock_context as name_mock: + source_mock.return_value = 'attr = 42' + name_mock.side_effect = ImportError + loader = self.ExecutionLoaderSubclass() + code = loader.get_code('blah') + self.assertEqual(code.co_filename, '<string>') + module = types.ModuleType('blah') + exec(code, module.__dict__) + self.assertEqual(module.attr, 42) + + +class Frozen_ELGetCodeTests(ExecutionLoaderGetCodeTests, unittest.TestCase): + ExecutionLoaderSubclass = Frozen_EL + +class Source_ELGetCodeTests(ExecutionLoaderGetCodeTests, unittest.TestCase): + ExecutionLoaderSubclass = Source_EL + + +class ExecutionLoaderInitModuleTests: + + def mock_is_package(self, return_value): + return mock.patch.object(self.ExecutionLoaderSubclass, 'is_package', + return_value=return_value) + + @contextlib.contextmanager + def mock_methods(self, is_package, filename): + is_package_manager = self.mock_is_package(is_package) + get_filename_manager = mock.patch.object(self.ExecutionLoaderSubclass, + 'get_filename', return_value=filename) + with is_package_manager as mock_is_package: + with get_filename_manager as mock_get_filename: + yield {'is_package': mock_is_package, + 'get_filename': mock_get_filename} + + def test_toplevel(self): + # Verify __loader__, __file__, and __package__; no __path__. + name = 'blah' + path = os.path.join('some', 'path', '{}.py'.format(name)) + with self.mock_methods(False, path): + loader = self.ExecutionLoaderSubclass() + module = types.ModuleType(name) + loader.init_module_attrs(module) + self.assertIs(module.__loader__, loader) + self.assertEqual(module.__file__, path) + self.assertEqual(module.__package__, '') + self.assertFalse(hasattr(module, '__path__')) + + def test_package(self): + # Verify __loader__, __file__, __package__, and __path__. + name = 'pkg' + path = os.path.join('some', 'pkg', '__init__.py') + with self.mock_methods(True, path): + loader = self.ExecutionLoaderSubclass() + module = types.ModuleType(name) + loader.init_module_attrs(module) + self.assertIs(module.__loader__, loader) + self.assertEqual(module.__file__, path) + self.assertEqual(module.__package__, 'pkg') + self.assertEqual(module.__path__, [os.path.dirname(path)]) + + def test_submodule(self): + # Verify __package__ and not __path__; test_toplevel() takes care of + # other attributes. + name = 'pkg.submodule' + path = os.path.join('some', 'pkg', 'submodule.py') + with self.mock_methods(False, path): + loader = self.ExecutionLoaderSubclass() + module = types.ModuleType(name) + loader.init_module_attrs(module) + self.assertEqual(module.__package__, 'pkg') + self.assertEqual(module.__file__, path) + self.assertFalse(hasattr(module, '__path__')) + + def test_get_filename_ImportError(self): + # If get_filename() raises ImportError, don't set __file__. + name = 'blah' + path = 'blah.py' + with self.mock_methods(False, path) as mocked_methods: + mocked_methods['get_filename'].side_effect = ImportError + loader = self.ExecutionLoaderSubclass() + module = types.ModuleType(name) + loader.init_module_attrs(module) + self.assertFalse(hasattr(module, '__file__')) + + +class Frozen_ELInitModuleTests(ExecutionLoaderInitModuleTests, unittest.TestCase): + ExecutionLoaderSubclass = Frozen_EL + +class Source_ELInitModuleTests(ExecutionLoaderInitModuleTests, unittest.TestCase): + ExecutionLoaderSubclass = Source_EL + + +##### SourceLoader concrete methods ############################################ +class SourceLoader: + + # Globals that should be defined for all modules. + source = (b"_ = '::'.join([__name__, __file__, __cached__, __package__, " + b"repr(__loader__)])") + + def __init__(self, path): + self.path = path + + def get_data(self, path): + if path != self.path: + raise IOError + return self.source + + def get_filename(self, fullname): + return self.path + + def module_repr(self, module): + return '<module>' + + +Frozen_SourceOnlyL, Source_SourceOnlyL = make_abc_subclasses(SourceLoader) + + +class SourceLoader(SourceLoader): + + source_mtime = 1 + + def __init__(self, path, magic=None): + super().__init__(path) + self.bytecode_path = self.util.cache_from_source(self.path) + self.source_size = len(self.source) + if magic is None: + magic = self.util.MAGIC_NUMBER + data = bytearray(magic) + data.extend(self.init._w_long(self.source_mtime)) + data.extend(self.init._w_long(self.source_size)) + code_object = compile(self.source, self.path, 'exec', + dont_inherit=True) + data.extend(marshal.dumps(code_object)) + self.bytecode = bytes(data) + self.written = {} + + def get_data(self, path): + if path == self.path: + return super().get_data(path) + elif path == self.bytecode_path: + return self.bytecode + else: + raise OSError + + def path_stats(self, path): + if path != self.path: + raise IOError + return {'mtime': self.source_mtime, 'size': self.source_size} + + def set_data(self, path, data): + self.written[path] = bytes(data) + return path == self.bytecode_path + + +Frozen_SL, Source_SL = make_abc_subclasses(SourceLoader) +Frozen_SL.util = frozen_util +Source_SL.util = source_util +Frozen_SL.init = frozen_init +Source_SL.init = source_init + + +class SourceLoaderTestHarness: + + def setUp(self, *, is_package=True, **kwargs): + self.package = 'pkg' + if is_package: + self.path = os.path.join(self.package, '__init__.py') + self.name = self.package + else: + module_name = 'mod' + self.path = os.path.join(self.package, '.'.join(['mod', 'py'])) + self.name = '.'.join([self.package, module_name]) + self.cached = self.util.cache_from_source(self.path) + self.loader = self.loader_mock(self.path, **kwargs) + + def verify_module(self, module): + self.assertEqual(module.__name__, self.name) + self.assertEqual(module.__file__, self.path) + self.assertEqual(module.__cached__, self.cached) + self.assertEqual(module.__package__, self.package) + self.assertEqual(module.__loader__, self.loader) + values = module._.split('::') + self.assertEqual(values[0], self.name) + self.assertEqual(values[1], self.path) + self.assertEqual(values[2], self.cached) + self.assertEqual(values[3], self.package) + self.assertEqual(values[4], repr(self.loader)) + + def verify_code(self, code_object): + module = types.ModuleType(self.name) + module.__file__ = self.path + module.__cached__ = self.cached + module.__package__ = self.package + module.__loader__ = self.loader + module.__path__ = [] + exec(code_object, module.__dict__) + self.verify_module(module) + + +class SourceOnlyLoaderTests(SourceLoaderTestHarness): + + """Test importlib.abc.SourceLoader for source-only loading. + + Reload testing is subsumed by the tests for + importlib.util.module_for_loader. + + """ + + def test_get_source(self): + # Verify the source code is returned as a string. + # If an OSError is raised by get_data then raise ImportError. + expected_source = self.loader.source.decode('utf-8') + self.assertEqual(self.loader.get_source(self.name), expected_source) + def raise_OSError(path): + raise OSError + self.loader.get_data = raise_OSError + with self.assertRaises(ImportError) as cm: + self.loader.get_source(self.name) + self.assertEqual(cm.exception.name, self.name) + + def test_is_package(self): + # Properly detect when loading a package. + self.setUp(is_package=False) + self.assertFalse(self.loader.is_package(self.name)) + self.setUp(is_package=True) + self.assertTrue(self.loader.is_package(self.name)) + self.assertFalse(self.loader.is_package(self.name + '.__init__')) + + def test_get_code(self): + # Verify the code object is created. + code_object = self.loader.get_code(self.name) + self.verify_code(code_object) + + def test_source_to_code(self): + # Verify the compiled code object. + code = self.loader.source_to_code(self.loader.source, self.path) + self.verify_code(code) + + def test_load_module(self): + # Loading a module should set __name__, __loader__, __package__, + # __path__ (for packages), __file__, and __cached__. + # The module should also be put into sys.modules. + with util.uncache(self.name): + module = self.loader.load_module(self.name) + self.verify_module(module) + self.assertEqual(module.__path__, [os.path.dirname(self.path)]) + self.assertIn(self.name, sys.modules) + + def test_package_settings(self): + # __package__ needs to be set, while __path__ is set on if the module + # is a package. + # Testing the values for a package are covered by test_load_module. + self.setUp(is_package=False) + with util.uncache(self.name): + module = self.loader.load_module(self.name) + self.verify_module(module) + self.assertTrue(not hasattr(module, '__path__')) + + def test_get_source_encoding(self): + # Source is considered encoded in UTF-8 by default unless otherwise + # specified by an encoding line. + source = "_ = 'ü'" + self.loader.source = source.encode('utf-8') + returned_source = self.loader.get_source(self.name) + self.assertEqual(returned_source, source) + source = "# coding: latin-1\n_ = ü" + self.loader.source = source.encode('latin-1') + returned_source = self.loader.get_source(self.name) + self.assertEqual(returned_source, source) + + +class Frozen_SourceOnlyLTests(SourceOnlyLoaderTests, unittest.TestCase): + loader_mock = Frozen_SourceOnlyL + util = frozen_util + +class Source_SourceOnlyLTests(SourceOnlyLoaderTests, unittest.TestCase): + loader_mock = Source_SourceOnlyL + util = source_util + + +@unittest.skipIf(sys.dont_write_bytecode, "sys.dont_write_bytecode is true") +class SourceLoaderBytecodeTests(SourceLoaderTestHarness): + + """Test importlib.abc.SourceLoader's use of bytecode. + + Source-only testing handled by SourceOnlyLoaderTests. + + """ + + def verify_code(self, code_object, *, bytecode_written=False): + super().verify_code(code_object) + if bytecode_written: + self.assertIn(self.cached, self.loader.written) + data = bytearray(self.util.MAGIC_NUMBER) + data.extend(self.init._w_long(self.loader.source_mtime)) + data.extend(self.init._w_long(self.loader.source_size)) + data.extend(marshal.dumps(code_object)) + self.assertEqual(self.loader.written[self.cached], bytes(data)) + + def test_code_with_everything(self): + # When everything should work. + code_object = self.loader.get_code(self.name) + self.verify_code(code_object) + + def test_no_bytecode(self): + # If no bytecode exists then move on to the source. + self.loader.bytecode_path = "<does not exist>" + # Sanity check + with self.assertRaises(OSError): + bytecode_path = self.util.cache_from_source(self.path) + self.loader.get_data(bytecode_path) + code_object = self.loader.get_code(self.name) + self.verify_code(code_object, bytecode_written=True) + + def test_code_bad_timestamp(self): + # Bytecode is only used when the timestamp matches the source EXACTLY. + for source_mtime in (0, 2): + assert source_mtime != self.loader.source_mtime + original = self.loader.source_mtime + self.loader.source_mtime = source_mtime + # If bytecode is used then EOFError would be raised by marshal. + self.loader.bytecode = self.loader.bytecode[8:] + code_object = self.loader.get_code(self.name) + self.verify_code(code_object, bytecode_written=True) + self.loader.source_mtime = original + + def test_code_bad_magic(self): + # Skip over bytecode with a bad magic number. + self.setUp(magic=b'0000') + # If bytecode is used then EOFError would be raised by marshal. + self.loader.bytecode = self.loader.bytecode[8:] + code_object = self.loader.get_code(self.name) + self.verify_code(code_object, bytecode_written=True) + + def test_dont_write_bytecode(self): + # Bytecode is not written if sys.dont_write_bytecode is true. + # Can assume it is false already thanks to the skipIf class decorator. + try: + sys.dont_write_bytecode = True + self.loader.bytecode_path = "<does not exist>" + code_object = self.loader.get_code(self.name) + self.assertNotIn(self.cached, self.loader.written) + finally: + sys.dont_write_bytecode = False + + def test_no_set_data(self): + # If set_data is not defined, one can still read bytecode. + self.setUp(magic=b'0000') + original_set_data = self.loader.__class__.mro()[1].set_data + try: + del self.loader.__class__.mro()[1].set_data + code_object = self.loader.get_code(self.name) + self.verify_code(code_object) + finally: + self.loader.__class__.mro()[1].set_data = original_set_data + + def test_set_data_raises_exceptions(self): + # Raising NotImplementedError or OSError is okay for set_data. + def raise_exception(exc): + def closure(*args, **kwargs): + raise exc + return closure + + self.setUp(magic=b'0000') + self.loader.set_data = raise_exception(NotImplementedError) + code_object = self.loader.get_code(self.name) + self.verify_code(code_object) + + +class Frozen_SLBytecodeTests(SourceLoaderBytecodeTests, unittest.TestCase): + loader_mock = Frozen_SL + init = frozen_init + util = frozen_util + +class SourceSLBytecodeTests(SourceLoaderBytecodeTests, unittest.TestCase): + loader_mock = Source_SL + init = source_init + util = source_util + + +class SourceLoaderGetSourceTests: + + """Tests for importlib.abc.SourceLoader.get_source().""" + + def test_default_encoding(self): + # Should have no problems with UTF-8 text. + name = 'mod' + mock = self.SourceOnlyLoaderMock('mod.file') + source = 'x = "ü"' + mock.source = source.encode('utf-8') + returned_source = mock.get_source(name) + self.assertEqual(returned_source, source) + + def test_decoded_source(self): + # Decoding should work. + name = 'mod' + mock = self.SourceOnlyLoaderMock("mod.file") + source = "# coding: Latin-1\nx='ü'" + assert source.encode('latin-1') != source.encode('utf-8') + mock.source = source.encode('latin-1') + returned_source = mock.get_source(name) + self.assertEqual(returned_source, source) + + def test_universal_newlines(self): + # PEP 302 says universal newlines should be used. + name = 'mod' + mock = self.SourceOnlyLoaderMock('mod.file') + source = "x = 42\r\ny = -13\r\n" + mock.source = source.encode('utf-8') + expect = io.IncrementalNewlineDecoder(None, True).decode(source) + self.assertEqual(mock.get_source(name), expect) + + +class Frozen_SourceOnlyLGetSourceTests(SourceLoaderGetSourceTests, unittest.TestCase): + SourceOnlyLoaderMock = Frozen_SourceOnlyL + +class Source_SourceOnlyLGetSourceTests(SourceLoaderGetSourceTests, unittest.TestCase): + SourceOnlyLoaderMock = Source_SourceOnlyL + + +class SourceLoaderInitModuleAttrTests: + + """Tests for importlib.abc.SourceLoader.init_module_attrs().""" + + def test_init_module_attrs(self): + # If __file__ set, __cached__ == importlib.util.cached_from_source(__file__). + name = 'blah' + path = 'blah.py' + loader = self.SourceOnlyLoaderMock(path) + module = types.ModuleType(name) + loader.init_module_attrs(module) + self.assertEqual(module.__loader__, loader) + self.assertEqual(module.__package__, '') + self.assertEqual(module.__file__, path) + self.assertEqual(module.__cached__, self.util.cache_from_source(path)) + + def test_no_get_filename(self): + # No __file__, no __cached__. + with mock.patch.object(self.SourceOnlyLoaderMock, 'get_filename') as mocked: + mocked.side_effect = ImportError + name = 'blah' + loader = self.SourceOnlyLoaderMock('blah.py') + module = types.ModuleType(name) + loader.init_module_attrs(module) + self.assertFalse(hasattr(module, '__file__')) + self.assertFalse(hasattr(module, '__cached__')) + + +class Frozen_SLInitModuleAttrTests(SourceLoaderInitModuleAttrTests, unittest.TestCase): + SourceOnlyLoaderMock = Frozen_SourceOnlyL + util = frozen_util + + # Difficult to test under source thanks to cross-module mocking needs. + @mock.patch('importlib._bootstrap.cache_from_source') + def test_cache_from_source_NotImplementedError(self, mock_cache_from_source): + # If importlib.util.cache_from_source() raises NotImplementedError don't set + # __cached__. + mock_cache_from_source.side_effect = NotImplementedError + name = 'blah' + path = 'blah.py' + loader = self.SourceOnlyLoaderMock(path) + module = types.ModuleType(name) + loader.init_module_attrs(module) + self.assertEqual(module.__file__, path) + self.assertFalse(hasattr(module, '__cached__')) + + +class Source_SLInitModuleAttrTests(SourceLoaderInitModuleAttrTests, unittest.TestCase): + SourceOnlyLoaderMock = Source_SourceOnlyL + util = source_util -def test_main(): - from test.support import run_unittest - classes = [] - for class_ in globals().values(): - if (inspect.isclass(class_) and - issubclass(class_, unittest.TestCase) and - issubclass(class_, InheritanceTests)): - classes.append(class_) - run_unittest(*classes) if __name__ == '__main__': - test_main() + unittest.main() |