[Python-ideas] Generator-based context managers can't skip __exit__

Ram Rachum ram at rachum.com
Sun Nov 6 01:02:34 EST 2016


Sorry, I was wrong at quoting the workaround I do, it's actually this:

            try:
                yield
            except GeneratorExit:
                raise
            except:
                cleanup()
                raise
            else:
                cleanup()

This works, but it's ugly! And I basically need to do this to every
generator-based context manager that I want to be able to run just the
`__enter__` of without the `__exit__`. I wish there was a better solution
than including this in my code.

On Sun, Nov 6, 2016 at 7:51 AM, Nick Coghlan <ncoghlan at gmail.com> wrote:

> On 6 November 2016 at 14:46, Ram Rachum <ram at rachum.com> wrote:
> > I worked around this problem by adding `except GeneratorExit: raise` in
> my
> > context manager, but that's an ugly solution.
>
> Adding `except GeneratorExit: raise` to a try statement with a finally
> clause won't prevent the finally clause from executing.
>
> Selective non-idempotent cleanup behaviour really isn't a good idea,
> so the language is fighting you for a reason here - the meaning of a
> "finally" clause is that the code it contains *will* get executed, and
> you have to try incredibly hard to keep that from happening since it's
> an implicit part of cleaning up unfinished generators, and we don't
> let you switch the nominal class of generator objects at runtime.
> Indeed, yield inside try/finally was prohibited for years prior to PEP
> 342, as the runtime previously couldn't guarantee that the finally
> clause would actually execute.
>
> If you *don't* want the code to execute unconditionally, then you need
> to be more explicit about when you *do* want it to execute with some
> combination of "except" and "else" clauses. For example:
>
>     >>> @contextmanager
>     ... def cm():
>     ...     print("enter")
>     ...     try:
>     ...         yield
>     ...     except Exception:
>     ...         print("Normal exception, not GeneratorExit,
> KeyboardInterrupt or SystemExit")
>     ...     else:
>     ...         print("No exception")
>     ...
>     >>> cm().__enter__()
>     enter
>
> Cheers,
> Nick.
>
> --
> Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20161106/c912245f/attachment.html>


More information about the Python-ideas mailing list