Consider the following:
PyObject* fmt = PyUnicode_FromString("{0!r}");
PyObject* tup = PyTuple_New(2);
PyTuple_SetItem(tup, 0, PyUnicode_FromString("hello"));
PyTuple_SetItem(tup, 1, PyUnicode_FromString("world"));
PyObject* formatted = PyObject_CallMethod(fmt, "format", "O", tup);
PyObject* bytes = PyUnicode_AsEncodedString(formatted, "UTF-8", "strict");
printf(PyBytes_AS_STRING(bytes));
I expect it to act like this python code:
>>> u'{0!r}'.format((u"hello", u"world"))
"(u'hello', u'world')"
However my output is simply:
u'hello'
I can imagine it is actually calling the function like:
>>> u'{0!r}'.format(u"hello", u"world")
u'hello'
What I'm looking for:
The issue appears to be with the way Py_BuildValue
works (which seems to be used by PyObject_CallMethod
). From the docs (emphasis mine):
Py_BuildValue() does not always build a tuple. It builds a tuple only if its format string contains two or more format units. If the format string is empty, it returns None; if it contains exactly one format unit, it returns whatever object is described by that format unit. To force it to return a tuple of size 0 or one, parenthesize the format string.
This means that instead of building the format string "O"
with tup
into args=(tup,)
and calling fmt.format(*args)
(expanding to fmt.format(("hello", "world"))
), it builds args=tup
, and so fmt.format(*args)
expands to fmt.format("hello", "world")
, as you thought. The solution is also in the docs:
To force it to return a tuple of size 0 or one, parenthesize the format string.
So, just change:
PyObject* formatted = PyObject_CallMethod(fmt, "format", "O", tup);
To:
PyObject* formatted = PyObject_CallMethod(fmt, "format", "(O)", tup);
And you get the desired output of ('hello', 'world')
. Full code snippet (compiled with gcc thissnippet.c -I /usr/include/python3.4m/ -l python3.4m
):
#include <Python.h>
int main() {
Py_Initialize();
PyObject* fmt = PyUnicode_FromString("{0!r}");
PyObject* tup = PyTuple_New(2);
PyTuple_SetItem(tup, 0, PyUnicode_FromString("hello"));
PyTuple_SetItem(tup, 1, PyUnicode_FromString("world"));
PyObject* formatted = PyObject_CallMethod(fmt, "format", "(O)", tup);
PyObject* bytes = PyUnicode_AsEncodedString(formatted, "UTF-8", "strict");
printf(PyBytes_AS_STRING(bytes));
Py_Finalize();
}