[Python-checkins] bpo-44246: Restore compatibility in entry_points (GH-26468)

jaraco webhook-mailer at python.org
Mon May 31 12:19:50 EDT 2021


https://github.com/python/cpython/commit/c34ed08d975fb7daa7b329f7c631647782290393
commit: c34ed08d975fb7daa7b329f7c631647782290393
branch: main
author: Jason R. Coombs <jaraco at jaraco.com>
committer: jaraco <jaraco at jaraco.com>
date: 2021-05-31T12:19:42-04:00
summary:

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

files:
A Misc/NEWS.d/next/Library/2021-05-31-11-34-56.bpo-44246.yHAkF0.rst
M Lib/importlib/metadata/__init__.py
M Lib/test/test_importlib/test_metadata_api.py

diff --git a/Lib/importlib/metadata/__init__.py b/Lib/importlib/metadata/__init__.py
index 2e3403e5a565c..d2116cfe6f7d6 100644
--- a/Lib/importlib/metadata/__init__.py
+++ b/Lib/importlib/metadata/__init__.py
@@ -204,7 +204,100 @@ def matches(self, **params):
         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 @@ def __getitem__(self, name):  # -> EntryPoint:
         """
         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 825edc10f121a..3506493463d82 100644
--- a/Lib/test/test_importlib/test_metadata_api.py
+++ b/Lib/test/test_importlib/test_metadata_api.py
@@ -130,6 +130,22 @@ def test_entry_points_dict_construction(self):
         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 0000000000000..b93f8b02dc476
--- /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]``).



More information about the Python-checkins mailing list