How to pop the interpreter's stack?

Steven D'Aprano steve+comp.lang.python at pearwood.info
Mon Dec 27 00:38:26 EST 2010


On Sun, 26 Dec 2010 17:12:50 -0800, misnomer at gmail.com wrote:

> On Dec 23, 3:22 am, Steven D'Aprano <steve
> +comp.lang.pyt... at pearwood.info> wrote:
>> You seem to have completely missed that there will be no bug report,
>> because this isn't a bug. (Or if it is a bug, the bug is elsewhere,
>> external to the function that raises the exception.) It is part of the
>> promised API. The fact that the exception is generated deep down some
>> chain of function calls is irrelevant.
> 
> While the idea of being able to do this (create a general validation
> exception) sounds mildly appealing at first, what happens when the
> module implementing this documented API and documented error, has a bug?
> It seems that the user, or even module developer in the midst of
> writing, would now have no tools to easily tackle the problem, and no
> useful information to submit in the required bug report.

That's very true, but the same applies to *any* use of encapsulation. Any 
time you hide information, you hide information (duh!). This doesn't stop 
us from doing this:

def public(x):
    if condition: return _private(x)
    elif other_condition: return _more_private(x+1)
    else: return _yet_another_private(x-1)

If you call public(42), and get the wrong answer, it's a bug, but the 
source of the bug is hidden from the caller. If you have access to the 
source code, you can work out where the bug lies (which of the three 
private functions is buggy?) given the argument, but the return result 
itself does not expose any information about where the bug lies. This is 
considered an unavoidable but acceptable side-effect of an otherwise 
desirable state of affairs: information hiding and encapsulation. The 
caller being unaware of where and how the result is calculated is 
considered a good thing, and the fact that it occasionally adds to the 
debugging effort is considered such a trivial cost that it normally isn't 
remarked upon, except by lunatics and die-hard fans of spaghetti code 
using GOTO. But I repeat myself.

Why should exceptions *necessarily* be different? As I've repeatedly 
acknowledged, for an unexpected exception (a bug), the developer needs 
all the help he can get, and the current behaviour is the right way to do 
it. You won't hear me argue differently. But for a documented, explicit, 
expected, deliberate exception, Python breaks encapsulation by exposing 
the internal details of any internal subroutines used to generate that 
exception. This leads to messy tracebacks that obscure the source of bugs 
in the caller's code:

>>> import re
>>> re.compile(r"(")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.6/re.py", line 190, in compile
    return _compile(pattern, flags)
  File "/usr/lib/python2.6/re.py", line 245, in _compile
    raise error, v # invalid expression
sre_constants.error: unbalanced parenthesis

I think critics of my position have forgotten what it's like to learning 
the language. One of the most valuable skills to learn is to *ignore 
parts of the traceback* -- a skill that takes practice and familiarity 
with the library or function in use. To those who are less familiar with 
the function, it can be very difficult to determine which parts of the 
traceback are relevant and which aren't. In this case, the caller has 
nothing to do with _compile, and the traceback looks like it's an 
internal bug in a subroutine, when in fact it is actually due to bad 
input. The experienced developer learns (by trial and error, possibly) to 
ignore nearly half of the error message in this case.

In principle, the traceback could be roughly half as big, which means the 
caller would find it half as difficult to read and understand:

>>> re.compile(r"(")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.6/re.py", line 190, in compile
    raise error, v # invalid expression
sre_constants.error: unbalanced parenthesis

With a one-to-one correspondence between the function called, and the 
function reporting an error, it is easier to recognise that the error 
lies in the input rather than some internal error in some subroutine you 
have nothing to do with. Unfortunately there's no straightforward way to 
consistently get this in Python without giving up the advantages of 
delegating work to subroutines.

It need not be that way. This could, in principle, be left up to the 
developer of the public function to specify (somehow!) that some specific 
exceptions are expected, and should be treated as coming from public() 
rather than from some internal subroutine. I don't have a concrete 
proposal for such, although I did suggest a work-around. I expected 
disinterest ("I don't see the point"). I didn't expect the level of 
hostility to the idea that exceptions should (if and when possible) point 
to the source of the error rather than some accidental implementation-
specific subroutine. Go figure.


-- 
Steven



More information about the Python-list mailing list