else without except
As most of you probably know, you can use else with try blocks: try: do_stuff() except SomeExceptionClass: handle_error() else: no_error_occurred() Here, no_error_occurred will only be called if do_stuff() didn't raise an exception. However, the following is invalid syntax: try: do_stuff() else: no_error_occurred() Now you might say that this isn't needed as you can achieve the same result with do_stuff() no_error_occurred() However, what if I want to use finally as well: try: do_stuff() else: no_error_occurred() finally: cleanup() and I need no_error_occurred to be called before cleanup? For my actual use case, I've done try: do_stuff() except Exception: raise else: no_error_occurred() finally: cleanup() This seems very non-Pythonic. Is there a reason why else without except has to be invalid syntax?
On 2023-06-30 14:55, Daniel Walker wrote:
As most of you probably know, you can use else with try blocks:
try: do_stuff() except SomeExceptionClass: handle_error() else: no_error_occurred()
Here, no_error_occurred will only be called if do_stuff() didn't raise an exception.
However, the following is invalid syntax:
try: do_stuff() else: no_error_occurred()
Now you might say that this isn't needed as you can achieve the same result with
do_stuff() no_error_occurred()
However, what if I want to use finally as well:
try: do_stuff() else: no_error_occurred() finally: cleanup()
and I need no_error_occurred to be called before cleanup? For my actual use case, I've done
try: do_stuff() except Exception: raise else: no_error_occurred() finally: cleanup()
This seems very non-Pythonic. Is there a reason why else without except has to be invalid syntax?
What would be the difference between try: do_stuff() else: no_error_occurred() finally: cleanup() and try: do_stuff() no_error_occurred() finally: cleanup() ?
2023-06-30 19:54 UTC+02:00, MRAB <python@mrabarnett.plus.com>:
On 2023-06-30 14:55, Daniel Walker wrote:
As most of you probably know, you can use else with try blocks:
try: do_stuff() except SomeExceptionClass: handle_error() else: no_error_occurred()
Here, no_error_occurred will only be called if do_stuff() didn't raise an exception.
However, the following is invalid syntax:
try: do_stuff() else: no_error_occurred()
Now you might say that this isn't needed as you can achieve the same result with
do_stuff() no_error_occurred()
However, what if I want to use finally as well:
try: do_stuff() else: no_error_occurred() finally: cleanup()
and I need no_error_occurred to be called before cleanup? For my actual use case, I've done
try: do_stuff() except Exception: raise else: no_error_occurred() finally: cleanup()
This seems very non-Pythonic. Is there a reason why else without except has to be invalid syntax?
What would be the difference between
try: do_stuff() else: no_error_occurred() finally: cleanup()
and
try: do_stuff() no_error_occurred() finally: cleanup()
?
One could argue that it's conceptually not the same although the actual execution is the same. In the first case you intend to only catch the exceptions generated by do_stuff(). In the second case, you intend to catch those generated by do_stuff() and those generated by no_error_occurred(). If later on an "except" is added, the developper doing the modification should be reminded to move the call to no_error_occurred() into an "else". With real-world non-trivial code, it might not be so simple to see. I'm not an expert, but I actually see no downside to having an "else" without an "except". Best regards, Celelibi
On Wed, 2 Aug 2023 at 02:02, Celelibi <celelibi@gmail.com> wrote:
If later on an "except" is added, the developper doing the modification should be reminded to move the call to no_error_occurred() into an "else". With real-world non-trivial code, it might not be so simple to see.
Can you give us an example of real-world non-trivial code that would benefit from this? ChrisA
Is this a relevant argument (either way) here? While I appreciate considering the applicability of the argument to existing code is generally a good thing, I'm not sure that it makes sense for cases like this where a logical outcome seems to be missing. If you can try/finally and then implicitly pass both the generic except and else clauses, and you can try/except/finally and then implicitly pass the else clause, why shouldn't the construction be logically consistent to cover the other option that is try/else/finally and then implicitly pass the generic except clause? And, for what it's worth, finding current scenarios for something like this seems heavily biased by the fact that such a construction doesn't currently exist and so code will be written to explicitly avoid it... Or just ignore being necessarily precise, as is often the case with implementations of try/except that I've seen (see the prior discussion between MRAB and Celelibi about overinclusion in the try block). I'll ask the same question as OP: "Is there a *reason* why else without except has to be invalid syntax?" On Tue, Aug 1, 2023 at 10:18 AM Chris Angelico <rosuav@gmail.com> wrote:
On Wed, 2 Aug 2023 at 02:02, Celelibi <celelibi@gmail.com> wrote:
If later on an "except" is added, the developper doing the modification should be reminded to move the call to no_error_occurred() into an "else". With real-world non-trivial code, it might not be so simple to see.
Can you give us an example of real-world non-trivial code that would benefit from this?
ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/XOWJDT... Code of Conduct: http://python.org/psf/codeofconduct/
Reading on mobile I missed that you were referencing Celelibi's answer and not the thread generally [facepalm]. My apologies for that. I've been previously bitten (multiple times) by code where a developer has used a try/except to catch errors from one of several actions in a try block, but a bug arose when one of the other functions in that block raised the same error in an edge case. (I'm unfortunately not able to share that code right now, but it is used by more than just a small team.) This could have been fixed by better use of the else clause, instead of just try/except, so I'd support ways to make the else clause more functional. I might be persuaded that it feels nonsensical to have an "else" if there is no "except", however... (because semantically if we haven't excepted anything, there is nothing for "else" to stand against). On Tue, Aug 1, 2023 at 11:59 AM Mitch <mitchell.negus.57@gmail.com> wrote:
Is this a relevant argument (either way) here?
While I appreciate considering the applicability of the argument to existing code is generally a good thing, I'm not sure that it makes sense for cases like this where a logical outcome seems to be missing. If you can try/finally and then implicitly pass both the generic except and else clauses, and you can try/except/finally and then implicitly pass the else clause, why shouldn't the construction be logically consistent to cover the other option that is try/else/finally and then implicitly pass the generic except clause?
And, for what it's worth, finding current scenarios for something like this seems heavily biased by the fact that such a construction doesn't currently exist and so code will be written to explicitly avoid it... Or just ignore being necessarily precise, as is often the case with implementations of try/except that I've seen (see the prior discussion between MRAB and Celelibi about overinclusion in the try block).
I'll ask the same question as OP: "Is there a *reason* why else without except has to be invalid syntax?"
On Tue, Aug 1, 2023 at 10:18 AM Chris Angelico <rosuav@gmail.com> wrote:
On Wed, 2 Aug 2023 at 02:02, Celelibi <celelibi@gmail.com> wrote:
If later on an "except" is added, the developper doing the modification should be reminded to move the call to no_error_occurred() into an "else". With real-world non-trivial code, it might not be so simple to see.
Can you give us an example of real-world non-trivial code that would benefit from this?
ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/XOWJDT... Code of Conduct: http://python.org/psf/codeofconduct/
On 1 Aug 2023, at 19:59, Mitch <mitchell.negus.57@gmail.com> wrote:
Is this a relevant argument (either way) here?
Adding features to the language always has a cost in that it slightly complicates the language and makes it harder to teach. Because of that the bar for adding features is high. Showing how a new feature would improve realistic code patterns helps to defend to proposal. Ronald — Twitter / micro.blog: @ronaldoussoren Mastodon: @ronald@blog.ronaldoussoren.net. Blog: https://blog.ronaldoussoren.net/
2023-08-01 21:46 UTC+02:00, Ronald Oussoren via Python-ideas <python-ideas@python.org>:
On 1 Aug 2023, at 19:59, Mitch <mitchell.negus.57@gmail.com> wrote:
Is this a relevant argument (either way) here?
Adding features to the language always has a cost in that it slightly complicates the language and makes it harder to teach. Because of that the bar for adding features is high.
Showing how a new feature would improve realistic code patterns helps to defend to proposal.
Ronald
To me it's a simplification of the language and its teaching because it removes an exception (pun not intended). A 'try' might be followed by some blocks: - except with an exception - bare except - else - finally They are all optional. And if present, they must appear in that order. They are optional, except for the 'else', which must follow either an 'except' with an exception, or a bare 'except'. I think this could be made simpler by allowing 'else' without any 'except'. Celelibi
On Wed, 2 Aug 2023 at 04:03, Mitch <mitchell.negus.57@gmail.com> wrote:
I'll ask the same question as OP: "Is there a reason why else without except has to be invalid syntax?"
A better question is: "Is there a reason why else without except should be valid syntax?" ChrisA
Hmm, ok, I can work with that :) If I were writing Python right now, I would argue that if you're going to build this try/except/else/finally construct, each of the three result clauses represent potential outcomes. The fail case, the success case, and the no-matter-what case. And then, you should be consistent about what happens when any of the three are omitted. If `except` is omitted, then catch the generic exception and do nothing and exit. If `else` is omitted, do nothing and exit. And finally, if `finally` is omitted, (surprise!) do nothing and exit. Then, with this as a guiding principle, a user can choose to omit any or all of those actions that they desire, except where it is unreasonable to omit that combination. `try` by itself just silently catches errors and does nothing, and so it seems justifiably invalid as an abuse of the construct. Same principle actually applies to `try/else` in my mind, since it is functionally no different. But the `try/else/finally` statement actually has meaning: try something, do something if it succeeds, and then do something else regardless of what happens. I don't see an obvious reason why it doesn't exist (except perhaps the notion that an else without an except is just semantically odd), and in fact it comes to me as something of a surprise that it's disallowed. All said, I'm obviously not writing Python today—and I also don't maintain it—so I fully appreciate my own dreams of logical consistency above all else are unlikely to be an opinion shared among the developers. I do appreciate that this language is not changing nonstop beneath my feet, so the inertia is frustratingly welcome. -Mitch On Tue, Aug 1, 2023 at 3:06 PM Chris Angelico <rosuav@gmail.com> wrote:
On Wed, 2 Aug 2023 at 04:03, Mitch <mitchell.negus.57@gmail.com> wrote:
I'll ask the same question as OP: "Is there a reason why else without except has to be invalid syntax?"
A better question is: "Is there a reason why else without except should be valid syntax?"
ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/BMYQ2U... Code of Conduct: http://python.org/psf/codeofconduct/
On Wed, 2 Aug 2023 at 08:22, Mitch <mitchell.negus.57@gmail.com> wrote:
If `except` is omitted, then catch the generic exception and do nothing and exit.
Not sure what you mean here. If you have a try-finally with no except clause, no exceptions will be caught; the try will be run, then the finally, and then you'll see any exception from the try block. Perhaps what you're intending is for an omitted except clause to behave like "except: raise"? Because that would probably have most of the effect of the proposed "try-else", without a dangling else. And I'd still like to see an actual real-world (or "near-real-world") example that would benefit from this. Would adding "except: raise" be a problem? ChrisA
Yeah, my mental model got the better of me there. I conceptualize try/except as deferring subsequent reraises in the try block, so in that case the "catch all --> do nothing ( --> defer reraise)" and "catch nothing (--> defer reraise)" cases were equivalent. But the way I wrote it was confusing because it sounds like I was implying it just passed, especially because catching the exception requires a manual reraise and the implicit case does not. Hopefully that makes sense? And yes, I think I was narrowing the case that was originally presented to just allowing an omitted except following the behavior you said. Either way, sorry, to drag this out. I'm not particularly concerned about the current setup because, like you said, just adding an except/raise isn't hard and only two lines. Also, I do actually put more weight on the linguistic semantics than my previous messages suggested: try/else linguistically strikes me as meaning more-or-less the same as try/except—the else provides the opposite condition with respect to what's excepted, not what's being tried. And I certainly do *not* advocate for adding or renaming a keyword. On Tue, Aug 1, 2023 at 5:15 PM Chris Angelico <rosuav@gmail.com> wrote:
On Wed, 2 Aug 2023 at 08:22, Mitch <mitchell.negus.57@gmail.com> wrote:
If `except` is omitted, then catch the generic exception and do nothing
and exit.
Not sure what you mean here. If you have a try-finally with no except clause, no exceptions will be caught; the try will be run, then the finally, and then you'll see any exception from the try block.
Perhaps what you're intending is for an omitted except clause to behave like "except: raise"? Because that would probably have most of the effect of the proposed "try-else", without a dangling else.
And I'd still like to see an actual real-world (or "near-real-world") example that would benefit from this. Would adding "except: raise" be a problem?
ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/OK5LMB... Code of Conduct: http://python.org/psf/codeofconduct/
2023-08-01 18:14 UTC+02:00, Chris Angelico <rosuav@gmail.com>:
On Wed, 2 Aug 2023 at 02:02, Celelibi <celelibi@gmail.com> wrote:
If later on an "except" is added, the developper doing the modification should be reminded to move the call to no_error_occurred() into an "else". With real-world non-trivial code, it might not be so simple to see.
Can you give us an example of real-world non-trivial code that would benefit from this?
I guess Daniel Walker (OP) would be the best to answer that since he's the one who came up with this need. I've rarely used 'else' with a 'try'. Actually, now that I think about it, I guess a 'try' block without an 'except' and just a 'finally' would probably be a good candidate for a context manager. (Which I'm a big fan of. ^^) Here's what I think could be a not so far fetched example: writing a structured file through a serializing class. Conceptually the code could look like this: myfile = MySerializer("foo.bin") try: # Write data with myfile.write() else: # Write footer with myfile.write() finally: myfile.close() You might want to keep it as a 'try' statement instead of a 'with' because in the future you might want to handle some the exceptions generated by MySerializer but not those generated by the OS. (Although I agree, you can have a 'try' in a 'with'.) And in the real world, that code might look something like this. It write two blocks of data, each preceeded by the data length and followed by a checksum. And the footer is the checksum of the whole data. assert len(data) <= 256 myfile = MySerializer("foo.bin") try: myfile.write(128) myfile.write(data[:128]) myfile.write(crc32(data[:128])) myfile.write(len(data) - 128) myfile.write(data[128:]) myfile.write(crc32(data[128:])) myfile.write(crc32(data)) finally: myfile.close() I don't know about you, but I don't think it's obvious which 'write' should be put in an 'else' block when an 'except' gets added. Even if we added comments to identify the data blocks and footers, it's not obvious that the exceptions that could be raised by writing the footer shouldn't be captured by the future 'except'. This example is not perfect, but here it is. Celelibi
On Tue, 8 Aug 2023 at 00:09, Celelibi <celelibi@gmail.com> wrote:
Actually, now that I think about it, I guess a 'try' block without an 'except' and just a 'finally' would probably be a good candidate for a context manager. (Which I'm a big fan of. ^^)
Yeah, context managers are often a good way of writing a try/finally. Certainly not *always*, but they can be very elegant.
Here's what I think could be a not so far fetched example: writing a structured file through a serializing class. Conceptually the code could look like this:
myfile = MySerializer("foo.bin") try: # Write data with myfile.write() else: # Write footer with myfile.write() finally: myfile.close()
You might want to keep it as a 'try' statement instead of a 'with' because in the future you might want to handle some the exceptions generated by MySerializer but not those generated by the OS. (Although I agree, you can have a 'try' in a 'with'.)
Not sure how the generalization would go here, but in order to make "try-else" useful, it has to be highly likely that the "else" block be capable of raising the same exception that would be caught in the hypothetical "except" (and, importantly, that you not want this). I'm having trouble imagining what sort of exception would be like this, since a partly-written file is almost certainly not going to parse a useful footer. Maybe it's like a PKZIP file where the footer is actually measured from the end of the file?? Not a common technique by any means, although given Zip's ubiquity, maybe it deserves more consideration. Still, that would probably want to be coded as two entirely separate blocks, since - as per your comment - the "write out the central directory" part would have to happen when there's a serialization (or in this case, perhaps compression) problem, but NOT when there's other types of problem, or Ctrl-C, or anything like that.
And in the real world, that code might look something like this. It write two blocks of data, each preceeded by the data length and followed by a checksum. And the footer is the checksum of the whole data.
assert len(data) <= 256 myfile = MySerializer("foo.bin") try: myfile.write(128) myfile.write(data[:128]) myfile.write(crc32(data[:128])) myfile.write(len(data) - 128) myfile.write(data[128:]) myfile.write(crc32(data[128:])) myfile.write(crc32(data)) finally: myfile.close()
I don't know about you, but I don't think it's obvious which 'write' should be put in an 'else' block when an 'except' gets added. Even if we added comments to identify the data blocks and footers, it's not obvious that the exceptions that could be raised by writing the footer shouldn't be captured by the future 'except'.
Yeah, if there's a problem in any of these blocks, I have no idea what should get written. If the line "myfile.write(data[128:])", fails... do you write out its CRC? Do you write out the global CRC? You've already successfully written the length here, so I'd say the file is irreparably broken at that point, and all you can do is report that serialization failed. This is why real-world examples are so important. Inventing examples invariably leads to this sort of thing. You've had a marvellous attempt at it, and...
This example is not perfect, but here it is.
... exactly. ChrisA
participants (6)
-
Celelibi
-
Chris Angelico
-
Daniel Walker
-
Mitch
-
MRAB
-
Ronald Oussoren