python / python/typeshed

What is the correct way to subclass asyncio.StreamReader?

Open
#12,423 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

topic: asyncio
Dominant language
Python
Stars
5.1k
Forks
2.1k
Avg merge
1d 19h
Merged PRs (30d)
82

Description

#11755 changed the signature of asyncio.StreamReader.readuntil to make the separator argument take an internal type _ReaduntilBuffer that just has ... for its definition. This is confusing because inside the python 3.12 stdlib code this argument is already typed as bytes, which is the obvious choice. When I try to write my own subclass that takes bytes for the argument mypy complains I'm violating Liskov. Since this class is specifically designed as a base class that users would want to subclass themselves, it seems like there should be a way to do this that doesn't use internal types, no?

import asyncio
import typing

class StreamReaderWrapper(asyncio.StreamReader):
    def __init__(self):
        self._reader: asyncio.StreamReader | None = None

    @typing.override
    async def read(self, n: int = -1) -> bytes:
        assert self._reader is not None
        return await self._reader.read(n)

    @typing.override
    async def readline(self) -> bytes:
        assert self._reader is not None
        return await self._reader.readline()

    @typing.override
    async def readexactly(self, n: int) -> bytes:
        assert self._reader is not None
        return await self._reader.readexactly(n)

    @typing.override
    async def readuntil(self, separator: bytes = b'\n') -> bytes:
        assert self._reader is not None
        return await self._reader.readuntil(separator)
foo.py:4: error: Argument 1 of "readuntil" is incompatible with supertype "StreamReader"; supertype defines the argument type as "_ReaduntilBuffer"  [override]
foo.py:4: note: This violates the Liskov substitution principle
foo.py:4: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides

Note that using bytes | bytearray | memoryview as the type instead (taken from #11755) doesn't work either, same error

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Inspect the asyncio.StreamReader.readuntil stub and the Python 3.12 stdlib implementation, starting with the signature changed by #11755. Reproduce the override error with the provided StreamReaderWrapper and mypy, then establish a public annotation that permits the documented subclassing use case without exposing an unsuitable internal type.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
developer-experience
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.