Skip to content

Commit 5fb7029

Browse files
authored
Merge pull request #323 from python/bugfix/300-entry-points-by-index
EntryPoints compatibility
2 parents f794723 + 7afc501 commit 5fb7029

File tree

3 files changed

+137
-1
lines changed

3 files changed

+137
-1
lines changed

CHANGES.rst

+19
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
v4.4.0
2+
=======
3+
4+
* #300: Restore compatibility in the result from
5+
``Distribution.entry_points`` (``EntryPoints``) to honor
6+
expectations in older implementations and issuing
7+
deprecation warnings for these cases:
8+
9+
- ``EntryPoints`` objects are once again mutable, allowing
10+
for ``sort()`` and other list-based mutation operations.
11+
Avoid deprecation warnings by casting to a
12+
mutable sequence (e.g.
13+
``list(dist.entry_points).sort()``).
14+
15+
- ``EntryPoints`` results once again allow
16+
for access by index. To avoid deprecation warnings,
17+
cast the result to a Sequence first
18+
(e.g. ``tuple(dist.entry_points)[0]``).
19+
120
v4.3.1
221
=======
322

importlib_metadata/__init__.py

+102-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,100 @@ def matches(self, **params):
209209
return all(map(operator.eq, params.values(), attrs))
210210

211211

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

tests/test_api.py

+16
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,22 @@ def test_entry_points_dict_construction(self):
133133
assert expected.category is DeprecationWarning
134134
assert "Construction of dict of EntryPoints is deprecated" in str(expected)
135135

136+
def test_entry_points_by_index(self):
137+
"""
138+
Prior versions of Distribution.entry_points would return a
139+
tuple that allowed access by index.
140+
Capture this now deprecated use-case
141+
See python/importlib_metadata#300 and bpo-44246.
142+
"""
143+
eps = distribution('distinfo-pkg').entry_points
144+
with warnings.catch_warnings(record=True) as caught:
145+
eps[0]
146+
147+
# check warning
148+
expected = next(iter(caught))
149+
assert expected.category is DeprecationWarning
150+
assert "Accessing entry points by index is deprecated" in str(expected)
151+
136152
def test_entry_points_groups_getitem(self):
137153
"""
138154
Prior versions of entry_points() returned a dict. Ensure

0 commit comments

Comments
 (0)