Skip to content

Warn about @contextlib.contextmanager without try/finally in generator functions #2832

Description

Is your feature request related to a problem?

If you decorate a generator function with @contextlib.contextmanager and use the context manager cm inside a generator function, the context manager's __exit__ will not be always be called because the generator raises GeneratorExit, which ends the generator that defines cm.

Notice that cm exit never appears, as cm's __exit__ is never called.

from __future__ import generator_stop

import contextlib
import weakref
import sys


@contextlib.contextmanager
def cm():
    print("cm enter")
    a = yield
    print('cm exit')

def genfunc():
    with cm():
        print("stepping")
        yield

def main():
    gen = genfunc()
    ref = weakref.ref(gen, print)
    next(gen)


if __name__ == "__main__":
    main()

Output:

cm enter
stepping
<weakref at 0x7f4739e38e08; dead>

Describe the solution you'd like

Generator functions decorated with @contextlib.contextmanager should not be used in generators unless the context manager has finally:

@contextlib.contextmanager
def cm():
    try:
        yield
    finally:
        # cleanup code here
        ...

or specifically handles GeneratorExit:

@contextlib.contextmanager
def cm():
    try:
        yield
    except GeneratorExit:
        # cleanup code here
        ...

Additional context

Add any other context about the feature request here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

Labels

Enhancement ✨Improvement to a componentNeeds PRThis issue is accepted, sufficiently specified and now needs an implementation

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions