Skip to content

nullcontext treated as having no __enter__ or __exit__ method #10109

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

Closed
l0b0 opened this issue Feb 19, 2021 · 7 comments
Closed

nullcontext treated as having no __enter__ or __exit__ method #10109

l0b0 opened this issue Feb 19, 2021 · 7 comments
Labels
bug mypy got something wrong topic-join-v-union Using join vs. using unions topic-ternary-expression a if b else c

Comments

@l0b0
Copy link

l0b0 commented Feb 19, 2021

Bug Report

Using nullcontext to randomly get a specific context manager results in mypy errors.

To Reproduce

Run mypy on the following code:

$ cat test.py 
from contextlib import nullcontext
from random import getrandbits

with (open("path") if bool(getrandbits(1)) else nullcontext()):
    pass

Expected Behavior

As far as I can tell from looking at contextlib.py it does have __enter__ and __exit__, so this error should not happen.

Actual Behavior

$ mypy test.py 
test.py:4: error: "object" has no attribute "__enter__"  [attr-defined]
test.py:4: error: "object" has no attribute "__exit__"  [attr-defined]
Found 2 errors in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 0.800

  • Mypy command-line flags: None

  • Mypy configuration options from mypy.ini (and other config files):

    [mypy]
    check_untyped_defs = true
    disallow_any_generics = true
    disallow_untyped_defs = true
    no_implicit_optional = true
    show_error_codes = true
    warn_redundant_casts = true
    warn_return_any = true
    warn_unused_ignores = true
    
  • Python version used: 3.8.6

  • Operating system and version: Arch Linux

@l0b0 l0b0 added the bug mypy got something wrong label Feb 19, 2021
@JelleZijlstra
Copy link
Member

The issue isn't with nullcontext, it's that mypy infers (open("path") if bool(getrandbits(1)) else nullcontext()) to be of type object. Variants of this have been reported many times. A workaround is to first declare a variable like cm: ContextManager[None] = (open("path") if bool(getrandbits(1)) else nullcontext()).

l0b0 added a commit to linz/geostore that referenced this issue Feb 21, 2021
By randomly using one or two assets we test both branches *eventually,*
and since this is a particularly expensive test the tradeoff between
learning about any issues early and a faster run is probably worth it at
least in the short term.

Had to make `S3Object` a proper `ContextManager` subclass after working
around a `mypy` limitation related to grouping brackets
<python/mypy#10109 (comment)>.
@l0b0
Copy link
Author

l0b0 commented Feb 21, 2021

Thank you @JelleZijlstra, is there an overarching issue I can follow? That way we can also close this as a duplicate.

l0b0 added a commit to linz/geostore that referenced this issue Feb 21, 2021
By randomly using one or two assets we test both branches *eventually,*
and since this is a particularly expensive test the tradeoff between
learning about any issues early and a faster run is probably worth it at
least in the short term.

Had to make `S3Object` a proper `ContextManager` subclass after working
around a `mypy` limitation related to grouping brackets
<python/mypy#10109 (comment)>.
l0b0 added a commit to linz/geostore that referenced this issue Feb 21, 2021
By randomly using one or two assets we test both branches *eventually,*
and since this is a particularly expensive test the tradeoff between
learning about any issues early and a faster run is probably worth it at
least in the short term.

Had to make `S3Object` a proper `ContextManager` subclass after working
around a `mypy` limitation related to grouping brackets
<python/mypy#10109 (comment)>.
l0b0 added a commit to linz/geostore that referenced this issue Feb 23, 2021
By randomly using one or two assets we test both branches *eventually,*
and since this is a particularly expensive test the tradeoff between
learning about any issues early and a faster run is probably worth it at
least in the short term.

Had to make `S3Object` a proper `ContextManager` subclass after working
around a `mypy` limitation related to grouping brackets
<python/mypy#10109 (comment)>.
@OddBloke
Copy link
Contributor

The workaround described doesn't seem to work for me. With:

from contextlib import nullcontext
from random import getrandbits
from typing import ContextManager

example: ContextManager[None] = (open("path") if bool(getrandbits(1)) else nullcontext())
with example:
    pass

I still get:

reproducer2.py:5: error: Incompatible types in assignment (expression has type "object", variable has type "ContextManager[None]")
Found 1 error in 1 file (checked 1 source file)

@graingert
Copy link
Contributor

I have to use a TextIO | to make this work

from contextlib import nullcontext
from random import getrandbits
from typing import ContextManager, TextIO


example: TextIO | ContextManager[None] = (open("path") if bool(getrandbits(1)) else nullcontext())

with example:
    pass

https://mypy-play.net/?mypy=latest&python=3.10&flags=strict&gist=d3c744b47fe06aba0782a8b0672d0240

@OddBloke
Copy link
Contributor

Thanks Tom, that works for me also!

@AlexWaygood AlexWaygood added topic-ternary-expression a if b else c topic-join-v-union Using join vs. using unions labels Apr 1, 2022
@jamesderlin
Copy link

Seems like a duplicate of #5512.

@JelleZijlstra
Copy link
Member

Yes, one of many in the #join-v-union genre.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong topic-join-v-union Using join vs. using unions topic-ternary-expression a if b else c
Projects
None yet
Development

No branches or pull requests

6 participants