In python, for metaclass, we can change the magic method for class with closure:
class MyType(type):
def __new__(mcls, name, bases, namespace):
# other data
original_getattribute = namespace.get("__getattribute__")
def __getattribute__(self, name):
# do something with other data
if original_getattribute is not None:
return original_getattribute(self, name)
return super(type_instance, self).__getattribute__(name)
type_instance = super().__new__(mcls, name, bases, namespace)
return type_instance
In python, the closures can be added automatically and naturally. However, in C it cannot. Below it is a dangerous example:
static PyObject*
MyType_type_getattro(PyObject* self, PyObject* name)
{
PyTypeObject* type_instance = Py_TYPE(self);
// do something with the type
getattrofunc original_getattro; // use the way to get it
if (original_getattro) {
return original_getattro(self, name);
}
return type_instance->tp_base->tp_getattro(self, name);
}
static PyObject*
MyType_new(PyTypeObject* cls, PyObject* args, PyObject* kwargs)
{
// do something with args and kwargs
PyObject* type_instance = PyType_Type.tp_new(cls, args, kwargs);
// do something with type_instance
((PyTypeObject*)type_instance)->tp_getattro = MyType_type_getattro;
return type_instance;
}
The problem is: if one class is created base on a MyType, the Py_TYPE(self) will be this type, while this type’s tp_base->tp_getattro is still MyType_type_getattro which means that it will cause stackoverflow and crash. However, the function point cannot accept the function with real closure.
To solve the problem, here introduce the struct PyFuncClosure:
struct PyFuncClosure {
void* data;
void (*clean_func)(void*);
};
and the struct with all slots:
struct PyTypeSlotClosure {
PyFuncClosure getattroclosure;
PyFuncClosure setattroclosure;
PyFuncClosure hashclosure;
// and many other slots
};
The tp_slot_closures will be the slot of PyTypeObject and the type is PyTypeSlotClosure.
When the type is delete, the clean_func will be called. So don’t share same data between different type tp_slot_closures.
All slot function point typedef will add a new typedef with an extra argument void* closure
Effect
The code that do with tp_* straightly will be effected. Don’t change the slot directly if the type contains the closure for this slot. When call this slot directly, check whether contain the slot closure.
If the code called PyObject_GetAttr it can still work.