Skip to content

Commit 1ebbbee

Browse files
authored
Make BufferedReader generic over a protocol (#13533)
1 parent b444909 commit 1ebbbee

File tree

2 files changed

+37
-8
lines changed

2 files changed

+37
-8
lines changed
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
from _io import BufferedReader
12
from gzip import GzipFile
2-
from io import FileIO, TextIOWrapper
3+
from io import FileIO, RawIOBase, TextIOWrapper
34
from typing_extensions import assert_type
45

6+
BufferedReader(RawIOBase())
7+
58
assert_type(TextIOWrapper(FileIO("")).buffer, FileIO)
69
assert_type(TextIOWrapper(FileIO(13)).detach(), FileIO)
710
assert_type(TextIOWrapper(GzipFile("")).buffer, GzipFile)

stdlib/_io.pyi

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,36 @@ class BytesIO(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc]
8888
def readlines(self, size: int | None = None, /) -> list[bytes]: ...
8989
def seek(self, pos: int, whence: int = 0, /) -> int: ...
9090

91-
class BufferedReader(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes
92-
raw: RawIOBase
93-
def __init__(self, raw: RawIOBase, buffer_size: int = 8192) -> None: ...
91+
class _BufferedReaderStream(Protocol):
92+
def read(self, n: int = ..., /) -> bytes: ...
93+
# Optional: def readall(self) -> bytes: ...
94+
def readinto(self, b: memoryview, /) -> int | None: ...
95+
def seek(self, pos: int, whence: int, /) -> int: ...
96+
def tell(self) -> int: ...
97+
def truncate(self, size: int, /) -> int: ...
98+
def flush(self) -> object: ...
99+
def close(self) -> object: ...
100+
@property
101+
def closed(self) -> bool: ...
102+
def readable(self) -> bool: ...
103+
def seekable(self) -> bool: ...
104+
105+
# The following methods just pass through to the underlying stream. Since
106+
# not all streams support them, they are marked as optional here, and will
107+
# raise an AttributeError if called on a stream that does not support them.
108+
109+
# @property
110+
# def name(self) -> Any: ... # Type is inconsistent between the various I/O types.
111+
# @property
112+
# def mode(self) -> str: ...
113+
# def fileno(self) -> int: ...
114+
# def isatty(self) -> bool: ...
115+
116+
_BufferedReaderStreamT = TypeVar("_BufferedReaderStreamT", bound=_BufferedReaderStream, default=_BufferedReaderStream)
117+
118+
class BufferedReader(BufferedIOBase, _BufferedIOBase, BinaryIO, Generic[_BufferedReaderStreamT]): # type: ignore[misc] # incompatible definitions of methods in the base classes
119+
raw: _BufferedReaderStreamT
120+
def __init__(self, raw: _BufferedReaderStreamT, buffer_size: int = 8192) -> None: ...
94121
def peek(self, size: int = 0, /) -> bytes: ...
95122
def seek(self, target: int, whence: int = 0, /) -> int: ...
96123
def truncate(self, pos: int | None = None, /) -> int: ...
@@ -111,8 +138,8 @@ class BufferedRandom(BufferedIOBase, _BufferedIOBase, BinaryIO): # type: ignore
111138
def peek(self, size: int = 0, /) -> bytes: ...
112139
def truncate(self, pos: int | None = None, /) -> int: ...
113140

114-
class BufferedRWPair(BufferedIOBase, _BufferedIOBase):
115-
def __init__(self, reader: RawIOBase, writer: RawIOBase, buffer_size: int = 8192, /) -> None: ...
141+
class BufferedRWPair(BufferedIOBase, _BufferedIOBase, Generic[_BufferedReaderStreamT]):
142+
def __init__(self, reader: _BufferedReaderStreamT, writer: RawIOBase, buffer_size: int = 8192, /) -> None: ...
116143
def peek(self, size: int = 0, /) -> bytes: ...
117144

118145
class _TextIOBase(_IOBase):
@@ -131,8 +158,7 @@ class _TextIOBase(_IOBase):
131158
@type_check_only
132159
class _WrappedBuffer(Protocol):
133160
# "name" is wrapped by TextIOWrapper. Its type is inconsistent between
134-
# the various I/O types, see the comments on TextIOWrapper.name and
135-
# TextIO.name.
161+
# the various I/O types.
136162
@property
137163
def name(self) -> Any: ...
138164
@property

0 commit comments

Comments
 (0)