Search code examples
pythonc++python-c-api

PyObject_CallObject failing on bytearray method


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?


Solution

  • 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);