E.g. this code:
def foo():
x = 5
raise
def bar():
try:
foo()
except:
# access x here
is it possible to access x somehow? Thanks.
CodePudding user response:
Depending on your use case, the more sensible approach would be to create a custom exception and give it the data:
class MyException(Exception):
def __init__(self, x, *args, **kwargs):
super().__init__(*args, **kwargs)
self.x = x
def foo():
x = 5
raise MyException(x)
def bar():
try:
foo()
except MyException as e:
print(e.x)
If you really need to be able to access all local variables, you can traverse the traceback of the exception to retrieve the locals of each stack frame. Here is a simple example that would work for your specific example:
def bar():
try:
foo()
except Exception as e: # BaseException if you want to catch *everything*
frame = e.__traceback__.tb_next.tb_frame
print(frame.f_locals["x"])
Generally, the next frame will not necessarily be the one that raised the exception. You can traverse until you reach that frame like so:
import traceback
def bar():
try:
foo()
except Exception as e:
# Get the last frame yielded, which will be where the exception was raised.
*_, (frame, _) = traceback.walk_tb(e.__traceback__)
print(frame.f_locals["x"])
CodePudding user response:
Acces to the code
object of the function via __code__
attribute:
co_varnames
: tuple of names of arguments and local variablesco_consts
: tuple of constants used in the bytecode
This method could be useful to debug or to do dirty hack or to learn more about... python language!
It is not a universal solution but it depends on the body of the function under investigation. A more sophisticated tool that can used is inspect
from the standard library.
def foo():
x = 5
raise
def bar():
try:
foo()
except:
local_var = {foo.__code__.co_varnames[0]: foo.__code__.co_consts[1]}
print(local_var.items())
bar()
#dict_items([('x', 5)])