Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit d555ade

Browse files
committedMar 10, 2023
[alt] typing: accept buffers in IO.write
Co-authored-by: JelleZijlstra <jelle.zijlstra@gmail.com>
1 parent 390058e commit d555ade

File tree

7 files changed

+68
-12
lines changed

7 files changed

+68
-12
lines changed
 

‎stdlib/codecs.pyi

+2-1
Original file line numberDiff line numberDiff line change
@@ -272,8 +272,9 @@ class StreamRecoder(BinaryIO):
272272
def readlines(self, sizehint: int | None = None) -> list[bytes]: ...
273273
def __next__(self) -> bytes: ...
274274
def __iter__(self) -> Self: ...
275+
# Base class accepts more types than just bytes
275276
def write(self, data: bytes) -> None: ... # type: ignore[override]
276-
def writelines(self, list: Iterable[bytes]) -> None: ...
277+
def writelines(self, list: Iterable[bytes]) -> None: ... # type: ignore[override]
277278
def reset(self) -> None: ...
278279
def __getattr__(self, name: str) -> Any: ...
279280
def __enter__(self) -> Self: ...

‎stdlib/http/client.pyi

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ class HTTPMessage(email.message.Message):
101101

102102
def parse_headers(fp: io.BufferedIOBase, _class: Callable[[], email.message.Message] = ...) -> HTTPMessage: ...
103103

