[Python-Dev] Calling Methods from Pythons C API with Keywords

Campbell Barton cbarton at metavr.com
Wed Jun 20 12:17:22 CEST 2007

Hrvoje Nikšić wrote:
> On Wed, 2007-06-20 at 00:21 +1000, Campbell Barton wrote:
>> I want to add my own call's before and after PyLists standard functions
>> but have a proplem with functons that use keywords and have no API
>> equivalent.
>> For example, I cant use the API's PyList_Sort because that dosnt support
>> keywords like...
>> ls.sort(key=lambda a: a.foo))
>> And the Problem with PyObject_CallMethod is that it dosnt accept keywords.
> Note that you can always simply call PyObject_Call on the bound method
> object retrieved using PyObject_GetAttrString.  The hardest part is
> usually constructing the keywords dictionary, a job best left to
> Py_BuildValue and friends.  When I need that kind of thing in more than
> one place, I end up with a utility function like this one:
> /* Equivalent to PyObject_CallMethod but accepts keyword args.  The
>    format... arguments should produce a dictionary that will be passed
>    as keyword arguments to obj.method.
>    Usage example:
>      PyObject *res = call_method(lst, "sort", "{s:O}", "key", keyfun));
> */
> PyObject *
> call_method(PyObject *obj, const char *methname, char *format, ...)
> {
>   va_list va;
>   PyObject *meth = NULL, *args = NULL, *kwds = NULL, *ret = NULL;
>   args = PyTuple_New(0);
>   if (!args)
>     goto out;
>   meth = PyObject_GetAttrString(obj, methname);
>   if (!meth)
>     goto out;
>   va_start(va, format);
>   kwds = Py_VaBuildValue(format, va);
>   va_end(va);
>   if (!kwds)
>     goto out;
>   ret = PyObject_Call(meth, args, kwds);
>  out:
>   Py_XDECREF(meth);
>   Py_XDECREF(args);
>   Py_XDECREF(kwds);
>   return ret;
> }
> It would be nice for the Python C API to support a more convenient way
> of calling objects and methods with keyword arguments.

Thanks for the hint, I ended up using PyObject_Call.
This seems to work, EXPP_PyTuple_New_Prepend - is a utility function 
that returns a new tuple with self at the start (needed so args starts 
with self)

I dont think I can use PyObject_GetAttrString because the subtype would 
return a reference to this function - rather then the lists original 
function, Id need an instance of a list and dont have one at that point.

static PyObject * MaterialList_sort(BPy_MaterialList *self, PyObject 
*args, PyObject *keywds )
	PyObject *ret;
	PyObject *newargs = EXPP_PyTuple_New_Prepend(args, (PyObject *)self);
	sync_list_from_materials__internal(self); # makes sure the list matches 
blenders materials

	ret = PyObject_Call(PyDict_GetItemString(PyList_Type.tp_dict, "sort"), 
newargs, keywds);
	if (ret)
		sync_materials_from_list__internal(self); # makes blenders materials 
match the lists
	return ret;


Later on Ill probably avoid using PyDict_GetItemString on 
PyList_Type.tp_dict all the time since the methods for lists does not 
change during python running. - Can probably be assigned to a constant.

Campbell J Barton (ideasman42)

More information about the Python-Dev mailing list