2026, Jan 03 17:00

Override RawIOBase.readinto without pyright errors: match WriteableBuffer and MaybeNone (or Buffer)

Learn why pyright flags overriding RawIOBase.readinto with bytearray, and fix it by matching the base signature: use WriteableBuffer and MaybeNone (or Buffer) to stay compatible.

Overriding io.RawIOBase.readinto with a precise type hint looks deceptively simple until a type checker like pyright calls it out as incompatible. If you annotate the parameter as bytearray, pyright reports a mismatch against the base signature. The fix is to mirror the base type exactly, not to narrow it.

Minimal repro of the problem

from io import RawIOBase
class StreamPort(RawIOBase):
    def readinto(self, data_buf: bytearray) -> int:
        pass  # implementation omitted

io.py:6:9 - error: Method "readinto" overrides class "_RawIOBase" in an incompatible manner
Parameter 2 type mismatch: base parameter is type "WriteableBuffer", override parameter is type "bytearray"
"Buffer" is not assignable to "bytearray" (reportIncompatibleMethodOverride)
1 error, 0 warnings, 0 informations

The environment in question is Python 3.13.3 with pyright 1.1.400.

What’s actually wrong

The base method expects a WriteableBuffer. That’s a broader contract than a concrete bytearray. Narrowing an argument type in an override breaks subtyping rules. If the base can accept any WriteableBuffer, an override must accept the same. If you don’t want to accept an arbitrary WriteableBuffer, the way forward is to define a separate, more restricted method instead of overriding readinto.

Solution: match the base signature

The most direct fix is to reuse the exact type alias the base uses. You can import it from the _typeshed package under a TYPE_CHECKING guard and annotate the method accordingly.

from io import RawIOBase
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from _typeshed import MaybeNone, WriteableBuffer
class DeviceReader(RawIOBase):
    def readinto(self, target: WriteableBuffer) -> int | MaybeNone:
        ...

Type checkers ship the _typeshed package, so you can rely on it when type checking. That’s how pyright knows the signature of RawIOBase.readinto in the first place. The return type includes MaybeNone for the same reason.

There’s also an alternative that avoids importing from _typeshed. WriteableBuffer is a type alias for collections.abc.Buffer, so the following is equivalent. It replaces MaybeNone via a local alias using the Any trick.

from collections.abc import Buffer
from io import RawIOBase
from typing import Any, TypeAlias
type MaybeNone = Any
class BinarySource(RawIOBase):
    def readinto(self, sink: Buffer) -> int | MaybeNone:
        ...

For background on why MaybeNone is an alias for Any, see “The Any Trick”: https://typing.python.org/en/latest/guides/writing_stubs.html#the-any-trick.

Why this matters

Matching the base signature preserves substitutability and keeps pyright from flagging reportIncompatibleMethodOverride. You can depend on the _typeshed definitions while type checking, which is exactly how tools know the correct stdlib contracts.

Takeaways

When overriding a stdlib method like RawIOBase.readinto, use the same parameter and return types as the base. If you need the official alias, import it from _typeshed under TYPE_CHECKING. If you prefer a public type, annotate with collections.abc.Buffer and set up MaybeNone via the Any trick. And if your implementation truly needs a narrower input, don’t override readinto—add your own method with the restricted type instead.