diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 3b2a288fa22..92fb7875de1 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -175,11 +175,12 @@ are also provided to help in implementing the core ABCs. An abstract base class for a :term:`loader` which implements the optional :pep:`302` protocol for loaders which inspect modules. - .. method:: is_package(fullname) + .. method:: get_code(fullname) - An abstract method to return a true value if the module is a package, a - false value otherwise. :exc:`ImportError` is raised if the - :term:`loader` cannot find the module. + An abstract method to return the :class:`code` object for a module. + :keyword:`None` is returned if the module does not have a code object + (e.g. built-in module). :exc:`ImportError` is raised if loader cannot + find the requested module. .. method:: get_source(fullname) @@ -188,12 +189,11 @@ are also provided to help in implementing the core ABCs. source is available (e.g. a built-in module). Raises :exc:`ImportError` if the loader cannot find the module specified. - .. method:: get_code(fullname) + .. method:: is_package(fullname) - An abstract method to return the :class:`code` object for a module. - :keyword:`None` is returned if the module does not have a code object - (e.g. built-in module). :exc:`ImportError` is raised if loader cannot - find the requested module. + An abstract method to return a true value if the module is a package, a + false value otherwise. :exc:`ImportError` is raised if the + :term:`loader` cannot find the module. .. class:: PyLoader @@ -274,7 +274,8 @@ find and load modules. An :term:`importer` for built-in modules. All known built-in modules are listed in :data:`sys.builtin_module_names`. This class implements the - :class:`importlib.abc.Finder` and :class:`importlib.abc.Loader` ABCs. + :class:`importlib.abc.Finder` and :class:`importlib.abc.InspectLoader` + ABCs. Only class methods are defined by this class to alleviate the need for instantiation. diff --git a/Lib/importlib/NOTES b/Lib/importlib/NOTES index 2220a166d82..44cfc09bd63 100644 --- a/Lib/importlib/NOTES +++ b/Lib/importlib/NOTES @@ -1,9 +1,7 @@ to do ///// -* Fill in docstrings for ABCs. - -* Implement InspectLoader for BuiltinImporter and FrozenImporter. +* Implement InspectLoader for FrozenImporter. + Expose function to see if a frozen module is a package. diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py index e94c1d2ddd1..736e1b6665e 100644 --- a/Lib/importlib/_bootstrap.py +++ b/Lib/importlib/_bootstrap.py @@ -173,6 +173,16 @@ def _check_name(method): return inner +def _requires_builtin(fxn): + """Decorator to verify the named module is built-in.""" + def wrapper(self, fullname): + if fullname not in sys.builtin_module_names: + raise ImportError("{0} is not a built-in module".format(fullname)) + return fxn(self, fullname) + _wrap(wrapper, fxn) + return wrapper + + def _suffix_list(suffix_type): """Return a list of file suffixes based on the imp file type.""" return [suffix[0] for suffix in imp.get_suffixes() @@ -204,10 +214,9 @@ class BuiltinImporter: @classmethod @set_package @set_loader + @_requires_builtin def load_module(cls, fullname): """Load a built-in module.""" - if fullname not in sys.builtin_module_names: - raise ImportError("{0} is not a built-in module".format(fullname)) is_reload = fullname in sys.modules try: return imp.init_builtin(fullname) @@ -216,6 +225,24 @@ class BuiltinImporter: del sys.modules[fullname] raise + @classmethod + @_requires_builtin + def get_code(cls, fullname): + """Return None as built-in modules do not have code objects.""" + return None + + @classmethod + @_requires_builtin + def get_source(cls, fullname): + """Return None as built-in modules do not have source code.""" + return None + + @classmethod + @_requires_builtin + def is_package(cls, fullname): + """Return None as built-in module are never packages.""" + return False + class FrozenImporter: diff --git a/Lib/importlib/abc.py b/Lib/importlib/abc.py index 6b00569ef17..c9c542ad1fa 100644 --- a/Lib/importlib/abc.py +++ b/Lib/importlib/abc.py @@ -14,7 +14,6 @@ class Loader(metaclass=abc.ABCMeta): """Abstract method which when implemented should load a module.""" raise NotImplementedError -Loader.register(machinery.BuiltinImporter) Loader.register(machinery.FrozenImporter) @@ -75,6 +74,8 @@ class InspectLoader(Loader): module.""" return NotImplementedError +InspectLoader.register(machinery.BuiltinImporter) + class PyLoader(_bootstrap.PyLoader, InspectLoader): diff --git a/Lib/importlib/test/builtin/test_finder.py b/Lib/importlib/test/builtin/test_finder.py index eacc36c27a5..ef8e8fe67ab 100644 --- a/Lib/importlib/test/builtin/test_finder.py +++ b/Lib/importlib/test/builtin/test_finder.py @@ -1,6 +1,7 @@ from importlib import machinery from .. import abc from .. import util +from . import util as builtin_util import sys import unittest @@ -9,13 +10,11 @@ class FinderTests(abc.FinderTests): """Test find_module() for built-in modules.""" - assert 'errno' in sys.builtin_module_names - name = 'errno' - def test_module(self): # Common case. - with util.uncache(self.name): - self.assert_(machinery.BuiltinImporter.find_module(self.name)) + with util.uncache(builtin_util.NAME): + found = machinery.BuiltinImporter.find_module(builtin_util.NAME) + self.assert_(found) def test_package(self): # Built-in modules cannot be a package. @@ -40,8 +39,9 @@ class FinderTests(abc.FinderTests): def test_ignore_path(self): # The value for 'path' should always trigger a failed import. - with util.uncache(self.name): - loader = machinery.BuiltinImporter.find_module(self.name, ['pkg']) + with util.uncache(builtin_util.NAME): + loader = machinery.BuiltinImporter.find_module(builtin_util.NAME, + ['pkg']) self.assert_(loader is None) diff --git a/Lib/importlib/test/builtin/test_loader.py b/Lib/importlib/test/builtin/test_loader.py index 8a69d049ff1..cfedb5d3c7f 100644 --- a/Lib/importlib/test/builtin/test_loader.py +++ b/Lib/importlib/test/builtin/test_loader.py @@ -2,6 +2,7 @@ import importlib from importlib import machinery from .. import abc from .. import util +from . import util as builtin_util import sys import types @@ -12,9 +13,6 @@ class LoaderTests(abc.LoaderTests): """Test load_module() for built-in modules.""" - assert 'errno' in sys.builtin_module_names - name = 'errno' - verification = {'__name__': 'errno', '__package__': '', '__loader__': machinery.BuiltinImporter} @@ -30,8 +28,8 @@ class LoaderTests(abc.LoaderTests): def test_module(self): # Common case. - with util.uncache(self.name): - module = self.load_module(self.name) + with util.uncache(builtin_util.NAME): + module = self.load_module(builtin_util.NAME) self.verify(module) def test_package(self): @@ -48,9 +46,9 @@ class LoaderTests(abc.LoaderTests): def test_module_reuse(self): # Test that the same module is used in a reload. - with util.uncache(self.name): - module1 = self.load_module(self.name) - module2 = self.load_module(self.name) + with util.uncache(builtin_util.NAME): + module1 = self.load_module(builtin_util.NAME) + module2 = self.load_module(builtin_util.NAME) self.assert_(module1 is module2) def test_unloadable(self): @@ -65,9 +63,36 @@ class LoaderTests(abc.LoaderTests): self.assertRaises(ImportError, self.load_module, 'importlib') +class InspectLoaderTests(unittest.TestCase): + + """Tests for InspectLoader methods for BuiltinImporter.""" + + def test_get_code(self): + # There is no code object. + result = machinery.BuiltinImporter.get_code(builtin_util.NAME) + self.assert_(result is None) + + def test_get_source(self): + # There is no source. + result = machinery.BuiltinImporter.get_source(builtin_util.NAME) + self.assert_(result is None) + + def test_is_package(self): + # Cannot be a package. + result = machinery.BuiltinImporter.is_package(builtin_util.NAME) + self.assert_(not result) + + def test_not_builtin(self): + # Modules not built-in should raise ImportError. + for meth_name in ('get_code', 'get_source', 'is_package'): + method = getattr(machinery.BuiltinImporter, meth_name) + self.assertRaises(ImportError, method, builtin_util.BAD_NAME) + + + def test_main(): from test.support import run_unittest - run_unittest(LoaderTests) + run_unittest(LoaderTests, InspectLoaderTests) if __name__ == '__main__': diff --git a/Lib/importlib/test/builtin/util.py b/Lib/importlib/test/builtin/util.py new file mode 100644 index 00000000000..5704699ee23 --- /dev/null +++ b/Lib/importlib/test/builtin/util.py @@ -0,0 +1,7 @@ +import sys + +assert 'errno' in sys.builtin_module_names +NAME = 'errno' + +assert 'importlib' not in sys.builtin_module_names +BAD_NAME = 'importlib'