Skip to content

Commit

Permalink
Disallow specifying metaclass multiple times
Browse files Browse the repository at this point in the history
  • Loading branch information
fangerer committed Oct 13, 2022
1 parent b22fd87 commit bc8f9b2
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 2 deletions.
14 changes: 12 additions & 2 deletions hpy/devel/src/runtime/ctx_type.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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'. */
Expand Down
24 changes: 24 additions & 0 deletions test/test_hpytype.py
Original file line number Diff line number Diff line change
Expand Up @@ -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("""
Expand Down

0 comments on commit bc8f9b2

Please sign in to comment.