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:
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. -- Steve
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". -- Steve
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 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
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
_______________________________________________ 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. -- Steve
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