0

Consider the following verysimple.py:

if '__main__' == __name__:
    prnt('Line1')

Now, if I execute it with > python verysimple.py, I am, of course, greeted by:

    Traceback (most recent call last):
        File "verysimple.py", line 2, in <module>
          prnt('Line1')
    NameError: name 'prnt' is not defined`

I'd like to know where in the Python is pulling the traceback information from (especially the erroneous command).

I've already tried to find my way inside PyEval_FrameEx, but can't figure it out...

SebGR
  • 293
  • 2
  • 10
  • @Dayalrai rai because I'm looking for the C routine that does it... – SebGR Jul 04 '13 at 13:25
  • Out of curiosity: why do wnat to know? –  Jul 05 '13 at 12:03
  • @Evert I'd like to learn about the way Python is retrieving line number, source code from the opcode. – SebGR Jul 08 '13 at 08:26
  • @SebGR I think that actually happens well before that, when Python parses the code and converts it into its syntax tree, storing the line numbers along the way. It's only when Python starts to evaluate the code that it raises an exception. –  Jul 08 '13 at 11:42

1 Answers1

1

You were on the right track. When parsing the code tree, it just runs through PyEval_FrameEx multiple times. In the end, it will call format_exc_check_arg() to format the error, which for me happens at line 2100 in the ceval.c of the Python version 3.3.2 source. format_exc_check_arg() deduces the offensive 'object' (prnt) and calls PyErr_Format in errors.c to properly format the exception string (the exception type and its corresponding string, NAME_ERROR_MSG, were already passed to format_exc_check_arg() from this line 2100.

I used simply this code for testing:

prnt('Line1')

and then run it through a debug build of Python 3.3 I had around.

The surrounding code in PyEval_FrameEx is

    TARGET(LOAD_NAME)
        w = GETITEM(names, oparg);
        if ((v = f->f_locals) == NULL) {
            PyErr_Format(PyExc_SystemError,
                         "no locals when loading %R", w);
            why = WHY_EXCEPTION;
            break;
        }
        if (PyDict_CheckExact(v)) {
            x = PyDict_GetItem(v, w);
            Py_XINCREF(x);
        }
        else {
            x = PyObject_GetItem(v, w);
            if (x == NULL && PyErr_Occurred()) {
                if (!PyErr_ExceptionMatches(
                                PyExc_KeyError))
                    break;
                PyErr_Clear();
            }
        }
        if (x == NULL) {
            x = PyDict_GetItem(f->f_globals, w);
            Py_XINCREF(x);
            if (x == NULL) {
                if (PyDict_CheckExact(f->f_builtins)) {
                    x = PyDict_GetItem(f->f_builtins, w);
                    if (x == NULL) {
// below is the line where the PyExc_NameError will be properly formatted.
                        format_exc_check_arg(
                                    PyExc_NameError,
                                    NAME_ERROR_MSG, w);
                        break;
                    }
                    Py_INCREF(x);
                }
                else {
                    x = PyObject_GetItem(f->f_builtins, w);
                    if (x == NULL) {
                        if (PyErr_ExceptionMatches(PyExc_KeyError))
                            format_exc_check_arg(
                                        PyExc_NameError,
                                        NAME_ERROR_MSG, w);
                        break;
                    }
                }
            }
        }
        PUSH(x);
        DISPATCH();

Note that two lines above it, PyDict_GetItem(...) will be the line trying to find prnt inside the builtin statements & functions (I deduce that from f->builtins, to which w is applied, w itself gotten from the second statement in the above code. Since that dictionary lookup will fail, x == NULL and the NameError is set and formatted.

Hope this helps you further.