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).
|
(or *Py_tp_base[s]* slots if *bases* is ``NULL``, see below).
|
||||||
|
|
||||||
Metaclasses that override :c:member:`~PyTypeObject.tp_new` are not
|
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
|
(For backwards compatibility, other ``PyType_From*`` functions allow
|
||||||
such metaclasses. They ignore ``tp_new``, which may result in incomplete
|
such metaclasses. They ignore ``tp_new``, which may result in incomplete
|
||||||
initialization. This is deprecated and in Python 3.14+ such metaclasses will
|
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)
|
self.assertEqual(obj.pvalue, 0)
|
||||||
|
|
||||||
def test_heaptype_with_custom_metaclass(self):
|
def test_heaptype_with_custom_metaclass(self):
|
||||||
self.assertTrue(issubclass(_testcapi.HeapCTypeMetaclass, type))
|
metaclass = _testcapi.HeapCTypeMetaclass
|
||||||
self.assertTrue(issubclass(_testcapi.HeapCTypeMetaclassCustomNew, type))
|
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.assertIsInstance(t, type)
|
||||||
self.assertEqual(t.__name__, "HeapCTypeViaMetaclass")
|
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."
|
msg = "Metaclasses with custom tp_new are not supported."
|
||||||
with self.assertRaisesRegex(TypeError, msg):
|
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):
|
def test_heaptype_with_custom_metaclass_deprecation(self):
|
||||||
|
metaclass = _testcapi.HeapCTypeMetaclassCustomNew
|
||||||
|
|
||||||
# gh-103968: a metaclass with custom tp_new is deprecated, but still
|
# gh-103968: a metaclass with custom tp_new is deprecated, but still
|
||||||
# allowed for functions that existed in 3.11
|
# allowed for functions that existed in 3.11
|
||||||
# (PyType_FromSpecWithBases is used here).
|
# (PyType_FromSpecWithBases is used here).
|
||||||
class Base(metaclass=_testcapi.HeapCTypeMetaclassCustomNew):
|
class Base(metaclass=metaclass):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Class creation from C
|
||||||
with warnings_helper.check_warnings(
|
with warnings_helper.check_warnings(
|
||||||
('.*custom tp_new.*in Python 3.14.*', DeprecationWarning),
|
('.*custom tp_new.*in Python 3.14.*', DeprecationWarning),
|
||||||
):
|
):
|
||||||
sub = _testcapi.make_type_with_base(Base)
|
sub = _testcapi.make_type_with_base(Base)
|
||||||
self.assertTrue(issubclass(sub, 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):
|
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
|
HeapCTypeMetaclassCustomNew_slots
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static PyType_Spec HeapCTypeMetaclassNullNew_spec = {
|
||||||
|
.name = "_testcapi.HeapCTypeMetaclassNullNew",
|
||||||
|
.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
|
||||||
|
.slots = empty_type_slots
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
|
@ -1231,6 +1237,13 @@ _PyTestCapi_Init_Heaptype(PyObject *m) {
|
||||||
}
|
}
|
||||||
PyModule_AddObject(m, "HeapCTypeMetaclassCustomNew", HeapCTypeMetaclassCustomNew);
|
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(
|
PyObject *HeapCCollection = PyType_FromMetaclass(
|
||||||
NULL, m, &HeapCCollection_spec, NULL);
|
NULL, m, &HeapCCollection_spec, NULL);
|
||||||
if (HeapCCollection == NULL) {
|
if (HeapCCollection == NULL) {
|
||||||
|
|
|
@ -4236,7 +4236,7 @@ _PyType_FromMetaclass_impl(
|
||||||
metaclass);
|
metaclass);
|
||||||
goto finally;
|
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 (_allow_tp_new) {
|
||||||
if (PyErr_WarnFormat(
|
if (PyErr_WarnFormat(
|
||||||
PyExc_DeprecationWarning, 1,
|
PyExc_DeprecationWarning, 1,
|
||||||
|
|
Loading…
Reference in New Issue