Consider the following code:
from io import TextIOWrapper from typing import List from zipfile import ZipFile def read_zip_lines(zippath: str, filename: str) -> List[str]: with ZipFile(zippath) as zf: with zf.open(filename) as bfp: with TextIOWrapper(bfp, 'utf-8') as fp: return fp.readlines()
Running mypy v0.782 on the above code under Python 3.6.9 fails with the following error:
zfopen.py:8: error: Argument 1 to "TextIOWrapper" has incompatible type "IO[bytes]"; expected "BinaryIO"
However, I feel that this code should not be regarded as an error, as ZipFile.open()
returns a binary filehandle, which TextIOWrapper
accepts. Moreover, IO[bytes]
and BinaryIO
are (as far as I understand) effectively the same thing; it’s just that BinaryIO
is declared as a subclass of IO[bytes]
. I would naïvely expect IO[bytes]
to be accepted everywhere that BinaryIO
is, except that’s not how subclasses work, and I’m not sure how to properly make use of this subclassing when typing.
Who is in error here, and how does the error get fixed?
- Is typeshed in error for declaring the return type of
ZipFile.open()
asIO[bytes]
instead ofBinaryIO
? - Is typeshed in error for declaring the type of the first argument to
TextIOWrapper
asBinaryIO
instead ofIO[bytes]
? - Is the
typing
module in error for makingBinaryIO
a subclass ofIO[bytes]
instead of an alias? - Is my code in error for not performing some sort of cast on
bfp
? - Is my thinking in error for expecting
bfp
to be passable toTextIOWrapper
unmodified?
Advertisement
Answer
This shorter test case with mypy 0.782 gets the same error:
binary_file = io.open('foo.bin', 'rb') text_file = io.TextIOWrapper(binary_file, encoding='utf-8', newline='')
whether binary_file
is explicitly declared as IO[bytes]
or inferred.
Fix: Use mypy 0.770
or mypy 0.790
.
It was a regression in mypy’s typeshed (Issue 4349) and the fix is in mypy 0.790, fixing both zipfile.open()
and io.open()
.