From: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com> Date: Wed, 11 Dec 2019 01:47:06 +0000 (-0800) Subject: bpo-39022, bpo-38594: Sync with importlib_metadata 1.3 (GH-17568) (GH-17569) X-Git-Tag: v3.8.1~13 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b738237d6792acba85b1f6e6c8993a812c7fd815;p=thirdparty%2FPython%2Fcpython.git bpo-39022, bpo-38594: Sync with importlib_metadata 1.3 (GH-17568) (GH-17569) * bpo-39022, bpo-38594: Sync with importlib_metadata 1.3 including improved docs for custom finders and better serialization support in EntryPoints. * 📜🤖 Added by blurb_it. * Correct module reference (cherry picked from commit b7a0109cd2bafaa21a4d50aad307e901c68f9156) Co-authored-by: Jason R. Coombs --- diff --git a/Doc/library/importlib.metadata.rst b/Doc/library/importlib.metadata.rst index 4a4a8f7dfa0f..dc6b66ca384d 100644 --- a/Doc/library/importlib.metadata.rst +++ b/Doc/library/importlib.metadata.rst @@ -216,9 +216,9 @@ system `finders`_. To find a distribution package's metadata, ``importlib.metadata`` queries the list of `meta path finders`_ on `sys.meta_path`_. -By default ``importlib.metadata`` installs a finder for distribution packages -found on the file system. This finder doesn't actually find any *packages*, -but it can find the packages' metadata. +The default ``PathFinder`` for Python includes a hook that calls into +``importlib.metadata.MetadataPathFinder`` for finding distributions +loaded from typical file-system-based paths. The abstract class :py:class:`importlib.abc.MetaPathFinder` defines the interface expected of finders by Python's import system. @@ -239,9 +239,9 @@ properties indicating the path to search and names to match and may supply other relevant context. What this means in practice is that to support finding distribution package -metadata in locations other than the file system, you should derive from -``Distribution`` and implement the ``load_metadata()`` method. Then from -your finder, return instances of this derived ``Distribution`` in the +metadata in locations other than the file system, subclass +``Distribution`` and implement the abstract methods. Then from +a custom finder, return instances of this derived ``Distribution`` in the ``find_distributions()`` method. diff --git a/Lib/importlib/metadata.py b/Lib/importlib/metadata.py index 8cb45ec1ef3a..53f9fb593466 100644 --- a/Lib/importlib/metadata.py +++ b/Lib/importlib/metadata.py @@ -37,7 +37,8 @@ class PackageNotFoundError(ModuleNotFoundError): """The package was not found.""" -class EntryPoint(collections.namedtuple('EntryPointBase', 'name value group')): +class EntryPoint( + collections.namedtuple('EntryPointBase', 'name value group')): """An entry point as defined by Python packaging conventions. See `the packaging docs on entry points @@ -107,6 +108,12 @@ class EntryPoint(collections.namedtuple('EntryPointBase', 'name value group')): """ return iter((self.name, self)) + def __reduce__(self): + return ( + self.__class__, + (self.name, self.value, self.group), + ) + class PackagePath(pathlib.PurePosixPath): """A reference to a path in a package""" @@ -334,10 +341,21 @@ class DistributionFinder(MetaPathFinder): """ class Context: + """ + Keyword arguments presented by the caller to + ``distributions()`` or ``Distribution.discover()`` + to narrow the scope of a search for distributions + in all DistributionFinders. + + Each DistributionFinder may expect any parameters + and should attempt to honor the canonical + parameters defined below when appropriate. + """ name = None """ Specific name for which a distribution finder should match. + A name of ``None`` matches all distributions. """ def __init__(self, **kwargs): @@ -347,6 +365,9 @@ class DistributionFinder(MetaPathFinder): def path(self): """ The path that a distribution finder should search. + + Typically refers to Python package paths and defaults + to ``sys.path``. """ return vars(self).get('path', sys.path) diff --git a/Lib/test/test_importlib/test_main.py b/Lib/test/test_importlib/test_main.py index 4d5b1273d9d1..c5f1dbbae325 100644 --- a/Lib/test/test_importlib/test_main.py +++ b/Lib/test/test_importlib/test_main.py @@ -1,6 +1,8 @@ # coding: utf-8 import re +import json +import pickle import textwrap import unittest import importlib.metadata @@ -181,3 +183,34 @@ class DirectoryTest(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase): with self.add_sys_path(egg): with self.assertRaises(PackageNotFoundError): version('foo') + + +class TestEntryPoints(unittest.TestCase): + def __init__(self, *args): + super(TestEntryPoints, self).__init__(*args) + self.ep = importlib.metadata.EntryPoint('name', 'value', 'group') + + def test_entry_point_pickleable(self): + revived = pickle.loads(pickle.dumps(self.ep)) + assert revived == self.ep + + def test_immutable(self): + """EntryPoints should be immutable""" + with self.assertRaises(AttributeError): + self.ep.name = 'badactor' + + def test_repr(self): + assert 'EntryPoint' in repr(self.ep) + assert 'name=' in repr(self.ep) + assert "'name'" in repr(self.ep) + + def test_hashable(self): + """EntryPoints should be hashable""" + hash(self.ep) + + def test_json_dump(self): + """ + json should not expect to be able to dump an EntryPoint + """ + with self.assertRaises(Exception): + json.dumps(self.ep) diff --git a/Misc/NEWS.d/next/Library/2019-12-10-23-34-48.bpo-39022.QDtIxI.rst b/Misc/NEWS.d/next/Library/2019-12-10-23-34-48.bpo-39022.QDtIxI.rst new file mode 100644 index 000000000000..4af21be60742 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-12-10-23-34-48.bpo-39022.QDtIxI.rst @@ -0,0 +1 @@ +Update importliib.metadata to include improvements from importlib_metadata 1.3 including better serialization of EntryPoints and improved documentation for custom finders. \ No newline at end of file