Home > Enterprise >  Is it possible to access objects from a method which have just raised an exception?
Is it possible to access objects from a method which have just raised an exception?

Time:07-10

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 variables

  • co_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)])
  • Related