mypy does not honour open() overrides from typeshed. · Issue #11193 · python/mypy (original) (raw)

Bug Report

It seems like mypy is not honoring the typeshed Path.open overrides exactly, as from these I would expect mypy to think the values returned by open was:

The same is the case for the open() builtin.

I'm reporting this as a mypy issue, because the typing in typeshed looks right to me for the most part, and I can't even tell from typeshed where mypy would be getting typing.TextIO from. I will however report a seperate issue against typeshed for mixing the io. and typing.IO heirarchies in return types, as this is maybe where typing.BinaryIO comes from, but even so the other overrides should take precedence as far as I can tell.

To Reproduce

The problem can be seen when running mypy on the following code which contain unit tests which run with no errors:

import io import pathlib import tempfile import typing import unittest

class TestOpen(unittest.TestCase): def setUp(self) -> None: self._tmp_path = tempfile.TemporaryDirectory() self.tmp_path = pathlib.Path(self._tmp_path.name) self.tmp_file = self.tmp_path / "file"

def tearDown(self) -> None:
    self._tmp_path.cleanup()

def test_open_text_stream(self) -> None:
    with self.tmp_file.open("w") as text_stream:
        text_io: typing.TextIO = text_stream  # noqa: F841
        text_io_base: io.TextIOBase = text_stream  # noqa: F841
        assert isinstance(text_stream, io.TextIOBase)

def test_open_buffered_stream(self) -> None:
    with self.tmp_file.open("wb") as buffered_stream:
        binary_io: typing.BinaryIO = buffered_stream  # noqa: F841
        buffered_io_base: io.BufferedIOBase = buffered_stream  # noqa: F841
        assert isinstance(buffered_stream, io.BufferedIOBase)

def test_open_raw_stream(self) -> None:
    with self.tmp_file.open("wb", buffering=0) as raw_stream:
        binary_io: typing.BinaryIO = raw_stream  # noqa: F841
        raw_io_base: io.RawIOBase = raw_stream  # noqa: F841
        assert isinstance(binary_io, io.RawIOBase)

if name == "main": unittest.main()

Expected Behavior

I expect mypy to not find any errors in the shared code.

Actual Behavior

Mypy reports the following type errors:

$ poetry run mypy --show-error-codes --show-error-context  test_open_ut.py
test_open_ut.py: note: In member "test_open_text_stream" of class "TestOpen":
test_open_ut.py:20: error: Incompatible types in assignment (expression has type "TextIO", variable has type "TextIOBase")  [assignment]
test_open_ut.py:21: error: Subclass of "TextIO" and "TextIOBase" cannot exist: would have incompatible method signatures  [unreachable]
test_open_ut.py: note: In member "test_open_buffered_stream" of class "TestOpen":
test_open_ut.py:26: error: Incompatible types in assignment (expression has type "BinaryIO", variable has type "BufferedIOBase")  [assignment]
test_open_ut.py:27: error: Subclass of "BinaryIO" and "BufferedIOBase" cannot exist: would have incompatible method signatures  [unreachable]
test_open_ut.py: note: In member "test_open_raw_stream" of class "TestOpen":
test_open_ut.py:32: error: Incompatible types in assignment (expression has type "BinaryIO", variable has type "RawIOBase")  [assignment]
test_open_ut.py:33: error: Subclass of "BinaryIO" and "RawIOBase" cannot exist: would have incompatible method signatures  [unreachable]

Your Environment

[mypy]  
# Kept as seperate config file as some plugins don't support pyproject.toml  
# (e.g pydantic.mypy)  
# https://mypy.readthedocs.io/en/stable/config_file.html  
python_version = 3.7  
strict = True  
warn_unreachable = True  
warn_unused_configs = True  

The code for this can be found here