Can I get the local variables of a Python function from which an exception was thrown?
Question:
I’m writing a custom logging system for a project. If a function throws an exception, I want to log its local variables. Is it possible to access the raising function’s local variables from the except block that caught the exception? For example:
def myfunction():
v1 = get_a_value()
raise Exception()
try:
myfunction()
except:
# can I access v1 from here?
Answers:
def myFunction()
v1 = get_a_value()
raise Exception(v1)
try:
myFunction()
except Exception, e:
v1 = e.args[0]
Yes, if it’s your own exception type. Like this:
>>> class MyExn(Exception):
... def __init__(self, val):
... self.val = val
... def __str__(self):
... print "something wrong with:", str(self.val)
...
>>> def foo():
... val = 42
... raise MyExn(val)
...
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in foo
__main__.MyExnsomething wrong with: 42
>>> # Or in a try block:
>>> try:
... foo()
... except MyExn as e:
... print e.val
...
42
>>>
It’s generally cleaner design to pass the value to the exception, if you know that your exception handling code is going to need it. However, if you’re writing a debugger or something like that, where you will need to access variables without knowing which ones they are in advance, you can access an arbitrary variable in the context where the exception was thrown:
def myfunction():
v1 = get_a_value()
raise Exception()
try:
myfunction()
except:
# can I access v1 from here?
v1 = inspect.trace()[-1][0].f_locals['v1']
The functionality of the trace
function, and the format of the traceback
objects it deals with, are described in the inspect
module documentation.
try:
myfunction()
except:
import sys
type, value, tb = sys.exc_info()
while tb.tb_next:
tb = tb.tb_next
frame = tb.tb_frame
print frame.f_locals['v1']
You can look up the local variables in the frame object, which you can get from sys.exc_info
.
>>> import sys
>>> def f(a):
... b = a - 1
... print 1.0 / b
...
>>> try:
... f(1)
... except Exception, e:
... print sys.exc_info()[2].tb_next.tb_frame.f_locals
...
{'a': 1, 'b': 0}
You’ll have to include the appropriate number of tb_next
s depending on from how deep in the stack the exception was thrown.
Useful for “handling” exceptions without using raise Exception(some_def_var)
and also without using a library such as “inspect”, for example.
However, I think if is to handle the exception “in real”, might be better to use something like raise Exception(some_def_var)
. See @capfredf answer.
class MyClass():
def __init__(self):
self.my_attr_I = ""
self.my_attr_II = ""
def my_def_I(self):
try:
self.my_def_II()
except Exception as e:
print(self.my_attr_I)
print(self.my_attr_II)
def my_def_II(self):
self.my_attr_I = "TO EXCEPT I"
self.my_attr_II = "TO except II"
# NOTE: We will treat it as a general exception! By Questor
zero_division = 1/0
I’m writing a custom logging system for a project. If a function throws an exception, I want to log its local variables. Is it possible to access the raising function’s local variables from the except block that caught the exception? For example:
def myfunction():
v1 = get_a_value()
raise Exception()
try:
myfunction()
except:
# can I access v1 from here?
def myFunction()
v1 = get_a_value()
raise Exception(v1)
try:
myFunction()
except Exception, e:
v1 = e.args[0]
Yes, if it’s your own exception type. Like this:
>>> class MyExn(Exception):
... def __init__(self, val):
... self.val = val
... def __str__(self):
... print "something wrong with:", str(self.val)
...
>>> def foo():
... val = 42
... raise MyExn(val)
...
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in foo
__main__.MyExnsomething wrong with: 42
>>> # Or in a try block:
>>> try:
... foo()
... except MyExn as e:
... print e.val
...
42
>>>
It’s generally cleaner design to pass the value to the exception, if you know that your exception handling code is going to need it. However, if you’re writing a debugger or something like that, where you will need to access variables without knowing which ones they are in advance, you can access an arbitrary variable in the context where the exception was thrown:
def myfunction():
v1 = get_a_value()
raise Exception()
try:
myfunction()
except:
# can I access v1 from here?
v1 = inspect.trace()[-1][0].f_locals['v1']
The functionality of the trace
function, and the format of the traceback
objects it deals with, are described in the inspect
module documentation.
try:
myfunction()
except:
import sys
type, value, tb = sys.exc_info()
while tb.tb_next:
tb = tb.tb_next
frame = tb.tb_frame
print frame.f_locals['v1']
You can look up the local variables in the frame object, which you can get from sys.exc_info
.
>>> import sys
>>> def f(a):
... b = a - 1
... print 1.0 / b
...
>>> try:
... f(1)
... except Exception, e:
... print sys.exc_info()[2].tb_next.tb_frame.f_locals
...
{'a': 1, 'b': 0}
You’ll have to include the appropriate number of tb_next
s depending on from how deep in the stack the exception was thrown.
Useful for “handling” exceptions without using raise Exception(some_def_var)
and also without using a library such as “inspect”, for example.
However, I think if is to handle the exception “in real”, might be better to use something like raise Exception(some_def_var)
. See @capfredf answer.
class MyClass():
def __init__(self):
self.my_attr_I = ""
self.my_attr_II = ""
def my_def_I(self):
try:
self.my_def_II()
except Exception as e:
print(self.my_attr_I)
print(self.my_attr_II)
def my_def_II(self):
self.my_attr_I = "TO EXCEPT I"
self.my_attr_II = "TO except II"
# NOTE: We will treat it as a general exception! By Questor
zero_division = 1/0