I am trying to use the PEP384 style type-construction with PyType_FromSpec.
I couldn't find much documentation on this, so first: is this the expected
way to make types going forward? I have always used static objects, but
this API is actually very convenient for programatically generated types.
As for my actual question: while nested structs like tp_as_number and
tp_as_mapping have been broken up such that you just pass Py_nb_add etc.,
tp_methods still just wants a null-terminated array of PyMethodDef objects.
This would be fine, but the resulting PyCFunction objects hold pointers
into this array, meaning I must ensure that the array outlives the type. I
could put the array in a capsule and assign it to the type, but then if a
user deletes the attribute from the type, it will free the memory calling
those methods will be a use after free. I could make a metaclass that has
room for the array on the type object itself, but it appears that the type
object is always allocated with PyType_GenericAlloc(&PyType_type, nmembers)
where nmembers is the size of the Py_tp_members slot. Also, even if I could
get the space, this would leak an implementation detail into the class
hierarchy, which is probably fine but seems messy.
One idea I have is to change PyCFunctionObject to hold a PyMethodDef
*value* instead of a pointer. I don't think the extra foot print size will
affect much, and will lower the total memory usage of a PyCFunctionObject
by one pointer. If anything, it avoids a second indirection to find the
function's implementation. I wanted to poll this list before working on
this to see if this has already been discussed or if I am just doing
something wrong. PyCFunctionObject is not part of the limited ABI, so this
should be a stable ABI compatible change. We can make this same change in
PyType_Ready for statically allocated types.