I have an iterable PyObject
that I need to pass as the list of arguments to a Python callable, ala
xs = [1,2,5,7]
some_function(*xs)
However, PyObject_CallObject
only allows tuples to be passed as the arguments' container.
I'm not sure, though, what is the best way to create a new tuple containing the elements of an iterable. Here's a tentative code:
PyObject* unpack_call(PyObject* callable, PyObject* args) {
PyObject* tuple;
if (PyTuple_Check(args)) {
tuple = args;
} else {
PyObject* iter = PyObject_GetIter(args);
if (!iter) return NULL;
tuple = PyTuple_New(0);
for (Py_ssize_t pos=0; ; ++pos) {
PyObject* arg = PyIter_Next(iter);
if (!arg) break;
PyTuple_SetItem(tuple,pos,arg);
}
}
return PyObject_CallObject(callable,tuple);
}
I'm not sure if I need to grow the size of the tuple myself or not. What's confusing me is the sentence in the documentation saying:
The tuple will always grow or shrink at the end.
I'm also not sure if I need to increment or decrement reference counts for any of these objects.
You're much better using PySequence_Tuple
which can create a tuple directly from an iterable. There's probably other similar options to do this too.
If you did want to do it your way then you do need to call PyTuple_Resize
each time you add to it (or resize it in chunks possibly). What the sentence
The tuple will always grow or shrink at the end.
tells you is that extra space is at the end of the tuple.
PyTuple_SetItem
steals a reference, so you don't need to touch the reference count of the items you're adding. You should be doing some error checking though - PyIter_Next
can raise an exception. You also need to decref iter
and the tuple when you're done with them.