summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/whatsnew/3.12.rst7
-rw-r--r--Lib/test/test_capi.py29
-rw-r--r--Misc/NEWS.d/next/C API/2022-07-29-10-41-59.gh-issue-95388.aiRSgr.rst2
-rw-r--r--Modules/_testcapi/heaptype.c16
-rw-r--r--Objects/typeobject.c26
5 files changed, 80 insertions, 0 deletions
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index 67396f8..be05997 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -205,6 +205,10 @@ Pending Removal in Python 3.14
(Contributed by Jason R. Coombs and Hugo van Kemenade in :gh:`93963`.)
+* Creating :c:data:`immutable types <Py_TPFLAGS_IMMUTABLETYPE>` with mutable
+ bases using the C API.
+
+
Pending Removal in Future Versions
----------------------------------
@@ -458,6 +462,9 @@ Deprecated
:c:type:`PyConfig` instead.
(Contributed by Victor Stinner in :gh:`77782`.)
+* Creating :c:data:`immutable types <Py_TPFLAGS_IMMUTABLETYPE>` with mutable
+ bases is deprecated and will be disabled in Python 3.14.
+
Removed
-------
diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py
index 013229a..c743557 100644
--- a/Lib/test/test_capi.py
+++ b/Lib/test/test_capi.py
@@ -15,6 +15,7 @@ import textwrap
import threading
import time
import unittest
+import warnings
import weakref
from test import support
from test.support import MISSING_C_DOCSTRINGS
@@ -644,6 +645,34 @@ class CAPITest(unittest.TestCase):
with self.assertRaises(SystemError):
_testcapi.create_type_from_repeated_slots(variant)
+ 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')
+
+ # Can change the method on the mutable base
+ MutableBase.meth = lambda self: 'changed'
+ self.assertEqual(instance.meth(), 'changed')
+
+
def test_pynumber_tobase(self):
from _testcapi import pynumber_tobase
self.assertEqual(pynumber_tobase(123, 2), '0b1111011')
diff --git a/Misc/NEWS.d/next/C API/2022-07-29-10-41-59.gh-issue-95388.aiRSgr.rst b/Misc/NEWS.d/next/C API/2022-07-29-10-41-59.gh-issue-95388.aiRSgr.rst
new file mode 100644
index 0000000..c389d13
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2022-07-29-10-41-59.gh-issue-95388.aiRSgr.rst
@@ -0,0 +1,2 @@
+Creating :c:data:`immutable types <Py_TPFLAGS_IMMUTABLETYPE>` with mutable
+bases is deprecated and is planned to be disabled in Python 3.14.
diff --git a/Modules/_testcapi/heaptype.c b/Modules/_testcapi/heaptype.c
index 12889e8..514541c 100644
--- a/Modules/_testcapi/heaptype.c
+++ b/Modules/_testcapi/heaptype.c
@@ -365,6 +365,21 @@ create_type_from_repeated_slots(PyObject *self, PyObject *variant_obj)
}
+
+static PyObject *
+make_immutable_type_with_base(PyObject *self, PyObject *base)
+{
+ assert(PyType_Check(base));
+ PyType_Spec ImmutableSubclass_spec = {
+ .name = "ImmutableSubclass",
+ .basicsize = (int)((PyTypeObject*)base)->tp_basicsize,
+ .slots = empty_type_slots,
+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE,
+ };
+ return PyType_FromSpecWithBases(&ImmutableSubclass_spec, base);
+}
+
+
static PyMethodDef TestMethods[] = {
{"pytype_fromspec_meta", pytype_fromspec_meta, METH_O},
{"test_type_from_ephemeral_spec", test_type_from_ephemeral_spec, METH_NOARGS},
@@ -375,6 +390,7 @@ static PyMethodDef TestMethods[] = {
{"test_from_spec_invalid_metatype_inheritance",
test_from_spec_invalid_metatype_inheritance,
METH_NOARGS},
+ {"make_immutable_type_with_base", make_immutable_type_with_base, METH_O},
{NULL},
};
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 1980fcb..0801d9f 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -3676,6 +3676,32 @@ PyType_FromMetaclass(PyTypeObject *metaclass, PyObject *module,
goto finally;
}
+ /* If this is an immutable type, check if all bases are also immutable,
+ * and (for now) fire a deprecation warning if not.
+ * (This isn't necessary for static types: those can't have heap bases,
+ * and only heap types can be mutable.)
+ */
+ if (spec->flags & Py_TPFLAGS_IMMUTABLETYPE) {
+ for (int i=0; i<PyTuple_GET_SIZE(bases); i++) {
+ PyTypeObject *b = (PyTypeObject*)PyTuple_GET_ITEM(bases, i);
+ if (!b) {
+ 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.",
+ spec->name,
+ b->tp_name))
+ {
+ goto finally;
+ }
+ }
+ }
+ }
+
/* Calculate the metaclass */
if (!metaclass) {