It seems that the same argument that explains why generators are so good for defining iterators, also applies to the PEP 310 use case: it's just much more natural to write
def with_file(filename): f = open(filename) try: yield f finally: f.close()
than having to write a class with __entry__ and __exit__ and __except__ methods (I've lost track of the exact proposal at this point).
Indeed - the transaction example is very easy to write this way:
def transaction(): begin_transaction() try: yield None except: abort_transaction() raise else: commit_transaction()
> Also note that, unlike the for-loop translation, this does *not* > invoke iter() on the result of EXPR; that's debatable but given that > the most common use case should not be an alternate looping syntax > (even though it *is* technically a loop) but a more general "macro > statement expansion", I think we can expect EXPR to produce a value > that is already an iterator (rather than merely an interable).
Not supporting iterables makes it harder to write a class which is inherently usable in a with block, though. The natural way to make iterable classes is to use 'yield' in the definition of __iter__ - if iter() is not called, then that trick can't be used.
Finally, I think it would be cool if the generator could trap occurrences of break, continue and return occurring in BODY. We could introduce a new class of exceptions for these, named ControlFlow, and (only in the body of a with statement), break would raise BreakFlow, continue would raise ContinueFlow, and return EXPR would raise ReturnFlow(EXPR) (EXPR defaulting to None of course).
Perhaps 'continue' could be used to pass a value into the iterator, rather than 'return'? (I believe this has been suggested previously in the context of for loops)
This would permit 'return' to continue to mean breaking out of the containing function (as for other loops).
So a block could return a value to the generator using a return statement; the generator can catch this by catching ReturnFlow. (Syntactic sugar could be "VAR = yield ..." like in Ruby.)
So, "VAR = yield x" would expand to something like:
try: yield x except ReturnFlow, ex: VAR = ReturnFlow.value
?
With a little extra magic we could also get the behavior that if the generator doesn't handle ControlFlow exceptions but re-raises them, they would affect the code containing the with statement; this means that the generator can decide whether return, break and continue are handled locally or passed through to the containing block.
That seems a little bit _too_ magical - it would be nice if break and continue were defined to be local, and return to be non-local, as for the existing loop constructs. For other non-local control flow, application specific exceptions will still be available.
Regardless, the ControlFlow exceptions do seem like a very practical way of handling the underlying implementation.
Note that EXPR doesn't have to return a generator; it could be any object that implements next() and next_ex(). (We could also require next_ex() or even next() with an argument; perhaps this is better.)
With this restriction (i.e. requiring next_ex, next_exc, or Terry's suggested __next__), then the backward's compatible version would be simply your desired semantics, plus an attribute check to exclude old-style iterators:
it = EXPR if not hasattr(it, "__next__"): raise TypeError("'with' block requires 2nd gen iterator API support") err = None while True: try: VAR = it.next(err) except StopIteration: break try: err = None BODY except Exception, err: # Pretend "except Exception:" == "except:" pass
The generator objects created by using yield would supply the new API, so would be usable immediately inside such 'with' blocks.
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