The problem Link to heading

When a function only needs to write to a stream, typing the argument as IO[str] is overkill, since you are demanding read, seek, close and the rest of the interface, even though you will never use them.

The tip Link to heading

Starting with Python 3.14, the io module exposes the Writer[T] and Reader[T] protocols. They describe only the minimum contract (write() or read()), so any object that implements just that method already satisfies the type.

Before Link to heading

1
2
3
4
from typing import IO

def dump_json(tax_list: list[OperationResult], output: IO[str]) -> None:
    ...

After Link to heading

1
2
3
4
from io import Writer

def dump_json(tax_list: list[OperationResult], output: Writer[str]) -> None:
    ...

On line 3, the signature now says exactly what the function does, it becomes easier to test (a stub with write() is enough), and it accepts more legitimate objects.

Where the example came from Link to heading

The snippet above comes from capital_gains/cli.py, part of a refactoring of a technical assignment where I explored more advanced Python techniques. If you want the full context of the project, the presentation is here.

That’s it, folks!

See you next time!

{}’s