
Very often I use the following construction (especially in operators):
try: something except SomeError: try: something_else except AnotherError: try: something_completely_different except Whatever: return NotImplemented
I propose a construction to simplify the staircase hierarchy of exception handlers:
try: something except SomeError try: something_else except AnotherError try: something_completely_different except Whatever: return NotImplemented
This would be somewhat analogous to `else if` blocks.
This pattern is very common in operator implementations. And this proposal could simplify it a bit.
haael

How is this different from the existing exception handling?
From https://docs.python.org/3/tutorial/errors.html#exceptions :
The last except clause may omit the exception name(s), to serve as a
wildcard. Use this with extreme caution, since it is easy to mask a real programming error in this way! It can also be used to print an error message and then re-raise the exception (allowing a caller to handle the exception as well):
```python import sys
try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error: {0}".format(err)) except ValueError: print("Could not convert data to an integer.") except: print("Unexpected error:", sys.exc_info()[0]) raise ```
https://docs.python.org/3/library/exceptions.html
https://docs.python.org/3/c-api/exceptions.html
On Sun, Oct 11, 2020, 12:25 PM haael haael@interia.pl wrote:
Very often I use the following construction (especially in operators):
try: something except SomeError: try: something_else except AnotherError: try: something_completely_different except Whatever: return NotImplemented
I propose a construction to simplify the staircase hierarchy of exception handlers:
try: something except SomeError try: something_else except AnotherError try: something_completely_different except Whatever: return NotImplemented
This would be somewhat analogous to `else if` blocks.
This pattern is very common in operator implementations. And this proposal could simplify it a bit.
haael _______________________________________________ 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/XNR5PV... Code of Conduct: http://python.org/psf/codeofconduct/

I'm not advocating for it (nor against it). But the OP proposes something clear different from the example in current Python.
On Sun, Oct 11, 2020, 12:39 PM Wes Turner
try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error: {0}".format(err)) except ValueError: print("Could not convert data to an integer.") except: print("Unexpected error:", sys.exc_info()[0]) raise
I.e.
try: might_raise_OSError() except OSError as err try: might_raise_ValueError() except ValueError try: might_raise_other() except: oh_well()
The proposal is to more concisely catch exceptions raised within the 'except' suites themselves.

Nested exceptions indicated by a 'try' suffix added to the exception expression
How could these (else-if and nested conditional) syntaxes be combined?
On Sun, Oct 11, 2020, 12:46 PM David Mertz mertz@gnosis.cx wrote:
I'm not advocating for it (nor against it). But the OP proposes something clear different from the example in current Python.
On Sun, Oct 11, 2020, 12:39 PM Wes Turner
try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error: {0}".format(err)) except ValueError: print("Could not convert data to an integer.") except: print("Unexpected error:", sys.exc_info()[0]) raise
I.e.
try: might_raise_OSError() except OSError as err try: might_raise_ValueError() except ValueError try: might_raise_other() except: oh_well()
The proposal is to more concisely catch exceptions raised within the 'except' suites themselves.

(I must have missed the 'try' suffix)
On Sun, Oct 11, 2020, 1:04 PM Wes Turner wes.turner@gmail.com wrote:
Nested exceptions indicated by a 'try' suffix added to the exception expression
How could these (else-if and nested conditional) syntaxes be combined?
On Sun, Oct 11, 2020, 12:46 PM David Mertz mertz@gnosis.cx wrote:
I'm not advocating for it (nor against it). But the OP proposes something clear different from the example in current Python.
On Sun, Oct 11, 2020, 12:39 PM Wes Turner
try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error: {0}".format(err)) except ValueError: print("Could not convert data to an integer.") except: print("Unexpected error:", sys.exc_info()[0]) raise
I.e.
try: might_raise_OSError() except OSError as err try: might_raise_ValueError() except ValueError try: might_raise_other() except: oh_well()
The proposal is to more concisely catch exceptions raised within the 'except' suites themselves.

