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 | [EMAIL PROTECTED] | Brisbane, Australia --------------------------------------------------------------- http://boredomandlaziness.skystorm.net _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com