How to decide when to use typing.IO or io.IOBase?

I notice there are “two series” of IO stream-style interface provide by Python standard library, which are

  • typing.IO, including BinaryIO and TextIO
  • io.IOBase, including RawIOBase, TextIOBase etc.

IOBase is good at functionality due to default mixin methods. But IO[AnyStr] seems designed for typing.

My project require to design my own stream-like class. So, I wonder:
:red_question_mark:Question 1: Which one would be better to work as a parent class? Is it common to use none of typing.IO[AnyStr] for typing hints?

Besides, I see typeshed/stdlib/builtins.pyi designs both override for these two base classes.

def open() -> TextIOWrapper: 
def open() -> BinaryIO: ...  
# etc..

So, comes the …
:red_question_mark:Question 2. What’s the role of IO[AnyStr]? Is it a redundant design? Is there any case where typing.IO[AnyStr] works well but io.IOBase not.

typing.IO is a pseudo-protocol. It provides no implementation and was introduced before protocols existed to provide a way to say “this is a file-like object” or “this argument requires a file-like object”. As such it is somewhat of a legacy feature, but still required for full compatibility with legacy annotations.

io.IOBase (and its sub-classes) on the other hand is a utility that allows you to implement a class for “file-like” object without implementing all functionality yourself.

In doubt, you can derive from both, but make sure (by using a type checker) that you implement the full protocol mandated by typing.IO if you do so.

1 Like

The docs have some notes on this in the section ABCs and Protocols for working with I/O. In particular there is a io.Reader and io.Writer that often simpler and useful for a lot of cases.

1 Like