GeneratorExit inheriting from Exception
Should GeneratorExit inherit from Exception or BaseException? Currently, a generator that catches Exception and continues on to yield another value can't be closed properly (you get a runtime error pointing out that the generator ignored GeneratorExit). The only decent reference I could find to it in the old PEP 348/352 discussions is Guido writing [1]:
when GeneratorExit or StopIteration reach the outer level of an app, it's a bug like all the others that bare 'except:' WANTS to catch.
(at that point in the conversation, I believe bare except was considered the equivalent of "except Exception:") While I agree with what Guido says about GeneratorExit being a bug if it reaches the outer level of an app, it seems like a bit of a trap that a correctly written generator can't write "except Exception:" without preceding it with an "except GeneratorExit:" that reraises the exception. Isn't that exactly the idiom we're trying to get rid of for SystemExit and KeyboardInterrupt? Regards, Nick. [1] http://mail.python.org/pipermail/python-dev/2005-August/055173.html -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
On Sat, 2006-03-18 at 22:53 +1000, Nick Coghlan wrote:
Should GeneratorExit inherit from Exception or BaseException?
Actually, this prompts me to write about an issue I have with PEP 352. I actually don't think it's necessary (yes, I know it's already in the tree). What I would much rather is is for StandardError to be renamed Error, for Exception to remain the base class of the exception hierarchy, and for KeyboardInterrupt to be moved to inherit directly from Exception. GeneratorExit, SystemExit, and StopIteration would continue to inherit from Exception. The reasoning is this: anything that can be raised is an Exception. Not all Exceptions are Errors. Anything that signals an error condition is an Error, and anything that signals a warning condition is a Warning. Thus the basic hierarchy /ought/ to be: Exception +- KeyboardInterrupt +- GeneratorExit +- SystemExit +- StopIteration +- Error | +- ImportError | +- (etc.) | +- Warning +- UserWarning +- (etc.) Use defined errors should inherit from Error, not Exception. With this, "except Exception" would be a synonym for bare except, while "except Error" would be the standard idiom for letting non-error exceptions pass through. I don't know whether this is possible for Python 2.5, but I think it should be what we strive for for Py3K, and I do not think BaseException is at all necessary. -Barry
Barry Warsaw wrote:
On Sat, 2006-03-18 at 22:53 +1000, Nick Coghlan wrote:
Should GeneratorExit inherit from Exception or BaseException?
Actually, this prompts me to write about an issue I have with PEP 352. I actually don't think it's necessary (yes, I know it's already in the tree).
What I would much rather is is for StandardError to be renamed Error, for Exception to remain the base class of the exception hierarchy, and for KeyboardInterrupt to be moved to inherit directly from Exception. GeneratorExit, SystemExit, and StopIteration would continue to inherit from Exception.
The reasoning is this: anything that can be raised is an Exception. Not all Exceptions are Errors. Anything that signals an error condition is an Error, and anything that signals a warning condition is a Warning. Thus the basic hierarchy /ought/ to be:
Exception +- KeyboardInterrupt +- GeneratorExit +- SystemExit +- StopIteration +- Error | +- ImportError | +- (etc.) | +- Warning +- UserWarning +- (etc.)
Cool! That's so far the clearest solution. An additional bonus is that except statements look nicer: except: # still catches all Exceptions, just like except Exception: except Error: # is what you normally should do Cheers, Georg
Georg Brandl <g.brandl@gmx.net> wrote:
Exception +- KeyboardInterrupt +- GeneratorExit +- SystemExit +- StopIteration +- Error
+- ImportError +- (etc.)
+- Warning +- UserWarning +- (etc.)
Cool! That's so far the clearest solution. An additional bonus is that except statements look nicer:
except: # still catches all Exceptions, just like except Exception:
except Error: # is what you normally should do
+1 on the general idea, I just don't specifically like that "except:" is the "wrong" thing to do: part of the PEP352 idea was that people writing "except:" out of ignorance would still not cause their program to intercept KeyboardInterrupt, or StopIteration. Unless this new proposal also includes changing the meaning of "except:" to "except Error". Also, under this new proposal, we could even remove Exception from the builtins namespace in Py3k. It's almost always wrong to use it, and if you really really need it, it's spelled exceptions.Exception. -- Giovanni Bajo
On Sat, 2006-03-18 at 19:32 +0100, Giovanni Bajo wrote:
+1 on the general idea, I just don't specifically like that "except:" is the "wrong" thing to do: part of the PEP352 idea was that people writing "except:" out of ignorance would still not cause their program to intercept KeyboardInterrupt, or StopIteration.
Unless this new proposal also includes changing the meaning of "except:" to "except Error".
It's worth debating. OT1H, it's a semantic different for Python 2.x (although +1 on the idea for Py3K). OTOH, I think people generally want to just catch errors and not all exceptions. Going along with that, maybe the interpreter should do something different when an Exception that's not an Error reaches the top (e.g. not print a traceback if KeyboardInterrupt is seen -- we usually just catch that, print "Interrupted" and exit).
Also, under this new proposal, we could even remove Exception from the builtins namespace in Py3k. It's almost always wrong to use it, and if you really really need it, it's spelled exceptions.Exception.
I'm not sure I'd go as far as hiding Exception, since I don't think the penalty is that great and it makes it easier to document. -Barry
Barry Warsaw wrote:
Unless this new proposal also includes changing the meaning of "except:" to "except Error".
It's worth debating. OT1H, it's a semantic different for Python 2.x (although +1 on the idea for Py3K).
I was speaking of Py3K here, yes.
Going along with that, maybe the interpreter should do something different when an Exception that's not an Error reaches the top (e.g. not print a traceback if KeyboardInterrupt is seen -- we usually just catch that, print "Interrupted" and exit).
SystemExit is already special-cased, as far as I can tell. KeyboardInterrupt could be in fact be special cased as well (I saw many Python newbies -- but otherwise experienced -- being disgusted at first when they interrupt their code with CTRL+C: they expect the program to exit "almost silently").
Also, under this new proposal, we could even remove Exception from the builtins namespace in Py3k. It's almost always wrong to use it, and if you really really need it, it's spelled exceptions.Exception.
I'm not sure I'd go as far as hiding Exception, since I don't think the penalty is that great and it makes it easier to document.
The situation (in Py3k) I was thinking is when people see this code: except: # something and want to change it so to get a name to the exception object. I *think* many could get confused and write: except Exception, e: # something which changes the meaning. It "sounds" correct, but it's wrong. Of course, it's easy to argue that "Exception" is just that, and people actually meant "Error". In a way, the current PEP352 is superior here because it makes harder to do the "bad" thing by giving it a complex name (BaseException). Giovanni Bajo
Giovanni Bajo wrote:
The situation (in Py3k) I was thinking is when people see this code:
except: # something
and want to change it so to get a name to the exception object. I *think* many could get confused and write:
except Exception, e: # something
If except clauses are changed to use "as", then as long as we're still allowing bare excepts, that could become except as e: ... Greg
Barry Warsaw wrote:
On Sat, 2006-03-18 at 22:53 +1000, Nick Coghlan wrote:
Should GeneratorExit inherit from Exception or BaseException?
Actually, this prompts me to write about an issue I have with PEP 352. I actually don't think it's necessary (yes, I know it's already in the tree).
What I would much rather is is for StandardError to be renamed Error, for Exception to remain the base class of the exception hierarchy, and for KeyboardInterrupt to be moved to inherit directly from Exception. GeneratorExit, SystemExit, and StopIteration would continue to inherit from Exception.
The reasoning is this: anything that can be raised is an Exception. Not all Exceptions are Errors. Anything that signals an error condition is an Error, and anything that signals a warning condition is a Warning. Thus the basic hierarchy /ought/ to be:
Exception +- KeyboardInterrupt +- GeneratorExit +- SystemExit +- StopIteration +- Error | +- ImportError | +- (etc.) | +- Warning +- UserWarning +- (etc.)
Use defined errors should inherit from Error, not Exception. With this, "except Exception" would be a synonym for bare except, while "except Error" would be the standard idiom for letting non-error exceptions pass through.
I don't know whether this is possible for Python 2.5,
well, one thing to consider is all the class MyException(Exception): in current code.
but I think it should be what we strive for for Py3K, and I do not think BaseException is at all necessary.
-Barry
------------------------------------------------------------------------
_______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/pedronis%40strakt.com
On Sat, 2006-03-18 at 16:50 +0100, Samuele Pedroni wrote:
I don't know whether this is possible for Python 2.5,
well, one thing to consider is all the
class MyException(Exception):
in current code.
Yep, which is why I'm not sure we can do this for Py2.5. However as PEP 352 talks about a transition plan for Py3k, I think we should document the ultimate desired hierarchy (and maybe implement that in the p3yk branch ;), and then think about how to transition to it in Py2.5. One possible approach is to revert BaseException out of Py2.5, re-position KeyboardInterrupt, and add Error as an alias for StandardError. Then we can encourage people to start using Error as the base classes for their own errors. -Barry
Barry Warsaw wrote:
One possible approach is to revert BaseException out of Py2.5, re-position KeyboardInterrupt, and add Error as an alias for StandardError. Then we can encourage people to start using Error as the base classes for their own errors.
Also maybe start issuing warnings whenever you inherit directly from Exception. Greg
Greg Ewing wrote:
Barry Warsaw wrote:
One possible approach is to revert BaseException out of Py2.5, re-position KeyboardInterrupt, and add Error as an alias for StandardError. Then we can encourage people to start using Error as the base classes for their own errors.
Also maybe start issuing warnings whenever you inherit directly from Exception.
Ugh. I hate it when it's made (virtually) impossible to write code that runs warnings-free on both Python X.Y and X.(Y+1). Just
Just van Rossum wrote:
Greg Ewing wrote:
Barry Warsaw wrote:
One possible approach is to revert BaseException out of Py2.5, re-position KeyboardInterrupt, and add Error as an alias for StandardError. Then we can encourage people to start using Error as the base classes for their own errors. Also maybe start issuing warnings whenever you inherit directly from Exception.
Ugh. I hate it when it's made (virtually) impossible to write code that runs warnings-free on both Python X.Y and X.(Y+1).
This was one of the key things that led to the approach in PEP 352. Barry's hierarchy is very similar to some of the suggestions made during the PEP 348 discussion, and they were rejected because they made the transition a hell of a lot harder without any concomitant benefit. I know this because I was one of the people making those suggestions. With PEP 352 (tweaked to move GeneratorExit out from under Exception): - "except:" continues to mean catch everything - "except Exception:" now does the right thing - inheriting from Exception continues to be correct for user exceptions - errors may choose to inherit from one of the existing Errors instead With Barry's proposed hierarchy: - "except:" continues to mean catch everything - "except Exception:" continues to do the wrong thing - all code has to change to do "except Error:" instead - inheriting from Exception becomes incorrect for user exceptions - all code has to change to inherit from Error instead - non-error user exceptions (such as completion of a search using nested loops) have no clear parent to inherit from (both Error and Exception are wrong, albeit for different reasons. The additional pain required in order to have 'Exception' at the root of the hierarchy just isn't worth it. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
On Sun, 2006-03-19 at 17:31 +1000, Nick Coghlan wrote:
With PEP 352 (tweaked to move GeneratorExit out from under Exception): - "except:" continues to mean catch everything - "except Exception:" now does the right thing - inheriting from Exception continues to be correct for user exceptions - errors may choose to inherit from one of the existing Errors instead
With Barry's proposed hierarchy: - "except:" continues to mean catch everything - "except Exception:" continues to do the wrong thing - all code has to change to do "except Error:" instead - inheriting from Exception becomes incorrect for user exceptions - all code has to change to inherit from Error instead - non-error user exceptions (such as completion of a search using nested loops) have no clear parent to inherit from (both Error and Exception are wrong, albeit for different reasons.
The additional pain required in order to have 'Exception' at the root of the hierarchy just isn't worth it.
One quibble. Since the term used for the general concept of something that is raised and caught is "exception" and since all the raise-able objects live in a module called "exceptions", it is confusing that "except Exception" will not catch all exceptions. -Barry
Barry Warsaw wrote:
One quibble. Since the term used for the general concept of something that is raised and caught is "exception" and since all the raise-able objects live in a module called "exceptions", it is confusing that "except Exception" will not catch all exceptions.
My thoughts exactly. -- Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | Carpe post meridiam! | Christchurch, New Zealand | (I'm not a morning person.) | greg.ewing@canterbury.ac.nz +--------------------------------------+
Just van Rossum wrote:
Greg Ewing wrote:
Also maybe start issuing warnings whenever you inherit directly from Exception.
Ugh. I hate it when it's made (virtually) impossible to write code that runs warnings-free on both Python X.Y and X.(Y+1).
Yes, that could be a problem. Maybe there could be a __future__ import which says "I know about the new exception scheme, trust me when I inherit from Exception." Greg
Sigh. Enough already. PEP 352 was chosen to minimize incompatibilities and maximize gain with minimal changes in the tree. Also note that Warnings can sometimes be raised and should then treated as errors, so Warning would have to inherit from Error. I vote for the status quo in HEAD, except I've got to think more about the pros and cons of making GeneratorExit as special as KeyboardInterrupt. -- --Guido van Rossum (home page: http://www.python.org/~guido/)
[Guido]
Sigh. Enough already. PEP 352 was chosen to minimize incompatibilities and maximize gain with minimal changes in the tree. Also note that Warnings can sometimes be raised and should then treated as errors, so Warning would have to inherit from Error.
I vote for the status quo in HEAD, except I've got to think more about the pros and cons of making GeneratorExit as special as KeyboardInterrupt.
While Guido is thinking, could one of the proponents please enumerate the reasons for treating GeneratorExit like KeyboardInterrupt and SystemExit. To me, they obviously should be under Exception, and not treated like KeyboardInterrupt or SystemExit, so that probably means that I'm missing something about GeneratorExit. Raymond
Raymond Hettinger wrote:
While Guido is thinking, could one of the proponents please enumerate the reasons for treating GeneratorExit like KeyboardInterrupt and SystemExit.
To me, they obviously should be under Exception, and not treated like KeyboardInterrupt or SystemExit, so that probably means that I'm missing something about GeneratorExit.
If GeneratorExit *isn't* special, then correct catch-all exception handling around a yield expression in a generator looks like: def run_tasks(tasks, failed_tasks) for task in tasks: try: yield task() except GeneratorExit: # don't break gen.close() raise except Exception: failed_tasks.append((task, sys.exc_info())) Having to explicitly reraise a terminating exception like that is the exact idiom we're trying to get rid of for SystemExit and KeyboardInterrupt, so it makes sense to me to avoid it for GeneratorExit as well. Given that the default system-level excepthook *won't* ignore GeneratorExit (and so an error message will still be printed), I see moving it out with the other two terminating exceptions as the best option. The only way for that approach to do the wrong thing is if GeneratorExit is raised outside a generator's pseudothread, at which point I'd be blaming the code that raised it explicitly (the interpreter core only ever raises it as a result of a call to gen.close()). Note that this argument does *not* apply to StopIteration - that should stay under Exception because it should never accidentally leak beyond the code that is calling the next() method. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
On Sun, Mar 19, 2006, Greg Ewing wrote:
Barry Warsaw wrote:
One possible approach is to revert BaseException out of Py2.5, re-position KeyboardInterrupt, and add Error as an alias for StandardError. Then we can encourage people to start using Error as the base classes for their own errors.
Also maybe start issuing warnings whenever you inherit directly from Exception.
-1 -- I occasionally use exceptions as a multi-loop break. That's a perfectly valid Python practice, those exceptions should inherit from Exception, and there should not be any warnings raised. -- Aahz (aahz@pythoncraft.com) <*> http://www.pythoncraft.com/ "19. A language that doesn't affect the way you think about programming, is not worth knowing." --Alan Perlis
On Mon, 2006-03-20 at 08:18 -0800, Aahz wrote:
-1 -- I occasionally use exceptions as a multi-loop break. That's a perfectly valid Python practice, those exceptions should inherit from Exception, and there should not be any warnings raised.
Exactly! But they're not errors, so "except Exception" should catch them but "except Error" <wink> should not. This does speak to a generic ControlFlow (but not ControlFlowError) base class. -Barry
Aahz wrote:
Also maybe start issuing warnings whenever you inherit directly from Exception.
-1 -- I occasionally use exceptions as a multi-loop break. That's a perfectly valid Python practice, those exceptions should inherit from Exception, and there should not be any warnings raised.
There probably should be a ControlFlowException category for these that would also include StopIteration and GeneratorExit. I don't think it should include *all* exceptions other than Errors or Warnings, though. SystemExit and KeyboardInterrupt remain two things that you will almost always not want to catch, even in a top-level catch-almost-everything loop. So I'd leave these two out on their own. -- Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | Carpe post meridiam! | Christchurch, New Zealand | (I'm not a morning person.) | greg.ewing@canterbury.ac.nz +--------------------------------------+
On 3/18/06, Barry Warsaw <barry@python.org> wrote:
On Sat, 2006-03-18 at 22:53 +1000, Nick Coghlan wrote:
Should GeneratorExit inherit from Exception or BaseException?
Actually, this prompts me to write about an issue I have with PEP 352. I actually don't think it's necessary (yes, I know it's already in the tree).
Much to personal pain and sprint time. Are you trying to make me shed a manly tear, Barry?!? =)
What I would much rather is is for StandardError to be renamed Error, for Exception to remain the base class of the exception hierarchy, and for KeyboardInterrupt to be moved to inherit directly from Exception. GeneratorExit, SystemExit, and StopIteration would continue to inherit from Exception.
So it isn't that PEP 352 is unnecessary since there is nothing here about deprecating string execptions and making built-in exceptions new-style. You just want to change the renaming of the exceptions.
The reasoning is this: anything that can be raised is an Exception. Not all Exceptions are Errors. Anything that signals an error condition is an Error, and anything that signals a warning condition is a Warning. Thus the basic hierarchy /ought/ to be:
Exception +- KeyboardInterrupt +- GeneratorExit +- SystemExit +- StopIteration +- Error | +- ImportError | +- (etc.) | +- Warning +- UserWarning +- (etc.)
Use defined errors should inherit from Error, not Exception. With this, "except Exception" would be a synonym for bare except, while "except Error" would be the standard idiom for letting non-error exceptions pass through.
I still like my idea of a ControlFlowException, but that died with PEP 348 (along with part of my innocence).
I don't know whether this is possible for Python 2.5, but I think it should be what we strive for for Py3K, and I do not think BaseException is at all necessary.
I view PEP 352 as the PEP that fixed an issue with overreaching 'except' clauses, officially getting us off of string exceptions, and making built-in exceptions new-style classes. I do not view it as the be-all-end-all hierarchy for Py3K. With that said, I am not changing the PEP. Another PEP that suggests the hierarchy for Py3K can be suggested, but I am not doing it. I am totally burned out on exceptions after two PEPs on the subject matter. Just remember that PEP 348 was meant for Py3K when we are supposed to break stuff and how much resistence I hit. Granted my changes were more radical, but even in the end, small changes were resisted heavily. In other words, make sure you have the time and energy to take this on. =) But the above suggestions seem reasonable, especially if bare 'except' statements go away. So I am supportive of the idea. -Brett
On Sat, 2006-03-18 at 15:37 -0800, Brett Cannon wrote:
Actually, this prompts me to write about an issue I have with PEP 352. I actually don't think it's necessary (yes, I know it's already in the tree).
Much to personal pain and sprint time. Are you trying to make me shed a manly tear, Barry?!? =)
So it isn't that PEP 352 is unnecessary since there is nothing here about deprecating string execptions and making built-in exceptions new-style. You just want to change the renaming of the exceptions.
Yes, sorry Brett! No the other things about PEP 352 are all good, and I was only commenting about the new hierarchy changes. I still don't like that part of the PEP, but I appreciate the competing compromises being made so I can live with it for Python 2.x.
I still like my idea of a ControlFlowException, but that died with PEP 348 (along with part of my innocence).
Good! The sooner you replace that with soul-crushing defeatism the happier you will be!
Just remember that PEP 348 was meant for Py3K when we are supposed to break stuff and how much resistence I hit. Granted my changes were more radical, but even in the end, small changes were resisted heavily. In other words, make sure you have the time and energy to take this on. =)
I know all about soul-crushing PEP championship, which is why I'm such a happy person. :) -Barry
Barry Warsaw wrote:
Exception +- KeyboardInterrupt +- GeneratorExit +- SystemExit +- StopIteration +- Error | +- ImportError | +- (etc.) | +- Warning +- UserWarning +- (etc.)
+42! This is beautifully clear and simple, especially compared to some of the other exception hierarchy reorganisations that have been proposed. Greg
On Sun, 2006-03-19 at 13:49 +1200, Greg Ewing wrote:
Barry Warsaw wrote:
Exception +- KeyboardInterrupt +- GeneratorExit +- SystemExit +- StopIteration +- Error | +- ImportError | +- (etc.) | +- Warning +- UserWarning +- (etc.)
+42! This is beautifully clear and simple, especially compared to some of the other exception hierarchy reorganisations that have been proposed.
I still believe in this, and I'm thankful for the support I've seen. It won't happen for Python 2.x, but I do plan on addressing this for Py3K. -Barry
Exception +- KeyboardInterrupt +- GeneratorExit +- SystemExit +- StopIteration This would look even better to me and be easier to learn and remember if the above specifics were gathered under one general category parallel to Error and Warning. Not sure what. Not NonErrorNonWarning though. SystemException is too long and too specific. Maybe Control? No, I don't have a specific use other than didactic, but that is worth something, and I can imagine that someone else might. +- Error | +- ImportError | +- (etc.) | +- Warning +- UserWarning +- (etc.) Otherwise, looks good to me. Terry Jan Reedy
Terry Reedy wrote:
Exception +- KeyboardInterrupt +- GeneratorExit +- SystemExit +- StopIteration
This would look even better to me and be easier to learn and remember if the above specifics were gathered under one general category parallel to Error and Warning. Not sure what. Not NonErrorNonWarning though. SystemException is too long and too specific. Maybe Control?
No, I don't have a specific use other than didactic, but that is worth something, and I can imagine that someone else might.
I'd vote for ControlFlowException if StopIteration is included in the category, and TerminatingException if it isn't. Regards, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
Barry Warsaw wrote:
Exception +- KeyboardInterrupt +- GeneratorExit +- SystemExit +- StopIteration +- Error | +- ImportError | +- (etc.) | +- Warning +- UserWarning +- (etc.)
Use defined errors should inherit from Error, not Exception. With this, "except Exception" would be a synonym for bare except, while "except Error" would be the standard idiom for letting non-error exceptions pass through.
I don't know whether this is possible for Python 2.5, but I think it should be what we strive for for Py3K, and I do not think BaseException is at all necessary.
The main problems I have with the idea are the fact that a large fraction of the user-defined exceptions out there already inherit from Exception (and this has been pushed for a long time as the right thing to do) and the fact that "except Exception:" has also been pushed for years as the preferred alternative to a bare "except:". Rather than trying to change course midstream, I *like* the fact that the PEP 352 hierarchy introduces BaseException to bring the language itself into line with what people have already been taught. Breaking things in Py3k is all well and good, but breaking them gratuitously is something else entirely :) I also agree with the point made during the PEP 348/352 discussions that StopIteration reaching any except clause that isn't specifically expecting it *is* an error. Direct calls to an iterator's next method need to expect the StopIteration and decide how to handle it - not handling it is a bug. For KeyboardInterrupt/SystemExit/GeneratorExit, it is clear that the standard behaviour should be to reraise them - the entire point of those exceptions is to shut down an entire call stack. StopIteration, on the other hand, merely indicates that a particular iterator has completed, not that the entire stack of iterators should be shut down. Regards, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
Nick Coghlan <ncoghlan@gmail.com> wrote:
Rather than trying to change course midstream, I *like* the fact that the PEP 352 hierarchy introduces BaseException to bring the language itself into line with what people have already been taught. Breaking things in Py3k is all well and good, but breaking them gratuitously is something else entirely :)
I really like this too, but Barry's proposal doesn't really *break* anything. Existing Python code that creates a FooBar(Exception) and then catches it with either "except FooBar:" or "except Exception, e:" + check for FooBar, will still work as expected. At *worse*, it would be catching too much, like SystemExit or GeneratorExit, which are still pretty uncommon exception. OTOH, I also understand that people have been told that deriving from Exception is the right thing to do forever now. Giovanni Bajo
Giovanni Bajo wrote:
OTOH, I also understand that people have been told that deriving from Exception is the right thing to do forever now.
Have we really being telling them to derive *directly* from Exception, or just that deriving somehow from Exception will become mandatory? For the purpose of minimising bare-except problems, recommending direct derivation from Exception seems like a particularly bad idea, whether the exception hierarchy is changed or not. Greg
On 3/19/06, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Have we really being telling them to derive *directly* from Exception, or just that deriving somehow from Exception will become mandatory?
It doesn't matter. Most code that tries to be a good citizen today derives its exceptions from Exception.
For the purpose of minimising bare-except problems, recommending direct derivation from Exception seems like a particularly bad idea, whether the exception hierarchy is changed or not.
Why? With PEP 352 (== HEAD status quo) this works just fine. I have a problem with using Error as the focal point since so many exceptions (user-defined or otherwise) aren't errors. Not to mention the Warnings which sometimes can be raised as Errors (but without changing their type). -- --Guido van Rossum (home page: http://www.python.org/~guido/)
On Sun, 2006-03-19 at 19:18 -0800, Guido van Rossum wrote:
I have a problem with using Error as the focal point since so many exceptions (user-defined or otherwise) aren't errors.
I'm not sure that's totally true in practice. I think most user-defined exceptions are actually errors. Ideally, StandardError would be called Error (or there'd be an alias of that name) and people should be deriving their error exceptions from Error. Their non-error exceptions would be derived from Exception. The last thing I'll suggest in this thread for Python 2.5 is to add an alias called Error for StandardError. Then, users can begin to do the sensible thing (as above). -Barry
Barry Warsaw wrote:
Ideally, StandardError would be called Error ... Their non-error exceptions would be derived from Exception.
Having something called StandardError suggests that there are "non-standard errors" around somewhere. Are we to have Error Police going around making sure everyone's errors are standardised? :=) Also, "standard error" sounds like some sort of statistical term to me... -- Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | Carpe post meridiam! | Christchurch, New Zealand | (I'm not a morning person.) | greg.ewing@canterbury.ac.nz +--------------------------------------+
Nick Coghlan wrote:
Should GeneratorExit inherit from Exception or BaseException?
Currently, a generator that catches Exception and continues on to yield another value can't be closed properly (you get a runtime error pointing out that the generator ignored GeneratorExit).
The only decent reference I could find to it in the old PEP 348/352 discussions is Guido writing [1]:
when GeneratorExit or StopIteration reach the outer level of an app, it's a bug like all the others that bare 'except:' WANTS to catch.
(at that point in the conversation, I believe bare except was considered the equivalent of "except Exception:")
While I agree with what Guido says about GeneratorExit being a bug if it reaches the outer level of an app, it seems like a bit of a trap that a correctly written generator can't write "except Exception:" without preceding it with an "except GeneratorExit:" that reraises the exception. Isn't that exactly the idiom we're trying to get rid of for SystemExit and KeyboardInterrupt?
The last comment I heard from Guido on this topic was that he was still thinking about it. However, I now have an additional data point - if GeneratorExit inherits directly from BaseException, it makes it much easier to write exception handling code in generators that does the right thing on both Python 2.4 and 2.5. In 2.4, PEP 342 hasn't happened, so "except Exception:" can't misbehave in response to GeneratorExit (the latter doesn't exist, and nor does generator finalisation). If GeneratorExit inherits directly from BaseException, the code still does the right thing since the exception isn't caught. OTOH, if GeneratorExit inherits from Exception (as in current SVN), then two things will be needed to make the generator work correctly: 1. add a preceding exception clause to fix Python 2.5 behaviour: except GeneratorExit: raise except Exception: # whatever 2. add header code to the module to make it work again on Python 2.4: try: GeneratorExit except NameError: class GeneratorExit(Exception): pass IMO, that would be an ugly bit of backwards incompatibility (even though I wouldn't expect such broad exception handling in generators to be at all common). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
On 3/25/06, Nick Coghlan <ncoghlan@gmail.com> wrote:
The last comment I heard from Guido on this topic was that he was still thinking about it.
Not exactly. I'm delegating the thinking mostly to others.
However, I now have an additional data point - if GeneratorExit inherits directly from BaseException, it makes it much easier to write exception handling code in generators that does the right thing on both Python 2.4 and 2.5.
In 2.4, PEP 342 hasn't happened, so "except Exception:" can't misbehave in response to GeneratorExit (the latter doesn't exist, and nor does generator finalisation). If GeneratorExit inherits directly from BaseException, the code still does the right thing since the exception isn't caught.
OTOH, if GeneratorExit inherits from Exception (as in current SVN), then two things will be needed to make the generator work correctly:
1. add a preceding exception clause to fix Python 2.5 behaviour: except GeneratorExit: raise except Exception: # whatever
2. add header code to the module to make it work again on Python 2.4:
try: GeneratorExit except NameError: class GeneratorExit(Exception): pass
IMO, that would be an ugly bit of backwards incompatibility (even though I wouldn't expect such broad exception handling in generators to be at all common).
I can't see all that much use for GeneratorExit in code that needs to be compatible with 2.4, since the rest of the machinery that makes exception handling around yield feasible doesn't exist. Rather than speaking of "data points" which are really just "ideas", try to come up with a data point that represents an actual (not made-up) use case to show the difference. I'm saying this because, while I believe there *may* be something here, I also believe that the decision to derive an exception from BaseException instead of Exception should not be taken lightly -- lest we set the wrong example and render the nice feature we're trying to create (that "except Exception"does the right thing almost all of the time) useless. -- --Guido van Rossum (home page: http://www.python.org/~guido/)
Guido van Rossum wrote:
On 3/25/06, Nick Coghlan <ncoghlan@gmail.com> wrote:
OTOH, if GeneratorExit inherits from Exception (as in current SVN), then two things will be needed to make the generator work correctly:
1. add a preceding exception clause to fix Python 2.5 behaviour: except GeneratorExit: raise except Exception: # whatever
2. add header code to the module to make it work again on Python 2.4:
try: GeneratorExit except NameError: class GeneratorExit(Exception): pass
IMO, that would be an ugly bit of backwards incompatibility (even though I wouldn't expect such broad exception handling in generators to be at all common).
I can't see all that much use for GeneratorExit in code that needs to be compatible with 2.4, since the rest of the machinery that makes exception handling around yield feasible doesn't exist.
I agree entirely - my goal is to make sure it stays that way. The kind of code I'm talking about would be an *existing* Python 2.4 generator that happens to do something like: def gen(tasks): """yield the results of a bunch of task functions""" for task in tasks: try: yield (task, task()) except Exception, ex: yield ExceptionOccurred(task, ex) If you run such a generator on Python 2.5, but don't run it to completion before it is garbage collected, you will get an error message printed on stderr saying that an exception was ignored when this generator was cleaned up. If you use the new PEP 342 features to try to explicitly close it before it is garbage collected, you'll get the exception directly. The culprit is the RuntimeError raised when the generator's close() method gets upset because the generator swallowed GeneratorExit. If GeneratorExit inherits directly from BaseException, such unexpected behaviour won't happen - the only way for an existing generator to break is if it contained a bare except clause, and that code was *already* dubious (e.g. it probably swallowed KeyboardInterrupt). I don't have any actual live examples of a generator with a broad exception clause like the one above, but toy generators like the one above are legal in 2.4 and result in spurious errors with current SVN. Regards, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
I can't see all that much use for GeneratorExit in code that needs to be compatible with 2.4, since the rest of the machinery that makes exception handling around yield feasible doesn't exist.
I agree entirely - my goal is to make sure it stays that way.
The kind of code I'm talking about would be an *existing* Python 2.4 generator that happens to do something like:
def gen(tasks): """yield the results of a bunch of task functions""" for task in tasks: try: yield (task, task()) except Exception, ex: yield ExceptionOccurred(task, ex)
If you run such a generator on Python 2.5, but don't run it to completion before it is garbage collected, you will get an error message printed on stderr saying that an exception was ignored when this generator was cleaned up. If you use the new PEP 342 features to try to explicitly close it before it is garbage collected, you'll get the exception directly.
The culprit is the RuntimeError raised when the generator's close() method gets upset because the generator swallowed GeneratorExit.
If GeneratorExit inherits directly from BaseException, such unexpected behaviour won't happen - the only way for an existing generator to break is if it contained a bare except clause, and that code was *already* dubious (e.g. it probably swallowed KeyboardInterrupt).
I don't have any actual live examples of a generator with a broad exception clause like the one above, but toy generators like the one above are legal in 2.4 and result in spurious errors with current SVN.
I can't say that I care enough about this hypothetical inter-version flimflam to warrant mucking-up the otherwise useful distinction between Exception and BaseException. special-cases-aren't-special-enough ... Raymond
On 3/25/06, Nick Coghlan <ncoghlan@gmail.com> wrote:
The kind of code I'm talking about would be an *existing* Python 2.4 generator that happens to do something like:
def gen(tasks): """yield the results of a bunch of task functions""" for task in tasks: try: yield (task, task()) except Exception, ex: yield ExceptionOccurred(task, ex)
This is purely hypothetical. It doesn't look like good style at all.
If you run such a generator on Python 2.5, but don't run it to completion before it is garbage collected, you will get an error message printed on stderr saying that an exception was ignored when this generator was cleaned up. If you use the new PEP 342 features to try to explicitly close it before it is garbage collected, you'll get the exception directly.
I think this is fine. The code breaks with the new yield semantics. But that's because the except clause was overly broad. It's easy to rewrite it like this, which is better style anyway because the scope of the try/except is limited. try: value = (task, task()) except Exception, ex: value = ExceptionOccurred(task, ex) yield value
The culprit is the RuntimeError raised when the generator's close() method gets upset because the generator swallowed GeneratorExit.
If GeneratorExit inherits directly from BaseException, such unexpected behaviour won't happen - the only way for an existing generator to break is if it contained a bare except clause, and that code was *already* dubious (e.g. it probably swallowed KeyboardInterrupt).
I don't have any actual live examples of a generator with a broad exception clause like the one above, but toy generators like the one above are legal in 2.4 and result in spurious errors with current SVN.
I don't want to cater to hypotheticals. Unless you find real code out there doing this kind of thing I don't believe the problem is real. I like to resolve corner cases so that *likely* situations are handled reasonably. Just in case you feel inclined to argue this further, let me argue that there's also a *downside* to making GeneratorExit inherit from BaseException: if it ever "leaks" out of some code that was supposed to catch it but somehow didn't, and there's an outer "except Exception:" trying to protect against buggy code, that except clause is bypassed. So perhaps we can turn this into a requirement for exceptions that inherit from BaseException instead of Exception: the chance that they get raised by buggy code should be nihil. I think that SystemExit and KeyboardExit both qualify -- the former is raised by *non-buggy* code with the intention of falling all the way through; the latter is not raised by code at all but by the end user. I don't think GeneratorExit qualifies. -- --Guido van Rossum (home page: http://www.python.org/~guido/)
Guido van Rossum wrote:
On 3/25/06, Nick Coghlan <ncoghlan@gmail.com> wrote:
The kind of code I'm talking about would be an *existing* Python 2.4 generator that happens to do something like:
def gen(tasks): """yield the results of a bunch of task functions""" for task in tasks: try: yield (task, task()) except Exception, ex: yield ExceptionOccurred(task, ex)
This is purely hypothetical. It doesn't look like good style at all.
If you run such a generator on Python 2.5, but don't run it to completion before it is garbage collected, you will get an error message printed on stderr saying that an exception was ignored when this generator was cleaned up. If you use the new PEP 342 features to try to explicitly close it before it is garbage collected, you'll get the exception directly.
I think this is fine. The code breaks with the new yield semantics. But that's because the except clause was overly broad. It's easy to rewrite it like this, which is better style anyway because the scope of the try/except is limited.
try: value = (task, task()) except Exception, ex: value = ExceptionOccurred(task, ex) yield value
Works for me. Consider the issue dropped :) Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Re: the discussion in: http://mail.python.org/pipermail/python-dev/2006-March/062823.html Just as an FYI, the tlslite package (http://trevp.net/tlslite/) got broken in Python 2.5 and needed the exact fix quoted in the URL above. It was an easy fix, but the argument isn't hypothetical any more! A little late to bother changing anything, though. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.2 (MingW32) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFF5S1Zhk3bo0lNTrURAjIuAKC1ASOfx0L2+hf+3EKa2hktZYRjEgCeNRAn n395GwS11yM2AMSK67b5oNA= =+iBp -----END PGP SIGNATURE-----
participants (16)
-
Aahz
-
Barry Warsaw
-
Brett Cannon
-
Georg Brandl
-
Giovanni Bajo
-
Greg Ewing
-
Guido van Rossum
-
Just van Rossum
-
Matthew Fleming
-
Nick Coghlan
-
Nick Coghlan
-
Raymond Hettinger
-
Raymond Hettinger
-
Samuele Pedroni
-
Stephen Warren
-
Terry Reedy