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

Missing information about non-yielding generator causing a RuntimeError in contextmanager decorator of contextlib #131658

Closed
ArthurHNL opened this issue Mar 24, 2025 · 2 comments
Labels
docs Documentation in the Doc dir

Comments

@ArthurHNL
Copy link

Documentation

When using the contextmanager decorator from contextlib, the documentation clearly states how it should be used. However, it does not mention that a RuntimeError is thrown when the provided generator never yields a value. See the following example:

@contextmanager
def my_optional_resource():
  if resource_is_required():
    resource = None
    try:
      resource = initialize_resource()
      yield resource
    finally:
      if resource:
        cleanup_resource(resource)

If in the example above is_resource_required() is False, the resource is never initialized and the generator never yields. Personally I expected nothing would actually happen and that the variable originating from the context manager would be None in any with statement using it. However it appears that (at least cpython) throws a RuntimeError indicating that the generator did not yield.

It feels like it makes sense to throw an exception here, but it would be benificial to include this in the documentation.

For completeness, this is a full example to replicate the exception being thrown:

from contextlib import contextmanager

def resource_is_required():
    return False

@contextmanager
def my_optional_resource():
  if resource_is_required():
    resource = None
    try:
      resource = initialize_resource()
      yield resource
    finally:
      if resource:
        cleanup_resource(resource)
        
with my_optional_resource() as resource:
    print(resource)

This yields the following exception output:

Traceback (most recent call last):
  File "main.py", line 17, in <module>
    with my_optional_resource() as resource:
  File "/usr/lib/python3.8/contextlib.py", line 115, in __enter__
    raise RuntimeError("generator didn't yield") from None
RuntimeError: generator didn't yield

It appears that the same happens for the asynccontextmanager decorator, but I did not attempt to replicate this.

@ArthurHNL ArthurHNL added the docs Documentation in the Doc dir label Mar 24, 2025
@sergey-miryanov
Copy link
Contributor

Docs clearly state "This iterator must yield exactly one value" (my bold). So, if you don't yield any value, you break the contract.

@ericvsmith
Copy link
Member

I don’t think there’s any need to document this. If we started to document things like this, the exceptional cases would overwhelm the normal usage. The existing exception is clear and precise, so I don’t think there’s an actual problem to be fixed.

If you’d like to start a larger discussion that every possible exception should be documented, then you should start a discussion on discuss.python.org.

@ericvsmith ericvsmith closed this as not planned Won't fix, can't repro, duplicate, stale Mar 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs Documentation in the Doc dir
Projects
Status: Todo
Development

No branches or pull requests

3 participants