-
-
Notifications
You must be signed in to change notification settings - Fork 30.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How should contextmanager/ContextDecorator work with generators? #81924
Comments
The docs for ContextDecorator (of which contextmanager is a case) describe its semantics as:
def f():
with cm():
# Do stuff
@cm()
def f():
# Do stuff However, when decorating a generator, the equivalence is broken: from contextlib import contextmanager
@contextmanager
def cm():
print("start")
yield
print("stop")
def gen_using_with():
with cm():
yield from map(print, range(2))
@cm()
def gen_using_decorator():
yield from map(print, range(2))
print("using with")
list(gen_using_with())
print("==========")
print("using decorator")
list(gen_using_decorator()) results in
i.e., when used as a decorator, the entire contextmanager is executed first before iterating over the generator (which is unsurprising given the implementation of ContextDecorator: ContextDecorator returns a function that executes the context manager and returns the generator, which is only iterated over later). Should this be considered as a bug in ContextDecorator, and should ContextDecorator instead detect when it is used to decorate a generator (e.g. with inspect.isgeneratorfunction), and switch its implementation accordingly in that case? |
This is a documentation bug, as the current behaviour is as intended, but the documented equivalence doesn't hold for generator functions: for a generator function, the CM will only be applied when the generator is instantiated, whereas the inline context manager version will be held open until the generator is closed or destroyed. That said, an approach similar to the one discussed in bpo-37398 could also be applied, here, with a separate "ContextDecorator.generator()" class method added to give the "wrapped yield from" behaviour. If anyone is interested in pursuing that, it can be filed as a separate enhancement issue (leaving this bug to cover the fact that the existing documentation is only accurate for regular synchronous functions) |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: