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

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

AltStyle によって変換されたページ (->オリジナル) /