[Python-Dev] Re: anonymous blocks
Brian Sabbey
sabbey at u.washington.edu
Wed Apr 27 02:45:01 CEST 2005
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
More information about the Python-Dev
mailing list