[Python-Dev] PEP 377 - allow enter() methods to skip the statement body (original) (raw)

Nick Coghlan ncoghlan at gmail.com
Sun Mar 15 21:28:37 CET 2009


glyph at divmod.com wrote:

On 12:56 pm, ncoghlan at 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 | ncoghlan at gmail.com | Brisbane, Australia



More information about the Python-Dev mailing list