Hello all,
Victor Stinner recently added the function PyObject_CallNoArgs() for
calling an object without any arguments, see
https://docs.python.org/3.9/c-api/object.html#c.PyObject_CallNoArgs
The next obvious question is: should we have PyObject_CallOneArg(),
PyObject_CallTwoArgs()? Of course, this cannot continue forever, so I
suggest adding the 1 and 2 argument variants which would cover most use
cases. Cython has something like that and it becomes very natural to use
these functions.
The main advantage is that we can implement these variants much more
efficiently than the existing PyObject_CallFunction() or
PyObject_CallFunctionObjArgs().
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.