From 1d691913a3fb884e5afa06bd21ae0c17ba331833 Mon Sep 17 00:00:00 2001 From: Huanyi Xie <74809554+xiehuanyi@users.noreply.github.com> Date: Mon, 12 Oct 2026 11:57:20 +0300 Subject: [PATCH 1/2] Accept integer-like binary file seek positions --- stdlib/@tests/test_cases/check_io.py | 15 ++++++++++++++- stdlib/_io.pyi | 12 ++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/stdlib/@tests/test_cases/check_io.py b/stdlib/@tests/test_cases/check_io.py index c3ee5aa4c6c0..951baa7829fa 100644 --- a/stdlib/@tests/test_cases/check_io.py +++ b/stdlib/@tests/test_cases/check_io.py @@ -1,6 +1,6 @@ from _io import BufferedReader, BufferedRWPair, BufferedWriter from gzip import GzipFile -from io import FileIO, RawIOBase, TextIOWrapper +from io import BufferedRandom, BytesIO, FileIO, RawIOBase, TextIOWrapper from socket import SocketIO from typing import Any from typing_extensions import assert_type @@ -16,3 +16,16 @@ assert_type(TextIOWrapper(FileIO("")).buffer, FileIO) assert_type(TextIOWrapper(FileIO(13)).detach(), FileIO) assert_type(TextIOWrapper(GzipFile("")).buffer, GzipFile) + + +class Index: + def __index__(self) -> int: + return 1 + + +assert_type(BytesIO().seek(Index()), int) +assert_type(FileIO("").seek(Index()), int) +assert_type(BufferedReader(BytesIO()).seek(Index()), int) +assert_type(BufferedWriter(BytesIO()).seek(Index()), int) +assert_type(BufferedRandom(FileIO("")).seek(Index()), int) +BytesIO().seek("1") # type: ignore[arg-type] diff --git a/stdlib/_io.pyi b/stdlib/_io.pyi index 5e216123be98..d278236165bf 100644 --- a/stdlib/_io.pyi +++ b/stdlib/_io.pyi @@ -6,7 +6,7 @@ from collections.abc import Callable, Iterable, Iterator from io import BufferedIOBase, RawIOBase, TextIOBase, UnsupportedOperation as UnsupportedOperation from os import _Opener from types import TracebackType -from typing import IO, Any, BinaryIO, Final, Generic, Literal, Protocol, TextIO, TypeVar, overload, type_check_only +from typing import IO, Any, BinaryIO, Final, Generic, Literal, Protocol, SupportsIndex, TextIO, TypeVar, overload, type_check_only from typing_extensions import Self, disjoint_base _S = TypeVar("_S", bound=str) @@ -107,7 +107,7 @@ class FileIO(RawIOBase, _RawIOBase, BinaryIO): # type: ignore[misc] # incompat ) -> None: ... @property def closefd(self) -> bool: ... - def seek(self, pos: int, whence: int = 0, /) -> int: ... + def seek(self, pos: SupportsIndex, whence: int = 0, /) -> int: ... def read(self, size: int | None = -1, /) -> bytes | MaybeNone: ... @disjoint_base @@ -121,7 +121,7 @@ class BytesIO(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] def getbuffer(self) -> memoryview: ... def read1(self, size: int | None = -1, /) -> bytes: ... def readlines(self, size: int | None = None, /) -> list[bytes]: ... - def seek(self, pos: int, whence: int = 0, /) -> int: ... + def seek(self, pos: SupportsIndex, whence: int = 0, /) -> int: ... @type_check_only class _BufferedReaderStream(Protocol): @@ -160,7 +160,7 @@ class BufferedReader(BufferedIOBase, _BufferedIOBase, BinaryIO, Generic[_Buffere def __init__(self, raw: _BufferedReaderStreamT, buffer_size: int = 8192) -> None: ... def peek(self, size: int = 0, /) -> bytes: ... - def seek(self, target: int, whence: int = 0, /) -> int: ... + def seek(self, target: SupportsIndex, whence: int = 0, /) -> int: ... def truncate(self, pos: int | None = None, /) -> int: ... @type_check_only @@ -198,7 +198,7 @@ class BufferedWriter(BufferedIOBase, _BufferedIOBase, BinaryIO, Generic[_Buffere def __init__(self, raw: _BufferedWriterStreamT, buffer_size: int = 8192) -> None: ... def write(self, buffer: ReadableBuffer, /) -> int: ... - def seek(self, target: int, whence: int = 0, /) -> int: ... + def seek(self, target: SupportsIndex, whence: int = 0, /) -> int: ... def truncate(self, pos: int | None = None, /) -> int: ... @disjoint_base @@ -211,7 +211,7 @@ class BufferedRandom(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore else: def __init__(self, raw: RawIOBase, buffer_size: int = 8192) -> None: ... - def seek(self, target: int, whence: int = 0, /) -> int: ... # stubtest needs this + def seek(self, target: SupportsIndex, whence: int = 0, /) -> int: ... # stubtest needs this def peek(self, size: int = 0, /) -> bytes: ... def truncate(self, pos: int | None = None, /) -> int: ... From 29aa0957f5a08f4eefcbf0ebb6cef8863147319d Mon Sep 17 00:00:00 2001 From: Huanyi Xie <74809554+xiehuanyi@users.noreply.github.com> Date: Mon, 12 Oct 2026 12:30:30 +0300 Subject: [PATCH 2/2] Keep BytesIO seek compatible with existing subclasses --- stdlib/@tests/test_cases/check_io.py | 11 +++++++++-- stdlib/_io.pyi | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/stdlib/@tests/test_cases/check_io.py b/stdlib/@tests/test_cases/check_io.py index 951baa7829fa..7e2c8001f3a5 100644 --- a/stdlib/@tests/test_cases/check_io.py +++ b/stdlib/@tests/test_cases/check_io.py @@ -23,9 +23,16 @@ def __index__(self) -> int: return 1 -assert_type(BytesIO().seek(Index()), int) assert_type(FileIO("").seek(Index()), int) assert_type(BufferedReader(BytesIO()).seek(Index()), int) assert_type(BufferedWriter(BytesIO()).seek(Index()), int) assert_type(BufferedRandom(FileIO("")).seek(Index()), int) -BytesIO().seek("1") # type: ignore[arg-type] +FileIO("").seek("1") # type: ignore[arg-type] + + +class NarrowBytesIO(BytesIO): + def seek(self, pos: int, whence: int = 0, /) -> int: + return super().seek(pos, whence) + + +assert_type(NarrowBytesIO().seek(1), int) diff --git a/stdlib/_io.pyi b/stdlib/_io.pyi index d278236165bf..9012e0fd5348 100644 --- a/stdlib/_io.pyi +++ b/stdlib/_io.pyi @@ -121,7 +121,7 @@ class BytesIO(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] def getbuffer(self) -> memoryview: ... def read1(self, size: int | None = -1, /) -> bytes: ... def readlines(self, size: int | None = None, /) -> list[bytes]: ... - def seek(self, pos: SupportsIndex, whence: int = 0, /) -> int: ... + def seek(self, pos: int, whence: int = 0, /) -> int: ... @type_check_only class _BufferedReaderStream(Protocol):