Accordingly, I would like to suggest that 'with' revert to something resembling the PEP 310 definition:
resource = EXPR if hasattr(resource, "__enter__"): VAR = resource.__enter__() else: VAR = None try: try: BODY except: raise # Force realisation of sys.exc_info() for use in __exit__() finally: if hasattr(resource, "__exit__"): VAR = resource.__exit__() else: VAR = None
Generator objects could implement this protocol, with the following behaviour:
def __enter__():
try:
return self.next()
except StopIteration:
raise RuntimeError("Generator exhausted, unable to enter with block")
def __exit__(): try: return self.next() except StopIteration: return None
def __except__(*exc_info): pass
def __no_except__(): pass
One peculiarity of this is that every other 'yield' would not be allowed in the 'try' block of a try/finally statement (TBOATFS). Specifically, a 'yield' reached through the call to __exit__ would not be allowed in the TBOATFS.
It gets even more complicated when one considers that 'next' may be called inside BODY. In such a case, it would not be sufficient to just disallow every other 'yield' in the TBOATFS. It seems like 'next' would need some hidden parameter that indicates whether 'yield' should be allowed in the TBOATFS.
(I assume that if a TBOATFS contains an invalid 'yield', then an exception will be raised immediately before its 'try' block is executed. Or would the exception be raised upon reaching the 'yield'?)
These are also possible by combining a normal for loop with a non-looping with (but otherwise using Guido's exception injection semantics):
def auto_retry(attempts): success = [False] failures = [0] except = [None]
def block(): try: yield None except: failures[0] += 1 else: success[0] = True
while not success[0] and failures[0] < attempts: yield block() if not success[0]: raise Exception # You'd actually propagate the last inner failure
for attempt in auto_retry(3): with attempt: do_something_that_might_fail()
I think your example above is a good reason to *allow* 'with' to loop. Writing 'auto_retry' with a looping 'with' would be pretty straightforward and intuitive. But the above, non-looping 'with' example requires two fairly advanced techniques (inner functions, variables-as-arrays trick) that would probably be lost on some python users (and make life more difficult for the rest).
But I do see the appeal to having a non-looping 'with'. In many (most?) uses of generators, 'for' and looping 'with' could be used interchangeably. This seems ugly-- more than one way to do it and all that.
-Brian _______________________________________________ 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