
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

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
How is this different from the existing exception handling? From https://docs.python.org/3/tutorial/errors.html#exceptions : 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:

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
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. -- Steve

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:

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:

On 11/10/2020 17:20, haael wrote:
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 16/10/2020 01:36, Chris Angelico wrote: 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

On Fri, Oct 16, 2020 at 02:14:27AM +0100, Rob Cliffe via Python-ideas wrote:
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 :-)
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. -- Steve

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
How is this different from the existing exception handling? From https://docs.python.org/3/tutorial/errors.html#exceptions : 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:

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
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. -- Steve

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:

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:

On 11/10/2020 17:20, haael wrote:
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 16/10/2020 01:36, Chris Angelico wrote: 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

On Fri, Oct 16, 2020 at 02:14:27AM +0100, Rob Cliffe via Python-ideas wrote:
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 :-)
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. -- Steve
participants (8)
-
Alex Hall
-
Chris Angelico
-
David Mertz
-
haael
-
Mathew Elman
-
Rob Cliffe
-
Steven D'Aprano
-
Wes Turner