Skip to content
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

Open
anntzer mannequin opened this issue Aug 1, 2019 · 2 comments
Open

How should contextmanager/ContextDecorator work with generators? #81924

anntzer mannequin opened this issue Aug 1, 2019 · 2 comments
Labels
3.8 (EOL) end of life docs Documentation in the Doc dir

Comments

@anntzer
Copy link
Mannequin

anntzer mannequin commented Aug 1, 2019

BPO 37743
Nosy @ncoghlan

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:

assignee = None
closed_at = None
created_at = <Date 2019-08-01.23:33:04.730>
labels = ['3.8', 'docs']
title = 'How should contextmanager/ContextDecorator work with generators?'
updated_at = <Date 2019-09-03.10:21:10.928>
user = 'https://github.com/anntzer'

bugs.python.org fields:

activity = <Date 2019-09-03.10:21:10.928>
actor = 'Antony.Lee'
assignee = 'none'
closed = False
closed_date = None
closer = None
components = ['Documentation']
creation = <Date 2019-08-01.23:33:04.730>
creator = 'Antony.Lee'
dependencies = []
files = []
hgrepos = []
issue_num = 37743
keywords = []
message_count = 2.0
messages = ['348878', '351074']
nosy_count = 1.0
nosy_names = ['ncoghlan']
pr_nums = []
priority = 'normal'
resolution = None
stage = None
status = 'open'
superseder = None
type = None
url = 'https://bugs.python.org/issue37743'
versions = ['Python 3.8']

@anntzer
Copy link
Mannequin Author

anntzer mannequin commented Aug 1, 2019

The docs for ContextDecorator (of which contextmanager is a case) describe its semantics as:

... for any construct of the following form:
    def f():
        with cm():
            # Do stuff
ContextDecorator lets you instead write:
    @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

using with
start
0
1
stop
\==========
using decorator
start
stop
0
1

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?

@anntzer anntzer mannequin added stdlib Python modules in the Lib dir 3.8 (EOL) end of life labels Aug 1, 2019
@ncoghlan
Copy link
Contributor

ncoghlan commented Sep 3, 2019

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)

@ncoghlan ncoghlan added docs Documentation in the Doc dir and removed stdlib Python modules in the Lib dir labels Sep 3, 2019
@ncoghlan ncoghlan removed their assignment Sep 3, 2019
@ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.8 (EOL) end of life docs Documentation in the Doc dir
Projects
None yet
Development

No branches or pull requests

1 participant