-1

I am using the python API for C/C++ and I would like to retrieve the line number in case of a NameError.

I followed the instructions found within the question bellow:

How to retrieve filename and lineno attribute of SyntaxError

and I wrote the following code:

PyObject *ptype = NULL, *pvalue = NULL, *ptraceback = NULL;
PyObject *comp = NULL, *eval = NULL;
char code[] = {"A=undef_var"};

comp = Py_CompileString(code, "", Py_file_input);
if (comp) {
    eval = PyEval_EvalCode(comp, PyEval_GetBuiltins(), NULL);
}
if (!comp || !eval) {
     PyErr_PrintEx(1); // inside this function the PyErr_Fetch(ptype, pvalue, ptraceback) is called

     // retrieve the information gained from PyErr_Fetch() called inside PyErr_PrintEx(1) 
     pvalue     = PySys_GetObject("last_value"); 
     ptype      = PySys_GetObject("last_type");  
     ptraceback = PySys_GetObject("last_traceback");  
     PyErr_NormalizeException(ptype, pvalue, ptraceback);

    PyObject* line_no = PyObject_GetAttrString(pvalue,"lineno");
    if (line_no) {
         PyObject* line_no_str = PyObject_Str(line_no);
         PyObject* line_no_unicode =     PyUnicode_AsEncodedString(line_no_str,"utf-8", "Error");
         char *actual_line_no = PyBytes_AsString(line_no_unicode);
    }
}

The code above returns the correct line number, in case the python code contains a SyntaxError (e.g. for a simple python code like "A="), but in case of a NameError the line number is not set correctly to the pvalue object (e.g. for the python code: "A=undefined_var").

Any ideas how can I solve this problem?

tsahmatsis
  • 51
  • 5
  • Can't you just use boost library for this? Then you could check out this stackoverflow link: [How to get Python exception text](https://stackoverflow.com/questions/1418015/how-to-get-python-exception-text/37780954) – Spezi94 Oct 19 '17 at 08:39
  • Unfortunately, I cannot use the boost library. Thanks for the answer! – tsahmatsis Oct 19 '17 at 08:46
  • Do note that you **absolutely have to check** the return value of **each and every `Py_*` function**, separately, and act accordingly. – Antti Haapala -- Слава Україні Oct 19 '17 at 09:20
  • You'd notice that `NameError` doesn't even have the attribute `lineno`. – Antti Haapala -- Слава Україні Oct 19 '17 at 09:23
  • What's the _Python_ version (and _OS_) that you're using? For me, _Python35_ on _Win_ throws an _Access violation_ (_segfault_) in `Py_CompileString`. – CristiFati Oct 19 '17 at 09:26
  • @Antti Haapala I do check them separately, I have changed the code litle bit. I am interested in cases that the parsing is Ok and the evaluation fails.. – tsahmatsis Oct 19 '17 at 09:44
  • I don't see the check for the return value of `PyObject_GetAttrString(pvalue,"lineno");` not being `NULL` – Antti Haapala -- Слава Україні Oct 19 '17 at 09:46
  • @CristiFati Python 3.3 on windows 7 – tsahmatsis Oct 19 '17 at 09:46
  • *"NameError the line number is not set correctly to the pvalue object (e.g. for the python code: "A=undefined_var")."* - so what happens *here*? – Antti Haapala -- Слава Україні Oct 19 '17 at 09:47
  • @AnttiHaapala I cannot retrieve the line number of the error...because of the evaluation error. The undefined_var is unknown and this makes the PyEval_EvalCode to fail. – tsahmatsis Oct 19 '17 at 09:50
  • *it does fail because an exception was raised*. Your code is again incorrect. You have the exception **only when the `eval` failed**. – Antti Haapala -- Слава Україні Oct 19 '17 at 10:00
  • @AnttiHaapala My initial code was transferred here by hand, and it is not my original code. I just wanted to give a general idea of my problem. Don't be so strict. I have added some extra checks to catch the exceptions for both parsing and evaluation errors. But my question still has to do on how to retrieve the line number when an evaluation error happens. Disregard the two functions I use, lets say I use the PyRun_String function which parses and executes the code. I had the same problem as well... – tsahmatsis Oct 19 '17 at 10:21
  • 1
    If you had done it correctly, you would have *known* that `NameError` doesn't have the attribute `lineno` and *that* is why it doesn't work. You need to access the [traceback](https://stackoverflow.com/questions/1796510/accessing-a-python-traceback-from-the-c-api) – Antti Haapala -- Слава Україні Oct 19 '17 at 10:27
  • @AnttiHaapala Thank you for your last answer. I have read this post before you mentioned it, but I have tried it upon SyntaxError and the traceback was either NULL or TypeNone and I thought that it would not work with me. Now I tried it upon NameError and worked. – tsahmatsis Oct 19 '17 at 11:24

1 Answers1

1

With the help of @Antti Haapala and considering the solutions posted to this question I concluded to the solution below:

PyObject *ptype = NULL, *pvalue = NULL, *ptraceback = NULL;
PyObject *compile_obj = NULL, *eval_obj = NULL;
PyObject *line_no = NULL, *line_no_str = NULL, *line_no_unicode = NULL;
char *actual_line_no = NULL;
char code[] = { "A=undef_var" };
int line_num = 0;

compile_obj = Py_CompileString(code, "", Py_file_input);
if (compile_obj) {
    eval_obj = PyImport_ExecCodeModule((char *)"", compile_obj);
}
if (!compile_obj || !eval_obj) {
    PyErr_PrintEx(1); // inside this function the PyErr_Fetch(ptype, pvalue, ptraceback) is called
                      // retrieve the information gained from PyErr_Fetch() called inside PyErr_PrintEx(1) 
    pvalue     = PySys_GetObject("last_value");
    ptype      = PySys_GetObject("last_type");
    ptraceback = PySys_GetObject("last_traceback");
    if (ptype) {
        PyErr_NormalizeException(&ptype, &pvalue, &ptraceback);
    }
    if (compile_obj) { // NameError
        if (ptraceback) {
            PyTracebackObject *tb_o = (PyTracebackObject *)ptraceback;
            line_num = tb_o->tb_lineno;
        }
    } else { //Syntax Error
        line_no = PyObject_GetAttrString(pvalue, "lineno");
        if (line_no) {
            line_no_str = PyObject_Str(line_no);
            if (line_no_str)     line_no_unicode = PyUnicode_AsEncodedString(line_no_str, "utf-8", "Error");
            if (line_no_unicode) actual_line_no = PyBytes_AsString(line_no_unicode);
            if (actual_line_no)  line_num = atoi(actual_line_no);

            Py_XDECREF(line_no);
            Py_XDECREF(line_no_str);
            Py_XDECREF(line_no_unicode);
        }
    }
}
Py_XDECREF(compile_obj);
Py_XDECREF(eval_obj);

return line_num;
tsahmatsis
  • 51
  • 5