"while ... try" - block or "for ... try" - block

I propose two new control flows for Python: "while ... try": while expr try: suite1 except SomeException: suite2 else: suite3 This executes suite1 as long as handled exceptions are thrown and expr is True. * If an unhandled exception is thrown, it passes the exception on to the surrounding or the stack. * If no exception occurs, life goes on as normal, suite3 is executed and execution goes on afterwards. The control flow is thus equivalent to: while expr: try: suite1 except SomeException: suite2 else: suite3 break But it's neater, very natural (in my opinion) and saves an indentation level. One further enhancement: If expr is encountered to be False, some special exception "NoMoreTriesException" could be raised. It can be catched in the same "while ... try" block. Usecase: while network_is_up() try: connect_to_server() except ConnectError: time.sleep(timeout) except NoMoreTriesException: print("Couldn't establish connection") else: download_stuff() finally: make_sure_resource_is_freed() Another usecase: while receive_packet() try: check_packet() except ChecksumError: print("You sent the wrong thing. Try again.") except NoMoreTriesException: print("I couldn't get a single useful packet from you :(") else: process_packet() finally: close_connection() A similar thing could be made with "for ... try": for password in passwords_i_remember try: connect(password) except WrongPassError: pass # No pun intended except NoMoreTriesException: print("Not a single one worked.") else: check_mailbox() The advantages are the same as for "while ... try". Cheers, Manuel

Folks, I'm a moderator. Volunteer, true, but a moderator nonetheless. And I've had enough. So: Netiquette discussions are always fascinating, but please, let's stop talking about how other people should post. Most of my e-mail from this list over the last few days has been from people on this list nattering about how gmail foo and mailer blah, and it is OFF TOPIC and NO LONGER WELCOME. (Frankly, you should have figured this out for yourselves.) If you feel absolutely compelled to tell people that they are lousy netizens and/or correct them on their posting style, please do so PRIVATELY and not on this list. Please e-mail me PRIVATELY if you disagree with this. Brett Cannon is the other list admin, and you should feel free to tell him how much you dislike me & my overbearing moderator ways. Further e-mails on netiquette will result in those people no longer being able to post without moderation. thanks, --titus p.s. This is not aimed especially at Ethan -- it was just the latest e-mail that pushed me over the edge :) -- C. Titus Brown, ctb@msu.edu

On Wed, Jan 11, 2012 at 12:43, C. Titus Brown <ctb@msu.edu> wrote:
And just FYI to people, I wouldn't bother complaining to me because I support Titus on this one; stuff has gone off the rails here, but I have been flat-out ignoring the list as a whole because of it instead of getting worked up like Titus. He's just a more "delicate flower" than me when comes to this and apparently cares more. =) -Brett

For me, control flow matching indentation is probably the single most important feature of python. What I would support is a more convenient way to inject an exception handler into an expression. For example, (a more complicated version of) your first use case would be improved by a way to say (except ConnectError) ==> (time.sleep(timeout); continue) and your password checker example would be improved by a way to say (except WrongPassError) ==> (pass) More details below. 2012/1/11 Manuel Bärenz <manuel@enigmage.de>:
Without your explanation, I would still have assumed that the exception terminated the while.
This would be a lot easier for me to follow with the extra indents. As best I can tell, under normal circumstances, that will just keep making new connections, and never get to the download until the network itself goes down. Or does the else bind to the try, rather than the while -- in which case it is still an infinite loop, but at least succeeds. I'm also not sure which resource needs to be freed, so I'll just call it special_resource. For what I think you wanted, I can read it a lot easier as: with special_resource: for keep_trying in range(3): # not infinity if not network_is_up(): # I would prefer this elsewhere ... time.sleep(timeout) continue try connect_to_server(): download_stuff() break except ConnectError: time.sleep(timeout) else: print("Couldn't establish connection") which I would in turn prefer to reduce to: retry=WaitPolicy('sleep_and_retry', 3) with ensure_network(onfail=retry) as network: with connect_to_server(transport=network, onfail=retry): download_stuff()
This time, it looks like only the final packet gets processed. If else binds to the try, it makes more sense, but still complains about not getting a single useful packet even after getting and processing a dozen. I would prefer: with connection: while receive_packet(): if OK == check_packet(): process_packet() else: print("You sent the wrong thing. Try again.") or at least (following your API more closely): with connection: got_any = False while receive_packet(): try check_packet(): process_packet() got_any = True except ChecksumError: print("You sent the wrong thing. Try again.") if not got_any: print("I couldn't get a single useful packet from you :(")
I honestly don't see that as an improvement over for password in passwords_i_remember: try connect(password): check_mailbox() break except WrongPassError: pass # No pun intended else: print("Not a single one worked.")

On 11.01.2012 17:58, Jim Jewett wrote:
On 12.01.2012 01:00, Steven D'Aprano wrote:
The interpreter could easily distinguish the two, since try is a reserved keyword..
try: result = self.cache[n] except KeyError: result = self.cache[n] = self.f(n) return result I would have wanted to write something like: while True try: return self.cache[n] except KeyError: self.cache[n] = self.f(n) That's how I came up with the idea. But you presented enough reasonable arguments against it such that I won't consider it anymore. Thanks for your comments.

Manuel Bärenz wrote:
This conflates two naturally distinct operations, looping and exception handling, into a single construct that tries to do both. "Loop" is a natural operation. "Catch exceptions" is a natural operation. "Loop and catch exceptions" is not, it is two operations and so should be written as two operations. It isn't obvious that handled exceptions remain in the loop. Because there are two equally good behaviours -- handled exceptions remain in the loop, or handled exceptions exit the loop -- people will get confused by the construct and repeatedly be surprised it doesn't do what they expect. Your proposed syntax doesn't allow the user to write code which couldn't be written before, it adds no new power or expressiveness to Python. The sole advantage is that it saves one line and one indent level, which is a trivial advantage: there are no shortages of either newlines or indents, and if anyone is writing such deeply nested code that they are regularly worried about indents, their code almost certainly is in desperate need of refactoring. Disadvantages include that it increases complexity of the language: the compiler becomes more complex, there is another feature for people to learn. Every time somebody writes a loop with a try, they will have to decide whether to write it the old way or the new way. The try clause can be easily missed while skimming code, making the construct inelegant. The syntax also clashes with existing while...else. What about nested try blocks? If we introduce this, will people try writing this? while expr try try: ... except InnerException: ... ... except OuterException: ... This seems like a reasonable thing to do. I know that's exactly what I'd try. It also raises one special case above all other cases. An arbitrary loop containing a try block looks like this: while expr: preamble # 0 or more lines before the try block try block postscript # 0 or more lines after the try block Your proposed syntax only covers the case where both the preamble and the postscript are 0 lines. What is so special about that case that it needs extra syntax? It's not that common. Existing syntax is consistent, natural and obvious. Your proposal looks like you've picked two block constructs, a while loop and a try block, and nailed them together. -1 on this suggestion. [...]
That use-case is already handled in existing Python by the while...else form. There is no need to add an additional built-in exception for this. -- Steven

I also only see trouble with this construct. I initially assumed you meant: while expr try: # ... except IOError: print 'error' would actually equal: try: while expr: # ... except IOError: print 'error' This makes sense because the "except" is after the while loop suite so it should mean that being in the except suite removes the possibility of reentering the while suite. Anyhow, because of the ambiguity, and because we'll immediately need for/try if/try and try comprehensions (some of which have contradicting "else" semantics) I'm -2 on this. Yuval Greenfield

On Jan 12, 1:08 am, Manuel Bärenz <man...@enigmage.de> wrote:
I propose two new control flows for Python:
Guido recently said: "There seems to be a trend in proposing more random variants of the existing compound statements. Please don't do this. The existing complement of compound statements is quite sufficient and the new proposals do nothing but complicate the parser, the documentation, and the learning process for occasional users." https://groups.google.com/group/python-ideas/msg/5b884fd1caef2f74

2012/1/11 Manuel Bärenz <manuel@enigmage.de>
<snip> Manuel, Thanks for the improvement idea, but I must admit I'm -1 on this. Having first looked at the proposed syntax without your explanation, I found it hard to be sure what it does, and this goes against the spirit of Python. The compound statement it purports to replace is just one indent deeper and much clearer. Eli

Folks, I'm a moderator. Volunteer, true, but a moderator nonetheless. And I've had enough. So: Netiquette discussions are always fascinating, but please, let's stop talking about how other people should post. Most of my e-mail from this list over the last few days has been from people on this list nattering about how gmail foo and mailer blah, and it is OFF TOPIC and NO LONGER WELCOME. (Frankly, you should have figured this out for yourselves.) If you feel absolutely compelled to tell people that they are lousy netizens and/or correct them on their posting style, please do so PRIVATELY and not on this list. Please e-mail me PRIVATELY if you disagree with this. Brett Cannon is the other list admin, and you should feel free to tell him how much you dislike me & my overbearing moderator ways. Further e-mails on netiquette will result in those people no longer being able to post without moderation. thanks, --titus p.s. This is not aimed especially at Ethan -- it was just the latest e-mail that pushed me over the edge :) -- C. Titus Brown, ctb@msu.edu

On Wed, Jan 11, 2012 at 12:43, C. Titus Brown <ctb@msu.edu> wrote:
And just FYI to people, I wouldn't bother complaining to me because I support Titus on this one; stuff has gone off the rails here, but I have been flat-out ignoring the list as a whole because of it instead of getting worked up like Titus. He's just a more "delicate flower" than me when comes to this and apparently cares more. =) -Brett

For me, control flow matching indentation is probably the single most important feature of python. What I would support is a more convenient way to inject an exception handler into an expression. For example, (a more complicated version of) your first use case would be improved by a way to say (except ConnectError) ==> (time.sleep(timeout); continue) and your password checker example would be improved by a way to say (except WrongPassError) ==> (pass) More details below. 2012/1/11 Manuel Bärenz <manuel@enigmage.de>:
Without your explanation, I would still have assumed that the exception terminated the while.
This would be a lot easier for me to follow with the extra indents. As best I can tell, under normal circumstances, that will just keep making new connections, and never get to the download until the network itself goes down. Or does the else bind to the try, rather than the while -- in which case it is still an infinite loop, but at least succeeds. I'm also not sure which resource needs to be freed, so I'll just call it special_resource. For what I think you wanted, I can read it a lot easier as: with special_resource: for keep_trying in range(3): # not infinity if not network_is_up(): # I would prefer this elsewhere ... time.sleep(timeout) continue try connect_to_server(): download_stuff() break except ConnectError: time.sleep(timeout) else: print("Couldn't establish connection") which I would in turn prefer to reduce to: retry=WaitPolicy('sleep_and_retry', 3) with ensure_network(onfail=retry) as network: with connect_to_server(transport=network, onfail=retry): download_stuff()
This time, it looks like only the final packet gets processed. If else binds to the try, it makes more sense, but still complains about not getting a single useful packet even after getting and processing a dozen. I would prefer: with connection: while receive_packet(): if OK == check_packet(): process_packet() else: print("You sent the wrong thing. Try again.") or at least (following your API more closely): with connection: got_any = False while receive_packet(): try check_packet(): process_packet() got_any = True except ChecksumError: print("You sent the wrong thing. Try again.") if not got_any: print("I couldn't get a single useful packet from you :(")
I honestly don't see that as an improvement over for password in passwords_i_remember: try connect(password): check_mailbox() break except WrongPassError: pass # No pun intended else: print("Not a single one worked.")

