I've been trying to get a specific (and most likely useless) effect on the traceback when I raise an error.
I would like to hide the raise Exception("Message") of the traceback inside my function and instead have my trace indicate the function call that triggered the raise inside the function
def crash(something):
if something > 200:
raise ValueError("Value too large !")
if __name__ == '__main__':
crash(500)
Normally generates :
Traceback (most recent call last):
File "file.py", line 7, in <module>
crash(500)
File "file.py", line 3, in crash
raise ValueError("Value too large !")
ValueError: Value too large !
Instead I would like to have this :
Traceback (most recent call last):
File "file.py", line 7, in <module>
crash(500)
ValueError: Value too large !
I find it clearer for the user trying to use the module because it clearly shows where the problem is as otherwise he could potentially think the module itself is at fault
I tried to find solution that would "remove" the last call from the traceback but it always had "side effects" like showing part of the code trying to remove the last trace call INSIDE the traceback itself, making it even more confusing. Sometimes it would also repeat the the traceback over what I really want.
For example using this :
def crash():
frame = sys._getframe(1)
tb = types.TracebackType(None, frame, frame.f_lasti, frame.f_lineno)
raise ValueError("Wrong value").with_traceback(tb)
Prints this :
Traceback (most recent call last):
File "file.py", line 34, in <module>
crash()
File "file.py", line 9, in crash
raise ValueError("Wrong value").with_traceback(tb)
File "file.py", line 34, in <module>
crash()
ValueError: Wrong value
I also tried using another function to create the trace myself but ended up with a weird behavior
def crash():
raise exception_no_raise(ValueError("Wrong value"))
def exception_no_raise(exc: Exception):
tb = None
depth = 0
while True:
try:
sys._getframe(depth)
depth += 1
except ValueError:
break
# for i in range(depth-1, 1, -1):
# frame = sys._getframe(i)
# tb = types.TracebackType(tb, frame, frame.f_lasti, frame.f_lineno)
# traceback.print_tb(tb)
# print(file=sys.stderr)
frame = sys._getframe(depth-1)
tb = types.TracebackType(tb, frame, frame.f_lasti, frame.f_lineno)
return exc.with_traceback(tb)
Prints :
Traceback (most recent call last):
File "file.py", line 32, in <module>
crash()
File "file.py", line 7, in crash
raise exception_no_raise(ValueError("Wrong value"))
File "file.py", line 32, in <module>
crash()
ValueError: Wrong value
Even tho when you use traceback.print_tb(tb) (before the return) you get the exact trace I want even tho using it to raise the exception doesn't print it :
File "file.py", line 34, in <module>
crash()
The last solution I found was to "strip" the last traceback from the tb.tb_next chain of traces after catching the exception and then re-raise it (but it showed the raise code anyway so ...)
I have difficulties to grasp how exactly traceback works and it seems that it changes drastically from version to version as I found code from Python 2 and 3 and also code the works in Python 3.8 and not before (related to the fact that you couldn't write to the tb.next of a trace before
Thanks for your help and clarifications !