In the following code, I have a function that can take either a string, or some TextIOBase
. If a string is passed, it is interpreted as a path to a file that should be opened and read. If a TextIOBase
is passed, the contents of that stream will be read:
from typing import Union
import io
def function(foo: Union[str, io.TextIOBase]) -> None:
if isinstance(foo, str):
foo = open(foo, "w")
This seems like it should be okay, because Unions are supposed to be covariant, meaning that a subclass of one of the types in the union should satisfy the type annotation, and in this case the output type of open()
is a subclass of TextIOBase
. However, mypy complains with:
union.py:6: error: Incompatible types in assignment (expression has type "TextIO", variable has type "Union[str, TextIOBase]")
Found 1 error in 1 file (checked 1 source file)
I figured maybe there is an issue with the ambiguity of the return type of open()
, based on the passed arguments, so I tried making a StringIO
instead, but got the same error. Any thoughts? Why is mypy mad at me?
I have also tried this with some toy classes (e.g. Union[str, T1]
, then assigning a T2
, where T2
is a subclass of T1), which mypy is perfectly happy with.
The typing
module has a dedicated object for that: typing.TextIO
. The return type of open
is determined based on the mode
argument and evaluates as one of these types: TextIO
, BinaryIO
.