gl...@divmod.com wrote: > > On 12:56 pm, ncogh...@gmail.com wrote: >> PEP 377 is a proposal to allow context manager __enter__() methods to >> skip the body of the with statement by raising a specific (new) flow >> control exception. >> >> Since there is a working reference implementation now, I thought it was >> time to open it up for broader discussion. > > Why not allow a context manager to implement some other method, for the > sake of argument let's say "__start__", which was invoked with a > callable object and could choose to evaluate or not evaluate the > statement body by simply not calling that object (or perhaps iterable, > in the case of a generator)?
So the with statement would in effect create a separate code object for the statement body that still shared the scope of the containing function, and then pass a zero-argument callable in to the new method to allow it to execute that code? There are some practical hurdles to that idea (specifically, creating a callable which uses its parent's namespace rather than having its own), but the basic concept seems sound. Rough spec for the concept: Implementing __enter__/__exit__ on a CM would work as per PEP 343. Implementing __with__ instead would give the CM complete control over whether or not to execute the block. The implementation of contextlib.GeneratorContextManager would then change so that instead of providing __enter__/__exit__ as it does now it would instead provide __with__ as follows: def __with__(self, exec_block): try: return self.gen.next() except StopIteration: pass else: try: exec_block() except: exc_type, value, traceback = sys.exc_info() try: self.gen.throw(type, value, traceback) raise RuntimeError("generator didn't stop after throw()") except StopIteration, exc: # Suppress the exception *unless* it's the same exception that # was passed to throw(). This prevents a StopIteration # raised inside the "with" statement from being suppressed return exc is not value except: # only re-raise if it's *not* the exception that was # passed to throw(), because __exit__() must not raise # an exception unless __exit__() itself failed. But throw() # has to raise the exception to signal propagation, so this # fixes the impedance mismatch between the throw() protocol # and the __exit__() protocol. if sys.exc_info()[1] is not value: raise else: try: self.gen.next() except StopIteration: return else: raise RuntimeError("generator didn't stop") More radical in some ways that what I was suggesting, but also cleaner and more powerful. Cheers, Nick. -- Nick Coghlan | ncogh...@gmail.com | Brisbane, Australia --------------------------------------------------------------- _______________________________________________ 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