What is PEP 688?
PEP 688: Making the buffer protocol accessible in Python (Python 3.12) gives the buffer protocol a spelling in the type system, collections.abc.Buffer, and removes the typing special case that let bytearray and memoryview pass wherever bytes was expected.
If mypy 2.0 started flagging bytearray arguments that checked clean for years, this PEP is why.
Why did mypy start rejecting bytearray?
The original typing spec carved out a promotion: bytearray and memoryview were treated as assignable to bytes, on the theory that most code accepting binary data should accept all three. PEP 688 removed that promotion because it made the type system lie (a bytes annotation no longer guaranteed bytes).
mypy shipped the new behavior behind --strict-bytes in 1.15 and enabled it by default in mypy 2.0 (May 2026). The release notes state: “Per PEP 688, mypy no longer treats bytearray and memoryview values as assignable to the bytes type.” Every mypy 2.0 user inherits this, with or without strict mode:
$ mypy app.py
app.py:5: error: Argument 1 to "hexdump" has incompatible type "bytearray"; expected "bytes" [arg-type]
app.py:6: error: Argument 1 to "hexdump" has incompatible type "memoryview[int]"; expected "bytes" [arg-type]
--no-strict-bytes restores the old promotion while you migrate.
Why was the old promotion unsound?
A bytes annotation promises an immutable, hashable value. bytearray is neither: a function that stores its argument can have it mutated out from under it, and one that uses it as a dict key gets TypeError: unhashable type: 'bytearray' at runtime. The promotion let both bugs through type checking.
Annotate with Buffer instead
For functions that genuinely accept any binary data, annotate with Buffer, the protocol type this PEP added:
from collections.abc import Buffer # 3.12+; typing_extensions.Buffer for older versions
def hexdump(data: Buffer) -> str:
return bytes(data).hex()bytes, bytearray, and memoryview all satisfy Buffer, as does any C extension type implementing the buffer protocol (NumPy arrays, for example). The check works at runtime too: issubclass(bytearray, Buffer) returns True.
PEP 688 also lets pure-Python classes implement the protocol via __buffer__ and __release_buffer__ methods, which previously only C extensions could do.
How do other checkers treat it?
Pyright and ty already enforced PEP 688 semantics by default; Pyright’s escape hatch is the disableBytesTypePromotions setting. mypy was the last major checker holding the promotion. A codebase upgrading to mypy 2.0, or migrating between checkers, hits these errors as a batch.
Learn More
- PEP 688: Making the buffer protocol accessible in Python
- mypy 2.0 release notes
- collections.abc.Buffer documentation
- What is PEP 561? covers the other typing PEP that surfaces as a surprising checker error