On Sun, Oct 11, 2020 at 01:04:46PM -0400, Wes Turner wrote:
(I must have missed the 'try' suffix)
Indeed. And this is, in my opinion, a major problem with the suggestion. It obfuscates the code, hiding the fact that a new try block has been started.
To be fair to the proposal, it does save a line and an indent level; saving lines is not much gain, since lines are cheap, but saving an indent is a small gain, since horizontal space is at a premium. So the proposal does have a small gain. A very small one.
But the cost is to decrease readability and increase the likelihood of misreading the code, as Wes did.
The analogy with `elif` is not a good one. `elif` is a distinct keyword, and it is at the start of the line:
if condition elif condition
whereas this proposal for an "excepttry" cuts the keyword in half, sticking a condition (the exception to catch) in between:
try: ... except Exception try:
We don't spell `elif` like this:
if condition: else condition if:
but that's what this proposal suggests for "excepttry".
Another problem is the potential ambiguity:
# Version 1 try: ... except A: try: ... except B: ... except C: ...
# Version 2 try: ... except A: try: ... except B: ... except C: ...
Using the proposed syntax, both of these would be written as:
try: ... except A try: ... except B: ... except C: ...
with no way of telling whether the "except B" goes with the outer try or the inner try.

On Mon, Oct 12, 2020 at 09:40:40PM +1100, Steven D'Aprano wrote:
Using the proposed syntax, both of these would be written as:
try: ... except A try: ... except B: ... except C: ...
with no way of telling whether the "except B" goes with the outer try or the inner try.
Oops, sorry, that's a typo, I meant the "except C".

On Sun, Oct 11, 2020 at 6:48 PM David Mertz mertz@gnosis.cx wrote:
I'm not advocating for it (nor against it). But the OP proposes something clear different from the example in current Python.
On Sun, Oct 11, 2020, 12:39 PM Wes Turner
try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error: {0}".format(err)) except ValueError: print("Could not convert data to an integer.") except: print("Unexpected error:", sys.exc_info()[0]) raise
I.e.
try: might_raise_OSError() except OSError as err try: might_raise_ValueError() except ValueError try: might_raise_other() except: oh_well()
The proposal is to more concisely catch exceptions raised within the 'except' suites themselves.
But I think Wes has shown pretty clear evidence that the proposal is easily misinterpreted. It looks too similar to existing syntax. Something that's more visually distinct at a glance is required.

It stood out pretty clearly to me. In my example I added an 'as err' clause that the OP. That does make it stand out less.
I've used those nested try's myself at times. But I'm concerned the syntax would encourage an antipattern.
Although they are not identical, it seems like this should address most actual use cases:
try: might_raise_OSError() might_raise_ValueError() might_raise_other() except OSError as err: pass except ValueError: pass except: oh_well()
On Sun, Oct 11, 2020, 1:05 PM Alex Hall alex.mojaki@gmail.com wrote:
On Sun, Oct 11, 2020 at 6:48 PM David Mertz mertz@gnosis.cx wrote:
I'm not advocating for it (nor against it). But the OP proposes something clear different from the example in current Python.
On Sun, Oct 11, 2020, 12:39 PM Wes Turner
try: f = open('myfile.txt') s = f.readline() i = int(s.strip()) except OSError as err: print("OS error: {0}".format(err)) except ValueError: print("Could not convert data to an integer.") except: print("Unexpected error:", sys.exc_info()[0]) raise
I.e.
try: might_raise_OSError() except OSError as err try: might_raise_ValueError() except ValueError try: might_raise_other() except: oh_well()
The proposal is to more concisely catch exceptions raised within the 'except' suites themselves.
But I think Wes has shown pretty clear evidence that the proposal is easily misinterpreted. It looks too similar to existing syntax. Something that's more visually distinct at a glance is required.

This is quite similar to something I suggested on Python Discussions https://discuss.python.org/t/add-way-to-reenter-previous-try-block/4526

