When writing a class that implements a file-like interface we can inherit one of the abstract base classes from the io
module, for example TextIOBase
, as shown in Adapt an iterator to behave like a file-like object in Python.
On the other hand, in type annotations we are supposed to use the classes derived from typing.IO
(e.g. TextIO
) to represent such objects, as shown in Type hint for a file or file-like object? or Type-checking issue with io.TextIOBase in a Union.
However, this does not seem to work as I expected:
import io
import sys
import typing
class MyIO(io.TextIOBase):
def write(self, text: str):
pass
def hello(f: typing.TextIO):
f.write('hello')
hello(sys.stdout) # type checks
hello(open('temp.txt', 'w')) # type checks
hello(MyIO()) # does not type check
When running mypy on this code (using Python 3.7.3 and mypy 0.910), we get
error: Argument 1 to "hello" has incompatible type "MyIO"; expected "TextIO"
Question
How can the MyIO
class be written such that it is accepted as a function argument of type typing.TextIO
(without just using typing.cast(typing.TextIO, ...)
)?
Failed attempts
Using
typing.TextIO
as a base class is not possible:When using
class MyIO(typing.TextIO)
:error: Cannot instantiate abstract class "MyIO" with abstract attributes "__enter__", "__exit__", ... and "writelines" (15 methods suppressed)
When using
class MyIO(io.TextIOBase, typing.TextIO):
:error: Definition of "readlines" in base class "IOBase" is incompatible with definition in base class "IO"
and the same for several other methods.
Overriding
__new__
and annotatingtyping.TextIO
as return type does not work:def __new__(cls, *args, **kwargs) -> typing.TextIO: return super().__new__(cls, *args, **kwargs)
results in
error: Incompatible return type for "__new__" (returns "TextIO", but must return a subtype of "MyIO")
error: Incompatible return value type (got "MyIO", expected "TextIO")
Or is this already supposed to work, and I'm using too old a version of Python and/or mypy? Using --python-version 3.8
or 3.9
or 3.10
as option for mypy does not change anything, however.