[Python-Dev] Calling Methods from Pythons C API with Keywords (original) (raw)

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 PyListSort because that dosnt support keywords like...

ls.sort(key=lambda a: a.foo)) And the Problem with PyObjectCallMethod is that it dosnt accept keywords. Note that you can always simply call PyObjectCall on the bound method object retrieved using PyObjectGetAttrString. The hardest part is usually constructing the keywords dictionary, a job best left to PyBuildValue 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 PyObjectCallMethod 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 = callmethod(lst, "sort", "{s:O}", "key", keyfun)); */ PyObject * callmethod(PyObject *obj, const char *methname, char *format, ...) { valist va; PyObject *meth = NULL, *args = NULL, *kwds = NULL, *ret = NULL; args = PyTupleNew(0); if (!args) goto out; meth = PyObjectGetAttrString(obj, methname); if (!meth) goto out; vastart(va, format); kwds = PyVaBuildValue(format, va); vaend(va); if (!kwds) goto out; ret = PyObjectCall(meth, args, kwds); out: PyXDECREF(meth); PyXDECREF(args); PyXDECREF(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); Py_DECREF(newargs);

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