From 23e7f5a4ac041087d138521ccda54d716ce74069 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 10 May 2026 16:07:10 +0300 Subject: [PATCH 1/2] gh-149609: Raise deprecation warnings for `abc.{abstractclassmethod,abstractstaticmethod,abstractproperty}` --- Doc/library/abc.rst | 6 +- Doc/whatsnew/3.16.rst | 11 +++- Lib/abc.py | 21 +++++++ Lib/test/test_abc.py | 61 ++++++++++++++----- ...-05-10-16-06-27.gh-issue-149609.yLKPXo.rst | 3 + 5 files changed, 81 insertions(+), 21 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-05-10-16-06-27.gh-issue-149609.yLKPXo.rst diff --git a/Doc/library/abc.rst b/Doc/library/abc.rst index 8112cfee7d204d..f82041b0acddc3 100644 --- a/Doc/library/abc.rst +++ b/Doc/library/abc.rst @@ -237,7 +237,7 @@ The :mod:`!abc` module also supports the following legacy decorators: .. decorator:: abstractclassmethod .. versionadded:: 3.2 - .. deprecated:: 3.3 + .. deprecated-removed:: 3.3 3.18 It is now possible to use :class:`classmethod` with :func:`abstractmethod`, making this decorator redundant. @@ -258,7 +258,7 @@ The :mod:`!abc` module also supports the following legacy decorators: .. decorator:: abstractstaticmethod .. versionadded:: 3.2 - .. deprecated:: 3.3 + .. deprecated-removed:: 3.3 3.18 It is now possible to use :class:`staticmethod` with :func:`abstractmethod`, making this decorator redundant. @@ -278,7 +278,7 @@ The :mod:`!abc` module also supports the following legacy decorators: .. decorator:: abstractproperty - .. deprecated:: 3.3 + .. deprecated-removed:: 3.3 3.18 It is now possible to use :class:`property`, :meth:`property.getter`, :meth:`property.setter` and :meth:`property.deleter` with :func:`abstractmethod`, making this decorator redundant. diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index d9beda92aba6a3..013d27771a8bf1 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -139,9 +139,14 @@ sysconfig Deprecated ========== -* module_name: - TODO - +* :mod:`abc` + + * Soft-deprecated since Python 3.3 :class:`abc.abstractclassmethod`, + :class:`abc.abstractstaticmethod`, and :class:`abc.abstractproperty` + now raise a :exc:`DeprecationWarning`. + These classes will be removed in Python 3.18, instead + use :func:`abc.abstractmethod` with :dec:`classmethod`, + :dec:`staticmethod`, and :class:`property` respectively. .. Add deprecations above alphabetically, not here at the end. diff --git a/Lib/abc.py b/Lib/abc.py index f8a4e11ce9c3b1..e02dd13c386f79 100644 --- a/Lib/abc.py +++ b/Lib/abc.py @@ -36,11 +36,15 @@ class C(ABC): def my_abstract_classmethod(cls, ...): ... + .. deprecated-removed: 3.3 3.18 + """ __isabstractmethod__ = True def __init__(self, callable): + import warnings + warnings._deprecated('abc.abstractclassmethod', remove=(3, 18)) callable.__isabstractmethod__ = True super().__init__(callable) @@ -56,11 +60,15 @@ class C(ABC): def my_abstract_staticmethod(...): ... + .. deprecated-removed: 3.3 3.18 + """ __isabstractmethod__ = True def __init__(self, callable): + import warnings + warnings._deprecated('abc.abstractstaticmethod', remove=(3, 18)) callable.__isabstractmethod__ = True super().__init__(callable) @@ -76,10 +84,23 @@ class C(ABC): def my_abstract_property(self): ... + .. deprecated-removed: 3.3 3.18 + """ __isabstractmethod__ = True + def __init__( + self, + fget=None, + fset=None, + fdel=None, + doc=None, + ): + import warnings + warnings._deprecated('abc.abstractproperty', remove=(3, 18)) + super().__init__(fget, fset, fdel, doc) + try: from _abc import (get_cache_token, _abc_init, _abc_register, diff --git a/Lib/test/test_abc.py b/Lib/test/test_abc.py index 80ee9e0ba56e75..59a45a2eda07b0 100644 --- a/Lib/test/test_abc.py +++ b/Lib/test/test_abc.py @@ -11,20 +11,30 @@ import abc import _py_abc from inspect import isabstract +from test.support import warnings_helper def test_factory(abc_ABCMeta, abc_get_cache_token): class TestLegacyAPI(unittest.TestCase): def test_abstractproperty_basics(self): - @abc.abstractproperty - def foo(self): pass + with self.assertWarnsRegex( + DeprecationWarning, + 'abstractproperty', + ): + @abc.abstractproperty + def foo(self): pass + self.assertTrue(foo.__isabstractmethod__) def bar(self): pass self.assertNotHasAttr(bar, "__isabstractmethod__") - class C(metaclass=abc_ABCMeta): - @abc.abstractproperty - def foo(self): return 3 + with self.assertWarnsRegex( + DeprecationWarning, + 'abstractproperty', + ): + class C(metaclass=abc_ABCMeta): + @abc.abstractproperty + def foo(self): return 3 self.assertRaises(TypeError, C) class D(C): @property @@ -33,16 +43,26 @@ def foo(self): return super().foo self.assertFalse(getattr(D.foo, "__isabstractmethod__", False)) def test_abstractclassmethod_basics(self): - @abc.abstractclassmethod - def foo(cls): pass + with self.assertWarnsRegex( + DeprecationWarning, + 'abstractclassmethod', + ): + @abc.abstractclassmethod + def foo(cls): pass + self.assertTrue(foo.__isabstractmethod__) @classmethod def bar(cls): pass self.assertFalse(getattr(bar, "__isabstractmethod__", False)) - class C(metaclass=abc_ABCMeta): - @abc.abstractclassmethod - def foo(cls): return cls.__name__ + with self.assertWarnsRegex( + DeprecationWarning, + 'abstractclassmethod', + ): + class C(metaclass=abc_ABCMeta): + @abc.abstractclassmethod + def foo(cls): return cls.__name__ + self.assertRaises(TypeError, C) class D(C): @classmethod @@ -51,16 +71,26 @@ def foo(cls): return super().foo() self.assertEqual(D().foo(), 'D') def test_abstractstaticmethod_basics(self): - @abc.abstractstaticmethod - def foo(): pass + with self.assertWarnsRegex( + DeprecationWarning, + 'abstractstaticmethod', + ): + @abc.abstractstaticmethod + def foo(): pass + self.assertTrue(foo.__isabstractmethod__) @staticmethod def bar(): pass self.assertFalse(getattr(bar, "__isabstractmethod__", False)) - class C(metaclass=abc_ABCMeta): - @abc.abstractstaticmethod - def foo(): return 3 + with self.assertWarnsRegex( + DeprecationWarning, + 'abstractstaticmethod', + ): + class C(metaclass=abc_ABCMeta): + @abc.abstractstaticmethod + def foo(): return 3 + self.assertRaises(TypeError, C) class D(C): @staticmethod @@ -168,6 +198,7 @@ def method_two(self): msg = r"class C without an implementation for abstract methods 'method_one', 'method_two'" self.assertRaisesRegex(TypeError, msg, C) + @warnings_helper.ignore_warnings(category=DeprecationWarning) def test_abstractmethod_integration(self): for abstractthing in [abc.abstractmethod, abc.abstractproperty, abc.abstractclassmethod, diff --git a/Misc/NEWS.d/next/Library/2026-05-10-16-06-27.gh-issue-149609.yLKPXo.rst b/Misc/NEWS.d/next/Library/2026-05-10-16-06-27.gh-issue-149609.yLKPXo.rst new file mode 100644 index 00000000000000..124981bf6c3c95 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-05-10-16-06-27.gh-issue-149609.yLKPXo.rst @@ -0,0 +1,3 @@ +Raise :exc:`DeprecationWarning` on using :class:`abc.abstractclassmethod`, +:class:`abc.abstractstaticmethod`, and :class:`abc.abstractproperty`, +schedule its removal for Python 3.18. From 84cf7c00a6145c48053700791a4c1d9bce8e8618 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sun, 10 May 2026 16:11:59 +0300 Subject: [PATCH 2/2] Fix docs --- Doc/whatsnew/3.16.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.16.rst b/Doc/whatsnew/3.16.rst index 013d27771a8bf1..70bedd34b66117 100644 --- a/Doc/whatsnew/3.16.rst +++ b/Doc/whatsnew/3.16.rst @@ -145,8 +145,8 @@ Deprecated :class:`abc.abstractstaticmethod`, and :class:`abc.abstractproperty` now raise a :exc:`DeprecationWarning`. These classes will be removed in Python 3.18, instead - use :func:`abc.abstractmethod` with :dec:`classmethod`, - :dec:`staticmethod`, and :class:`property` respectively. + use :func:`abc.abstractmethod` with :func:`classmethod`, + :func:`staticmethod`, and :class:`property` respectively. .. Add deprecations above alphabetically, not here at the end.