Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2b90796

Browse filesBrowse files
authored
gh-103968: PyType_FromMetaclass: Allow metaclasses with tp_new=NULL (GH-105386)
1 parent 58f0bda commit 2b90796
Copy full SHA for 2b90796

File tree

Expand file treeCollapse file tree

5 files changed

+53
-9
lines changed
Filter options
Expand file treeCollapse file tree

5 files changed

+53
-9
lines changed

‎Doc/c-api/type.rst

Copy file name to clipboardExpand all lines: Doc/c-api/type.rst
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ The following functions and structs are used to create
258258
(or *Py_tp_base[s]* slots if *bases* is ``NULL``, see below).
259259
260260
Metaclasses that override :c:member:`~PyTypeObject.tp_new` are not
261-
supported.
261+
supported, except if ``tp_new`` is ``NULL``.
262262
(For backwards compatibility, other ``PyType_From*`` functions allow
263263
such metaclasses. They ignore ``tp_new``, which may result in incomplete
264264
initialization. This is deprecated and in Python 3.14+ such metaclasses will

‎Lib/test/test_capi/test_misc.py

Copy file name to clipboardExpand all lines: Lib/test/test_capi/test_misc.py
+36-7Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -671,31 +671,60 @@ def test_heaptype_with_setattro(self):
671671
self.assertEqual(obj.pvalue, 0)
672672

673673
def test_heaptype_with_custom_metaclass(self):
674-
self.assertTrue(issubclass(_testcapi.HeapCTypeMetaclass, type))
675-
self.assertTrue(issubclass(_testcapi.HeapCTypeMetaclassCustomNew, type))
674+
metaclass = _testcapi.HeapCTypeMetaclass
675+
self.assertTrue(issubclass(metaclass, type))
676676

677-
t = _testcapi.pytype_fromspec_meta(_testcapi.HeapCTypeMetaclass)
677+
# Class creation from C
678+
t = _testcapi.pytype_fromspec_meta(metaclass)
678679
self.assertIsInstance(t, type)
679680
self.assertEqual(t.__name__, "HeapCTypeViaMetaclass")
680-
self.assertIs(type(t), _testcapi.HeapCTypeMetaclass)
681+
self.assertIs(type(t), metaclass)
682+
683+
# Class creation from Python
684+
t = metaclass("PyClassViaMetaclass", (), {})
685+
self.assertIsInstance(t, type)
686+
self.assertEqual(t.__name__, "PyClassViaMetaclass")
687+
688+
def test_heaptype_with_custom_metaclass_null_new(self):
689+
metaclass = _testcapi.HeapCTypeMetaclassNullNew
690+
691+
self.assertTrue(issubclass(metaclass, type))
692+
693+
# Class creation from C
694+
t = _testcapi.pytype_fromspec_meta(metaclass)
695+
self.assertIsInstance(t, type)
696+
self.assertEqual(t.__name__, "HeapCTypeViaMetaclass")
697+
self.assertIs(type(t), metaclass)
698+
699+
# Class creation from Python
700+
with self.assertRaisesRegex(TypeError, "cannot create .* instances"):
701+
metaclass("PyClassViaMetaclass", (), {})
702+
703+
def test_heaptype_with_custom_metaclass_custom_new(self):
704+
metaclass = _testcapi.HeapCTypeMetaclassCustomNew
705+
706+
self.assertTrue(issubclass(_testcapi.HeapCTypeMetaclassCustomNew, type))
681707

682708
msg = "Metaclasses with custom tp_new are not supported."
683709
with self.assertRaisesRegex(TypeError, msg):
684-
t = _testcapi.pytype_fromspec_meta(_testcapi.HeapCTypeMetaclassCustomNew)
710+
t = _testcapi.pytype_fromspec_meta(metaclass)
685711

686712
def test_heaptype_with_custom_metaclass_deprecation(self):
713+
metaclass = _testcapi.HeapCTypeMetaclassCustomNew
714+
687715
# gh-103968: a metaclass with custom tp_new is deprecated, but still
688716
# allowed for functions that existed in 3.11
689717
# (PyType_FromSpecWithBases is used here).
690-
class Base(metaclass=_testcapi.HeapCTypeMetaclassCustomNew):
718+
class Base(metaclass=metaclass):
691719
pass
692720

721+
# Class creation from C
693722
with warnings_helper.check_warnings(
694723
('.*custom tp_new.*in Python 3.14.*', DeprecationWarning),
695724
):
696725
sub = _testcapi.make_type_with_base(Base)
697726
self.assertTrue(issubclass(sub, Base))
698-
self.assertIsInstance(sub, _testcapi.HeapCTypeMetaclassCustomNew)
727+
self.assertIsInstance(sub, metaclass)
699728

700729
def test_multiple_inheritance_ctypes_with_weakref_or_dict(self):
701730

+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:c:func:`PyType_FromMetaclass` now allows metaclasses with ``tp_new``
2+
set to ``NULL``.

‎Modules/_testcapi/heaptype.c

Copy file name to clipboardExpand all lines: Modules/_testcapi/heaptype.c
+13Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,12 @@ static PyType_Spec HeapCTypeMetaclassCustomNew_spec = {
744744
HeapCTypeMetaclassCustomNew_slots
745745
};
746746

747+
static PyType_Spec HeapCTypeMetaclassNullNew_spec = {
748+
.name = "_testcapi.HeapCTypeMetaclassNullNew",
749+
.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
750+
.slots = empty_type_slots
751+
};
752+
747753

748754
typedef struct {
749755
PyObject_HEAD
@@ -1231,6 +1237,13 @@ _PyTestCapi_Init_Heaptype(PyObject *m) {
12311237
}
12321238
PyModule_AddObject(m, "HeapCTypeMetaclassCustomNew", HeapCTypeMetaclassCustomNew);
12331239

1240+
PyObject *HeapCTypeMetaclassNullNew = PyType_FromMetaclass(
1241+
&PyType_Type, m, &HeapCTypeMetaclassNullNew_spec, (PyObject *) &PyType_Type);
1242+
if (HeapCTypeMetaclassNullNew == NULL) {
1243+
return -1;
1244+
}
1245+
PyModule_AddObject(m, "HeapCTypeMetaclassNullNew", HeapCTypeMetaclassNullNew);
1246+
12341247
PyObject *HeapCCollection = PyType_FromMetaclass(
12351248
NULL, m, &HeapCCollection_spec, NULL);
12361249
if (HeapCCollection == NULL) {

‎Objects/typeobject.c

Copy file name to clipboardExpand all lines: Objects/typeobject.c
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4236,7 +4236,7 @@ _PyType_FromMetaclass_impl(
42364236
metaclass);
42374237
goto finally;
42384238
}
4239-
if (metaclass->tp_new != PyType_Type.tp_new) {
4239+
if (metaclass->tp_new && metaclass->tp_new != PyType_Type.tp_new) {
42404240
if (_allow_tp_new) {
42414241
if (PyErr_WarnFormat(
42424242
PyExc_DeprecationWarning, 1,

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.