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

Steven Bethard steven.bethard at gmail.com
Mon Mar 16 19:26:22 CET 2009


On Mon, Mar 16, 2009 at 11:06 AM, Guido van Rossum <guido at python.org> wrote:
> Moreover, since the main use case seems to be fixing a corner case of
> the nested() context manager, perhaps the effort towards changing the
> language would be better directed towards supporting "with a, b:" as a
> shorthand for "with a: with b:" .
>
> On Mon, Mar 16, 2009 at 10:01 AM, Guido van Rossum <guido at python.org> wrote:
>> I have no right to speak because I haven't read through all the
>> details of the proposal, but reading this I am very sad that we have
>> to introduce a whole new exception (and one with special status as
>> well) in order to fix such a niggly corner case of the context manager
>> protocol.
>>
>> Since IIUC the original context manager design was intended to have
>> exactly one yield in the body of the context manager -- can't we just
>> declare fewer (or more) yields an error and rase an appropriate
>> TypeError or something?

It's not really a generator specific thing. You can generate similar
problems by just defining a class with an __enter__() method that
raises an exception.

But I agree that it seems like a big change for a small corner case.
Is there anything other than contextlib.nested() which needs this? If
there is no other use case, then I'm a strong +1 for Guido's
suggestion of providing syntactic support for ``with a, b:`` instead.

BTW, I think the explanation of the problem isn't as clear as it could
be. The core problem, if I understand it right, is that
contextlib.nested() is not equivalent to a real nested with statement
because it calls the nested __enter__() methods too early. A real
nested with statement translates into something like::

    mgr1.__enter__()
    try:
        mgr2.__enter__()
        try:
            BLOCK
        except:
            ...
    except:
        if not mgr1.__exit__(*sys.exc_info()):
            raise

But contextlib.nested() calls all the __enter__() methods in its own
__enter__() so it translates into something like::

    mgr1.__enter__()
    mgr2.__enter__()
    try:
        BLOCK
    except:
        ...

The key problem here is that ``mgr2.__enter__()`` is outside of the
try block, and the context manager has no way to put it inside. So the
thing that contextlib.nested() really needs is a way to be able to
insert statements into the BLOCK part of the code. (I'm not actually
suggesting we go this route, but that seems to be what
contextlib.nested() is really after.)

Steve
-- 
I'm not *in*-sane. Indeed, I am so far *out* of sane that you appear a
tiny blip on the distant coast of sanity.
        --- Bucky Katt, Get Fuzzy


More information about the Python-Dev mailing list