mirror of https://github.com/python/cpython
gh-103968: PyType_FromMetaclass: Allow metaclasses with tp_new=NULL (GH-105386)
This commit is contained in:
parent
58f0bda341
commit
2b90796be6
|
@ -258,7 +258,7 @@ The following functions and structs are used to create
|
|||
(or *Py_tp_base[s]* slots if *bases* is ``NULL``, see below).
|
||||
|
||||
Metaclasses that override :c:member:`~PyTypeObject.tp_new` are not
|
||||
supported.
|
||||
supported, except if ``tp_new`` is ``NULL``.
|
||||
(For backwards compatibility, other ``PyType_From*`` functions allow
|
||||
such metaclasses. They ignore ``tp_new``, which may result in incomplete
|
||||
initialization. This is deprecated and in Python 3.14+ such metaclasses will
|
||||
|
|
|
@ -671,31 +671,60 @@ class CAPITest(unittest.TestCase):
|
|||
self.assertEqual(obj.pvalue, 0)
|
||||
|
||||
def test_heaptype_with_custom_metaclass(self):
|
||||
self.assertTrue(issubclass(_testcapi.HeapCTypeMetaclass, type))
|
||||
self.assertTrue(issubclass(_testcapi.HeapCTypeMetaclassCustomNew, type))
|
||||
metaclass = _testcapi.HeapCTypeMetaclass
|
||||
self.assertTrue(issubclass(metaclass, type))
|
||||
|
||||
t = _testcapi.pytype_fromspec_meta(_testcapi.HeapCTypeMetaclass)
|
||||
# Class creation from C
|
||||
t = _testcapi.pytype_fromspec_meta(metaclass)
|
||||
self.assertIsInstance(t, type)
|
||||
self.assertEqual(t.__name__, "HeapCTypeViaMetaclass")
|
||||
self.assertIs(type(t), _testcapi.HeapCTypeMetaclass)
|
||||
self.assertIs(type(t), metaclass)
|
||||
|
||||
# Class creation from Python
|
||||
t = metaclass("PyClassViaMetaclass", (), {})
|
||||
self.assertIsInstance(t, type)
|
||||
self.assertEqual(t.__name__, "PyClassViaMetaclass")
|
||||
|
||||
def test_heaptype_with_custom_metaclass_null_new(self):
|
||||
metaclass = _testcapi.HeapCTypeMetaclassNullNew
|
||||
|
||||
self.assertTrue(issubclass(metaclass, type))
|
||||
|
||||
# Class creation from C
|
||||
t = _testcapi.pytype_fromspec_meta(metaclass)
|
||||
self.assertIsInstance(t, type)
|
||||
self.assertEqual(t.__name__, "HeapCTypeViaMetaclass")
|
||||
self.assertIs(type(t), metaclass)
|
||||
|
||||
# Class creation from Python
|
||||
with self.assertRaisesRegex(TypeError, "cannot create .* instances"):
|
||||
metaclass("PyClassViaMetaclass", (), {})
|
||||
|
||||
def test_heaptype_with_custom_metaclass_custom_new(self):
|
||||
metaclass = _testcapi.HeapCTypeMetaclassCustomNew
|
||||
|
||||
self.assertTrue(issubclass(_testcapi.HeapCTypeMetaclassCustomNew, type))
|
||||
|
||||
msg = "Metaclasses with custom tp_new are not supported."
|
||||
with self.assertRaisesRegex(TypeError, msg):
|
||||
t = _testcapi.pytype_fromspec_meta(_testcapi.HeapCTypeMetaclassCustomNew)
|
||||
t = _testcapi.pytype_fromspec_meta(metaclass)
|
||||
|
||||
def test_heaptype_with_custom_metaclass_deprecation(self):
|
||||
metaclass = _testcapi.HeapCTypeMetaclassCustomNew
|
||||
|
||||
# gh-103968: a metaclass with custom tp_new is deprecated, but still
|
||||
# allowed for functions that existed in 3.11
|
||||
# (PyType_FromSpecWithBases is used here).
|
||||
class Base(metaclass=_testcapi.HeapCTypeMetaclassCustomNew):
|
||||
class Base(metaclass=metaclass):
|
||||
pass
|
||||
|
||||
# Class creation from C
|
||||
with warnings_helper.check_warnings(
|
||||
('.*custom tp_new.*in Python 3.14.*', DeprecationWarning),
|
||||
):
|
||||
sub = _testcapi.make_type_with_base(Base)
|
||||
self.assertTrue(issubclass(sub, Base))
|
||||
self.assertIsInstance(sub, _testcapi.HeapCTypeMetaclassCustomNew)
|
||||
self.assertIsInstance(sub, metaclass)
|
||||
|
||||
def test_multiple_inheritance_ctypes_with_weakref_or_dict(self):
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
:c:func:`PyType_FromMetaclass` now allows metaclasses with ``tp_new``
|
||||
set to ``NULL``.
|
|
@ -744,6 +744,12 @@ static PyType_Spec HeapCTypeMetaclassCustomNew_spec = {
|
|||
HeapCTypeMetaclassCustomNew_slots
|
||||
};
|
||||
|
||||
static PyType_Spec HeapCTypeMetaclassNullNew_spec = {
|
||||
.name = "_testcapi.HeapCTypeMetaclassNullNew",
|
||||
.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
|
||||
.slots = empty_type_slots
|
||||
};
|
||||
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
|
@ -1231,6 +1237,13 @@ _PyTestCapi_Init_Heaptype(PyObject *m) {
|
|||
}
|
||||
PyModule_AddObject(m, "HeapCTypeMetaclassCustomNew", HeapCTypeMetaclassCustomNew);
|
||||
|
||||
PyObject *HeapCTypeMetaclassNullNew = PyType_FromMetaclass(
|
||||
&PyType_Type, m, &HeapCTypeMetaclassNullNew_spec, (PyObject *) &PyType_Type);
|
||||
if (HeapCTypeMetaclassNullNew == NULL) {
|
||||
return -1;
|
||||
}
|
||||
PyModule_AddObject(m, "HeapCTypeMetaclassNullNew", HeapCTypeMetaclassNullNew);
|
||||
|
||||
PyObject *HeapCCollection = PyType_FromMetaclass(
|
||||
NULL, m, &HeapCCollection_spec, NULL);
|
||||
if (HeapCCollection == NULL) {
|
||||
|
|
|
@ -4236,7 +4236,7 @@ _PyType_FromMetaclass_impl(
|
|||
metaclass);
|
||||
goto finally;
|
||||
}
|
||||
if (metaclass->tp_new != PyType_Type.tp_new) {
|
||||
if (metaclass->tp_new && metaclass->tp_new != PyType_Type.tp_new) {
|
||||
if (_allow_tp_new) {
|
||||
if (PyErr_WarnFormat(
|
||||
PyExc_DeprecationWarning, 1,
|
||||
|
|
Loading…
Reference in New Issue