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

[1.12 regression] Erroneous Cannot infer type argument for open & gzip #18024

Open
Redoubts opened this issue Oct 23, 2024 · 5 comments
Open
Labels
bug mypy got something wrong

Comments

@Redoubts
Copy link

Bug Report

Seems like a regression from 11.2, consider the following

import gzip, shutil
from pathlib import Path


with open("/dev/null") as src, gzip.open("/dev/null", "wt") as dest:
    reveal_type(src)
    reveal_type(dest)
    shutil.copyfileobj(src, dest)


with Path("/dev/null").open() as src2, gzip.open("/dev/null", "wt") as dest2:
    reveal_type(src2)
    reveal_type(dest2)
    shutil.copyfileobj(src2, dest2)

I see errors even though this should be fine?

% mypy x.py     
x.py:6: note: Revealed type is "io.TextIOWrapper[io._WrappedBuffer]"
x.py:7: note: Revealed type is "typing.TextIO"
x.py:8: error: Cannot infer type argument 1 of "copyfileobj"  [misc]
x.py:12: note: Revealed type is "io.TextIOWrapper[io._WrappedBuffer]"
x.py:13: note: Revealed type is "typing.TextIO"
x.py:14: error: Cannot infer type argument 1 of "copyfileobj"  [misc]
Found 2 errors in 1 file (checked 1 source file)

no errors in 1.11.2:

x.py:6: note: Revealed type is "io.TextIOWrapper"
x.py:7: note: Revealed type is "typing.TextIO"
x.py:12: note: Revealed type is "io.TextIOWrapper"
x.py:13: note: Revealed type is "typing.TextIO"
Success: no issues found in 1 source file

Your Environment

  • Mypy version used: 1.13
  • Mypy command-line flags: mypy
  • Mypy configuration options from mypy.ini (and other config files): none
  • Python version used: Python 3.11.10
@Redoubts Redoubts added the bug mypy got something wrong label Oct 23, 2024
@brianschubert
Copy link
Collaborator

Looks potentially related to python/typeshed#12286 (which landed in v1.12.0 with #17586)

@hauntsaninja
Copy link
Collaborator

hauntsaninja commented Oct 24, 2024

mypy_primer -p test.py --bisect --debug --old 'v1.11.0' confirms it's the sync PR brianschubert mentions

@hauntsaninja hauntsaninja changed the title Erroneous Cannot infer type argument for open & gzip [1.12 regression] Erroneous Cannot infer type argument for open & gzip Oct 26, 2024
@JukkaL
Copy link
Collaborator

JukkaL commented Oct 28, 2024

Here's an attempt to create a simplified, self-contained reproducer:

from typing import AnyStr, Protocol, TypeVar, Generic, overload

class Buffer(Protocol):
    def __buffer__(self, flags: int, /) -> memoryview: ...

class IO(Generic[AnyStr]):
    @overload
    def write(self: IO[bytes], s: Buffer, /) -> int: ...
    @overload
    def write(self, s: AnyStr, /) -> int: ...
    def write(self, s):
        return 0

class TextIO(IO[str]):
    pass

_T_contra = TypeVar("_T_contra", contravariant=True)

class SupportsWrite(Protocol[_T_contra]):
    def write(self, s: _T_contra, /) -> object: ...

def foo(
    f: SupportsWrite[AnyStr],
) -> None: ...

def bar(d: TextIO) -> None:
    foo(d)  # Value of type variable "AnyStr" of "foo" cannot be "Buffer"

Interestingly, this fails on 1.11 as well, and doesn't depend on the changes in #17586.

I looked at this a bit, it seems that constraints inference from protocol generates invalid constraints (infer_constraints_from_protocol_members). The root of the problem seems to be in mypy.subtypes.find_member.

@ilevkivskyi I remember that you mentioned that you've looked into inconsistencies between find_member and analyze_member_access. This may be an example where they deviate.

@ilevkivskyi
Copy link
Member

I remember that you mentioned that you've looked into inconsistencies between find_member and analyze_member_access. This may be an example where they deviate.

Yes, and it may not be trivial to fix. Ultimately, we need to refactor this, so that everything uses analyze_member_access (e.g. also sub-classing overrides checks, although they are currently closer in semantics/implementation). As you know, this is a big project, and I didn't yet have courage to start it :-)

@JukkaL
Copy link
Collaborator

JukkaL commented Nov 11, 2024

It may still be possible to fix this specific regression by a more ad-hoc fix, by adding some functionality to find_member, even if we don't fully migrate to analyze_member_access until later. However, it may be turn out to be a bit messy, and it will increase code duplication.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

5 participants