Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Exception Handling in Python 3

251 views
Skip to first unread message

Steve Holden

unread,
Oct 24, 2010, 1:01:43 AM10/24/10
to pytho...@python.org
I was somewhat surprised to discover that Python 3 no longer allows an
exception to be raised in an except clause (or rather that it reports it
as a separate exception that occurred during the handling of the first).

So the following code:

>>> d = {}
>>> try:
... val = d['nosuch']
... except:
... raise AttributeError("No attribute 'nosuch'")
...

Give the traceback I expected and wanted in Python 2:

Traceback (most recent call last):
File "<stdin>", line 4, in <module>
AttributeError: No attribute 'nosuch'

but in Python 3.1 the traceback looks like this:

Traceback (most recent call last):
File "<stdin>", line 2, in <module>
KeyError: 'nosuch'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "<stdin>", line 4, in <module>
AttributeError: No attribute 'nosuch'

Modifying the code a little allows me to change the error message, but
not much else:

>>> d = {}
>>> try:
... val = d['nosuch']
... except KeyError as e:
... raise AttributeError("No attribute 'nosuch'") from e
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
KeyError: 'nosuch'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "<stdin>", line 4, in <module>
AttributeError: No attribute 'nosuch'
>>>

In a class's __getattr__() method this means that instead of being able
to say

try:
value = _attrs[name]
except KeyError:
raise AttributeError ...

I am forced to write

if name not in _attrs:
raise AttributeError ...
value = _attrs[name]

which requires an unnecessary second lookup on the attribute name. What
is the correct paradigm for this situation?

regards
Steve
--
Steve Holden +1 571 484 6266 +1 800 494 3119
PyCon 2011 Atlanta March 9-17 http://us.pycon.org/
See Python Video! http://python.mirocommunity.org/
Holden Web LLC http://www.holdenweb.com/

Chris Rebert

unread,
Oct 24, 2010, 1:26:23 AM10/24/10
to Steve Holden, pytho...@python.org
On Sat, Oct 23, 2010 at 10:01 PM, Steve Holden <st...@holdenweb.com> wrote:
> I was somewhat surprised to discover that Python 3 no longer allows an
> exception to be raised in an except clause (or rather that it reports it
> as a separate exception that occurred during the handling of the first).
<snip>

> Give the traceback I expected and wanted in Python 2:
>
> Traceback (most recent call last):
>  File "<stdin>", line 4, in <module>
> AttributeError: No attribute 'nosuch'
>
> but in Python 3.1 the traceback looks like this:
>
> Traceback (most recent call last):
>  File "<stdin>", line 2, in <module>
> KeyError: 'nosuch'
>
> During handling of the above exception, another exception occurred:
>
> Traceback (most recent call last):
>  File "<stdin>", line 4, in <module>
> AttributeError: No attribute 'nosuch'
>
> Modifying the code a little allows me to change the error message, but
> not much else:
<snip>

> What
> is the correct paradigm for this situation?

There doesn't seem to be one at the moment, although the issue isn't
very serious. Your Traceback is merely being made slightly longer/more
complicated than you'd prefer; however, conversely, what if a bug was
to be introduced into your exception handler? Then you'd likely very
much appreciate the "superfluous" Traceback info.

