Skip to content

Commit c34ed08

Browse files
authored
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
1 parent 410b70d commit c34ed08

File tree

3 files changed

+125
-1
lines changed

3 files changed

+125
-1
lines changed

Lib/importlib/metadata/__init__.py

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,100 @@ def matches(self, **params):
204204
return all(map(operator.eq, params.values(), attrs))
205205

206206

207-
class EntryPoints(tuple):
207+
class DeprecatedList(list):
208+
"""
209+
Allow an otherwise immutable object to implement mutability
210+
for compatibility.
211+
212+
>>> recwarn = getfixture('recwarn')
213+
>>> dl = DeprecatedList(range(3))
214+
>>> dl[0] = 1
215+
>>> dl.append(3)
216+
>>> del dl[3]
217+
>>> dl.reverse()
218+
>>> dl.sort()
219+
>>> dl.extend([4])
220+
>>> dl.pop(-1)
221+
4
222+
>>> dl.remove(1)
223+
>>> dl += [5]
224+
>>> dl + [6]
225+
[1, 2, 5, 6]
226+
>>> dl + (6,)
227+
[1, 2, 5, 6]
228+
>>> dl.insert(0, 0)
229+
>>> dl
230+
[0, 1, 2, 5]
231+
>>> dl == [0, 1, 2, 5]
232+
True
233+
>>> dl == (0, 1, 2, 5)
234+
True
235+
>>> len(recwarn)
236+
1
237+
"""
238+
239+
_warn = functools.partial(
240+
warnings.warn,
241+
"EntryPoints list interface is deprecated. Cast to list if needed.",
242+
DeprecationWarning,
243+
stacklevel=2,
244+
)
245+
246+
def __setitem__(self, *args, **kwargs):
247+
self._warn()
248+
return super().__setitem__(*args, **kwargs)
249+
250+
def __delitem__(self, *args, **kwargs):
251+
self._warn()
252+
return super().__delitem__(*args, **kwargs)
253+
254+
def append(self, *args, **kwargs):
255+
self._warn()
256+
return super().append(*args, **kwargs)
257+
258+
def reverse(self, *args, **kwargs):
259+
self._warn()
260+
return super().reverse(*args, **kwargs)
261+
262+
def extend(self, *args, **kwargs):
263+
self._warn()
264+
return super().extend(*args, **kwargs)
265+
266+
def pop(self, *args, **kwargs):
267+
self._warn()
268+
return super().pop(*args, **kwargs)
269+
270+
def remove(self, *args, **kwargs):
271+
self._warn()
272+
return super().remove(*args, **kwargs)
273+
274+
def __iadd__(self, *args, **kwargs):
275+
self._warn()
276+
return super().__iadd__(*args, **kwargs)
277+
278+
def __add__(self, other):
279+
if not isinstance(other, tuple):
280+
self._warn()
281+
other = tuple(other)
282+
return self.__class__(tuple(self) + other)
283+
284+
def insert(self, *args, **kwargs):
285+
self._warn()
286+
return super().insert(*args, **kwargs)
287+
288+
def sort(self, *args, **kwargs):
289+
self._warn()
290+
return super().sort(*args, **kwargs)
291+
292+
def __eq__(self, other):
293+
if not isinstance(other, tuple):
294+
self._warn()
295+
other = tuple(other)
296+
297+
return tuple(self).__eq__(other)
298+
299+
300+
class EntryPoints(DeprecatedList):
208301
"""
209302
An immutable collection of selectable EntryPoint objects.
210303
"""
@@ -215,6 +308,14 @@ def __getitem__(self, name): # -> EntryPoint:
215308
"""
216309
Get the EntryPoint in self matching name.
217310
"""
311+
if isinstance(name, int):
312+
warnings.warn(
313+
"Accessing entry points by index is deprecated. "
314+
"Cast to tuple if needed.",
315+
DeprecationWarning,
316+
stacklevel=2,
317+
)
318+
return super().__getitem__(name)
218319
try:
219320
return next(iter(self.select(name=name)))
220321
except StopIteration:

Lib/test/test_importlib/test_metadata_api.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,22 @@ def test_entry_points_dict_construction(self):
130130
assert expected.category is DeprecationWarning
131131
assert "Construction of dict of EntryPoints is deprecated" in str(expected)
132132

133+
def test_entry_points_by_index(self):
134+
"""
135+
Prior versions of Distribution.entry_points would return a
136+
tuple that allowed access by index.
137+
Capture this now deprecated use-case
138+
See python/importlib_metadata#300 and bpo-44246.
139+
"""
140+
eps = distribution('distinfo-pkg').entry_points
141+
with warnings.catch_warnings(record=True) as caught:
142+
eps[0]
143+
144+
# check warning
145+
expected = next(iter(caught))
146+
assert expected.category is DeprecationWarning
147+
assert "Accessing entry points by index is deprecated" in str(expected)
148+
133149
def test_entry_points_groups_getitem(self):
134150
# Prior versions of entry_points() returned a dict. Ensure
135151
# that callers using '.__getitem__()' are supported but warned to
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
In ``importlib.metadata``, restore compatibility in the result from
2+
``Distribution.entry_points`` (``EntryPoints``) to honor expectations in
3+
older implementations and issuing deprecation warnings for these cases: A. ``EntryPoints`` objects are once again mutable, allowing for ``sort()``
4+
and other list-based mutation operations. Avoid deprecation warnings by
5+
casting to a mutable sequence (e.g. ``list(dist.entry_points).sort()``). B. ``EntryPoints`` results once again allow for access by index. To avoid
6+
deprecation warnings, cast the result to a Sequence first (e.g.
7+
``tuple(dist.entry_points)[0]``).

0 commit comments

Comments
 (0)