Reputation: 769
I aim to create a metaclass (let's call it SlotsMeta
) in Cython that performs the following:
SlotsMeta
as metaclass and converts them into readonly
attributes in Cython.I am using trying to convert all class variables (as keys in the parameter **kwargs
) passed into the __cinit__
method of SlotsMeta
to a Tuple[str]
and assign it to SlotsMeta.__slots__
.
I have looked at the following references:
My implementation is as follows:
#cython: language_level=3
# copied from https://github.com/sagemath/sagelib/blob/master/sage/misc/classcall_metaclass.pyx
from cpython cimport PyObject, Py_XDECREF
cdef extern from "Python.h":
ctypedef PyObject *(*callfunc)(type, object, object) except NULL
ctypedef struct PyTypeObject_call "PyTypeObject":
callfunc tp_call # needed to call type.__call__ at very high speed.
cdef PyTypeObject_call PyType_Type # Python's type
cdef class SlotsMeta(type):
cdef readonly tuple __slots__
def __init__(mcls, str name, tuple bases, dict attrs):
attrs_keys = tuple(str(k) for k in attrs.keys())
mcls.__slots__ = attrs_keys
def __call__(cls, *args, **kwargs):
ptr = PyType_Type.tp_call(cls, args, kwargs)
inst = <object>ptr
inst.__init__(*args, **kwargs)
Py_XDECREF(ptr) # During the cast to <object> Cython did INCREF(res)
return inst
The test function that fails is:
class A(metaclass=SlotsMeta):
B: str = "H"
def test_slotsmeta():
a = A() # passes
assert a.B == "H" # passes
with pytest.raises(AttributeError): # passes
a.C # passes
with pytest.raises(AttributeError):
a.C = 500 # failed
assert a.__slots__ == ("B", ) # failed
What failed:
a.C
= 500 should not be possible but it is.a.__slots__
should return ("B", )
but it doesn't.a.__dict__
exists when it shouldn't.Upvotes: 1
Views: 93