Skip to content

Commit d3e9075

Browse files
authored
Merge pull request #451 from python/bugfix/422-deprecate-non-abstract
Deprecate Distribution without concrete methods
2 parents fb69af4 + 41240d0 commit d3e9075

File tree

5 files changed

+55
-1
lines changed

5 files changed

+55
-1
lines changed

Diff for: CHANGES.rst

+7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
v6.5.0
2+
======
3+
4+
* #422: Removed ABC metaclass from ``Distribution`` and instead
5+
deprecated construction of ``Distribution`` objects without
6+
concrete methods.
7+
18
v6.4.1
29
======
310

Diff for: docs/conf.py

+2
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,6 @@
6868
('py:class', 'importlib_metadata._meta._T'),
6969
# Workaround for #435
7070
('py:class', '_T'),
71+
# Other workarounds
72+
('py:class', 'importlib_metadata.DeprecatedNonAbstract'),
7173
]

Diff for: importlib_metadata/__init__.py

+20-1
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,26 @@ def __repr__(self):
348348
return f'<FileHash mode: {self.mode} value: {self.value}>'
349349

350350

351-
class Distribution(metaclass=abc.ABCMeta):
351+
class DeprecatedNonAbstract:
352+
def __new__(cls, *args, **kwargs):
353+
all_names = {
354+
name for subclass in inspect.getmro(cls) for name in vars(subclass)
355+
}
356+
abstract = {
357+
name
358+
for name in all_names
359+
if getattr(getattr(cls, name), '__isabstractmethod__', False)
360+
}
361+
if abstract:
362+
warnings.warn(
363+
f"Unimplemented abstract methods {abstract}",
364+
DeprecationWarning,
365+
stacklevel=2,
366+
)
367+
return super().__new__(cls)
368+
369+
370+
class Distribution(DeprecatedNonAbstract):
352371
"""A Python distribution package."""
353372

354373
@abc.abstractmethod

Diff for: tests/_context.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import contextlib
2+
3+
4+
# from jaraco.context 4.3
5+
class suppress(contextlib.suppress, contextlib.ContextDecorator):
6+
"""
7+
A version of contextlib.suppress with decorator support.
8+
9+
>>> @suppress(KeyError)
10+
... def key_error():
11+
... {}['']
12+
>>> key_error()
13+
"""

Diff for: tests/test_main.py

+13
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
import re
22
import pickle
33
import unittest
4+
import warnings
45
import importlib
56
import importlib_metadata
7+
import contextlib
68
import itertools
79
import pyfakefs.fake_filesystem_unittest as ffs
810

911
from . import fixtures
12+
from ._context import suppress
1013
from importlib_metadata import (
1114
Distribution,
1215
EntryPoint,
@@ -20,6 +23,13 @@
2023
)
2124

2225

26+
@contextlib.contextmanager
27+
def suppress_known_deprecation():
28+
with warnings.catch_warnings(record=True) as ctx:
29+
warnings.simplefilter('default', category=DeprecationWarning)
30+
yield ctx
31+
32+
2333
class BasicTests(fixtures.DistInfoPkg, unittest.TestCase):
2434
version_pattern = r'\d+\.\d+(\.\d)?'
2535

@@ -44,6 +54,9 @@ def test_package_not_found_mentions_metadata(self):
4454

4555
assert "metadata" in str(ctx.exception)
4656

57+
# expected to fail until ABC is enforced
58+
@suppress(AssertionError)
59+
@suppress_known_deprecation()
4760
def test_abc_enforced(self):
4861
with self.assertRaises(TypeError):
4962
type('DistributionSubclass', (Distribution,), {})()

0 commit comments

Comments
 (0)