jpo38
jpo38

Reputation: 21514

C++/embedded Python: Can I retrieve Python file name and line number when a C++ function is called from Python

I run a Python script from a C++ program using PyRun_SimpleFile. I defined a custom module (using PyImport_AppendInittab, as done here), so it can be imported in my Python script and some C++ code gets executed by the Python script when functions from this module are used, this is done through a callback PyObject* MyFunction(PyObject *self, PyObject *args) being invoked by Python interpreter.

I want to know the current script file name and line number within the callback function being invoked.

I could not find any way to retrieve this. Is this possible?

Note: This question is definitely not a duplicate of How to get the caller's method name in the called method?. I'm trying to retrieve file name and line number from C++ code executing and later executed by a Python script.

Upvotes: 2

Views: 352

Answers (1)

Szabolcs Dombi
Szabolcs Dombi

Reputation: 5783

You will need PyTraceBack_Here.

You can take a look at a traceback object's implementation here

Here is an example printig the traceback created by PyTraceBack_Here

#include <Python.h>

PyObject * mymodule_meth_test(PyObject * self) {
    PyTraceBack_Here(PyEval_GetFrame());
    PyObject * exc;
    PyObject * val;
    PyObject * tb;
    PyErr_Fetch(&exc, &val, &tb);
    PyTraceBack_Print(tb, PySys_GetObject("stderr"));
    Py_RETURN_NONE;
}

PyMethodDef module_methods[] = {
    {"test", (PyCFunction)mymodule_meth_test, METH_NOARGS, NULL},
    {},
};

PyModuleDef module_def = {PyModuleDef_HEAD_INIT, "mymodule", NULL, -1, module_methods};

extern "C" PyObject * PyInit_mymodule() {
    PyObject * module = PyModule_Create(&module_def);
    return module;
}

From the tb object you should be able to extract the filename and line number. It is an ordinary PyObject you can pass it to a python script or inspect it.

Here is how to extract the values without taking care of the refcounts:

    int line = PyLong_AsLong(PyObject_GetAttrString(PyObject_GetAttrString(tb, "tb_frame"), "f_lineno"));
    const char * filename = PyUnicode_AsUTF8(PyObject_GetAttrString(PyObject_GetAttrString(PyObject_GetAttrString(tb, "tb_frame"), "f_code"), "co_filename"));

Upvotes: 1

Related Questions