From 8a9080feffc757360d0d73e4173189586098ffb5 Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Thu, 2 Aug 2012 21:26:03 +1000 Subject: [PATCH] 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 | 77 ++++++++++------- Lib/importlib/abc.py | 82 +++++++++++++------ .../test_importlib/source/test_abc_loader.py | 23 ++++-- Lib/test/test_importlib/test_abc.py | 7 +- Misc/NEWS | 3 + 5 files changed, 127 insertions(+), 65 deletions(-) diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 05b18d3a4b1..49ad46f1e29 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. - - .. method:: find_loader(self, fullname): - - 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()`. - - .. versionadded: 3.3 - - .. 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. + 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:: invalidate_caches() - 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 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. + + .. 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. + + +.. class:: MetaPathFinder(Finder) + + 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 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 7fcf2de438f..5e71758afcf 100644 --- a/Lib/importlib/abc.py +++ b/Lib/importlib/abc.py @@ -23,6 +23,61 @@ def _register(abstract_cls, *classes): abstract_cls.register(frozen_cls) +class Finder(metaclass=abc.ABCMeta): + + """Common abstract base class for import finders. + + 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 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 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. + """ + raise NotImplementedError + +_register(PathEntryFinder, machinery.FileFinder) + + class Loader(metaclass=abc.ABCMeta): """Abstract base class for import loaders.""" @@ -40,33 +95,6 @@ class Loader(metaclass=abc.ABCMeta): raise NotImplementedError -class Finder(metaclass=abc.ABCMeta): - - """Abstract base class for import finders.""" - - @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()`. - """ - raise NotImplementedError - - @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. - """ - raise NotImplementedError - -_register(Finder, machinery.BuiltinImporter, machinery.FrozenImporter, - machinery.PathFinder, machinery.FileFinder) - - class ResourceLoader(Loader): """Abstract base class for loaders which can return data from their diff --git a/Lib/test/test_importlib/source/test_abc_loader.py b/Lib/test/test_importlib/source/test_abc_loader.py index 9db48821771..0d912b64692 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 MetaPathFinder(abc.MetaPathFinder): + def find_module(self, fullname, path): + super().find_module(fullname, path) + + 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 Finder(abc.Finder): - def find_module(self, _): - super().find_module(_) - - def find_loader(self, _): - super().find_loader(_) - 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 008bd21ff88..90f38b82d2d 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 f4dae73f4ab..58a331d1c3c 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.