chexxmex
chexxmex

Reputation: 143

Exceptions: How to display which function caused the exception?

I have a code block that looks like:

try:
    if x == 1:
        a()
    if x == 2:
        b()
    if x == 3:
        c()
except Exception:
    raise Exception("Problem in function")

Each of the functions a(), b()and c() are capable of having exceptions. Is there anyway I can raise an exception with a message that tells which function caused the exception.

I want to know if its possible here without using try, catch statements inside each of these functions separately.

Upvotes: 2

Views: 1626

Answers (5)

martineau
martineau

Reputation: 123463

Here's a generic way to do which uses the traceback.extract_tb() function to get a list of “pre-processed” stack trace entries which have a subset of what's in a TracebackException object in them from the most recent exception (this is the same information that's in tracebacks when they're display. The last entry contains information about the last function called (i.e. the most recent one) including its name.

import traceback

def a():
    pass

def b():
    raise RuntimeError('Oops!')

def c():
    pass


x = 2
try:
    if x == 1:
        a()
    if x == 2:
        b()
    if x == 3:
        c()
except Exception as exc:
    filename, lineno, funcname, text = traceback.extract_tb(exc.__traceback__)[-1]
    raise Exception(f"Problem in function {funcname}()") from exc

Here the output it displays:

Traceback (most recent call last):
  File "display-which-function-caused-the-exception.py", line 22, in <module>
    b()
  File "display-which-function-caused-the-exception.py", line 10, in b
    raise RuntimeError('Oops!')
RuntimeError: Oops!

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "display-which-function-caused-the-exception.py", line 27, in <module>
    raise Exception(f"Problem in function {funcname}()") from exc
Exception: Problem in function b()

Upvotes: 4

martineau
martineau

Reputation: 123463

You could define a decorator function that wrapped the function of interest so that the information needed was available (this is a variation of @chepner's answer):


def decorator(func):
    wraps(func)
    def wrapper(*args, **kwargs):
        global func_name
        func_name = func.__name__
        return func(*args, **kwargs)
    return wrapper


@decorator
def a():
    pass

@decorator
def b():
    raise RuntimeError('Uh Oh!')

@decorator
def a():
    pass


x = 2

try:
    if x == 1:
        a()
    if x == 2:
        b()
    if x == 3:
        c()
except Exception as exc:
    raise Exception(f"Problem in function {func_name}()")

Upvotes: 0

Nabil
Nabil

Reputation: 1278

You can try

import traceback
try:
    if x == 1:
        a()
    if x == 2:
        b()
    if x == 3:
        c()
except Exception as excp:
    stack = traceback.extract_stack()[:-3] + traceback.extract_tb(excp.__traceback__) 
    
    (filename, line, procname, text) = stack[-1]
    print (f" func {procname}")# 
   

    raise Exception("Problem in function")

Upvotes: 0

chepner
chepner

Reputation: 531165

This is probably too specific to your example, but I would in general try to refactor the code and make a specific note of which function is going to be executed.

if x == 1:
    func_name = "a"
    func = a
elif x == 2:
    func_name = "b"
    func = b
elif x == 3:
    func_name = "c"
    func = c

try:
    func()
except Exception:
    raise Exception(f"Problem in {func_name}")

You may be able to get rid of func_name and use func.__name__ instead, as long as all three functions are being referred by their "original" name.

Upvotes: 0

Ben Grossmann
Ben Grossmann

Reputation: 4772

The first thing I would think of is to try something like this:

try:
    if x == 1:
        a()
    if x == 2:
        b()
    if x == 3:
        c()
except Exception:
    raise Exception("Problem in function, x=%s"%x)

Now, when the exception is raised, you can see what the value of x was.

For something more detailed, perhaps something like this:

try:
    if x == 1:
        flabel = 'func_a'
        a()
    if x == 2:
        flabel = 'func_b'
        b()
    if x == 3:
        flable = 'func_c'
        c()
except Exception:
    raise Exception("Problem in function %s"%flabel)

Upvotes: 0

Related Questions