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
On 6 November 2016 at 14:46, Ram Rachum
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@gmail.com | Brisbane, Australia