This solution is not equivalent to what exactly you're looking for:
you can have any object x as long as x.readline() -> str
Instead we are defining a custom abstract base class that expects readline
abstract method to be defined by its child classes. Hence instead of any random object it would only accept instances of this new abstract base class, making it more explicit.
from abc import ABC, abstractmethod
class FileObject(ABC):
@abstractmethod
def readline(self):
raise NotImplementedError()
Now we are going to define a custom type that can work with Python's file objects and instances of FileObject
:
from typing import IO, TypeVar
StreamType = TypeVar('StreamType', IO, FileObject)
def func(name: str, stream: StreamType) -> None:
pass
Now let's test it using mypy:
from io import StringIO, BytesIO
class X(FileObject):
def readline(self):
pass
func('a', StringIO()) # passed
func('a', BytesIO()) # passed
func('a', open('foo.txt')) # passed
func('a', X()) # passed
func('a', object()) # failed
func('a', []) # failed
func('a', 1) # failed
Output:
$ mypy so.py
so.py:33: error: Type argument 1 of "func" has incompatible value "object"
so.py:34: error: Type argument 1 of "func" has incompatible value List[None]
so.py:35: error: Type argument 1 of "func" has incompatible value "int"