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
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4