summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason R. Coombs <jaraco@jaraco.com>2021-05-31 16:19:42 (GMT)
committerGitHub <noreply@github.com>2021-05-31 16:19:42 (GMT)
commitc34ed08d975fb7daa7b329f7c631647782290393 (patch)
tree6f6456952acac2ba522a559fae2570e769c322b4
parent410b70d39d9d77384f8b8597560f6731530149ca (diff)
downloadcpython-c34ed08d975fb7daa7b329f7c631647782290393.zip
cpython-c34ed08d975fb7daa7b329f7c631647782290393.tar.gz
cpython-c34ed08d975fb7daa7b329f7c631647782290393.tar.bz2
bpo-44246: Restore compatibility in entry_points (GH-26468)
* bpo-44246: Entry points performance improvements. From importlib_metadata 4.3.1. * bpo-44246: Sync with importlib_metadata 4.4
-rw-r--r--Lib/importlib/metadata/__init__.py103
-rw-r--r--Lib/test/test_importlib/test_metadata_api.py16
-rw-r--r--Misc/NEWS.d/next/Library/2021-05-31-11-34-56.bpo-44246.yHAkF0.rst7
3 files changed, 125 insertions, 1 deletions
diff --git a/Lib/importlib/metadata/__init__.py b/Lib/importlib/metadata/__init__.py
index 2e3403e..d2116cf 100644
--- a/Lib/importlib/metadata/__init__.py
+++ b/Lib/importlib/metadata/__init__.py
@@ -204,7 +204,100 @@ class EntryPoint(
return all(map(operator.eq, params.values(), attrs))
-class EntryPoints(tuple):
+class DeprecatedList(list):
+ """
+ Allow an otherwise immutable object to implement mutability
+ for compatibility.
+
+ >>> recwarn = getfixture('recwarn')
+ >>> dl = DeprecatedList(range(3))
+ >>> dl[0] = 1
+ >>> dl.append(3)
+ >>> del dl[3]
+ >>> dl.reverse()
+ >>> dl.sort()
+ >>> dl.extend([4])
+ >>> dl.pop(-1)
+ 4
+ >>> dl.remove(1)
+ >>> dl += [5]
+ >>> dl + [6]
+ [1, 2, 5, 6]
+ >>> dl + (6,)
+ [1, 2, 5, 6]
+ >>> dl.insert(0, 0)
+ >>> dl
+ [0, 1, 2, 5]
+ >>> dl == [0, 1, 2, 5]
+ True
+ >>> dl == (0, 1, 2, 5)
+ True
+ >>> len(recwarn)
+ 1
+ """
+
+ _warn = functools.partial(
+ warnings.warn,
+ "EntryPoints list interface is deprecated. Cast to list if needed.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+
+ def __setitem__(self, *args, **kwargs):
+ self._warn()
+ return super().__setitem__(*args, **kwargs)
+
+ def __delitem__(self, *args, **kwargs):
+ self._warn()
+ return super().__delitem__(*args, **kwargs)
+
+ def append(self, *args, **kwargs):
+ self._warn()
+ return super().append(*args, **kwargs)
+
+ def reverse(self, *args, **kwargs):
+ self._warn()
+ return super().reverse(*args, **kwargs)
+
+ def extend(self, *args, **kwargs):
+ self._warn()
+ return super().extend(*args, **kwargs)
+
+ def pop(self, *args, **kwargs):
+ self._warn()
+ return super().pop(*args, **kwargs)
+
+ def remove(self, *args, **kwargs):
+ self._warn()
+ return super().remove(*args, **kwargs)
+
+ def __iadd__(self, *args, **kwargs):
+ self._warn()
+ return super().__iadd__(*args, **kwargs)
+
+ def __add__(self, other):
+ if not isinstance(other, tuple):
+ self._warn()
+ other = tuple(other)
+ return self.__class__(tuple(self) + other)
+
+ def insert(self, *args, **kwargs):
+ self._warn()
+ return super().insert(*args, **kwargs)
+
+ def sort(self, *args, **kwargs):
+ self._warn()
+ return super().sort(*args, **kwargs)
+
+ def __eq__(self, other):
+ if not isinstance(other, tuple):
+ self._warn()
+ other = tuple(other)
+
+ return tuple(self).__eq__(other)
+
+
+class EntryPoints(DeprecatedList):
"""
An immutable collection of selectable EntryPoint objects.
"""
@@ -215,6 +308,14 @@ class EntryPoints(tuple):
"""
Get the EntryPoint in self matching name.
"""
+ if isinstance(name, int):
+ warnings.warn(
+ "Accessing entry points by index is deprecated. "
+ "Cast to tuple if needed.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return super().__getitem__(name)
try:
return next(iter(self.select(name=name)))
except StopIteration:
diff --git a/Lib/test/test_importlib/test_metadata_api.py b/Lib/test/test_importlib/test_metadata_api.py
index 825edc1..3506493 100644
--- a/Lib/test/test_importlib/test_metadata_api.py
+++ b/Lib/test/test_importlib/test_metadata_api.py
@@ -130,6 +130,22 @@ class APITests(
assert expected.category is DeprecationWarning
assert "Construction of dict of EntryPoints is deprecated" in str(expected)
+ def test_entry_points_by_index(self):
+ """
+ Prior versions of Distribution.entry_points would return a
+ tuple that allowed access by index.
+ Capture this now deprecated use-case
+ See python/importlib_metadata#300 and bpo-44246.
+ """
+ eps = distribution('distinfo-pkg').entry_points
+ with warnings.catch_warnings(record=True) as caught:
+ eps[0]
+
+ # check warning
+ expected = next(iter(caught))
+ assert expected.category is DeprecationWarning
+ assert "Accessing entry points by index is deprecated" in str(expected)
+
def test_entry_points_groups_getitem(self):
# Prior versions of entry_points() returned a dict. Ensure
# that callers using '.__getitem__()' are supported but warned to
diff --git a/Misc/NEWS.d/next/Library/2021-05-31-11-34-56.bpo-44246.yHAkF0.rst b/Misc/NEWS.d/next/Library/2021-05-31-11-34-56.bpo-44246.yHAkF0.rst
new file mode 100644
index 0000000..b93f8b0
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-05-31-11-34-56.bpo-44246.yHAkF0.rst
@@ -0,0 +1,7 @@
+In ``importlib.metadata``, restore compatibility in the result from
+``Distribution.entry_points`` (``EntryPoints``) to honor expectations in
+older implementations and issuing deprecation warnings for these cases: A. ``EntryPoints`` objects are once again mutable, allowing for ``sort()``
+and other list-based mutation operations. Avoid deprecation warnings by
+casting to a mutable sequence (e.g. ``list(dist.entry_points).sort()``). B. ``EntryPoints`` results once again allow for access by index. To avoid
+deprecation warnings, cast the result to a Sequence first (e.g.
+``tuple(dist.entry_points)[0]``).