From 8a9080feffc757360d0d73e4173189586098ffb5 Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Thu, 2 Aug 2012 21:26:03 +1000 Subject: Issue #15502: Bring the importlib ABCs into line with the current state of the import protocols given PEP 420. Original patch by Eric Snow. --- Doc/library/importlib.rst | 69 +++++++++++++--------- Lib/importlib/abc.py | 70 ++++++++++++++++------- Lib/test/test_importlib/source/test_abc_loader.py | 21 +++++-- Lib/test/test_importlib/test_abc.py | 7 ++- Misc/NEWS | 3 + 5 files changed, 116 insertions(+), 54 deletions(-) diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 05b18d3..49ad46f 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -125,32 +125,49 @@ are also provided to help in implementing the core ABCs. .. class:: Finder - An abstract base class representing a :term:`finder`. - See :pep:`302` for the exact definition for a finder. + An abstract base class representing a :term:`finder`. Finder + implementations should derive from (or register with) the more specific + :class:`MetaPathFinder` or :class:`PathEntryFinder` ABCs. - .. method:: find_loader(self, fullname): + .. method:: invalidate_caches() - An abstract method for finding a :term:`loader` for the specified - module. Returns a 2-tuple of ``(loader, portion)`` where portion is a - sequence of file system locations contributing to part of a namespace - package. The sequence may be empty. When present, `find_loader()` is - preferred over `find_module()`. + An optional method which, when called, should invalidate any internal + cache used by the finder. Used by :func:`invalidate_caches()` when + invalidating the caches of all cached finders. - .. versionadded: 3.3 + .. versionchanged:: 3.3 + The API signatures for meta path finders and path entry finders + were separated by PEP 420. Accordingly, the Finder ABC no + longer requires implementation of a ``find_module()`` method. - .. method:: find_module(fullname, path=None) - An abstract method for finding a :term:`loader` for the specified - module. If the :term:`finder` is found on :data:`sys.meta_path` and the - module to be searched for is a subpackage or module then *path* will - be the value of :attr:`__path__` from the parent package. If a loader - cannot be found, ``None`` is returned. +.. class:: MetaPathFinder(Finder) - .. method:: invalidate_caches() + An abstract base class representing a :term:`meta path finder`. + + .. versionadded:: 3.3 + + .. method:: find_module(fullname, path) + + An abstract method for finding a :term:`loader` for the specified + module. If this is a top-level import, *path* will be ``None``. + Otheriwse, this is a search for a subpackage or module and *path* + will be the value of :attr:`__path__` from the parent + package. If a loader cannot be found, ``None`` is returned. + + +.. class:: PathEntryFinder(Finder) + + An abstract base class representing a :term:`path entry finder`. + + .. versionadded:: 3.3 + + .. method:: find_loader(self, fullname): - An optional method which, when called, should invalidate any internal - cache used by the finder. Used by :func:`invalidate_caches()` when - invalidating the caches of all cached finders. + An abstract method for finding a :term:`loader` for the specified + module. Returns a 2-tuple of ``(loader, portion)`` where portion is a + sequence of file system locations contributing to part of a namespace + package. The sequence may be empty. .. class:: Loader @@ -569,8 +586,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.InspectLoader` - ABCs. + :class:`importlib.abc.MetaPathFinder` and + :class:`importlib.abc.InspectLoader` ABCs. Only class methods are defined by this class to alleviate the need for instantiation. @@ -579,8 +596,8 @@ find and load modules. .. class:: FrozenImporter An :term:`importer` for frozen modules. This class implements the - :class:`importlib.abc.Finder` and :class:`importlib.abc.InspectLoader` - ABCs. + :class:`importlib.abc.MetaPathFinder` and + :class:`importlib.abc.InspectLoader` ABCs. Only class methods are defined by this class to alleviate the need for instantiation. @@ -589,7 +606,7 @@ find and load modules. .. class:: PathFinder :term:`Finder` for :data:`sys.path`. This class implements the - :class:`importlib.abc.Finder` ABC. + :class:`importlib.abc.MetaPathFinder` ABC. This class does not perfectly mirror the semantics of :keyword:`import` in terms of :data:`sys.path`. No implicit path hooks are assumed for @@ -616,8 +633,8 @@ find and load modules. .. class:: FileFinder(path, \*loader_details) - A concrete implementation of :class:`importlib.abc.Finder` which caches - results from the file system. + A concrete implementation of :class:`importlib.abc.PathEntryFinder` which + caches results from the file system. The *path* argument is the directory for which the finder is in charge of searching. diff --git a/Lib/importlib/abc.py b/Lib/importlib/abc.py index 7fcf2de..5e71758 100644 --- a/Lib/importlib/abc.py +++ b/Lib/importlib/abc.py @@ -23,48 +23,76 @@ def _register(abstract_cls, *classes): abstract_cls.register(frozen_cls) -class Loader(metaclass=abc.ABCMeta): +class Finder(metaclass=abc.ABCMeta): - """Abstract base class for import loaders.""" + """Common abstract base class for import finders. - @abc.abstractmethod - def load_module(self, fullname): - """Abstract method which when implemented should load a module. - The fullname is a str.""" + Finder implementations should derive from the more specific + MetaPathFinder or PathEntryFinder ABCs rather than directly from Finder. + """ + + def find_module(self, fullname, path=None): + """An optional legacy method that should find a module. + The fullname is a str and the optional path is a str or None. + Returns a Loader object. + + The path finder will use this method only if find_loader() does + not exist. It may optionally be implemented for compatibility + with legacy third party reimplementations of the import system. + """ raise NotImplementedError + # invalidate_caches() is a completely optional method, so no default + # implementation is provided. See the docs for details. + + +class MetaPathFinder(Finder): + + """Abstract base class for import finders on sys.meta_path.""" + @abc.abstractmethod - def module_repr(self, module): - """Abstract method which when implemented calculates and returns the - given module's repr.""" + def find_module(self, fullname, path): + """Abstract method which when implemented should find a module. + The fullname is a str and the path is a str or None. + Returns a Loader object. + """ raise NotImplementedError +_register(MetaPathFinder, machinery.BuiltinImporter, machinery.FrozenImporter, + machinery.PathFinder) -class Finder(metaclass=abc.ABCMeta): - """Abstract base class for import finders.""" +class PathEntryFinder(Finder): + + """Abstract base class for path entry finders used by PathFinder.""" @abc.abstractmethod def find_loader(self, fullname): """Abstract method which when implemented returns a module loader. The fullname is a str. Returns a 2-tuple of (Loader, portion) where portion is a sequence of file system locations contributing to part of - a namespace package. The sequence may be empty. When present, - `find_loader()` is preferred over `find_module()`. + a namespace package. The sequence may be empty. """ raise NotImplementedError +_register(PathEntryFinder, machinery.FileFinder) + + +class Loader(metaclass=abc.ABCMeta): + + """Abstract base class for import loaders.""" + @abc.abstractmethod - def find_module(self, fullname, path=None): - """Abstract method which when implemented should find a module. - The fullname is a str and the optional path is a str or None. - Returns a Loader object. This method is only called if - `find_loader()` is not present. - """ + def load_module(self, fullname): + """Abstract method which when implemented should load a module. + The fullname is a str.""" raise NotImplementedError -_register(Finder, machinery.BuiltinImporter, machinery.FrozenImporter, - machinery.PathFinder, machinery.FileFinder) + @abc.abstractmethod + def module_repr(self, module): + """Abstract method which when implemented calculates and returns the + given module's repr.""" + raise NotImplementedError class ResourceLoader(Loader): diff --git a/Lib/test/test_importlib/source/test_abc_loader.py b/Lib/test/test_importlib/source/test_abc_loader.py index 9db4882..0d912b6 100644 --- a/Lib/test/test_importlib/source/test_abc_loader.py +++ b/Lib/test/test_importlib/source/test_abc_loader.py @@ -778,23 +778,32 @@ class SourceLoaderGetSourceTests(unittest.TestCase): expect = io.IncrementalNewlineDecoder(None, True).decode(source) self.assertEqual(mock.get_source(name), expect) + class AbstractMethodImplTests(unittest.TestCase): """Test the concrete abstractmethod implementations.""" - class Loader(abc.Loader): - def load_module(self, fullname): - super().load_module(fullname) - def module_repr(self, module): - super().module_repr(module) + class MetaPathFinder(abc.MetaPathFinder): + def find_module(self, fullname, path): + super().find_module(fullname, path) - class Finder(abc.Finder): + class PathEntryFinder(abc.PathEntryFinder): def find_module(self, _): super().find_module(_) def find_loader(self, _): super().find_loader(_) + class Finder(abc.Finder): + def find_module(self, fullname, path): + super().find_module(fullname, path) + + class Loader(abc.Loader): + def load_module(self, fullname): + super().load_module(fullname) + def module_repr(self, module): + super().module_repr(module) + class ResourceLoader(Loader, abc.ResourceLoader): def get_data(self, _): super().get_data(_) diff --git a/Lib/test/test_importlib/test_abc.py b/Lib/test/test_importlib/test_abc.py index 008bd21..90f38b8 100644 --- a/Lib/test/test_importlib/test_abc.py +++ b/Lib/test/test_importlib/test_abc.py @@ -30,11 +30,16 @@ class InheritanceTests: "{0} is not a superclass of {1}".format(superclass, self.__test)) -class Finder(InheritanceTests, unittest.TestCase): +class MetaPathFinder(InheritanceTests, unittest.TestCase): + superclasses = [abc.Finder] subclasses = [machinery.BuiltinImporter, machinery.FrozenImporter, machinery.PathFinder] +class PathEntryFinder(InheritanceTests, unittest.TestCase): + + superclasses = [abc.Finder] + subclasses = [machinery.FileFinder] class Loader(InheritanceTests, unittest.TestCase): diff --git a/Misc/NEWS b/Misc/NEWS index f4dae73..58a331d 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -72,6 +72,9 @@ Core and Builtins Library ------- +- Issue #15502: Bring the importlib ABCs into line with the current state + of the import protocols given PEP 420. Original patch by Eric Snow. + - Issue #15499: Launching a webbrowser in Unix used to sleep for a few seconds. Original patch by Anton Barkovsky. -- cgit v0.12