Question

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...

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top