diff options
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/importlib/_bootstrap.py | 38 | ||||
-rw-r--r-- | Lib/importlib/abc.py | 18 | ||||
-rw-r--r-- | Lib/importlib/test/source/test_abc_loader.py | 53 | ||||
-rw-r--r-- | Lib/importlib/test/test_abc.py | 8 |
4 files changed, 95 insertions, 22 deletions
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index ee3f1e6..2c5a1cf 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -315,16 +315,10 @@ class PyLoader: @module_for_loader def load_module(self, module): - """Load a source module.""" - return self._load_module(module) - - def _load_module(self, module): - """Initialize a module from source.""" + """Initialize the module.""" name = module.__name__ code_object = self.get_code(module.__name__) - # __file__ may have been set by the caller, e.g. bytecode path. - if not hasattr(module, '__file__'): - module.__file__ = self.source_path(name) + module.__file__ = self.get_filename(name) if self.is_package(name): module.__path__ = [module.__file__.rsplit(path_sep, 1)[0]] module.__package__ = module.__name__ @@ -334,6 +328,15 @@ class PyLoader: exec(code_object, module.__dict__) return module + def get_filename(self, fullname): + """Return the path to the source file, else raise ImportError.""" + path = self.source_path(fullname) + if path is not None: + return path + else: + raise ImportError("no source path available for " + "{0!r}".format(fullname)) + def get_code(self, fullname): """Get a code object from source.""" source_path = self.source_path(fullname) @@ -388,15 +391,16 @@ class PyPycLoader(PyLoader): """ - @module_for_loader - def load_module(self, module): - """Load a module from source or bytecode.""" - name = module.__name__ - source_path = self.source_path(name) - bytecode_path = self.bytecode_path(name) - # get_code can worry about no viable paths existing. - module.__file__ = source_path or bytecode_path - return self._load_module(module) + def get_filename(self, fullname): + """Return the source or bytecode file path.""" + path = self.source_path(fullname) + if path is not None: + return path + path = self.bytecode_path(fullname) + if path is not None: + return path + raise ImportError("no source or bytecode path available for " + "{0!r}".format(fullname)) def get_code(self, fullname): """Get a code object from source or bytecode.""" diff --git a/Lib/importlib/abc.py b/Lib/importlib/abc.py index 7b89d0b..c912280 100644 --- a/Lib/importlib/abc.py +++ b/Lib/importlib/abc.py @@ -76,7 +76,23 @@ InspectLoader.register(machinery.BuiltinImporter) InspectLoader.register(machinery.FrozenImporter) -class PyLoader(_bootstrap.PyLoader, ResourceLoader, InspectLoader): +class ExecutionLoader(InspectLoader): + + """Abstract base class for loaders that wish to support the execution of + modules as scripts. + + This ABC represents one of the optional protocols specified in PEP 302. + + """ + + @abc.abstractmethod + def get_filename(self, fullname:str) -> str: + """Abstract method which should return the value that __file__ is to be + set to.""" + raise NotImplementedError + + +class PyLoader(_bootstrap.PyLoader, ResourceLoader, ExecutionLoader): """Abstract base class to assist in loading source code by requiring only back-end storage methods to be implemented. diff --git a/Lib/importlib/test/source/test_abc_loader.py b/Lib/importlib/test/source/test_abc_loader.py index 6465d26..8c69cfd 100644 --- a/Lib/importlib/test/source/test_abc_loader.py +++ b/Lib/importlib/test/source/test_abc_loader.py @@ -218,6 +218,21 @@ class PyLoaderInterfaceTests(unittest.TestCase): with util.uncache(name), self.assertRaises(ImportError): mock.load_module(name) + def test_get_filename_with_source_path(self): + # get_filename() should return what source_path() returns. + name = 'mod' + path = os.path.join('path', 'to', 'source') + mock = PyLoaderMock({name: path}) + with util.uncache(name): + self.assertEqual(mock.get_filename(name), path) + + def test_get_filename_no_source_path(self): + # get_filename() should raise ImportError if source_path returns None. + name = 'mod' + mock = PyLoaderMock({name: None}) + with util.uncache(name), self.assertRaises(ImportError): + mock.get_filename(name) + class PyLoaderGetSourceTests(unittest.TestCase): @@ -283,6 +298,38 @@ class PyPycLoaderTests(PyLoaderTests): super().test_unloadable() +class PyPycLoaderInterfaceTests(unittest.TestCase): + + """Test for the interface of importlib.abc.PyPycLoader.""" + + def get_filename_check(self, src_path, bc_path, expect): + name = 'mod' + mock = PyPycLoaderMock({name: src_path}, {name: {'path': bc_path}}) + with util.uncache(name): + assert mock.source_path(name) == src_path + assert mock.bytecode_path(name) == bc_path + self.assertEqual(mock.get_filename(name), expect) + + def test_filename_with_source_bc(self): + # When source and bytecode paths present, return the source path. + self.get_filename_check('source_path', 'bc_path', 'source_path') + + def test_filename_with_source_no_bc(self): + # With source but no bc, return source path. + self.get_filename_check('source_path', None, 'source_path') + + def test_filename_with_no_source_bc(self): + # With not source but bc, return the bc path. + self.get_filename_check(None, 'bc_path', 'bc_path') + + def test_filename_with_no_source_or_bc(self): + # With no source or bc, raise ImportError. + name = 'mod' + mock = PyPycLoaderMock({name: None}, {name: {'path': None}}) + with util.uncache(name), self.assertRaises(ImportError): + mock.get_filename(name) + + class SkipWritingBytecodeTests(unittest.TestCase): """Test that bytecode is properly handled based on @@ -421,9 +468,9 @@ class MissingPathsTests(unittest.TestCase): def test_main(): from test.support import run_unittest run_unittest(PyLoaderTests, PyLoaderInterfaceTests, PyLoaderGetSourceTests, - PyPycLoaderTests, SkipWritingBytecodeTests, - RegeneratedBytecodeTests, BadBytecodeFailureTests, - MissingPathsTests) + PyPycLoaderTests, PyPycLoaderInterfaceTests, + SkipWritingBytecodeTests, RegeneratedBytecodeTests, + BadBytecodeFailureTests, MissingPathsTests) if __name__ == '__main__': diff --git a/Lib/importlib/test/test_abc.py b/Lib/importlib/test/test_abc.py index 6e09534..5229ba4 100644 --- a/Lib/importlib/test/test_abc.py +++ b/Lib/importlib/test/test_abc.py @@ -53,9 +53,15 @@ class InspectLoader(InheritanceTests, unittest.TestCase): machinery.FrozenImporter] +class ExecutionLoader(InheritanceTests, unittest.TestCase): + + superclasses = [abc.InspectLoader] + subclasses = [abc.PyLoader] + + class PyLoader(InheritanceTests, unittest.TestCase): - superclasses = [abc.Loader, abc.ResourceLoader, abc.InspectLoader] + superclasses = [abc.Loader, abc.ResourceLoader, abc.ExecutionLoader] class PyPycLoader(InheritanceTests, unittest.TestCase): |