mirror of https://github.com/python/cpython
gh-95388: Deprecate creating immutable types with mutable bases (GH-95533)
This commit is contained in:
parent
000c3874bf
commit
a613fedd6e
|
@ -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
|
||||
-------
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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.
|
|
@ -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},
|
||||
};
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue