Get last exception in pdb
Asked Answered
W

5

26

Is there a way to examine the last exception when in pdb/before entering pdb? (Using python 2.7.5).

Immediately (yes, I enter no other commands at all) after an exception being raised in my code, I do sys.exc_info(); this just results in (None, None, None). At this point, I can do pdb.pm(), and pdb starts at the point that the exception is raised.

I'd like to be able to examine this exception object (it is not stored in a variable before being raised).

There is nothing obviously helpful in http://docs.python.org/2/library/pdb.html or http://docs.python.org/2/library/sys.html

Edit: I know about set_trace. I'd like to examine the exception before I modify the code.

Washedout answered 6/10, 2013 at 16:19 Comment(1)
Get reference to the current exception has an answer that works for my use case: getting the last thrown exception when stepping through with pdb.Mirisola
D
12

You can use sys.last_value:

>>> no_such_var
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'no_such_var' is not defined
>>> import sys
>>> sys.last_value
NameError("name 'no_such_var' is not defined",)
>>> sys.last_value.args
("name 'no_such_var' is not defined",)

>>> no_such_var
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'no_such_var' is not defined
>>> import pdb, sys
>>> pdb.set_trace()
--Return--
> <stdin>(1)<module>()->None
(Pdb) sys.last_value
NameError("name 'no_such_var' is not defined",)

NOTE: This solution is not perfect. The value is set when an exception is not handled and the interpreter prints an error message and a stack traceback. For example, if the exception is caught using try .. except .., sys.last_value is not set.

