Nice general way to always invoke python debugger upon exception

492 Views Asked by At

I'd like to have my debugger run post_mortem() any time an exception is encountered, without having to modify the source that I'm working on. I see lots of examples that involve wrapping code in a try/except block, but I'd like to have it always run, regardless of what I'm working on.

I worked on a python wrapper script but that got to be ugly and pretty much unusable.

I use pudb, which is API-equivalent to pdb, so a pdb-specific answer is fine. I run code from within my editor (vim) and would like to have the pm come up any time an exception is encountered.

3

There are 3 best solutions below

1
On

It took a few months of not doing anything about it, but I happened to stumble upon a solution. I'm sure this is nothing new for the more experienced.

I have the following in my environment:

export PYTHONUSERBASE=~/.python
export PYTHONPATH=$PYTHONPATH:$PYTHONUSERBASE

And I have the following file:

~/.python/lib/python2.7/site-packages/usercustomize.py

With the following contents:

import traceback
import sys

try:
    import pudb as debugger
except ImportError:
    import pdb as debugger

def drop_debugger(type, value, tb):
  traceback.print_exception(type, value, tb)
  debugger.pm()

sys.excepthook = drop_debugger

__builtins__['debugger'] = debugger
__builtins__['st'] = debugger.set_trace

Now, whether interactively or otherwise, the debugger always jumps in upon an exception. It might be nice to smarten this up some.

It's important to make sure that you have no no-global-site-packages.txt in your site-packages. This will disable the usercustomize module with the default site.py (my virtualenv had a no-global-site-packages.txt)

Just in case it would help others, I left in the bit about modifying __builtins__. I find it quite handy to always be able to rely on some certain tools being available.

Flavor to taste.

4
On

A possible solution is to invoke pdb (I don't know about pudb, but I'll just assume it works the same) as a script:

python -m pdb script.py

Quoting the the documentation:

When invoked as a script, pdb will automatically enter post-mortem debugging if the program being debugged exits abnormally. After post-mortem debugging (or after normal exit of the program), pdb will restart the program.

2
On

A solution for pdb since Python 3.2 is to start the program under the debugger via -m pdb and tell pdb to continue via -c c:

python3 -m pdb -c c program.py

Quoting the pdb documentation:

When invoked as a script, pdb will automatically enter post-mortem debugging if the program being debugged exits abnormally. After post-mortem debugging (or after normal exit of the program), pdb will restart the program.

As-of pudb 2019.2: according to the pudb documentation, the official way involves changing your code a bit (and even then, I don't end up in post-mortem mode if I just run python3 program.py!):

To start the debugger without actually pausing use:

from pudb import set_trace; set_trace(paused=False)

at the top of your code. This will start the debugger without breaking, and run it until a predefined breakpoint is hit. You can also press b on a set_trace call inside the debugger, and it will prevent it from stopping there.

Although it is possible to start the program properly under the debugger via python3 -m pudb.run program.py, pudb's command-line args do not support anything like pdb's -c c. The pudb's --pre-run=COMMAND is for external commands, not pudb commands.

What I currently do is run python3 -m pudb.run program.py without mentioning pudb or set_tracein program.py at all and press c on the keyboard. This way pudb enters its post-mortem mode upon any unhandled exception. This however only works well when I know that the exception will be reproduced. For hunting down sporadically occuring exceptions I go back to the pdb solution.