3

Assume I have the following script:

def do_not_call_on_one(i):
    if i == 1:
        raise ValueError("You never listen.")

    print "Success!"

do_not_call_on_one(1)

On excution, you will see the following traceback:

Traceback (most recent call last):
  File "test.py", line 7, in <module>
    do_not_call_on_one(1)
  File "test.py", line 3, in do_not_call_on_one
    raise ValueError("You never listen.")
ValueError: You never listen.

Is there some way to manipulate the call stack, such that the error is emitted from the line which is actually causing the problem, like so?:

Traceback (most recent call last):
  File "test.py", line 7, in <module>
    do_not_call_on_one(1)
ValueError: You never listen.

This would save developer time that would otherwise be wasted scanning the call stack, searching for the incorrectly used function, when the correct behavior could be defined ahead of time.

Is there anything in Python that would allow an exception to use a modified traceback?

Update

There are buitins which are replicating this functionality:

# In test.py:
int('a')

# Executing 'python test.py' yields:
Traceback (most recent call last):
  File "test.py", line 1, in <module>
    int('a')
ValueError: invalid literal for int() with base 10: 'a'

Note: The traceback does not descend into the int() function to display a bunch of useless scopes (especially the unhelpful raise ValueError itself).

Zearin
  • 1,474
  • 2
  • 17
  • 36
gepoch
  • 711
  • 6
  • 17
  • It is helpful to know exactly where an error was thrown, which is information you would be getting rid of. If the same error could be thrown from two different points you wouldn't know exactly where to look. – FastTurtle Jun 20 '13 at 20:38
  • Thanks JAB. I must work on my search-fu :'( – gepoch Jun 20 '13 at 20:55
  • 2
    No, this is not a duplicate. For one thing, the linked article is from the perspective of the library USER who doesn't want to SEE library internals; this is from the perspective of the library AUTHOR who doesn't want to SHOW internals. For another, it's QUITE a valid thing to do: Perl does it all the time in the Carp module. Has Python nothing similar? – Ian Mar 25 '16 at 19:43
  • This is not a duplicate of the above; but do check out https://stackoverflow.com/questions/8275745/warnings-from-callers-perspective-aka-python-equivalent-of-perls-carp . – ijw Jul 06 '18 at 22:06

2 Answers2

3

tl;dr: probably yes, but I can't imagine that it's a good idea.

I'm going to assume that we could, if we tried hard enough, fake up a callstack to show an exception coming from a different place. But I don't think that's a good idea.

In the first place, it's generally understood that the function which raises an exception is not always at fault. If somebody broke the contract and passed you a parameter which you don't expect, it's okay to raise an exception. If this is intended to cover your ass by raising the exception in the caller's scope so nobody blames your function, I think that somebody (maybe you, maybe your automated testing's "blame" system) needs to re-think how they determine responsibility.

In the second place, I don't think you can define the "right" scope very well. You might imagine that it should always be raised in your caller's scope, because clearly it's not your fault. Well, what if it's not their fault either? Should every function just throw up their hands and say "wasn't my fault" when an exception occurs? Pretty soon our stacktraces will say nothing at all.

Even if you're right, and your function is blameless, you're about to make everybody else's life hell, because manipulating the callstack to hide your function will make everybody scratch their heads, and remove valuable evidence for debugging the situation.

This might seem like a good idea, but I don't think it is, and I think if you put yourself in someone else's shoes you could understand how difficult it would make their life to try to use a function that behaved this way.

mattbornski
  • 11,895
  • 4
  • 31
  • 25
  • It's actually really easy to customize uncaught exception tracebacks in Python, as I just discovered. http://docs.python.org/2/library/sys.html#sys.excepthook "The handling of such top-level exceptions can be customized by assigning another three-argument function to `sys.excepthook`." – JAB Jun 20 '13 at 20:51
1

I would suggest catching the exception at the point you want to be at the top of the call stack and then raise a new exception wrapping the old one (so that you can reference the original call stack to know what exception actually caused the problem; after all, what happens when print "Success!" fails because, say, somebody set sys.stdout = int?).


After some browsing, it seems that https://stackoverflow.com/a/2615442/138772 is exactly the kind of answer you want.

Community
  • 1
  • 1
JAB
  • 20,783
  • 6
  • 71
  • 80