Search code examples
pythonc++python-embedding

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.


Solution

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