Your quandary is due to the unresolved status of the "Open Issue:
Suppressing Context" in PEP 3141
(http://www.python.org/dev/peps/pep-3134/ ). I guess you could start a
discussion about closing that issue somehow.

Cheers,
Chris
--
http://blog.rebertia.com

Steve Holden

unread,
Oct 24, 2010, 1:42:33 AM10/24/10
to pytho...@python.org
On 10/24/2010 1:26 AM, Chris Rebert wrote:
>> I was somewhat surprised to discover that Python 3 no longer allows an
>> > exception to be raised in an except clause (or rather that it reports it
>> > as a separate exception that occurred during the handling of the first).
> <snip>
[snip]

>> > What
>> > is the correct paradigm for this situation?
> There doesn't seem to be one at the moment, although the issue isn't
> very serious. Your Traceback is merely being made slightly longer/more
> complicated than you'd prefer; however, conversely, what if a bug was
> to be introduced into your exception handler? Then you'd likely very
> much appreciate the "superfluous" Traceback info.
>
> Your quandary is due to the unresolved status of the "Open Issue:
> Suppressing Context" in PEP 3141
> (http://www.python.org/dev/peps/pep-3134/ ). I guess you could start a
> discussion about closing that issue somehow.

You're right about the issue not being serious, (and about the possible
solution, though I don't relish a lengthy discussion on python-dev) but
it does seem that there ought to be some way to suppress that
__context__. From the user's point of view the fact that I am raising
AttributeError because of some implementation detail of __getattr__() is
exposing *way* too much information.

I even tried calling sys.exc_clear(), but alas that doesn't help :(

Lawrence D'Oliveiro

unread,
Oct 24, 2010, 2:22:39 AM10/24/10
to
In message <mailman.176.1287896...@python.org>, Steve
Holden wrote:

> I was somewhat surprised to discover that Python 3 no longer allows an
> exception to be raised in an except clause (or rather that it reports it
> as a separate exception that occurred during the handling of the first).

So what exactly is the problem? Exceptions are so easy to get wrong, it’s
just trying to report more info in a complicated situation to help you track
down the problem. Why is that bad?

> In a class's __getattr__() method this means that instead of being able
> to say
>
> try:
> value = _attrs[name]
> except KeyError:
> raise AttributeError ...
>
> I am forced to write
>
> if name not in _attrs:
> raise AttributeError ...
> value = _attrs[name]

I don’t see why. Presumably if you caught the exception in an outer try-
except clause, you would pick up your AttributeError, not the KeyError,
right? Which is what you want, right?

Martin v. Loewis

unread,
Oct 24, 2010, 4:48:23 AM10/24/10
to Steve Holden
Am 24.10.2010 07:01, schrieb Steve Holden:
> I was somewhat surprised to discover that Python 3 no longer allows an
> exception to be raised in an except clause (or rather that it reports it
> as a separate exception that occurred during the handling of the first).

I think you are misinterpreting what you are seeing. The exception being
raised actually *is* an attribute error, and it actually is the
attribute error that gets reported. It's only that reporting an
exception that has a __context__ first reports the context, then reports
the actual exception.

You may now wonder whether it is possible to set __context__ to None
somehow. See PEP 3134:

Open Issue: Suppressing Context

As written, this PEP makes it impossible to suppress '__context__',
since setting exc.__context__ to None in an 'except' or 'finally'
clause will only result in it being set again when exc is raised.

Regards,
Martin

Peter Otten

unread,
Oct 24, 2010, 6:34:00 AM10/24/10
to pytho...@python.org
Steve Holden wrote:

> On 10/24/2010 1:26 AM, Chris Rebert wrote:

>>> I was somewhat surprised to discover that Python 3 no longer allows an
>>> > exception to be raised in an except clause (or rather that it reports
>>> > it as a separate exception that occurred during the handling of the
>>> > first).

>> <snip>
> [snip]
>>> > What
>>> > is the correct paradigm for this situation?
>> There doesn't seem to be one at the moment, although the issue isn't
>> very serious. Your Traceback is merely being made slightly longer/more
>> complicated than you'd prefer; however, conversely, what if a bug was
>> to be introduced into your exception handler? Then you'd likely very
>> much appreciate the "superfluous" Traceback info.
>>
>> Your quandary is due to the unresolved status of the "Open Issue:
>> Suppressing Context" in PEP 3141
>> (http://www.python.org/dev/peps/pep-3134/ ). I guess you could start a
>> discussion about closing that issue somehow.
>
> You're right about the issue not being serious, (and about the possible
> solution, though I don't relish a lengthy discussion on python-dev) but
> it does seem that there ought to be some way to suppress that
> __context__. From the user's point of view the fact that I am raising
> AttributeError because of some implementation detail of __getattr__() is
> exposing *way* too much information.
>
> I even tried calling sys.exc_clear(), but alas that doesn't help :(

You can install a custom excepthook:

>>> import traceback, sys
>>> from functools import partial
>>> def f():
... try: 1/0
... except: raise AttributeError
...
>>> f()


Traceback (most recent call last):

File "<stdin>", line 2, in f
ZeroDivisionError: int division or modulo by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):

File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in f
AttributeError
>>> sys.excepthook = partial(traceback.print_exception, chain=False)
>>> f()


Traceback (most recent call last):

File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in f
AttributeError

Peter

Steve Holden

unread,
Oct 24, 2010, 8:28:33 AM10/24/10
to Martin v. Loewis, Python List
On 10/24/2010 4:48 AM, Martin v. Loewis wrote:
> Am 24.10.2010 07:01, schrieb Steve Holden:
>> I was somewhat surprised to discover that Python 3 no longer allows an
>> exception to be raised in an except clause (or rather that it reports it
>> as a separate exception that occurred during the handling of the first).
>
> I think you are misinterpreting what you are seeing. The exception being
> raised actually *is* an attribute error, and it actually is the
> attribute error that gets reported. It's only that reporting an
> exception that has a __context__ first reports the context, then reports
> the actual exception.
>
I don't believe I *am* misinterpreting it. The fact of the matter is
that the context is irrelevant to the user, and there should be some way
to suppress it to avoid over-complicating the traceback.

This behavior is quite reasonable during testing, but I would prefer to
exclude an explicit raise directly in the except handler since that is
hardly to be construed as accidental (whereas an exception in a function
called in the handler perhaps *should* be reported).

> You may now wonder whether it is possible to set __context__ to None
> somehow. See PEP 3134:
>
> Open Issue: Suppressing Context
>
> As written, this PEP makes it impossible to suppress '__context__',
> since setting exc.__context__ to None in an 'except' or 'finally'
> clause will only result in it being set again when exc is raised.
>

I have already read that. Peter Otten has separately explained how to
suppress the behavior using sys.excepthook, which appears to be a
halfway satisfactory solution.

Steve Holden

unread,
Oct 24, 2010, 8:36:04 AM10/24/10
to pytho...@python.org

Yes, *if the exception is caught* then it doesn't make any difference.
If the exception creates a traceback, however, I maintain that the
additional information is confusing to the consumer (while helpful to
the debugger of the consumed code).

I don't want people to think this is a big deal, however. It was just an
"eh?" that I thought must mean I was missing some way of suppressing the
additional traceback. Peter Otten has already provided a solution using
sys.except_hook().

Lie Ryan

unread,
Oct 24, 2010, 11:46:21 AM10/24/10
to
On 10/24/10 16:01, Steve Holden wrote:
> I was somewhat surprised to discover that Python 3 no longer allows an
> exception to be raised in an except clause (or rather that it reports it
> as a separate exception that occurred during the handling of the first).

FYI, Java has a similar behavior. In Java, however, after a certain
length, some of the older exceptions will be suppressed and will only
print message informing that there are more exceptions above it.

John Nagle

unread,
Oct 24, 2010, 4:44:13 PM10/24/10
to
On 10/23/2010 10:42 PM, Steve Holden wrote:
> On 10/24/2010 1:26 AM, Chris Rebert wrote:
>>> I was somewhat surprised to discover that Python 3 no longer
>>> allows an
>>>> exception to be raised in an except clause (or rather that it
>>>> reports it as a separate exception that occurred during the
>>>> handling of the first).
>> <snip>
> [snip]
>>>> What is the correct paradigm for this situation?
>> There doesn't seem to be one at the moment, although the issue
>> isn't very serious. Your Traceback is merely being made slightly
>> longer/more complicated than you'd prefer; however, conversely,
>> what if a bug was to be introduced into your exception handler?
>> Then you'd likely very much appreciate the "superfluous" Traceback
>> info.
>>
>> Your quandary is due to the unresolved status of the "Open Issue:
>> Suppressing Context" in PEP 3141
>> (http://www.python.org/dev/peps/pep-3134/ ). I guess you could
>> start a discussion about closing that issue somehow.
>

This is a traceback issue only, right? The semantics of
the code below shouldn't change in Py3.x, I hope:

try :
...
try :
x = 1/0 # in a real program, input data might cause a problem
except ZeroDivisionError as msg:
raise RuntimeError("Math error on problem: " + str(msg))
except RuntimeError as msg :
print("Trouble: " + str(msg))

I have code where I'm reading and parsing a web page,
a process which can produce a wide range of errors. A try-block
around the read and parse catches the various errors and creates a
single user-defined "bad web page" exception object, which is then
raised. That gets caught further out, and is used to record the
troubled web page, schedule it for a retest, and such. This is
normal program operation, indicative of external problems, not a
code error or cause for program termination with a traceback.

Are exception semantics changing in a way which would affect that?

John Nagle

Steve Holden

unread,
Oct 24, 2010, 5:48:49 PM10/24/10
to pytho...@python.org
On 10/24/2010 4:44 PM, John Nagle wrote:
> Are exception semantics changing in a way which would affect that?

No, I don't believe so. I simply felt that the traceback gives too much
information in the case where an exception is specifically being raised
to replace the one currently being handled.

Ben Finney

unread,
Oct 24, 2010, 7:51:59 PM10/24/10
to
Steve Holden <st...@holdenweb.com> writes:

> I simply felt that the traceback gives too much information in the
> case where an exception is specifically being raised to replace the
> one currently being handled.

Ideally, that description of the problem would suggest the obvious
solution: replace the class of the exception and allow the object to
continue up the exception handler stack.

But that doesn't work either::

>>> d = {}
>>> try:
... val = d['nosuch']

... except KeyError as exc:
... exc.__class__ = AttributeError
... raise exc
...


Traceback (most recent call last):

File "<stdin>", line 2, in <module>
KeyError: 'nosuch'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):

File "<stdin>", line 4, in <module>
TypeError: __class__ assignment: only for heap types

which means, AFAICT, that re-binding ‘__class__’ is only allowed for
objects of a type defined in the Python run-time heap, not those defined
in C code (like the built-in-exception types).

--
\ “I wish there was a knob on the TV to turn up the intelligence. |
`\ There's a knob called ‘brightness’ but it doesn't work.” |
_o__) —Eugene P. Gallagher |
Ben Finney

Lawrence D'Oliveiro

unread,
Oct 24, 2010, 9:20:29 PM10/24/10
to
In message <mailman.190.1287924...@python.org>, Steve
Holden wrote:

> Yes, *if the exception is caught* then it doesn't make any difference.
> If the exception creates a traceback, however, I maintain that the
> additional information is confusing to the consumer (while helpful to
> the debugger of the consumed code).

Who needs the information more?

Message has been deleted

Martin v. Loewis

unread,
Oct 25, 2010, 2:57:52 AM10/25/10
to Steve Holden
Am 24.10.2010 23:48, schrieb Steve Holden:
> On 10/24/2010 4:44 PM, John Nagle wrote:
>> Are exception semantics changing in a way which would affect that?
>
> No, I don't believe so. I simply felt that the traceback gives too much
> information in the case where an exception is specifically being raised
> to replace the one currently being handled.

I think you have puzzled readers a lot (including me) with the statement:

"that Python 3 no longer allows an exception to be raised in an except
clause"

That certainly isn't the case.

Regards,
Martin

Steve Holden

unread,
Oct 25, 2010, 3:17:07 AM10/25/10
to pytho...@python.org
Of course it isn't. I believe the only readers puzzled by my assertion
would be those who did not read the parenthesized comment immediately
following the sentence you quoted, which read:

> (or rather that it reports it as a separate exception that occurred
> during the handling of the first)

I understand that this behavior is deliberate. I just don't feel that it
is universally helpful.

Gregory Ewing

unread,
Oct 29, 2010, 5:21:33 AM10/29/10
to
Steve Holden wrote:

> Yeah, that's a given. Ruby would probably let you do that, but Python
> insists that you don't dick around with the built-in types. And roghtly
> so, IMHO.

Some restrictions on this are necessary -- it obviously
wouldn't be safe to allow replacing the class of an
object with one having an incompatible C layout, and
most built-in types have their own unique layout.

I think it's easier in Ruby, because all objects in
Ruby look pretty much the same at the C level. Not
sure of the details, though.

--
Greg

Gregory Ewing

unread,
Oct 29, 2010, 5:30:39 AM10/29/10
to
Chris Rebert wrote:
> Your Traceback is merely being made slightly longer/more
> complicated than you'd prefer; however, conversely, what if a bug was
> to be introduced into your exception handler? Then you'd likely very
> much appreciate the "superfluous" Traceback info.

I think what's disturbing about this is that the two halves of
the extended traceback are printed in the wrong order. We're
all used to looking down the bottom of the traceback to see
where the error originated, but with the new format, that point
is buried somewhere in the middle.

--
Greg

Chris Rebert

unread,
Oct 29, 2010, 6:24:40 AM10/29/10
to Gregory Ewing, pytho...@python.org

True, but swapping the order would only worsen Steve's problem. Most
of his users presumably won't care about the underlying KeyError and
would rather be presented with the AttributeError as the proximate
"origin", despite that being technically inaccurate in the way you
suggest. Six of one, half dozen of the other though.

Cheers,
Chris

Antoine Pitrou

unread,
Oct 29, 2010, 7:02:52 AM10/29/10
to pytho...@python.org
On Sun, 24 Oct 2010 10:48:23 +0200
"Martin v. Loewis" <mar...@v.loewis.de> wrote:
>
> You may now wonder whether it is possible to set __context__ to None
> somehow. See PEP 3134:
>
> Open Issue: Suppressing Context
>
> As written, this PEP makes it impossible to suppress '__context__',
> since setting exc.__context__ to None in an 'except' or 'finally'
> clause will only result in it being set again when exc is raised.

It is not easily discoverable, but it is possible to suppress
__context__ by using a bare re-raise afterwards:

>>> try:
... try: 1/0
... except ZeroDivisionError: raise KeyError
... except BaseException as e:
... e.__context__ = None
... raise


...
Traceback (most recent call last):

File "<stdin>", line 3, in <module>
KeyError


Regards

Antoine.


Chris Rebert

unread,
Oct 29, 2010, 7:07:15 AM10/29/10
to Antoine Pitrou, pytho...@python.org
On Fri, Oct 29, 2010 at 4:02 AM, Antoine Pitrou <soli...@pitrou.net> wrote:
> On Sun, 24 Oct 2010 10:48:23 +0200
> "Martin v. Loewis" <mar...@v.loewis.de> wrote:
>>
>> You may now wonder whether it is possible to set __context__ to None
>> somehow. See PEP 3134:
>>
>> Open Issue: Suppressing Context
>>
>>     As written, this PEP makes it impossible to suppress '__context__',
>>     since setting exc.__context__ to None in an 'except' or 'finally'
>>     clause will only result in it being set again when exc is raised.
>
> It is not easily discoverable, but it is possible to suppress
> __context__ by using a bare re-raise afterwards:
>
>>>> try:
> ...   try: 1/0
> ...   except ZeroDivisionError: raise KeyError
> ... except BaseException as e:
> ...   e.__context__ = None
> ...   raise
> ...
> Traceback (most recent call last):
>  File "<stdin>", line 3, in <module>
> KeyError

So, how/why does that work?

Cheers,
Chris

Antoine Pitrou

unread,
Oct 29, 2010, 7:59:46 AM10/29/10
to pytho...@python.org
On Fri, 29 Oct 2010 04:07:15 -0700
Chris Rebert <cl...@rebertia.com> wrote:

> On Fri, Oct 29, 2010 at 4:02 AM, Antoine Pitrou <soli...@pitrou.net> wrote:
> > On Sun, 24 Oct 2010 10:48:23 +0200
> > "Martin v. Loewis" <mar...@v.loewis.de> wrote:
> >>

> >> You may now wonder whether it is possible to set __context__ to None
> >> somehow. See PEP 3134:
> >>
> >> Open Issue: Suppressing Context
> >>
> >>     As written, this PEP makes it impossible to suppress '__context__',
> >>     since setting exc.__context__ to None in an 'except' or 'finally'
> >>     clause will only result in it being set again when exc is raised.
> >

> > It is not easily discoverable, but it is possible to suppress
> > __context__ by using a bare re-raise afterwards:
> >
> >>>> try:
> > ...   try: 1/0
> > ...   except ZeroDivisionError: raise KeyError
> > ... except BaseException as e:
> > ...   e.__context__ = None
> > ...   raise

> > ...
> > Traceback (most recent call last):

> >  File "<stdin>", line 3, in <module>
> > KeyError
>
> So, how/why does that work?

A bare "raise" simply re-raises the currently known exception without
changing anything (neither the traceback nor the context).

This __context__ problem is mostly theoretical, anyway. If you want to
present exceptions to users in a different way, you can write a
catch-all except clause at the root of your program and use the
traceback module to do what you want.

Regards

Antoine.

Martin v. Loewis

unread,
Oct 29, 2010, 10:31:17 AM10/29/10
to Antoine Pitrou, Steve Holden
> It is not easily discoverable, but it is possible to suppress
> __context__ by using a bare re-raise afterwards:

I see. I'd wrap this like this:

def raise_no_context(e):
try:
raise e
except:
e.__context__=None
raise

d = {}
try:
val = d['nosuch']
except KeyError:
raise_no_context(AttributeError("No attribute 'nosuch'"))

The downside of this is that the innermost frame will be
raise_no_context, but I suppose that's ok because even the
next-inner frame already reveals implementation details that
developers have learned to ignore.

Regards,
Martin

Message has been deleted

MRAB

unread,
Oct 29, 2010, 1:19:23 PM10/29/10
to pytho...@python.org
On 29/10/2010 11:24, Chris Rebert wrote:
> On Fri, Oct 29, 2010 at 2:30 AM, Gregory Ewing
> <greg....@canterbury.ac.nz> wrote:
> True, but swapping the order would only worsen Steve's problem. Most
> of his users presumably won't care about the underlying KeyError and
> would rather be presented with the AttributeError as the proximate
> "origin", despite that being technically inaccurate in the way you
> suggest. Six of one, half dozen of the other though.
>
I've just come across the same problem myself.

I wanted to raise an exception that would be more meaningful to the
caller, but the traceback included info on the original exception,
which is an implementation detail.

I understand that it can be useful, but IMHO there should be a simple
way of suppressing it.

MRAB

unread,
Oct 29, 2010, 1:41:20 PM10/29/10
to pytho...@python.org
On 24/10/2010 13:28, Steve Holden wrote:

> On 10/24/2010 4:48 AM, Martin v. Loewis wrote:
>> Am 24.10.2010 07:01, schrieb Steve Holden:
>>> I was somewhat surprised to discover that Python 3 no longer allows an
>>> exception to be raised in an except clause (or rather that it reports it
>>> as a separate exception that occurred during the handling of the first).
>>
>> I think you are misinterpreting what you are seeing. The exception being
>> raised actually *is* an attribute error, and it actually is the
>> attribute error that gets reported. It's only that reporting an
>> exception that has a __context__ first reports the context, then reports
>> the actual exception.
>>
> I don't believe I *am* misinterpreting it. The fact of the matter is
> that the context is irrelevant to the user, and there should be some way
> to suppress it to avoid over-complicating the traceback.
>
> This behavior is quite reasonable during testing, but I would prefer to
> exclude an explicit raise directly in the except handler since that is
> hardly to be construed as accidental (whereas an exception in a function
> called in the handler perhaps *should* be reported).
>
>> You may now wonder whether it is possible to set __context__ to None
>> somehow. See PEP 3134:
>>
>> Open Issue: Suppressing Context
>>
>> As written, this PEP makes it impossible to suppress '__context__',
>> since setting exc.__context__ to None in an 'except' or 'finally'
>> clause will only result in it being set again when exc is raised.
>>
> I have already read that. Peter Otten has separately explained how to
> suppress the behavior using sys.excepthook, which appears to be a
> halfway satisfactory solution.
>
Suggestion: an explicit 'raise' in the exception handler excludes the
context, but if you want to include it then 'raise with'. For example:

# Exclude the context
try:
command_dict[command]()
except KeyError:
raise CommandError("Unknown command")

# Include the context
try:
command_dict[command]()
except KeyError:
raise with CommandError("Unknown command")

Ethan Furman

unread,
Oct 29, 2010, 1:57:25 PM10/29/10
to pytho...@python.org
MRAB wrote:
> On 24/10/2010 13:28, Steve Holden wrote:
>> On 10/24/2010 4:48 AM, Martin v. Loewis wrote:
>>> Am 24.10.2010 07:01, schrieb Steve Holden:
>>>> I was somewhat surprised to discover that Python 3 no longer allows an
>>>> exception to be raised in an except clause (or rather that it
>>>> reports it
>>>> as a separate exception that occurred during the handling of the
>>>> first).
>>>
>>> I think you are misinterpreting what you are seeing. The exception being
>>> raised actually *is* an attribute error, and it actually is the
>>> attribute error that gets reported. It's only that reporting an
>>> exception that has a __context__ first reports the context, then reports
>>> the actual exception.
>>>
>> I don't believe I *am* misinterpreting it. The fact of the matter is
>> that the context is irrelevant to the user, and there should be some way
>> to suppress it to avoid over-complicating the traceback.
>>
>> This behavior is quite reasonable during testing, but I would prefer to
>> exclude an explicit raise directly in the except handler since that is
>> hardly to be construed as accidental (whereas an exception in a function
>> called in the handler perhaps *should* be reported).
>>
>>> You may now wonder whether it is possible to set __context__ to None
>>> somehow. See PEP 3134:
>>>
>>> Open Issue: Suppressing Context
>>>
>>> As written, this PEP makes it impossible to suppress '__context__',
>>> since setting exc.__context__ to None in an 'except' or 'finally'
>>> clause will only result in it being set again when exc is raised.
>>>
>> I have already read that. Peter Otten has separately explained how to
>> suppress the behavior using sys.excepthook, which appears to be a
>> halfway satisfactory solution.
>>
> Suggestion: an explicit 'raise' in the exception handler excludes the
> context, but if you want to include it then 'raise with'. For example:
>
> # Exclude the context
> try:
> command_dict[command]()
> except KeyError:
> raise CommandError("Unknown command")
>
> # Include the context
> try:
> command_dict[command]()
> except KeyError:
> raise with CommandError("Unknown command")

+1

Presumably, this would also keep the context if an actual error occured.

~Ethan~

Ethan Furman

unread,
Oct 29, 2010, 1:56:28 PM10/29/10
to pytho...@python.org
MRAB wrote:
> On 29/10/2010 11:24, Chris Rebert wrote:
>> On Fri, Oct 29, 2010 at 2:30 AM, Gregory Ewing
>> <greg....@canterbury.ac.nz> wrote:
>> True, but swapping the order would only worsen Steve's problem. Most
>> of his users presumably won't care about the underlying KeyError and
>> would rather be presented with the AttributeError as the proximate
>> "origin", despite that being technically inaccurate in the way you
>> suggest. Six of one, half dozen of the other though.
>>
> I've just come across the same problem myself.
>
> I wanted to raise an exception that would be more meaningful to the
> caller, but the traceback included info on the original exception,
> which is an implementation detail.
>
> I understand that it can be useful, but IMHO there should be a simple
> way of suppressing it.

I agree. It seems to me that the suppression should happen on the raise
line, so that we aren't losing the extra information if an actual error
occurs in the error handler.

~Ethan~

Greg Ewing

unread,
Oct 29, 2010, 7:14:07 PM10/29/10
to pytho...@python.org
Chris Rebert wrote:
> On Fri, Oct 29, 2010 at 2:30 AM, Gregory Ewing
> <greg....@canterbury.ac.nz> wrote:

>>I think what's disturbing about this is that the two halves of
>>the extended traceback are printed in the wrong order. We're

> True, but swapping the order would only worsen Steve's problem.

Yes, I can see that what Steve's problem requires is a way
of explicitly saying "replace the current exception" without
attaching any context.

However, in the case where the replacement is accidental,
I think it would make more sense to display them in the
opposite order. Both of the exceptions represent bugs in
that situation, so you will want to address them both, and
you might as well get the traceback in a non-confusing order.

--
Greg

Lawrence D'Oliveiro

unread,
Oct 31, 2010, 7:24:05 PM10/31/10
to
In message <mailman.372.1288353...@python.org>, Antoine
Pitrou wrote:

> If you want to present exceptions to users in a different way ...

sys.stderr.write \
(
"Traceback (most recent call last):\n"
...
"AttributeError: blah blah blah ...\n"
)

John Nagle

unread,
Nov 1, 2010, 1:56:51 PM11/1/10
to
On 10/24/2010 5:36 AM, Steve Holden wrote:
> On 10/24/2010 2:22 AM, Lawrence D'Oliveiro wrote:
>> In message<mailman.176.1287896...@python.org>, Steve
>> Holden wrote:

> Yes, *if the exception is caught* then it doesn't make any difference.
> If the exception creates a traceback, however, I maintain that the
> additional information is confusing to the consumer (while helpful to
> the debugger of the consumed code).

If an exception propagates all the way out to the top level and
all the user gets is a system traceback, the program isn't very
good. Really. If you're concerned about the display format of
Python tracebacks seen by end users, you have bigger problems
with your code.

If it's a server-side program, you need to catch
exceptions and log them somewhere, traceback and all. If it's
a client-side GUI program, letting an exception unwind all the
way out of the program loses whatever the user was doing.

It's usually desirable to at least catch EnviromentError near
the top level of your program. If some external problem causes a
program abort, the user should get an error message, not a traceback.
If it's a program bug, you can let the traceback unwind, displaying
information that should be sent in with a bug report.

John Nagle

John Ladasky

unread,
Nov 6, 2010, 12:10:14 AM11/6/10
to
On Oct 29, 8:53 am, rantingrick <rantingr...@gmail.com> wrote:

> I am the programmer, and when i say to my interpretor "show this
> exception instead of that exception" i expect my interpretor to do
> exactly as i say or risk total annihilation!! I don't want my
> interpreter "interpreting" my intentions and then doing what it
> "thinks" is best for me. I have a wife already, i don't need a virtual
> one!

OK, Ranting Rick, that was funny, and worthy of your name. :^)

0 new messages