pallets / werkzeug

The comprehensive WSGI web application library.
https://werkzeug.palletsprojects.com
BSD 3-Clause "New" or "Revised" License
6.65k stars 1.73k forks source link

add breakpoint middleware #2893

Closed davidism closed 5 months ago

davidism commented 6 months ago

The interactive debugger is a constant source of (usually invalid) security reports. Nowadays, there are a lot of nice Python debuggers besides the built-in pdb. Python has a built-in breakpoint() function which will start the user's preferred debugger. For example, pudb, ipdb, pdbpp, or IDE debuggers such as PyCharm and VSCode. This middleware catches unhandled exceptions, finishes the response, then calls breakpoint() to allow the user to inspect the stack. This provides exactly as much information as the interactive debugger, but without the huge complexity of storing frames, threads, contexts, etc or serving a remote Python interpreter UI.

davidism commented 6 months ago

For example, pip install pudb, then run a Flask application with PYTHONBREAKPOINT=pudb.set_trace flask run --debug. On an unhandled exception, the browser displays a standard 500 error page, and the nice PUDB UI is displayed in the terminal. PyCharm sets this for you, so if you start a Flask app with the debug button, an unhandled exception will activate the debug panel frame inspector.

davidism commented 6 months ago

I deliberately named this WSGIMiddleware, since I think an ASGIMiddleware could easily be written as well, I just don't know quite enough about how ASGI handles errors to do that.

davidism commented 5 months ago

One thing I failed to account for is that breakpoint() doesn't deal with post-mortem debugging. Calling breakpoint() stops where it's called in the except block, which is after the function that raised it has exited (due to raising an exception). We want to debug the traceback that we caught, not the current state. Most debuggers do have a separate post_mortem() call instead of set_trace(), but it's not guaranteed, and we'd need some way to detect the debugger and then find its post mortem call instead.