104-
class HTTPResponse(io.BufferedIOBase, BinaryIO):
104+
class HTTPResponse(io.BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible method definitions in the base classes
105105
msg: HTTPMessage
106106
headers: HTTPMessage
107107
version: int

‎stdlib/io.pyi

+6-6
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ class BufferedIOBase(IOBase):
9090
def read(self, __size: int | None = ...) -> bytes: ...
9191
def read1(self, __size: int = ...) -> bytes: ...
9292

93-
class FileIO(RawIOBase, BinaryIO):
93+
class FileIO(RawIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes
9494
mode: str
9595
name: FileDescriptorOrPath # type: ignore[assignment]
9696
def __init__(
@@ -102,7 +102,7 @@ class FileIO(RawIOBase, BinaryIO):
102102
def read(self, __size: int = -1) -> bytes: ...
103103
def __enter__(self) -> Self: ...
104104

105-
class BytesIO(BufferedIOBase, BinaryIO):
105+
class BytesIO(BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes
106106
def __init__(self, initial_bytes: ReadableBuffer = ...) -> None: ...
107107
# BytesIO does not contain a "name" field. This workaround is necessary
108108
# to allow BytesIO sub-classes to add this field, as it is defined
@@ -113,17 +113,17 @@ class BytesIO(BufferedIOBase, BinaryIO):
113113
def getbuffer(self) -> memoryview: ...
114114
def read1(self, __size: int | None = -1) -> bytes: ...
115115

116-
class BufferedReader(BufferedIOBase, BinaryIO):
116+
class BufferedReader(BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of methods in the base classes
117117
def __enter__(self) -> Self: ...
118118
def __init__(self, raw: RawIOBase, buffer_size: int = ...) -> None: ...
119119
def peek(self, __size: int = 0) -> bytes: ...
120120

121-
class BufferedWriter(BufferedIOBase, BinaryIO):
121+
class BufferedWriter(BufferedIOBase, BinaryIO): # type: ignore[misc] # incompatible definitions of writelines in the base classes
122122
def __enter__(self) -> Self: ...
123123
def __init__(self, raw: RawIOBase, buffer_size: int = ...) -> None: ...
124124
def write(self, __buffer: ReadableBuffer) -> int: ...
125125

126-
class BufferedRandom(BufferedReader, BufferedWriter):
126+
class BufferedRandom(BufferedReader, BufferedWriter): # type: ignore[misc] # incompatible definitions of methods in the base classes
127127
def __enter__(self) -> Self: ...
128128
def seek(self, __target: int, __whence: int = 0) -> int: ... # stubtest needs this
129129

@@ -144,7 +144,7 @@ class TextIOBase(IOBase):
144144
def readlines(self, __hint: int = -1) -> list[str]: ... # type: ignore[override]
145145
def read(self, __size: int | None = ...) -> str: ...
146146

147-
class TextIOWrapper(TextIOBase, TextIO):
147+
class TextIOWrapper(TextIOBase, TextIO): # type: ignore[misc] # incompatible definitions of write in the base classes
148148
def __init__(
149149
self,
150150
buffer: IO[bytes],

‎stdlib/lzma.pyi

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ class LZMACompressor:
104104

105105
class LZMAError(Exception): ...
106106

107-
class LZMAFile(io.BufferedIOBase, IO[bytes]):
107+
class LZMAFile(io.BufferedIOBase, IO[bytes]): # type: ignore[misc] # incompatible definitions of writelines in the base classes
108108
def __init__(
109109
self,
110110
filename: _PathOrFile | None = None,

‎stdlib/tempfile.pyi

+22-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import io
22
import sys
3-
from _typeshed import BytesPath, GenericPath, StrPath, WriteableBuffer
3+
from _typeshed import BytesPath, GenericPath, ReadableBuffer, StrPath, WriteableBuffer
44
from collections.abc import Iterable, Iterator
55
from types import TracebackType
66
from typing import IO, Any, AnyStr, Generic, overload
@@ -215,7 +215,17 @@ class _TemporaryFileWrapper(Generic[AnyStr], IO[AnyStr]):
215215
def tell(self) -> int: ...
216216
def truncate(self, size: int | None = ...) -> int: ...
217217
def writable(self) -> bool: ...
218+
@overload
219+
def write(self: _TemporaryFileWrapper[str], s: str) -> int: ...
220+
@overload
221+
def write(self: _TemporaryFileWrapper[bytes], s: ReadableBuffer) -> int: ...
222+
@overload
218223
def write(self, s: AnyStr) -> int: ...
224+
@overload
225+
def writelines(self: _TemporaryFileWrapper[str], lines: Iterable[str]) -> None: ...
226+
@overload
227+
def writelines(self: _TemporaryFileWrapper[bytes], lines: Iterable[ReadableBuffer]) -> None: ...
228+
@overload
219229
def writelines(self, lines: Iterable[AnyStr]) -> None: ...
220230

221231
if sys.version_info >= (3, 11):
@@ -392,8 +402,18 @@ class SpooledTemporaryFile(IO[AnyStr], _SpooledTemporaryFileBase):
392402
def seek(self, offset: int, whence: int = ...) -> int: ...
393403
def tell(self) -> int: ...
394404
def truncate(self, size: int | None = None) -> None: ... # type: ignore[override]
405+
@overload
406+
def write(self: SpooledTemporaryFile[str], s: str) -> int: ...
407+
@overload
408+
def write(self: SpooledTemporaryFile[bytes], s: ReadableBuffer) -> int: ...
409+
@overload
395410
def write(self, s: AnyStr) -> int: ...
396-
def writelines(self, iterable: Iterable[AnyStr]) -> None: ... # type: ignore[override]
411+
@overload
412+
def writelines(self: SpooledTemporaryFile[str], lines: Iterable[str]) -> None: ...
413+
@overload
414+
def writelines(self: SpooledTemporaryFile[bytes], lines: Iterable[ReadableBuffer]) -> None: ...
415+
@overload
416+
def writelines(self, lines: Iterable[AnyStr]) -> None: ...
397417
def __iter__(self) -> Iterator[AnyStr]: ... # type: ignore[override]
398418
# These exist at runtime only on 3.11+.
399419
def readable(self) -> bool: ...

‎stdlib/typing.pyi

+15-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import collections # Needed by aliases like DefaultDict, see mypy issue 2986
22
import sys
33
import typing_extensions
44
from _collections_abc import dict_items, dict_keys, dict_values
5-
from _typeshed import IdentityFunction, Incomplete, SupportsKeysAndGetItem
5+
from _typeshed import IdentityFunction, Incomplete, ReadableBuffer, SupportsKeysAndGetItem
66
from abc import ABCMeta, abstractmethod
77
from contextlib import AbstractAsyncContextManager, AbstractContextManager
88
from re import Match as Match, Pattern as Pattern
@@ -687,8 +687,22 @@ class IO(Iterator[AnyStr], Generic[AnyStr]):
687687
@abstractmethod
688688
def writable(self) -> bool: ...
689689
@abstractmethod
690+
@overload
691+
def write(self: IO[str], __s: str) -> int: ...
692+
@abstractmethod
693+
@overload
694+
def write(self: IO[bytes], __s: ReadableBuffer) -> int: ...
695+
@abstractmethod
696+
@overload
690697
def write(self, __s: AnyStr) -> int: ...
691698
@abstractmethod
699+
@overload
700+
def writelines(self: IO[str], __lines: Iterable[str]) -> None: ...
701+
@abstractmethod
702+
@overload
703+
def writelines(self: IO[bytes], __lines: Iterable[ReadableBuffer]) -> None: ...
704+
@abstractmethod
705+
@overload
692706
def writelines(self, __lines: Iterable[AnyStr]) -> None: ...
693707
@abstractmethod
694708
def __next__(self) -> AnyStr: ...

‎test_cases/stdlib/typing/check_io.py

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from __future__ import annotations
2+
3+
import mmap
4+
from typing import IO, AnyStr
5+
6+
7+
def check_write(io_bytes: IO[bytes], io_str: IO[str], io_anystr: IO[AnyStr], any_str: AnyStr, buf: mmap.mmap) -> None:
8+
io_bytes.write(b"")
9+
io_bytes.write(buf)
10+
io_bytes.write("") # type: ignore
11+
io_bytes.write(any_str) # type: ignore
12+
13+
io_str.write(b"") # type: ignore
14+
io_str.write(buf) # type: ignore
15+
io_str.write("")
16+
io_str.write(any_str) # type: ignore
17+
18+
io_anystr.write(b"") # type: ignore
19+
io_anystr.write(buf) # type: ignore
20+
io_anystr.write("") # type: ignore
21+
io_anystr.write(any_str)

0 commit comments

Comments
 (0)
Please sign in to comment.