From bc8f9b2e5d84177b3a4fc5a86ef7d1a2a0ffa03a Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Thu, 13 Oct 2022 11:51:39 +0200 Subject: [PATCH] Disallow specifying metaclass multiple times --- hpy/devel/src/runtime/ctx_type.c | 14 ++++++++++++-- test/test_hpytype.py | 24 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/hpy/devel/src/runtime/ctx_type.c b/hpy/devel/src/runtime/ctx_type.c index cc803ec41..9cc2fa8f3 100644 --- a/hpy/devel/src/runtime/ctx_type.c +++ b/hpy/devel/src/runtime/ctx_type.c @@ -861,11 +861,16 @@ static PyObject *build_bases_from_params(HPyType_SpecParam *params) } _HPy_HIDDEN struct _typeobject *get_metatype(HPyType_SpecParam *params) { + struct _typeobject *res = NULL; if (params != NULL) { for (HPyType_SpecParam *p = params; p->kind != 0; p++) { switch (p->kind) { case HPyType_SpecParam_Metaclass: - return (struct _typeobject*) _h2py(p->object); + if (res) { + PyErr_SetString(PyExc_ValueError, "metaclass was specified multiple times"); + return NULL; + } + res = (struct _typeobject*) _h2py(p->object); break; default: // other values are intentionally ignored @@ -877,7 +882,7 @@ _HPy_HIDDEN struct _typeobject *get_metatype(HPyType_SpecParam *params) { has not explicitly been specified. We could default here to &PyType_Type but we actually want to use the bare 'PyType_FromSpecWithBases' if nothing was specified. */ - return NULL; + return res; } static inline Py_ssize_t count_members(PyType_Spec *spec) { @@ -1100,6 +1105,11 @@ ctx_Type_FromSpec(HPyContext *ctx, HPyType_Spec *hpyspec, return HPy_NULL; } struct _typeobject *metatype = get_metatype(params); + if (metatype == NULL && PyErr_Occurred()) { + PyMem_Free(spec->slots); + PyMem_Free(spec); + return HPy_NULL; + } #if HAVE_FROM_METACLASS /* On Python 3.12 an newer, we can just use 'PyType_FromMetaclass'. */ diff --git a/test/test_hpytype.py b/test/test_hpytype.py index f35d86af1..9b429ea7b 100644 --- a/test/test_hpytype.py +++ b/test/test_hpytype.py @@ -995,6 +995,30 @@ class Sub(mod.Dummy): pass assert isinstance(Sub(), mod.Dummy) + def test_specparam_multiple_metaclass_fails(self): + import pytest + mod = self.make_module(""" + static HPyType_Spec Dummy_spec = { + .name = "mytest.Dummy", + }; + + HPyDef_METH(make_dummy, "make_dummy", make_dummy_impl, HPyFunc_NOARGS) + static HPy make_dummy_impl(HPyContext *ctx, HPy module) + { + HPyType_SpecParam param[] = { + { HPyType_SpecParam_Metaclass, ctx->h_TypeType }, + { HPyType_SpecParam_Metaclass, ctx->h_LongType }, + { (HPyType_SpecParam_Kind)0 } + }; + return HPyType_FromSpec(ctx, &Dummy_spec, param); + } + @EXPORT(make_dummy) + @INIT + """) + + with pytest.raises(ValueError): + mod.make_dummy() + def test_metaclass(self): import pytest mod = self.make_module("""