From 99fb43ee09cf3e4174a9eb3acf125e954e8b3421 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Sun, 11 May 2025 18:21:21 +0200 Subject: [PATCH 1/4] Make BufferedWriter generic over a protocol --- stdlib/@tests/test_cases/check_io.py | 3 ++- stdlib/_io.pyi | 38 ++++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/stdlib/@tests/test_cases/check_io.py b/stdlib/@tests/test_cases/check_io.py index ce8c34aedbad..6e9f35e5f90f 100644 --- a/stdlib/@tests/test_cases/check_io.py +++ b/stdlib/@tests/test_cases/check_io.py @@ -1,9 +1,10 @@ -from _io import BufferedReader +from _io import BufferedReader, BufferedWriter from gzip import GzipFile from io import FileIO, RawIOBase, TextIOWrapper from typing_extensions import assert_type BufferedReader(RawIOBase()) +BufferedWriter(RawIOBase()) assert_type(TextIOWrapper(FileIO("")).buffer, FileIO) assert_type(TextIOWrapper(FileIO(13)).detach(), FileIO) diff --git a/stdlib/_io.pyi b/stdlib/_io.pyi index c77d75287c25..5cc86f78aa7f 100644 --- a/stdlib/_io.pyi +++ b/stdlib/_io.pyi @@ -88,6 +88,7 @@ class BytesIO(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] def readlines(self, size: int | None = None, /) -> list[bytes]: ... def seek(self, pos: int, whence: int = 0, /) -> int: ... +@type_check_only class _BufferedReaderStream(Protocol): def read(self, n: int = ..., /) -> bytes: ... # Optional: def readall(self) -> bytes: ... @@ -122,9 +123,36 @@ class BufferedReader(BufferedIOBase, _BufferedIOBase, BinaryIO, Generic[_Buffere def seek(self, target: int, whence: int = 0, /) -> int: ... def truncate(self, pos: int | None = None, /) -> int: ... -class BufferedWriter(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes - raw: RawIOBase - def __init__(self, raw: RawIOBase, buffer_size: int = 8192) -> None: ... +@type_check_only +class _BufferedWriterStream(Protocol): + def write(self, b: WriteableBuffer, /) -> int: ... + def seek(self, pos: int, whence: int, /) -> int: ... + def tell(self) -> int: ... + def truncate(self, size: int, /) -> int: ... + def flush(self) -> object: ... + def close(self) -> object: ... + @property + def closed(self) -> bool: ... + def writable(self) -> bool: ... + def seekable(self) -> bool: ... + + # The following methods just pass through to the underlying stream. Since + # not all streams support them, they are marked as optional here, and will + # raise an AttributeError if called on a stream that does not support them. + + # @property + # def name(self) -> Any: ... # Type is inconsistent between the various I/O types. + # @property + # def mode(self) -> str: ... + # def fileno(self) -> int: ... + # def isatty(self) -> bool: ... + +_BufferedWriterStreamT = TypeVar("_BufferedWriterStreamT", bound=_BufferedWriterStream, default=_BufferedWriterStream) + + +class BufferedWriter(BufferedIOBase, _BufferedIOBase, BinaryIO, Generic[_BufferedWriterStreamT]): # type: ignore[misc] # incompatible definitions of writelines in the base classes + raw: _BufferedWriterStreamT + 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 truncate(self, pos: int | None = None, /) -> int: ... @@ -138,8 +166,8 @@ class BufferedRandom(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore def peek(self, size: int = 0, /) -> bytes: ... def truncate(self, pos: int | None = None, /) -> int: ... -class BufferedRWPair(BufferedIOBase, _BufferedIOBase, Generic[_BufferedReaderStreamT]): - def __init__(self, reader: _BufferedReaderStreamT, writer: RawIOBase, buffer_size: int = 8192, /) -> None: ... +class BufferedRWPair(BufferedIOBase, _BufferedIOBase, Generic[_BufferedReaderStreamT, _BufferedWriterStreamT]): + def __init__(self, reader: _BufferedReaderStreamT, writer: _BufferedWriterStreamT, buffer_size: int = 8192, /) -> None: ... def peek(self, size: int = 0, /) -> bytes: ... class _TextIOBase(_IOBase): From f11ce854e7710b0bd236f5a3c57adf4c5f989b11 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 11 May 2025 16:23:09 +0000 Subject: [PATCH 2/4] [pre-commit.ci] auto fixes from pre-commit.com hooks --- stdlib/_io.pyi | 1 - 1 file changed, 1 deletion(-) diff --git a/stdlib/_io.pyi b/stdlib/_io.pyi index 5cc86f78aa7f..2b9d112e2d56 100644 --- a/stdlib/_io.pyi +++ b/stdlib/_io.pyi @@ -149,7 +149,6 @@ class _BufferedWriterStream(Protocol): _BufferedWriterStreamT = TypeVar("_BufferedWriterStreamT", bound=_BufferedWriterStream, default=_BufferedWriterStream) - class BufferedWriter(BufferedIOBase, _BufferedIOBase, BinaryIO, Generic[_BufferedWriterStreamT]): # type: ignore[misc] # incompatible definitions of writelines in the base classes raw: _BufferedWriterStreamT def __init__(self, raw: _BufferedWriterStreamT, buffer_size: int = 8192) -> None: ... From e4ccafe27cb689b9336e637637d4b5e3d251d545 Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Sun, 11 May 2025 18:23:38 +0200 Subject: [PATCH 3/4] Add a test --- stdlib/@tests/test_cases/check_io.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stdlib/@tests/test_cases/check_io.py b/stdlib/@tests/test_cases/check_io.py index 6e9f35e5f90f..7299d11a6187 100644 --- a/stdlib/@tests/test_cases/check_io.py +++ b/stdlib/@tests/test_cases/check_io.py @@ -1,4 +1,4 @@ -from _io import BufferedReader, BufferedWriter +from _io import BufferedReader, BufferedRWPair, BufferedWriter from gzip import GzipFile from io import FileIO, RawIOBase, TextIOWrapper from typing_extensions import assert_type @@ -6,6 +6,8 @@ BufferedReader(RawIOBase()) BufferedWriter(RawIOBase()) +BufferedRWPair(open("", "rb"), open("", "wb")) + assert_type(TextIOWrapper(FileIO("")).buffer, FileIO) assert_type(TextIOWrapper(FileIO(13)).detach(), FileIO) assert_type(TextIOWrapper(GzipFile("")).buffer, GzipFile) From 0668ed6fa139bfa6dd7d8a8a44d443af8cab8b5f Mon Sep 17 00:00:00 2001 From: Sebastian Rittau Date: Sun, 11 May 2025 19:02:38 +0200 Subject: [PATCH 4/4] `BufferedWriter` can handle non-blocking I/O --- stdlib/@tests/test_cases/check_io.py | 5 +++++ stdlib/_io.pyi | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/stdlib/@tests/test_cases/check_io.py b/stdlib/@tests/test_cases/check_io.py index 7299d11a6187..c3ee5aa4c6c0 100644 --- a/stdlib/@tests/test_cases/check_io.py +++ b/stdlib/@tests/test_cases/check_io.py @@ -1,10 +1,15 @@ from _io import BufferedReader, BufferedRWPair, BufferedWriter from gzip import GzipFile from io import FileIO, RawIOBase, TextIOWrapper +from socket import SocketIO +from typing import Any from typing_extensions import assert_type +socket: Any = None + BufferedReader(RawIOBase()) BufferedWriter(RawIOBase()) +BufferedWriter(SocketIO(socket, "r")) BufferedRWPair(open("", "rb"), open("", "wb")) diff --git a/stdlib/_io.pyi b/stdlib/_io.pyi index 2b9d112e2d56..a95b83d97c76 100644 --- a/stdlib/_io.pyi +++ b/stdlib/_io.pyi @@ -125,7 +125,7 @@ class BufferedReader(BufferedIOBase, _BufferedIOBase, BinaryIO, Generic[_Buffere @type_check_only class _BufferedWriterStream(Protocol): - def write(self, b: WriteableBuffer, /) -> int: ... + def write(self, b: WriteableBuffer, /) -> int | None: ... def seek(self, pos: int, whence: int, /) -> int: ... def tell(self) -> int: ... def truncate(self, size: int, /) -> int: ... pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy