[Python-Dev] PEP 340: Breaking out.

Nick Coghlan ncoghlan at gmail.com
Wed May 4 16:10:31 CEST 2005


Paul Moore wrote:
> Oh, and by the way - I prefer the keywordless form of the block
> statement (as used in my examples above). But it may exacerbate the
> issue with break unless we have a really strong name for these
> constructs

"may" exacerbate? Something of an understatement, unfortunately. 'break' and 
'continue' are going to be useless in most block statements, and in most of the 
cases where that is true, one would expect them to break out of a surrounding loop.

Reconsidering the non-looping semantics I discussed way back at the start of 
this exercise, this is what using auto_retry would look like with a single-pass 
block statement:

   for attempt in auto_retry(3, IOError):
       attempt:
           # Do something!
           # Including break and continue to get on with the next attempt!

(Now that's what I call a user defined statement - we're iterating over a list 
of them!)

Anyway, auto_retry and the block statements it returns could be implemented as:

   def suppressing(exc=Exception, on_success=None):
       """Suppresses the specified exception in the following block"""
       try:
           yield
       except exc:
           pass
       else:
           if on_success is not None:
                on_success()

   def just_do_it():
       """Simply executes the following block"""
       yield

   def auto_retry(times, exc=Exception):
       """Generates the specified number of attempts"""
       class cb():
           succeeded = False
           def __init__(self):
               cb.succeeded = True

       for i in xrange(times-1):
           yield suppressing(exc, cb)
           if cb.succeeded:
               break
       else:
           yield just_do_it()


(Prettier than my last attempt at writing this, but still not very pretty. 
However, I'm willing to trade the need for that callback in the implementation 
of auto_retry to get non-surprising behaviour from break and continue, as the 
latter is visible to the majority of users, but the former is not)

Note that the code above works, even *if* the block statement is a looping 
construct, making a mess out of TOOWTDI.

Making it single pass also simplifies the semantics of the block statement 
(using VAR1 and EXPR1 from PEP 340):

     finalised = False
     block_itr = EXPR1
     try:
         try:
             VAR1 = block_itr.next()
         except StopIteration:
             # Can still choose not to run the block at all
             finalised = True
         except:
             # There was an exception. Handle it or just reraise it.
             finalised = True
             exc = sys.exc_info()
             ext = getattr(block_itr, "__exit__", None)
             if ext is not None:
                 ext(*exc)   # May re-raise *exc
             else:
                 raise *exc   # Well, the moral equivalent :-)
     finally:
         if not finalised:
             # The block finished cleanly, or exited via
             # break, return or continue. Clean up the iterator.
             ext = getattr(block_itr, "__exit__", None)
             if ext is not None:
                 try:
                     ext(StopIteration)
                 except StopIteration:
                     pass


With single-pass semantics, an iterator used in a block statement would have 
it's .next() method called exactly once, and it's __exit__ method called exactly 
once if the call to .next() does not raise StopIteration. And there's no need to 
mess with the meaning of break, return or continue - they behave as usual, 
affecting the surrounding scope rather than the block statement.

The only new thing needed is an __exit__ method on generators (and the block 
syntax itself, of course).

Looks like I've come full circle, and am back to arguing for semantics closer to 
those in PEP 310. But I have a better reason now :)

> Actually,
> maybe referring to them as "block statements", but using no keyword,
> is perfectly acceptable. As I write, I'm finding it more and more
> natural.

Same here. Especially if the semantics are tweaked so that it *is* a 
straightforward statement instead of a loop.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia
---------------------------------------------------------------
             http://boredomandlaziness.skystorm.net


More information about the Python-Dev mailing list