[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