On 11.01.2012 17:58, Jim Jewett wrote:
On 12.01.2012 01:00, Steven D'Aprano wrote:
The interpreter could easily distinguish the two, since try is a reserved keyword..
try: result = self.cache[n] except KeyError: result = self.cache[n] = self.f(n) return result I would have wanted to write something like: while True try: return self.cache[n] except KeyError: self.cache[n] = self.f(n) That's how I came up with the idea. But you presented enough reasonable arguments against it such that I won't consider it anymore. Thanks for your comments.

Manuel Bärenz wrote:
This conflates two naturally distinct operations, looping and exception handling, into a single construct that tries to do both. "Loop" is a natural operation. "Catch exceptions" is a natural operation. "Loop and catch exceptions" is not, it is two operations and so should be written as two operations. It isn't obvious that handled exceptions remain in the loop. Because there are two equally good behaviours -- handled exceptions remain in the loop, or handled exceptions exit the loop -- people will get confused by the construct and repeatedly be surprised it doesn't do what they expect. Your proposed syntax doesn't allow the user to write code which couldn't be written before, it adds no new power or expressiveness to Python. The sole advantage is that it saves one line and one indent level, which is a trivial advantage: there are no shortages of either newlines or indents, and if anyone is writing such deeply nested code that they are regularly worried about indents, their code almost certainly is in desperate need of refactoring. Disadvantages include that it increases complexity of the language: the compiler becomes more complex, there is another feature for people to learn. Every time somebody writes a loop with a try, they will have to decide whether to write it the old way or the new way. The try clause can be easily missed while skimming code, making the construct inelegant. The syntax also clashes with existing while...else. What about nested try blocks? If we introduce this, will people try writing this? while expr try try: ... except InnerException: ... ... except OuterException: ... This seems like a reasonable thing to do. I know that's exactly what I'd try. It also raises one special case above all other cases. An arbitrary loop containing a try block looks like this: while expr: preamble # 0 or more lines before the try block try block postscript # 0 or more lines after the try block Your proposed syntax only covers the case where both the preamble and the postscript are 0 lines. What is so special about that case that it needs extra syntax? It's not that common. Existing syntax is consistent, natural and obvious. Your proposal looks like you've picked two block constructs, a while loop and a try block, and nailed them together. -1 on this suggestion. [...]
That use-case is already handled in existing Python by the while...else form. There is no need to add an additional built-in exception for this. -- Steven

I also only see trouble with this construct. I initially assumed you meant: while expr try: # ... except IOError: print 'error' would actually equal: try: while expr: # ... except IOError: print 'error' This makes sense because the "except" is after the while loop suite so it should mean that being in the except suite removes the possibility of reentering the while suite. Anyhow, because of the ambiguity, and because we'll immediately need for/try if/try and try comprehensions (some of which have contradicting "else" semantics) I'm -2 on this. Yuval Greenfield

On Jan 12, 1:08 am, Manuel Bärenz <man...@enigmage.de> wrote:
I propose two new control flows for Python:
Guido recently said: "There seems to be a trend in proposing more random variants of the existing compound statements. Please don't do this. The existing complement of compound statements is quite sufficient and the new proposals do nothing but complicate the parser, the documentation, and the learning process for occasional users." https://groups.google.com/group/python-ideas/msg/5b884fd1caef2f74

2012/1/11 Manuel Bärenz <manuel@enigmage.de>
<snip> Manuel, Thanks for the improvement idea, but I must admit I'm -1 on this. Having first looked at the proposed syntax without your explanation, I found it hard to be sure what it does, and this goes against the spirit of Python. The compound statement it purports to replace is just one indent deeper and much clearer. Eli
participants (11)
-
alex23
-
Arnaud Delobelle
-
Brett Cannon
-
C. Titus Brown
-
Eli Bendersky
-
Ethan Furman
-
Jim Jewett
-
Manuel Bärenz
-
Matt Joiner
-
Steven D'Aprano
-
Yuval Greenfield