Dropping frames from a traceback

I’m working on some refinements to the Python shell in Emacs, and this requires me to manipulate the stack traces a bit, for the user’s benefit.

The problem is that when the user asks to evaluate some region of a .py file, Emacs doesn’t send that code directly to the Python shell; rather, it wraps it around some plumbing code. If the user’s code raises an exception, the plumbing code shows up in the stack trace, which is undesirable.

So instead I send something like this to the Python shell:

   try:
        exec(user_code)
   except Exception:
        t, v, tb = sys.exc_info()
        sys.excepthook(t, v, tb.tb_next)

This does the expected thing (hide the current function and all previous frames from the traceback) in Python 2 and IPython. But in the standard Python 3 interpreter I still see the full traceback.

Is there a better way to approach this issue?

PS: In case there are any Emacs users out there, here is a link to the patch I’ve proposed to the Emacs Python shell, containing this and some other improvements: #49822 - 28.0.50; python-shell-send functions show no output - GNU bug report logs. Any feedback is welcome!

Short answer:
Not really. You can do a similar thing to the first answer here, i.e. using tb_next (remember that you can do this multiple times – tb.tb_next.tb_next…).

Uglier approaches:
You could take a look at how IPython, Jupyter, and Jinja2 handle their tracebacks and see if their solution works for you. As far as I know (and I might be incredibly wrong), they all format it as text, removing portions from the beginning and leaving only the user’s traceback portions.

Jinja does full traceback manipulation. It’s actually much easier in modern Python, no more ctypes or pypy hacks. Werkzeug dev server also does traceback manipulation. Another one to look at is Trio.

1 Like