Nick Coghlan wrote:
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

Reply via email to