It is suggested to employ if/else, the new pattern matching feature, or functional decomposition rather than nesting exceptions. https://discuss.python.org/t/add-way-to-reenter-previous-try-block/4526/2
Pattern matching will look syntactically cleaner.
Exceptions are slow (only when an exception is caught) compared to conditionals and/or pattern matching.
But sometimes catching exceptions is the only way; and in those cases I think it's easier to read them nested; just like nested conditionals.
A contrived bad code example:
try: f() except TypeError: log.info("isinatance conditionals are faster") except ConnectionError try: retry() except KeyboardInterrupt as e try: log.info("Ctrl C caught") flush() except Exception as e: log.exception(e)
On Mon, Oct 12, 2020, 5:42 AM Mathew Elman mathew.elman@ocado.com wrote:
This is quite similar to something I suggested on Python Discussions https://discuss.python.org/t/add-way-to-reenter-previous-try-block/4526 _______________________________________________ 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/EXWK52... Code of Conduct: http://python.org/psf/codeofconduct/

On 11/10/2020 17:20, haael wrote:
Very often I use the following construction (especially in operators):
try: something except SomeError: try: something_else except AnotherError: try: something_completely_different except Whatever: return NotImplemented
I propose a construction to simplify the staircase hierarchy of exception handlers:
try: something except SomeError try: something_else except AnotherError try: something_completely_different except Whatever: return NotImplemented
This would be somewhat analogous to `else if` blocks.
This pattern is very common in operator implementations. And this proposal could simplify it a bit.
haael
I sometimes encounter these somewhat analogous situations: (a) To accomplish a goal takes several stages; failure at any stage means the whole attempt must be abandoned. (b) I attempt various ways to accomplish a goal; success in any way means that the others need not be attempted. which, in common with the OP's example, can lead to indefinite nesting levels. One workaround I sometimes use is a pseudo-loop which is executed once only; whenever the process can be abandoned, I `break` out of the "loop". It ain't particularly elegant, but it works. Applying to the OP's example we might have:
for _ in '1': # pseudo-loop # NB One gotcha is to write a pseudo-loop with something like "while True" # and then forget to put in a final `break'. try: something break except SomeError: pass try: something_else break except AnotherError: pass try: something_completely_different except Whatever: return NotImplemented
If convenient (it isn't always) the code can be hived off to a function and the `break's replaced by `return's. If this applied to the OP's example it could maybe be written:
try: something return <some result> except SomeError: pass try: something_else return <some other result> except AnotherError: pass try: something_completely_different return <completely different result> except Whatever: return NotImplemented
or (more verbose but cleaner if the `return` statements might raise an exception (including NameError for a misspelt variable name)):
try: something except SomeError: pass else: return <some result> try: something_else except AnotherError: pass else: return <some other result> try: something_completely_different except Whatever: return NotImplemented else: return <completely different result>
I sometimes wish that Python provided nicer syntax for writing a "pseudo-loop", but I guess it's not an urgent need. And I guess the same applies to the OP's suggestion. So I'm -0.8 on it. Best wishes Rob Cliffe

On Fri, Oct 16, 2020 at 10:45 AM Rob Cliffe via Python-ideas python-ideas@python.org wrote:
One workaround I sometimes use is a pseudo-loop which is executed once only; whenever the process can be abandoned, I `break` out of the "loop". It ain't particularly elegant, but it works. Applying to the OP's example we might have:
for _ in '1': # pseudo-loop
# NB One gotcha is to write a pseudo-loop with something like "while True" # and then forget to put in a final `break'. try: something break except SomeError: pass try: something_else break except AnotherError: pass try: something_completely_different except Whatever: return NotImplemented
I sometimes wish that Python provided nicer syntax for writing a "pseudo-loop", but I guess it's not an urgent need. And I guess the same applies to the OP's suggestion. So I'm -0.8 on it.
You mean like.... a goto statement? I'm not sure what a "pseudo-loop" is, other than a way to use break as goto.
ChrisA

On 16/10/2020 01:36, Chris Angelico wrote:
On Fri, Oct 16, 2020 at 10:45 AM Rob Cliffe via Python-ideas python-ideas@python.org wrote:
One workaround I sometimes use is a pseudo-loop which is executed once only; whenever the process can be abandoned, I `break` out of the "loop". It ain't particularly elegant, but it works. Applying to the OP's example we might have:
for _ in '1': # pseudo-loop
# NB One gotcha is to write a pseudo-loop with something like "while True" # and then forget to put in a final `break'. try: something break except SomeError: pass try: something_else break except AnotherError: pass try: something_completely_different except Whatever: return NotImplemented
I sometimes wish that Python provided nicer syntax for writing a "pseudo-loop", but I guess it's not an urgent need. And I guess the same applies to the OP's suggestion. So I'm -0.8 on it.
You mean like.... a goto statement? I'm not sure what a "pseudo-loop" is, other than a way to use break as goto.
ChrisA
Is that bad? As I tried to explain, sometimes a process may need to be abandoned at multiple points instead of running to completion (it may succeed early or fail early). If the process were in a dedicated function, this would be done by multiple return statements, and nobody would raise an eyebrow. However, sometimes it is not convenient to hive the process off into its own function (it might necessitate passing and receiving long hard-to-maintain lists of what used to be local variables). However the concept remains the same. Again, nobody would raise an eyebrow at breaking out of a loop that is (potentially) executed multiple times. That's just as much using break as a goto - in fact it leads to more possible code paths, hence can be harder to analyse. Sorry, but I get the impression from your comment (comparing it to goto) that you think breaking out of a ... let's call it a once-only loop ... is a Bad Thing and to be discouraged. If I've misunderstood, I apologise and I'm happy. The fact remains that, in certain situations, I have found `for _ in '1':` or equivalent (suitably documented) to be the best way of coding that I can think of. Best wishes Rob Cliffe
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/5J4EEF... Code of Conduct: http://python.org/psf/codeofconduct/

On Fri, Oct 16, 2020 at 4:37 PM Rob Cliffe via Python-ideas python-ideas@python.org wrote:
Sorry, but I get the impression from your comment (comparing it to goto) that you think breaking out of a ... let's call it a once-only loop ... is a Bad Thing and to be discouraged. If I've misunderstood, I apologise and I'm happy. The fact remains that, in certain situations, I have found `for _ in '1':` or equivalent (suitably documented) to be the best way of coding that I can think of.
I'd say that giving it explicit language support is a bad thing. :)
ChrisA

On Fri, Oct 16, 2020 at 02:14:27AM +0100, Rob Cliffe via Python-ideas wrote:
You mean like.... a goto statement? I'm not sure what a "pseudo-loop" is, other than a way to use break as goto.
ChrisA
Is that bad? As I tried to explain, sometimes a process may need to be abandoned at multiple points instead of running to completion (it may succeed early or fail early).
The usual way to handle that is by returning from a function, as you point out.
If the process were in a dedicated function, this would be done by multiple return statements, and nobody would raise an eyebrow.
There are a (very small?) number of people who would raise an eyebrow, as they insist on a strict view of "Single entry, single exit" and would argue against both break and early return from a function.
http://wiki.c2.com/?SingleFunctionExitPoint
I'm not one of them :-)
However, sometimes it is not convenient to hive the process off into its own function (it might necessitate passing and receiving long hard-to-maintain lists of what used to be local variables).
Hmmm, well, I won't categorically say you *must* use a function, but I will say that using a function is probably the best solution.
If you nest your function inside the major function, you don't even need to pass arguments:
def function(a, b, c, d, e): # More local variables: f, g, h = 1, 2, 3
def inner(): # all locals a...h are visible in here return result
thing = inner()
so you may be able to move your processing to an inner function, without needing to explicit pass any arguments to it, and make use of return, rather than adding a loop so you can use break.

On 16/10/2020 07:55, Steven D'Aprano wrote:
Hmmm, well, I won't categorically say you *must* use a function, but I will say that using a function is probably the best solution.
If you nest your function inside the major function, you don't even need to pass arguments:
def function(a, b, c, d, e): # More local variables: f, g, h = 1, 2, 3 def inner(): # all locals a...h are visible in here return result thing = inner()
so you may be able to move your processing to an inner function, without needing to explicit pass any arguments to it, and make use of return, rather than adding a loop so you can use break.
Thank you - good suggestion. (Except for apps which are performance-critical where I would expect defining a function each time the major function is called to have significant overhead. My apps usually aren't.) Rob Cliffe
participants (8)
-
Alex Hall
-
Chris Angelico
-
David Mertz
-
haael
-
Mathew Elman
-
Rob Cliffe
-
Steven D'Aprano
-
Wes Turner