From 121bb1b3b9f04c3a3ebf2086e38b5e33293509a7 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 30 May 2024 12:52:39 +0300 Subject: [PATCH 1/3] gh-119775: Remove ability to create immutable types with mutable bases --- Doc/whatsnew/3.14.rst | 2 ++ Lib/test/test_capi/test_misc.py | 22 +++---------------- ...-05-30-12-51-21.gh-issue-119775.CBq9IG.rst | 2 ++ Objects/typeobject.c | 15 +++++-------- 4 files changed, 13 insertions(+), 28 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2024-05-30-12-51-21.gh-issue-119775.CBq9IG.rst diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 8c37825430c2cf..05d7ad3e3c41c7 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -254,3 +254,5 @@ Deprecated Removed ------- +* Creating :c:data:`immutable types ` with mutable + bases was deprecated since 3.12 and now raises a :exc:`TypeError`. diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index ed42d7b64302f9..3c001c949d04f4 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -777,33 +777,17 @@ def test_pytype_fromspec_with_repeated_slots(self): with self.assertRaises(SystemError): _testcapi.create_type_from_repeated_slots(variant) - @warnings_helper.ignore_warnings(category=DeprecationWarning) def test_immutable_type_with_mutable_base(self): - # Add deprecation warning here so it's removed in 3.14 - warnings._deprecated( - 'creating immutable classes with mutable bases', remove=(3, 14)) - class MutableBase: def meth(self): return 'original' - with self.assertWarns(DeprecationWarning): - ImmutableSubclass = _testcapi.make_immutable_type_with_base( - MutableBase) - instance = ImmutableSubclass() - - self.assertEqual(instance.meth(), 'original') - - # Cannot override the static type's method - with self.assertRaisesRegex( - TypeError, - "cannot set 'meth' attribute of immutable type"): - ImmutableSubclass.meth = lambda self: 'overridden' - self.assertEqual(instance.meth(), 'original') + with self.assertRaisesRegex(TypeError, 'Creating immutable type'): + _testcapi.make_immutable_type_with_base(MutableBase) # Can change the method on the mutable base MutableBase.meth = lambda self: 'changed' - self.assertEqual(instance.meth(), 'changed') + self.assertEqual(MutableBase().meth(), 'changed') def test_pynumber_tobase(self): from _testcapi import pynumber_tobase diff --git a/Misc/NEWS.d/next/C API/2024-05-30-12-51-21.gh-issue-119775.CBq9IG.rst b/Misc/NEWS.d/next/C API/2024-05-30-12-51-21.gh-issue-119775.CBq9IG.rst new file mode 100644 index 00000000000000..c342a3814ed5db --- /dev/null +++ b/Misc/NEWS.d/next/C API/2024-05-30-12-51-21.gh-issue-119775.CBq9IG.rst @@ -0,0 +1,2 @@ +Creating :c:data:`immutable types ` with mutable +bases was deprecated since 3.12 and now raises a :exc:`TypeError`. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 290306cdb677e5..d9f08c1d1da8c5 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4613,16 +4613,13 @@ _PyType_FromMetaclass_impl( goto finally; } if (!_PyType_HasFeature(b, Py_TPFLAGS_IMMUTABLETYPE)) { - if (PyErr_WarnFormat( - PyExc_DeprecationWarning, - 0, - "Creating immutable type %s from mutable base %s is " - "deprecated, and slated to be disallowed in Python 3.14.", + PyErr_Format( + PyExc_TypeError, + "Creating immutable type %s from mutable base %s", spec->name, - b->tp_name)) - { - goto finally; - } + b->tp_name + ); + goto finally; } } } From d03ae985f1682d56e744eb5828d082ffbda8f4ae Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sat, 1 Jun 2024 08:41:45 +0300 Subject: [PATCH 2/3] Address review --- Lib/test/test_capi/test_misc.py | 4 ---- Objects/typeobject.c | 5 ++--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 3c001c949d04f4..82fe3acc99ade6 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -785,10 +785,6 @@ def meth(self): with self.assertRaisesRegex(TypeError, 'Creating immutable type'): _testcapi.make_immutable_type_with_base(MutableBase) - # Can change the method on the mutable base - MutableBase.meth = lambda self: 'changed' - self.assertEqual(MutableBase().meth(), 'changed') - def test_pynumber_tobase(self): from _testcapi import pynumber_tobase small_number = 123 diff --git a/Objects/typeobject.c b/Objects/typeobject.c index d9f08c1d1da8c5..0095a79a2cafec 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4615,9 +4615,8 @@ _PyType_FromMetaclass_impl( if (!_PyType_HasFeature(b, Py_TPFLAGS_IMMUTABLETYPE)) { PyErr_Format( PyExc_TypeError, - "Creating immutable type %s from mutable base %s", - spec->name, - b->tp_name + "Creating immutable type %s from mutable base %N", + spec->name, b ); goto finally; } From 93c06dfcf67bb1d160683f1c20c50af74b412a93 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Sat, 1 Jun 2024 08:42:33 +0300 Subject: [PATCH 3/3] Remove method --- Lib/test/test_capi/test_misc.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 82fe3acc99ade6..e3b73f4b0bf8a5 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -778,9 +778,7 @@ def test_pytype_fromspec_with_repeated_slots(self): _testcapi.create_type_from_repeated_slots(variant) def test_immutable_type_with_mutable_base(self): - class MutableBase: - def meth(self): - return 'original' + class MutableBase: ... with self.assertRaisesRegex(TypeError, 'Creating immutable type'): _testcapi.make_immutable_type_with_base(MutableBase)