Repository navigation
bytes and bytearray __mod__ reject valid numeric formatting #16528
Description
Summary
The bytes.__mod__ and bytearray.__mod__ signatures introduced in #16502 reject valid numeric %-formatting. Besides false-positive operator diagnostics, this causes a str | bytes formatting result to become str | Unknown in Pyright.
The current parameter type appears to describe %b/%s arguments rather than the full set of supported formatting conversions:
def __mod__(self, value: Buffer | SupportsBytes | tuple[Buffer | SupportsBytes, ...], /) -> bytes: ...
# bytearray has the same parameter type, returning bytearray.Minimal repro
from typing import reveal_type
def format_bytes(number: int, value: bytes):
reveal_type(b"%d" % number) # Expected: bytes; observed: Unknown + operator error
reveal_type(b"%b:%d" % (value, number)) # Expected: bytes; observed: Unknown + operator error
reveal_type(b"%b" % value) # bytes, no error (control)
reveal_type(bytearray(b"%d") % number) # Expected: bytearray; observed: Unknown + operator error
def format_union(fmt: str | bytes, value: bytes, number: int):
result = fmt % (value, number)
reveal_type(result) # Expected: str | bytes; observed: str | UnknownObserved with Pyright 1.1.414, Python target 3.14, and typeshed revision 6dcbc4c3b0efddc3cb46daa4e5bbe7520979bd1a (the bundled builtins.pyi was verified to match upstream exactly). Current upstream main still has these signatures at the time of reporting. Mypy comparison was not performed because it is not installed in the available environment.
Representative diagnostics:
Operator "%" not supported for types "Literal[b"%d"]" and "int"
Operator "%" not supported for types "Literal[b"%b:%d"]" and "tuple[bytes, int]"
Operator "%" not supported for types "bytearray" and "int"
Operator "%" not supported for types "str | bytes" and "tuple[bytes, int]"
Operator "%" not supported for types "bytes" and "tuple[bytes, int]"
These are reportOperatorIssue diagnostics. The new bytes restriction excludes int, so the bytes branch fails and produces Unknown.
Runtime behavior and specification
Executed with CPython 3.14.7:
print(b"%d" % 3) # b'3'
print(b"%b:%d" % (b"path", 3)) # b'path:3'
result = bytearray(b"%d") % 3
print(repr(result), type(result).__name__) # bytearray(b'3') bytearrayPEP 461 explicitly supports numeric formatting codes for bytes and bytearray. %a/%r also support arbitrary objects, and the accepted argument type depends on the format string.
Suggested correction
Without format-string-sensitive typing, accept object while retaining precise return types, analogous to str.__mod__:
# bytes
def __mod__(self, value: object, /) -> bytes: ...
# bytearray
def __mod__(self, value: object, /) -> bytearray: ...This does not require reverting the corrected bytearray return type or introducing Any into the result.
Activity
srittau commentedon Oct 14, 2026
Yeah, that was a bit of a brain knot on my part.