bpo-44232: Fix type_new() error reporting (GH-26359)

Fix a regression in type() when a metaclass raises an exception. The
C function type_new() must properly report the exception when a
metaclass constructor raises an exception and the winner class is not
the metaclass.
This commit is contained in:
Victor Stinner 2021-05-25 22:28:10 +02:00 committed by GitHub
parent bd404ccac0
commit bd199e72fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 26 additions and 0 deletions

View File

@ -1334,6 +1334,24 @@ class ClassCreationTests(unittest.TestCase):
N(5) N(5)
self.assertEqual(str(cm.exception), expected_message) self.assertEqual(str(cm.exception), expected_message)
def test_metaclass_new_error(self):
# bpo-44232: The C function type_new() must properly report the
# exception when a metaclass constructor raises an exception and the
# winner class is not the metaclass.
class ModelBase(type):
def __new__(cls, name, bases, attrs):
super_new = super().__new__
new_class = super_new(cls, name, bases, {})
if name != "Model":
raise RuntimeWarning(f"{name=}")
return new_class
class Model(metaclass=ModelBase):
pass
with self.assertRaises(RuntimeWarning):
type("SouthPonies", (Model,), {})
class SimpleNamespaceTests(unittest.TestCase): class SimpleNamespaceTests(unittest.TestCase):

View File

@ -0,0 +1,4 @@
Fix a regression in :func:`type` when a metaclass raises an exception. The C
function :c:func:`type_new` must properly report the exception when a metaclass
constructor raises an exception and the winner class is not the metaclass.
Patch by Victor Stinner.

View File

@ -3256,6 +3256,9 @@ type_new_get_bases(type_new_ctx *ctx, PyObject **type)
if (winner->tp_new != type_new) { if (winner->tp_new != type_new) {
/* Pass it to the winner */ /* Pass it to the winner */
*type = winner->tp_new(winner, ctx->args, ctx->kwds); *type = winner->tp_new(winner, ctx->args, ctx->kwds);
if (*type == NULL) {
return -1;
}
return 1; return 1;
} }
@ -3307,6 +3310,7 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
PyObject *type = NULL; PyObject *type = NULL;
int res = type_new_get_bases(&ctx, &type); int res = type_new_get_bases(&ctx, &type);
if (res < 0) { if (res < 0) {
assert(PyErr_Occurred());
return NULL; return NULL;
} }
if (res == 1) { if (res == 1) {