A bare "except:" clause catches any exception. As of Python 3.0, this can be spelled more explicitly as "except BaseException:", and AFAIK the only difference between the two is that someone might rebind BaseException.
String exceptions were abolished during the 2.x line, without causing major upheaval. The deprecation and ultimate abolition of the bare except syntax would have less consequence than that, as it would merely require a mechanical transformation to "except BaseException:", without needing any corresponding changes at raise site.
Pros: * Remove the attractive nuisance of "hmm, my code's throwing an error that I don't recognize, I'll use try/except" and just catching everything.
* Eliminates the edge case wherein "except:" and "except ():" are almost diametrically opposite in meaning. Not often a concern, but we've just had a lengthy thread on python-list discussing this. It's generally agreed that an empty tuple has to be interpreted as catching nothing, but the false parallel with an empty clause is problematic.
Cons: * May make it harder to write Py2/Py3-compatible code. In Py2, "except:" will catch old-style-class exceptions.
* Any removal of any feature can cause examples and published code to break.
Undecided: * Forcing everyone to look at the code that uses "except:" means extra work to upgrade Python, but it might mean they notice a problem somewhere. Maybe it should be "except Exception", or maybe something even more specific. Is this a pro or a con?
Thoughts?
ChrisA
On 11.04.15 14:29, Chris Angelico wrote:
A bare "except:" clause catches any exception. As of Python 3.0, this can be spelled more explicitly as "except BaseException:", and AFAIK the only difference between the two is that someone might rebind BaseException.
String exceptions were abolished during the 2.x line, without causing major upheaval. The deprecation and ultimate abolition of the bare except syntax would have less consequence than that, as it would merely require a mechanical transformation to "except BaseException:", without needing any corresponding changes at raise site.
Pros:
- Remove the attractive nuisance of "hmm, my code's throwing an error
that I don't recognize, I'll use try/except" and just catching everything.
This doesn't different from the use of "except BaseException:".
- Eliminates the edge case wherein "except:" and "except ():" are
almost diametrically opposite in meaning. Not often a concern, but we've just had a lengthy thread on python-list discussing this. It's generally agreed that an empty tuple has to be interpreted as catching nothing, but the false parallel with an empty clause is problematic.
Is this a problem?
Cons:
- May make it harder to write Py2/Py3-compatible code. In Py2,
"except:" will catch old-style-class exceptions.
I think this is enough to not abolish bare except clauses at least while 2.7 in the use.
On Sat, Apr 11, 2015 at 10:10 PM, Serhiy Storchaka storchaka@gmail.com wrote:
Pros:
- Remove the attractive nuisance of "hmm, my code's throwing an error
that I don't recognize, I'll use try/except" and just catching everything.
This doesn't different from the use of "except BaseException:".
Apart from the fact that "except:" is short and convenient. If someone were to quickly scribble something down, it's more likely to be Exception than BaseException, which at very least would prevent the catching of KeyboardInterrupt and friends; and since you're forced to put _something_ in, there's a better chance that you'll go and put the right thing in.
- Eliminates the edge case wherein "except:" and "except ():" are
almost diametrically opposite in meaning. Not often a concern, but we've just had a lengthy thread on python-list discussing this. It's generally agreed that an empty tuple has to be interpreted as catching nothing, but the false parallel with an empty clause is problematic.
Is this a problem?
Hardly a huge one, but it's certainly not the other way around.
Cons:
- May make it harder to write Py2/Py3-compatible code. In Py2,
"except:" will catch old-style-class exceptions.
I think this is enough to not abolish bare except clauses at least while 2.7 in the use.
Are old-style classes often thrown? And is there any advantage to not just adding "(Exception)" to the classes' definitions? Currently, the bare except clause is even weirder in 2.7, because there are now three distinct levels of "catch everything":
# what most people should think of as "everything" except Exception as e: # what the exception hierarchy sees as "everything" except BaseException as e: # absolutely everything except:
Plus, the bare except clause doesn't allow capturing. One good use of "catch everything" is the generic logger at a significant boundary (maybe a web server that runs application code; if the app chokes, the server should catch that, log it, and return a 500), and this is nice and tidy with "except BaseException as e", but with "except:", you have to go digging. It's in many ways a wart; what benefit is it, really?
ChrisA
On Apr 11, 2015, at 04:29, Chris Angelico rosuav@gmail.com wrote:
A bare "except:" clause catches any exception. As of Python 3.0, this can be spelled more explicitly as "except BaseException:", and AFAIK the only difference between the two is that someone might rebind BaseException.
String exceptions were abolished during the 2.x line, without causing major upheaval.
I couldn't remember when this happened, so I look at the docs... But 2.7 (https://docs.python.org/2.7/reference/executionmodel.html#exceptions) still say "Exceptions can also be identified by strings, in which case the except clause is selected by object identity." Do the docs need to be fixed?
Meanwhile, in the C API (https://docs.python.org/2.7/c-api/exceptions.html#string-exceptions), it says "Changed in version 2.6: All exceptions to be raised or caught must be derived from BaseException." I think that one is correct, but until I sit down at a bigger screen where I can browse the source or run an interpreter, I'm not sure.
The deprecation and ultimate abolition of the bare except syntax would have less consequence than that, as it would merely require a mechanical transformation to "except BaseException:", without needing any corresponding changes at raise site.
Pros:
- Remove the attractive nuisance of "hmm, my code's throwing an error
that I don't recognize, I'll use try/except" and just catching everything.
But if you just teach people "spell that as except BaseException:" (or, more likely, "... as except Exception:"), it's still the exact same attractive nuisance. And, because "except:" is almost always a red flag in novice code, but "except Exception:" is occasionally reasonable, you're making it a tiny bit harder to spot the mistake when helping novices, without actually fixing it.
(Of course requiring an "as" clause would solve all of this, but nobody wants that...)
- Eliminates the edge case wherein "except:" and "except ():" are
almost diametrically opposite in meaning. Not often a concern, but we've just had a lengthy thread on python-list discussing this. It's generally agreed that an empty tuple has to be interpreted as catching nothing, but the false parallel with an empty clause is problematic.
Cons:
- May make it harder to write Py2/Py3-compatible code. In Py2,
"except:" will catch old-style-class exceptions.
If the C API docs are right and exceptions must be derived from BaseException even in 2.6, they can't be old-style class instances in the first place. (And if anyone's trying to write 2.5/3.x code, they still have to deal with string exceptions, not to mention that they can't get the value of any exceptions they handle, so they've got much bigger problems.)
Even if that's not right, what problems do old-style classes cause that new-style-but-not-BaseException-derived classes don't?
- Any removal of any feature can cause examples and published code to break.
Undecided:
- Forcing everyone to look at the code that uses "except:" means extra
work to upgrade Python, but it might mean they notice a problem somewhere. Maybe it should be "except Exception", or maybe something even more specific. Is this a pro or a con?
Thoughts?
ChrisA _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Sat, Apr 11, 2015 at 10:25 PM, Andrew Barnert abarnert@yahoo.com wrote:
On Apr 11, 2015, at 04:29, Chris Angelico rosuav@gmail.com wrote:
A bare "except:" clause catches any exception. As of Python 3.0, this can be spelled more explicitly as "except BaseException:", and AFAIK the only difference between the two is that someone might rebind BaseException.
String exceptions were abolished during the 2.x line, without causing major upheaval.
I couldn't remember when this happened, so I look at the docs... But 2.7 (https://docs.python.org/2.7/reference/executionmodel.html#exceptions) still say "Exceptions can also be identified by strings, in which case the except clause is selected by object identity." Do the docs need to be fixed?
1) Yes, sounds like it. 2) By identity? Not by equality?? 3) I have absolutely no idea about string exceptions - I do my best to avoid them.
:)
Meanwhile, in the C API (https://docs.python.org/2.7/c-api/exceptions.html#string-exceptions), it says "Changed in version 2.6: All exceptions to be raised or caught must be derived from BaseException." I think that one is correct, but until I sit down at a bigger screen where I can browse the source or run an interpreter, I'm not sure.
Python 2.7.3 (default, Mar 13 2014, 11:03:55) [GCC 4.7.2] on linux2 Type "help", "copyright", "credits" or "license" for more information.
raise 1
Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: exceptions must be old-style classes or derived from BaseException, not int
class OldException: pass
...
raise OldException
Traceback (most recent call last): File "<stdin>", line 1, in <module> __main__.OldException: <__main__.OldException instance at 0x7f0e6c89e1b8>
So that one probably needs a docs change too, although it's not as serious a problem.
Pros:
- Remove the attractive nuisance of "hmm, my code's throwing an error
that I don't recognize, I'll use try/except" and just catching everything.
But if you just teach people "spell that as except BaseException:" (or, more likely, "... as except Exception:"), it's still the exact same attractive nuisance. And, because "except:" is almost always a red flag in novice code, but "except Exception:" is occasionally reasonable, you're making it a tiny bit harder to spot the mistake when helping novices, without actually fixing it.
(Of course requiring an "as" clause would solve all of this, but nobody wants that...)
So you don't teach people to spell it as "except Exception". You teach them to look up the actual exception type they want to catch. That already happens, but I've seen quite a bit of code from my own students where the shorthand of "except:" is so tempting that it gets in. If that came straight back with SyntaxError, they'd be forced to put _something_ in, and would be more likely to put in a useful exception type.
Cons:
- May make it harder to write Py2/Py3-compatible code. In Py2,
"except:" will catch old-style-class exceptions.
If the C API docs are right and exceptions must be derived from BaseException even in 2.6, they can't be old-style class instances in the first place. (And if anyone's trying to write 2.5/3.x code, they still have to deal with string exceptions, not to mention that they can't get the value of any exceptions they handle, so they've got much bigger problems.)
Yeah, the wide-span compatibility issue is always going to be there. But given that the docs aren't quite right here, this is an issue with 2.7/3.x code.
Even if that's not right, what problems do old-style classes cause that new-style-but-not-BaseException-derived classes don't?
I don't know. I've never consciously raised or caught an old-style class. If I unwittingly did, there'd be no problem to any of my code if it suddenly started inheriting from Exception or StandardError.
ChrisA
On Apr 11, 2015, at 05:39, Chris Angelico rosuav@gmail.com wrote:
On Sat, Apr 11, 2015 at 10:25 PM, Andrew Barnert abarnert@yahoo.com wrote: On Apr 11, 2015, at 04:29, Chris Angelico rosuav@gmail.com wrote:
A bare "except:" clause catches any exception. As of Python 3.0, this can be spelled more explicitly as "except BaseException:", and AFAIK the only difference between the two is that someone might rebind BaseException.
String exceptions were abolished during the 2.x line, without causing major upheaval.
I couldn't remember when this happened, so I look at the docs... But 2.7 (https://docs.python.org/2.7/reference/executionmodel.html#exceptions) still say "Exceptions can also be identified by strings, in which case the except clause is selected by object identity." Do the docs need to be fixed?
- Yes, sounds like it.
That worries me a bit. If the docs say strings can be raised in 2.7, even if CPython doesn't allow it, I think you pretty much have to survey every implementation and make sure none of them allow it either before you can feel safe saying it's _just_ a doc problem...
- By identity? Not by equality??
Yeah, I vaguely remember the rule in the old days (as in 2.3-2.5-ish) was identity-or-isinstance.
Of course if you can't actually raise strings in 2.6 and 2.7 (from either Python or C), how they'd be identified if they _were_ raised is kind of irrelevant...
- I have absolutely no idea about string exceptions - I do my best to
avoid them.
Well, yeah, but if they really do still exist in 2.7 or even 2.6, I think that would count as a strike against removing bare except.
:)
Meanwhile, in the C API (https://docs.python.org/2.7/c-api/exceptions.html#string-exceptions), it says "Changed in version 2.6: All exceptions to be raised or caught must be derived from BaseException." I think that one is correct, but until I sit down at a bigger screen where I can browse the source or run an interpreter, I'm not sure.
Python 2.7.3 (default, Mar 13 2014, 11:03:55) [GCC 4.7.2] on linux2 Type "help", "copyright", "credits" or "license" for more information.
raise 1
Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: exceptions must be old-style classes or derived from BaseException, not int
class OldException: pass
...
raise OldException
Traceback (most recent call last): File "<stdin>", line 1, in <module> __main__.OldException: <__main__.OldException instance at 0x7f0e6c89e1b8>
So that one probably needs a docs change too, although it's not as serious a problem.
Maybe it means you can't raise them through the public C API, but you can still raise them through Python (or private C functions, presumably)?
(But it also says "caught"; does that mean C code can't use the PyErr_GivenExceptionMatches function to catch your OldException? That would be weird... But not entirely relevant here, so let's not worry about it.)
Pros:
- Remove the attractive nuisance of "hmm, my code's throwing an error
that I don't recognize, I'll use try/except" and just catching everything.
But if you just teach people "spell that as except BaseException:" (or, more likely, "... as except Exception:"), it's still the exact same attractive nuisance. And, because "except:" is almost always a red flag in novice code, but "except Exception:" is occasionally reasonable, you're making it a tiny bit harder to spot the mistake when helping novices, without actually fixing it.
(Of course requiring an "as" clause would solve all of this, but nobody wants that...)
So you don't teach people to spell it as "except Exception". You teach them to look up the actual exception type they want to catch. That already happens, but I've seen quite a bit of code from my own students where the shorthand of "except:" is so tempting that it gets in. If that came straight back with SyntaxError, they'd be forced to put _something_ in, and would be more likely to put in a useful exception type.
And you don't think they'd quickly learn that the way to keep being lazy is to write "except Exception: pass"?
That seems overly optimistic to me, but then you're the one teaching classes, so I'll defer to you.
(By the way, even "except TheRightException: pass" is going to be just as bad 90% of the time; they'll still be posting to StackOverflow asking "why my function does nothing and just returns?")
Cons:
- May make it harder to write Py2/Py3-compatible code. In Py2,
"except:" will catch old-style-class exceptions.
If the C API docs are right and exceptions must be derived from BaseException even in 2.6, they can't be old-style class instances in the first place. (And if anyone's trying to write 2.5/3.x code, they still have to deal with string exceptions, not to mention that they can't get the value of any exceptions they handle, so they've got much bigger problems.)
Yeah, the wide-span compatibility issue is always going to be there. But given that the docs aren't quite right here, this is an issue with 2.7/3.x code.
I agree that 2.5 can be pretty much ignored. 2.6, maybe not (a huge percentage of the libs on PyPI right now are 2.6+/3.3+), but it sounds like 2.6 and 2.7 are pretty much identical as far as exceptions anyway?
Even if that's not right, what problems do old-style classes cause that new-style-but-not-BaseException-derived classes don't?
I don't know. I've never consciously raised or caught an old-style class. If I unwittingly did, there'd be no problem to any of my code if it suddenly started inheriting from Exception or StandardError.
I suspect the issue isn't so much consciously raising them, as, say, conditionally using different third-party libs if they exist (or even using user-written plugins), and one of them raises an old-style class instance, and your app used to catch it and now it doesn't, and you don't notice it until one of your users complains...
ChrisA _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
On Sat, Apr 11, 2015 at 11:16 PM, Andrew Barnert abarnert@yahoo.com wrote:
That worries me a bit. If the docs say strings can be raised in 2.7, even if CPython doesn't allow it, I think you pretty much have to survey every implementation and make sure none of them allow it either before you can feel safe saying it's _just_ a doc problem...
- I have absolutely no idea about string exceptions - I do my best to
avoid them.
Well, yeah, but if they really do still exist in 2.7 or even 2.6, I think that would count as a strike against removing bare except.
I don't have a pile of other Python implementations handy, so I'll defer to someone else (Steven D'Aprano always seems to have a few dozen lying around, and might even have a handy script for running something on all of them and comparing the results!).
class OldException: pass
...
raise OldException
Traceback (most recent call last): File "<stdin>", line 1, in <module> __main__.OldException: <__main__.OldException instance at 0x7f0e6c89e1b8>
So that one probably needs a docs change too, although it's not as serious a problem.
Maybe it means you can't raise them through the public C API, but you can still raise them through Python (or private C functions, presumably)?
That would be an *extremely* weird situation, and could probably be called a bug. But I don't write C extensions, so again, I'll defer to someone else on this point.
And you don't think they'd quickly learn that the way to keep being lazy is to write "except Exception: pass"?
(By the way, even "except TheRightException: pass" is going to be just as bad 90% of the time; they'll still be posting to StackOverflow asking "why my function does nothing and just returns?")
It's always possible to write bad code. If the bad form is much shorter and more natural/discoverable than the good, then a lot more people will write bad code than if they're on par with each other. For instance, in Py2, it's very easy to write code that's Unicode-unaware simply by using the obvious string literal style everywhere; it runs just fine as long as everything's ASCII, and then it breaks on non-ASCII text. In Py3, you're forced up-front to differentiate between Unicode and byte strings, and the obvious string literal style gives you a Unicode string, so people take the obvious route and have proper Unicode handling.
try: input = raw_input except NameError: pass name = input("Enter your name: ") print("REPORT FOR " + name.upper())
rosuav@sikorsky:~$ python2 ucase.py Enter your name: Begüm Günceler REPORT FOR BEGüM GüNCELER rosuav@sikorsky:~$ python3 ucase.py Enter your name: Begüm Günceler REPORT FOR BEGÜM GÜNCELER
It's still possible for your code to be too naive, but you have a *much* better chance that simplistic code is Unicode-aware in Py3 than in Py2. Same with exception handling. Sure, you could still have "except ValueError: pass" and then wonder why your code isn't doing anything (or, worse, you create a completely different and hard-to-debug error elsewhere, because your function now returns None instead of something else - seen that happen a few times), but that's a lot less subtle than "why does my program go weird when I press Ctrl-C?" or "why does sys.exit(0) not work here?", which you'll get from a bare except.
Even if that's not right, what problems do old-style classes cause that new-style-but-not-BaseException-derived classes don't?
I don't know. I've never consciously raised or caught an old-style class. If I unwittingly did, there'd be no problem to any of my code if it suddenly started inheriting from Exception or StandardError.
I suspect the issue isn't so much consciously raising them, as, say, conditionally using different third-party libs if they exist (or even using user-written plugins), and one of them raises an old-style class instance, and your app used to catch it and now it doesn't, and you don't notice it until one of your users complains...
That would be a nuisance, yes. But hopefully that can be deflected back onto the third-party lib/user-written plugin, basically by saying:
What's new in MyLibrary v5.7.0: * Support for Python 3.6 * Old-style class exceptions are no longer supported. If your plugin defines custom exceptions, be sure that they derive from Exception or similar. * other changes, yada yada
Every document I've ever seen has taken one of two attitudes toward old-style classes. Either they should be considered deprecated, and every class you define should explicitly subclass something; or they're a really cool micro-optimization with a few fancy flexibilities that new-style classes don't have, but which come up only in the most obscure cases. If a library has to say "old-style class exceptions can't be supported now due to Python 3.6 compatibility requirements", how many people would actually be up in arms? It's a VERY small removal of backward compat in favour of forward compat and cleaner code, just like abolishing string exceptions was.
However, if it turns out there's some distinct advantage to having exceptions outside of the primary hierarchy, then I'll withdraw the suggestion, at least until post-2020 when Py2 support ends (and probably longer).
ChrisA
On Sat, Apr 11, 2015 at 11:44:23PM +1000, Chris Angelico wrote:
Every document I've ever seen has taken one of two attitudes toward old-style classes. Either they should be considered deprecated, and every class you define should explicitly subclass something; or they're a really cool micro-optimization with a few fancy flexibilities that new-style classes don't have, but which come up only in the most obscure cases.
I wouldn't necessarily believe that classic classes are still faster than new-style classes. I think it was certainly true back in the 2.2 and 2.3 days, but probably not in 2.7. In any case, I'd like to see the benchmarks demonstrating a speed advantage to classic classes before believing that they are an optimization in practice.
As far as I can see, the only things classic classes can do that new-style classes don't are:
(1) Per-instance overriding of dunder methods on the instance. (I'm sure there's a Design Pattern name for this, but I can never remember it.) You can override regular methods for a particular instance by giving it an instance attribute that is a bound method:
class Parrot: def speak(self): return "Want a cracker!"
polly = Parrot() polly.speak = types.MethodType(lambda self: "Who's a pretty boy?", polly)
but this doesn't work with dunders in new-style classes. In classic classes, it does.
(2) Automatic delegation including dunder methods.
Both of these may be important, but if you are migrating to 3.x you are going to lose them anyway, so you have to deal with it somehow. (Probably by crying a lot.)
On Sun, Apr 12, 2015 at 1:15 AM, Steven D'Aprano steve@pearwood.info wrote:
On Sat, Apr 11, 2015 at 11:44:23PM +1000, Chris Angelico wrote:
Every document I've ever seen has taken one of two attitudes toward old-style classes. Either they should be considered deprecated, and every class you define should explicitly subclass something; or they're a really cool micro-optimization with a few fancy flexibilities that new-style classes don't have, but which come up only in the most obscure cases.
I wouldn't necessarily believe that classic classes are still faster than new-style classes. I think it was certainly true back in the 2.2 and 2.3 days, but probably not in 2.7. In any case, I'd like to see the benchmarks demonstrating a speed advantage to classic classes before believing that they are an optimization in practice.
Not sure if there's a speed advantage or not, but apparently there's a size advantage:
http://lucumr.pocoo.org/2014/8/16/the-python-i-would-like-to-see/
Though the difference doesn't apply to instances - a new-style class instance is marginally smaller than its compatriot - and the difference would be dwarfed by actual content anyway, in a typical program. Hence "micro-optimization".
ChrisA
On 11.04.2015 17:15, Steven D'Aprano wrote:
On Sat, Apr 11, 2015 at 11:44:23PM +1000, Chris Angelico wrote:
Every document I've ever seen has taken one of two attitudes toward old-style classes. Either they should be considered deprecated, and every class you define should explicitly subclass something; or they're a really cool micro-optimization with a few fancy flexibilities that new-style classes don't have, but which come up only in the most obscure cases.
I wouldn't necessarily believe that classic classes are still faster than new-style classes. I think it was certainly true back in the 2.2 and 2.3 days, but probably not in 2.7. In any case, I'd like to see the benchmarks demonstrating a speed advantage to classic classes before believing that they are an optimization in practice.
See here (slide 39ff): http://www.egenix.com/library/presentations/PyCon-UK-2014-When-performance-m... https://github.com/egenix/when-performance-matters
Instance attribute lookups are faster on old-style classes.
On Sat, Apr 11, 2015 at 10:39:04PM +1000, Chris Angelico wrote:
On Sat, Apr 11, 2015 at 10:25 PM, Andrew Barnert abarnert@yahoo.com wrote:
On Apr 11, 2015, at 04:29, Chris Angelico rosuav@gmail.com wrote:
A bare "except:" clause catches any exception. As of Python 3.0, this can be spelled more explicitly as "except BaseException:", and AFAIK the only difference between the two is that someone might rebind BaseException.
String exceptions were abolished during the 2.x line, without causing major upheaval.
I couldn't remember when this happened, so I look at the docs... But 2.7 (https://docs.python.org/2.7/reference/executionmodel.html#exceptions) still say "Exceptions can also be identified by strings, in which case the except clause is selected by object identity." Do the docs need to be fixed?
- Yes, sounds like it.
- By identity? Not by equality??
- I have absolutely no idea about string exceptions - I do my best to
avoid them.
Good plan :-)
String exceptions were noisily deprecated in 2.5 and removed in 2.6, but they were obsolete for a long time before that. I don't think they were really common since the Python 1.5 days. And yes, they matched by identity! That was a common source of bugs back in 1.5 days.
Python 2.7 does allow you to raise from arbitrary old-style classes:
[steve@ando ~]$ python2.7 -c "class YeOldeClass: pass
raise YeOldeClass"
Traceback (most recent call last): File "<string>", line 2, in <module> __main__.YeOldeClass: <__main__.YeOldeClass instance at 0xb7f2d1cc>
but new style classes must be derived from BaseException in both 2.7 and 3.x, so people migrating from 2.x to 3.x will have to change their exceptions to inherit directly or indirectly from BaseException. This will not change that.
* In 3.x only code, bare except is equivalent to `except BaseException`;
* in 2.x only code, they are not the same;
* but if you are migrating to 3.x, or have 2+3 hybrid code, you cannot use non-BaseException exceptions, which means that in hybrid code a bare except is *effectively* equivalent to `except BaseException`.
So I don't think this proposed change would add any significant burden to migration. You still have to make sure all your exceptions are actual exceptions.
I think that a bare except is an attractive nuisance. Nearly always it should be catching a specific exception, or at most Exception. Very rarely you want to catch BaseException. I think that Python would be better off without bare except clauses, having to explicitly write Exception or (more rarely) BaseException is a good thing.
I can see four objections:
(1) In the interactive interpreter, sometimes you don't care about being careful. Being able to just catch everything with a bare except is fine for interactive use. (I do so myself.)
Response: I don't think much of this argument from laziness. If you're using the II a lot, you should have a startup file. Just define E = Exception in your startup file and then you can say "except E:" which is almost as convenient. Or use tab completion (enabled by default from 3.4 onwards). We shouldn't allow convenience in the interactive interpreter be an excuse for keeping a feature which gets misused as often as bare except does.
(2) Should 2to3 automatically translate "except:" to "except Exception:" or "except BaseException:"? The second is the literal translation, but the first is what the programmer probably intended. Probably, but not always.
Response: I think the 2to3 fixer will have to use BaseException, even though normally we recommend to catch Exception.
If the fixer uses Exception, some code that was working will break (it now catches too little), but some code that was broken (it caught too much) will be less broken. If the fixer uses BaseException, working code will stay working, and broken code will stay broken. 2to3 isn't responsible for fixing your bugs, and it certainly shouldn't introduce bugs if it can avoid it. Hence, it must transform bare except to catching BaseException. (Maybe it can ask the programmer?)
(3) Bare excepts have their uses and they aren't all bad. We should keep them.
Response: Yeah, well, that's like just your opinion!
*wink*
I guess if you mostly work with good-quality mature code written by experienced Python developers who never use bare except clauses except when they really are needed, you may not think this is a problem to be solved. Bare excepts aren't *all* bad, and if they are abused occasionally, oh well, any feature can be abused.
If, on the other hand, you mostly work with variable-quality code written by inexperienced, lazy, or just plain poor programmers who think that tossing a bare except around everything makes the code bug-free, then you might have a different opinion, and you might curse bare except as an attractive nuisance and want to see it die.
(4) At this late stage in Python's development we shouldn't remove even a dubious feature. Every little incompatibility between 2.7 and 3.x just slows down migration.
Response: I'm sympathetic to this argument, but I don't want to see "slows down migration" be used to block removal of mistakes. Even C removes features eventually (although on a much slower timescale than Python's relatively fast development cycle).
But perhaps we don't have to remove them *right now*. We could deprecate them, and leave them deprecated indefinitely, for eventual removal in Python 5000 or something.
Meanwhile, in the C API (https://docs.python.org/2.7/c-api/exceptions.html#string-exceptions), it says "Changed in version 2.6: All exceptions to be raised or caught must be derived from BaseException." I think that one is correct, but until I sit down at a bigger screen where I can browse the source or run an interpreter, I'm not sure.
I think that means that the C API is now fixed so that all exceptions raised from builtins are now real exceptions, not random classes and certainly not strings. But it doesn't affect the Python API.
Pros:
- Remove the attractive nuisance of "hmm, my code's throwing an error
that I don't recognize, I'll use try/except" and just catching everything.
But if you just teach people "spell that as except BaseException:" (or, more likely, "... as except Exception:"), it's still the exact same attractive nuisance. And, because "except:" is almost always a red flag in novice code, but "except Exception:" is occasionally reasonable, you're making it a tiny bit harder to spot the mistake when helping novices, without actually fixing it.
(Of course requiring an "as" clause would solve all of this, but nobody wants that...)
So you don't teach people to spell it as "except Exception". You teach them to look up the actual exception type they want to catch. That already happens, but I've seen quite a bit of code from my own students where the shorthand of "except:" is so tempting that it gets in. If that came straight back with SyntaxError, they'd be forced to put _something_ in, and would be more likely to put in a useful exception type.
That's the point. We cannot force people to catch the correct exception. We might not even know the correct exceptions to catch! But bare excepts are an attractive nuisance because (1) they're attractive because they're so easy (less typing is always good!) and (2) sometimes they're a bug magnet. We can't do anything about (2), but if people have to use an exception type, they may be less gung-ho about hiding bugs instead of fixing them ("except Exception is too much typing, I guess I'll just have to fix my code instead..." *wink*).
On 4/11/2015 08:25, Andrew Barnert wrote:
But if you just teach people "spell that as except BaseException:" (or, more likely, "... as except Exception:"), it's still the exact same attractive nuisance. And, because "except:" is almost always a red flag in novice code, but "except Exception:" is occasionally reasonable, you're making it a tiny bit harder to spot the mistake when helping novices, without actually fixing it.
I think it is kind of a false argument to say 'this should not be considered because people will just break it another way'.
Bare excepts were a mistake. I for one would support removing them in 2.7.10, but that's not on the table. As long as the 'new valid' syntax is also valid 2.7-3.5, it should be considered.
As I understand the proposal, `except ASubclassOfException:` would remain valid - an Exception type or object being required for an except clause is not a massive break in 2.7 (its already required there if you are not using bare excepts). The burden of supporting 2.7 through a hypothetical 3.6 without being able to use bare exceptions is minimal to none.
On 4/11/2015 7:29 AM, Chris Angelico wrote:
A bare "except:" clause catches any exception. As of Python 3.0, this can be spelled more explicitly as "except BaseException:", and AFAIK the only difference between the two is that someone might rebind BaseException.
String exceptions were abolished during the 2.x line, without causing major upheaval. The deprecation and ultimate abolition of the bare except syntax would have less consequence than that, as it would merely require a mechanical transformation to "except BaseException:", without needing any corresponding changes at raise site.
Thoughts?
* There are about 20 bare excepts in idlelib and an issue (15313) to remove them by adding the missing class or tuple. I have looked at a few and found it is hard to determine what should be added. I may eventually just add Exception if I cannot be more specific (or general).
* 'except:' is easily caught by a code checker, or reported by grep. This works both ways: don't need to change the language, but equally easy to find to change if the language is changed.
* As Steven said, there could be a long deprecation period.
For the record, as a Python 2/3 user of the occasional bare "except:" I was against this over on python-list, but Chris and Steven have convinced me otherwise. I'm now +0.5 and will probably be +1 in time.
I was using a few bare excepts in boundary code that needed to plough on in the face of failures of a callable, but have been convinced that I would be better off with "except Exception:" most of the time, and I'm going to put some work into stripping out my bare "except:"s and mostly catching Exception and possibly reporting BaseException (and reraising) as a debugging/insight aid.
Cheers, Cameron Simpson cs@zip.com.au
Ho, HaHa, Dodge, Parry, Spin, HA! THRUST! - Daffy Duck