In the following code I create a pointer to a PyObject, representing a bytearray, using the Python C API. I then extract the method "endswith" from the bytearray and try to call it on the original bytearray itself, expecting it to return Py_True.
However, it returns NULL
and the program prints "very sad".
#include<Python.h>
#include<iostream>
int main()
{
Py_Initialize();
//make a one-byte byte array
PyObject* oneByteArray = PyByteArray_FromStringAndSize("a", 1);
//get the method "endswith" from the object at oneByteArray
PyObject* arrayEndsWith = PyObject_GetAttrString(oneByteArray, "endswith");
//ask python if "a" ends with "a"
PyObject* shouldbetrue = PyObject_CallObject(arrayEndsWith, oneByteArray);
if (shouldbetrue == Py_True) std::cout << "happy\n";
if(shouldbetrue == NULL)std::cout << "very sad\n";
Py_Finalize();
return 0;
}
I have checked in Python that for bytearrays, foo
and bar
, foo.endswith(bar)
returns a Boolean. I also added PyCallable_Check(arrayEndsWith)
to the code above and verified that the object is callable. What is my mistake?
If you add the line PyErr_PrintEx(1)
it helpfully tells you:
TypeError: argument list must be a tuple
This is confirmed by the documentation for PyObject_CallObject
:
Call a callable Python object callable_object, with arguments given by the tuple args.
There's a whole bunch of ways of calling functions from the C-api. I picked one that doesn't require a tuple and it works for me (but pick the one you like):
PyObject* shouldbetrue = PyObject_CallFunctionObjArgs(arrayEndsWith, oneByteArray,NULL);