0

I'm trying to get the argument names of a Python function called from C++. A little background:

I have a C++ application that needs to call Python functions, passing them arguments by name. So far I have been able to do this by parsing the Python module's .py file; however I would like to be able to handle .pyc files as well.

Ideally this would be done via the Python C API (rather than trying to decompile the bytecode), but I can't see any obvious way of doing this. There are ways of doing this in Python e.g. with inspect but not in C++.

Anyone know of a possible solution? TIA.

1
  • You should take a look at SWIG. Commented May 31, 2018 at 8:04

1 Answer 1

2

You said that you would use the inspect module if you wanted to do this from python. Why not call the inspect module from the C API?

The following C code prints all arguments of a python function. It should be valid C++ code as well. I dont use the Python C API very often, so please tell me if some things could be improved.

#include <Python.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
    wchar_t* const program = Py_DecodeLocale(argv[0], nullptr);
    Py_SetProgramName(program);
    Py_Initialize();

    // Define a python function f with argument names x and y.
    // >>> def f(x, y): return x*y
    PyRun_SimpleString("def f(x, y): return x*y\n");

    // Get the function as python object.
    // >>> import sys
    // >>> f_function = sys.modules["__main__"].f
    PyObject* const sys_module_name = PyUnicode_DecodeFSDefault("sys");
    PyObject* const sys_module = PyImport_Import(sys_module_name);
    PyObject* const modules_dict = PyObject_GetAttrString(sys_module, "modules");
    PyObject* const main_name = PyUnicode_DecodeFSDefault("__main__");
    PyObject* const main_module = PyDict_GetItem(modules_dict, main_name);
    PyObject* const f_function = PyObject_GetAttrString(main_module, "f");

    // Get the inspect.getargspec function.
    // >>> import inspect
    // >>> getargspec_function = inspect.getargspec
    PyObject* const inspect_module_name = PyUnicode_DecodeFSDefault("inspect");
    PyObject* const inspect_module = PyImport_Import(inspect_module_name);
    PyObject* const getargspec_function = PyObject_GetAttrString(inspect_module, "getargspec");

    // Call the inspect.getargspec function.
    // >>> argspec = getargspec_function(f_function)
    PyObject* const argspec_call_args = PyTuple_New(1);
    PyTuple_SetItem(argspec_call_args, 0, f_function);
    PyObject* const argspec = PyObject_CallObject(getargspec_function, argspec_call_args);

    // Get args from argspec.
    // >>> f_args = argspec.args
    PyObject* const f_args = PyObject_GetAttrString(argspec, "args");

    // f_args now holds a python list with all arguments of f.

    // As example usage, you can print the arguments:
    // >>> for i, a in enumerate(f_args):
    // ...     print("Repr of arg", i, "is", repr(a))
    Py_ssize_t const num_args = PyList_Size(f_args);
    for (Py_ssize_t i = 0; i < num_args; ++i)
    {
        PyObject* const arg = PyList_GetItem(f_args, i);
        PyObject* const arg_repr = PyObject_Repr(arg);
        PyObject* const arg_str = PyUnicode_AsASCIIString(arg_repr);
        char const* const arg_c_str = PyBytes_AS_STRING(arg_str);
        printf("Repr of arg %ld is %s\n", i, arg_c_str);
    }

    Py_Finalize();
    PyMem_RawFree(program);

    return 0;
}
Sign up to request clarification or add additional context in comments.

1 Comment

Brilliant! Why didn't I think of that! Thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.