Dhobi answered 6/10, 2013 at 16:36 Comment(14)
I get: *** AttributeError: 'module' object has no attribute 'last_value'Selfconscious
@claymation, That attribute is not set if there was no exception. Execute a statement that causes an exception. For example: 1/0Dhobi
@Dhobi -> print 1 / 0, (Pdb) n, ZeroDivisionError: 'integer division or modulo by zero', (Pdb) import sys, (Pdb) print sys.last_value, *** AttributeError: 'module' object has no attribute 'last_value' - it does not exist (python 2.6)Polyvalent
Per the documentation, that value is only set if the exception was fatal.Mirisola
@zneak, Could you share the documentation url?Dhobi
@falsetru, you literally posted it yourself: docs.python.org/2/library/sys.html#sys.last_valueMirisola
@zneak, But, there's no mentation about fatal.Dhobi
@falsetru, what is your interpretation of "These three variables are not always defined; they are set when an exception is not handled and the interpreter prints an error message and a stack traceback"?Mirisola
@zneak, Ah, sorry for my poor English reading. Added that part in the answer. Thank you.Dhobi
I appreciate the attention that this is getting; but note that it still doesn't work when tracing through pdb. Try this and see if you can get the exception from the (pdb) prompt. Alternatively, set_trace() before 1/0, hit n and see if you can get the exception there.Mirisola
@zneak, Isn't try .. except ... handing exception ?Dhobi
Yes, but what if my catch clause was like (ZeroDivisionError, IOError, ValueError) and I wanted to get the complete message without adding a temporary ... as exc from pdb? As OP says: "I'd like to examine the exception before I modify the code."Mirisola
@zneak, How about import sys, then !sys.exc_info()? (didn't put this in the answer because OP already tried sys.exc_info() and got (None, None, None).Dhobi
@Dhobi My comment was using pdb in a script, not the shell, similar to zneak's pastebin after your comment @-ing mePolyvalent
R
16

You can retrieve the latest exception in pdb/ipdb via:

__exception__

The above is actually a tuple of the (exception, message).

Runabout answered 29/3, 2018 at 19:53 Comment(4)
"NameError: name '__exception__' is not defined"Pouncey
same, python 3.8.xAtrioventricular
Works for me on Google Colab. Thanks!Marissamarist
This worked for me when I started pdb first and then triggered the exception by stepping through the code. It did not work for me when the exception is triggered before starting pdb (e.g. with pdb.post_mortem).Subclass
F
15

Is this what you are looking for?

import pdb
try:
    1/0
except Exception as err:
    pdb.set_trace()

% test.py
--Return--
> /home/unutbu/pybin/test.py(8)<module>()->None
-> pdb.set_trace()
(Pdb) err
ZeroDivisionError('integer division or modulo by zero',)
(Pdb) quit

If you do not want to modify the code where the exception originates, you could instead redefine sys.excepthook:

import pdb
import sys
def excepthook(type, value, traceback):
    pdb.set_trace()
sys.excepthook = excepthook

1/0

% test.py
--Return--
> /home/unutbu/pybin/test.py(7)excepthook()->None
-> pdb.set_trace()
(Pdb) type
<type 'exceptions.ZeroDivisionError'>
(Pdb) value
ZeroDivisionError('integer division or modulo by zero',)
(Pdb) traceback
<traceback object at 0xb774f52c>
(Pdb) 
Furfural answered 6/10, 2013 at 16:24 Comment(0)
D
12

You can use sys.last_value:

>>> no_such_var
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'no_such_var' is not defined
>>> import sys
>>> sys.last_value
NameError("name 'no_such_var' is not defined",)
>>> sys.last_value.args
("name 'no_such_var' is not defined",)

>>> no_such_var
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'no_such_var' is not defined
>>> import pdb, sys
>>> pdb.set_trace()
--Return--
> <stdin>(1)<module>()->None
(Pdb) sys.last_value
NameError("name 'no_such_var' is not defined",)

NOTE: This solution is not perfect. The value is set when an exception is not handled and the interpreter prints an error message and a stack traceback. For example, if the exception is caught using try .. except .., sys.last_value is not set.

Dhobi answered 6/10, 2013 at 16:36 Comment(14)
I get: *** AttributeError: 'module' object has no attribute 'last_value'Selfconscious
@claymation, That attribute is not set if there was no exception. Execute a statement that causes an exception. For example: 1/0Dhobi
@Dhobi -> print 1 / 0, (Pdb) n, ZeroDivisionError: 'integer division or modulo by zero', (Pdb) import sys, (Pdb) print sys.last_value, *** AttributeError: 'module' object has no attribute 'last_value' - it does not exist (python 2.6)Polyvalent
Per the documentation, that value is only set if the exception was fatal.Mirisola
@zneak, Could you share the documentation url?Dhobi
@falsetru, you literally posted it yourself: docs.python.org/2/library/sys.html#sys.last_valueMirisola
@zneak, But, there's no mentation about fatal.Dhobi
@falsetru, what is your interpretation of "These three variables are not always defined; they are set when an exception is not handled and the interpreter prints an error message and a stack traceback"?Mirisola
@zneak, Ah, sorry for my poor English reading. Added that part in the answer. Thank you.Dhobi
I appreciate the attention that this is getting; but note that it still doesn't work when tracing through pdb. Try this and see if you can get the exception from the (pdb) prompt. Alternatively, set_trace() before 1/0, hit n and see if you can get the exception there.Mirisola
@zneak, Isn't try .. except ... handing exception ?Dhobi
Yes, but what if my catch clause was like (ZeroDivisionError, IOError, ValueError) and I wanted to get the complete message without adding a temporary ... as exc from pdb? As OP says: "I'd like to examine the exception before I modify the code."Mirisola
@zneak, How about import sys, then !sys.exc_info()? (didn't put this in the answer because OP already tried sys.exc_info() and got (None, None, None).Dhobi
@Dhobi My comment was using pdb in a script, not the shell, similar to zneak's pastebin after your comment @-ing mePolyvalent
B
3

You can run the script through pdb via python -m pdb -c continue script.py. It will enter post-mortem debugging on an uncaught exception and drop you in the pdb interface. Here you can examine sys.exc_info() in order to get the exception. For example:

$ echo "1 / 0" > script.py 
$ python -m pdb -c continue script.py 
Traceback (most recent call last):
  [...]
  File "/tmp/script.py", line 1, in <module>
    1 / 0
ZeroDivisionError: division by zero
Uncaught exception. Entering post mortem debugging
Running 'cont' or 'step' will restart the program
> /tmp/script.py(1)<module>()
-> 1 / 0
(Pdb) !import sys
(Pdb) p sys.exc_info()
(<class 'ZeroDivisionError'>, ZeroDivisionError('division by zero'), <traceback object at 0x7f3adcf09148>)
(Pdb) interact
*interactive*
>>> import sys
>>> sys.exc_info()
(<class 'ZeroDivisionError'>, ZeroDivisionError('division by zero'), <traceback object at 0x7f3adcf09148>)
Bespangle answered 28/10, 2019 at 13:21 Comment(3)
I only figured this out like two weeks ago myself, after using python for 12 years. It's actually a wonderful solution when available.Washedout
(<class 'AttributeError'>, AttributeError("'Pdb' object has no attribute 'do_sys'"), <traceback object at 0x10dabf800>) on python 3.10Labroid
^ For the above I ended up having to use !sys.exc_info() which apparently executes on a different frame.Labroid
S
2

I bumped into this post, but none of the answers did what I needed, which was to repeat all of the error information (including the traceback) that was spewed before the pdb.pm() step in the question. Here's what worked for me in the end:

Add the following lines to your .pdbrc file:

import sys
import traceback
alias rethrow traceback.print_exception(sys.last_type, sys.last_value, sys.last_traceback)

Now, next time you're in a pdb environment, you can just type rethrow, and it will repeat all the error information from before you had typed pdb.pm().

Subak answered 8/12, 2020 at 13:23 Comment(1)
This is the only solution that worked for me (python 3.8)Munition

© 2022 - 2024 — McMap. All rights reserved.