How to pop the interpreter's stack?

kj no.email at please.post
Wed Dec 22 11:52:38 EST 2010


In <mailman.65.1292517591.6505.python-list at python.org> Robert Kern <robert.kern at gmail.com> writes:

>Obfuscating the location that an exception gets raised prevents a lot of 
>debugging...

The Python interpreter does a lot of that "obfuscation" already, and I
find the resulting tracebacks more useful for it.

An error message is only useful to a given audience if that audience
can use the information in the message to modify what they are
doing to avoid the error.  It is of no use (certainly no *immediate*
use) to this audience to see tracebacks that go deep into code that
they don't know anything about and cannot change.

For example, consider this:

#-----------------------------------------------------------------
def foo(x, **k): pass

def bar(*a, **k):
    if len(a) > 1: raise TypeError('too many args')

def baz(*a, **k): _pre_baz(*a, **k)

def _pre_baz(*a, **k):
    if len(a) > 1: raise TypeError('too many args')

if __name__ == '__main__':
    from traceback import print_exc
    try: foo(1, 2)
    except: print_exc()
    try: bar(1, 2)
    except: print_exc()
    try: baz(1, 2)
    except: print_exc()
#-----------------------------------------------------------------


(The code in the "if __name__ == '__main__'" section is meant to
simulate the general case in which the functions defined in this file
are called by third-party code.)  When you run this code the output is
this (a few blank lines added for clarity):

Traceback (most recent call last):
  File "/tmp/ex2.py", line 5, in <module>
    try: foo(1, 2)
TypeError: foo() takes exactly 1 argument (2 given)

Traceback (most recent call last):
  File "/tmp/ex2.py", line 7, in <module>
    try: bar(1, 2)
  File "/tmp/example.py", line 4, in bar
    if len(a) > 1: raise TypeError('too many args')
TypeError: too many args

Traceback (most recent call last):
  File "/tmp/ex2.py", line 9, in <module>
    try: baz(1, 2)
  File "/tmp/example.py", line 6, in baz
    def baz(*a, **k): _pre_baz(*a, **k)
  File "/tmp/example.py", line 9, in _pre_baz
    if len(a) > 1: raise TypeError('too many args')
TypeError: too many args


In all cases, the *programming* errors are identical: functions called
with the wrong arguments.  The traceback from foo(1, 2) tells me this
very clearly, and I'm glad that Python is not also giving me the
traceback down to where the underlying C code throws the exception: I
don't need to see all this machinery.

In contrast, the tracebacks from bar(1, 2) and baz(1, 2) obscure the
fundamental problem with useless detail.  From the point of view of
the programmer that is using these functions, it is of no use to know
that the error resulted from some "raise TypeError" statement
somewhere, let alone that this happened in some obscure, private
function _pre_baz.

Perhaps I should have made it clearer in my original post that the
tracebacks I want to clean up are those from exceptions *explicitly*
raised by my argument-validating helper function, analogous to
_pre_baz above.  I.e. I want that when my spam function is called
(by code written by someone else) with the wrong arguments, the
traceback looks more like this

Traceback (most recent call last):
  File "/some/python/code.py", line 123, in <module>
    spam(some, bad, args)
TypeError: the second argument is bad


than like this:

Traceback (most recent call last):
  File "/some/python/code.py", line 123, in <module>
    spam(some, bad, args)
  File "/my/niftymodule.py", line 456, in niftymodule
    _pre_spam(*a, **k)
  File "/my/niftymodule.py", line 789, in __pre_spam
    raise TypeError('second argument to spam is bad')
TypeError: the second argument is bad


In my opinion, the idea that more is always better in a traceback
is flat out wrong.  As the example above illustrates, the most
useful traceback is the one that stops at the deepest point where
the *intended audience* for the traceback can take action, and goes
no further.  The intended audience for the errors generated by my
argument-checking functions should see no further than the point
where they called a function incorrectly.

~kj



More information about the Python-list mailing list