Reputation: 143
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
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
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
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
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
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