Skip to content

Commit c0a0c34

Browse files
Use PEP 688 (#10225)
Co-authored-by: Alex Waygood <[email protected]>
1 parent 56aeeb6 commit c0a0c34

16 files changed

+81
-65
lines changed

stdlib/_ctypes.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ class _CData(metaclass=_CDataMeta):
6363
def from_param(cls, obj: Any) -> Self | _CArgObject: ...
6464
@classmethod
6565
def in_dll(cls, library: CDLL, name: str) -> Self: ...
66+
def __buffer__(self, __flags: int) -> memoryview: ...
67+
def __release_buffer__(self, __buffer: memoryview) -> None: ...
6668

6769
class _SimpleCData(Generic[_T], _CData):
6870
value: _T

stdlib/_typeshed/__init__.pyi

Lines changed: 29 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,13 @@
22
#
33
# See the README.md file in this directory for more information.
44

5-
import array
6-
import ctypes
7-
import mmap
8-
import pickle
95
import sys
10-
from collections.abc import Awaitable, Callable, Iterable, Set as AbstractSet
6+
from collections.abc import Awaitable, Callable, Iterable, Sequence, Set as AbstractSet, Sized
117
from dataclasses import Field
128
from os import PathLike
139
from types import FrameType, TracebackType
14-
from typing import Any, AnyStr, ClassVar, Generic, Protocol, TypeVar
15-
from typing_extensions import Final, Literal, LiteralString, TypeAlias, final
10+
from typing import Any, AnyStr, ClassVar, Generic, Protocol, TypeVar, overload
11+
from typing_extensions import Buffer, Final, Literal, LiteralString, TypeAlias, final
1612

1713
_KT = TypeVar("_KT")
1814
_KT_co = TypeVar("_KT_co", covariant=True)
@@ -227,42 +223,33 @@ class SupportsNoArgReadline(Protocol[_T_co]):
227223
class SupportsWrite(Protocol[_T_contra]):
228224
def write(self, __s: _T_contra) -> object: ...
229225

230-
ReadOnlyBuffer: TypeAlias = bytes # stable
226+
# Unfortunately PEP 688 does not allow us to distinguish read-only
227+
# from writable buffers. We use these aliases for readability for now.
228+
# Perhaps a future extension of the buffer protocol will allow us to
229+
# distinguish these cases in the type system.
230+
ReadOnlyBuffer: TypeAlias = Buffer # stable
231231
# Anything that implements the read-write buffer interface.
232-
# The buffer interface is defined purely on the C level, so we cannot define a normal Protocol
233-
# for it (until PEP 688 is implemented). Instead we have to list the most common stdlib buffer classes in a Union.
234-
if sys.version_info >= (3, 8):
235-
WriteableBuffer: TypeAlias = (
236-
bytearray | memoryview | array.array[Any] | mmap.mmap | ctypes._CData | pickle.PickleBuffer
237-
) # stable
238-
else:
239-
WriteableBuffer: TypeAlias = bytearray | memoryview | array.array[Any] | mmap.mmap | ctypes._CData # stable
240-
# Same as _WriteableBuffer, but also includes read-only buffer types (like bytes).
241-
ReadableBuffer: TypeAlias = ReadOnlyBuffer | WriteableBuffer # stable
242-
_BufferWithLen: TypeAlias = ReadableBuffer # not stable # noqa: Y047
243-
244-
# Anything that implements the read-write buffer interface, and can be sliced/indexed.
245-
SliceableBuffer: TypeAlias = bytes | bytearray | memoryview | array.array[Any] | mmap.mmap
246-
IndexableBuffer: TypeAlias = bytes | bytearray | memoryview | array.array[Any] | mmap.mmap
247-
# https://github.com/python/typeshed/pull/9115#issuecomment-1304905864
248-
# Post PEP 688, they should be rewritten as such:
249-
# from collections.abc import Sequence
250-
# from typing import Sized, overload
251-
# class SliceableBuffer(Protocol):
252-
# def __buffer__(self, __flags: int) -> memoryview: ...
253-
# def __getitem__(self, __slice: slice) -> Sequence[int]: ...
254-
# class IndexableBuffer(Protocol):
255-
# def __buffer__(self, __flags: int) -> memoryview: ...
256-
# def __getitem__(self, __i: int) -> int: ...
257-
# class SupportsGetItemBuffer(SliceableBuffer, IndexableBuffer, Protocol):
258-
# def __buffer__(self, __flags: int) -> memoryview: ...
259-
# def __contains__(self, __x: Any) -> bool: ...
260-
# @overload
261-
# def __getitem__(self, __slice: slice) -> Sequence[int]: ...
262-
# @overload
263-
# def __getitem__(self, __i: int) -> int: ...
264-
# class SizedBuffer(Sized, Protocol): # instead of _BufferWithLen
265-
# def __buffer__(self, __flags: int) -> memoryview: ...
232+
WriteableBuffer: TypeAlias = Buffer
233+
# Same as WriteableBuffer, but also includes read-only buffer types (like bytes).
234+
ReadableBuffer: TypeAlias = Buffer # stable
235+
236+
class SliceableBuffer(Buffer, Protocol):
237+
def __getitem__(self, __slice: slice) -> Sequence[int]: ...
238+
239+
class IndexableBuffer(Buffer, Protocol):
240+
def __getitem__(self, __i: int) -> int: ...
241+
242+
class SupportsGetItemBuffer(SliceableBuffer, IndexableBuffer, Protocol):
243+
def __contains__(self, __x: Any) -> bool: ...
244+
@overload
245+
def __getitem__(self, __slice: slice) -> Sequence[int]: ...
246+
@overload
247+
def __getitem__(self, __i: int) -> int: ...
248+
249+
class SizedBuffer(Sized, Buffer, Protocol): ...
250+
251+
# for compatibility with third-party stubs that may use this
252+
_BufferWithLen: TypeAlias = SizedBuffer # not stable # noqa: Y047
266253

267254
ExcInfo: TypeAlias = tuple[type[BaseException], BaseException, TracebackType]
268255
OptExcInfo: TypeAlias = ExcInfo | tuple[None, None, None]

stdlib/array.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,5 +80,7 @@ class array(MutableSequence[_T], Generic[_T]):
8080
def __rmul__(self, __value: int) -> array[_T]: ...
8181
def __copy__(self) -> array[_T]: ...
8282
def __deepcopy__(self, __unused: Any) -> array[_T]: ...
83+
def __buffer__(self, __flags: int) -> memoryview: ...
84+
def __release_buffer__(self, __buffer: memoryview) -> None: ...
8385

8486
ArrayType = array

stdlib/binhex.pyi

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from _typeshed import _BufferWithLen
1+
from _typeshed import SizedBuffer
22
from typing import IO, Any
33
from typing_extensions import Literal, TypeAlias
44

@@ -28,9 +28,9 @@ class openrsrc:
2828

2929
class BinHex:
3030
def __init__(self, name_finfo_dlen_rlen: _FileInfoTuple, ofp: _FileHandleUnion) -> None: ...
31-
def write(self, data: _BufferWithLen) -> None: ...
31+
def write(self, data: SizedBuffer) -> None: ...
3232
def close_data(self) -> None: ...
33-
def write_rsrc(self, data: _BufferWithLen) -> None: ...
33+
def write_rsrc(self, data: SizedBuffer) -> None: ...
3434
def close(self) -> None: ...
3535

3636
def binhex(inp: str, out: str) -> None: ...

stdlib/builtins.pyi

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,8 @@ class bytes(ByteString):
695695
if sys.version_info >= (3, 11):
696696
def __bytes__(self) -> bytes: ...
697697

698+
def __buffer__(self, __flags: int) -> memoryview: ...
699+
698700
class bytearray(MutableSequence[int], ByteString):
699701
@overload
700702
def __init__(self) -> None: ...
@@ -810,6 +812,8 @@ class bytearray(MutableSequence[int], ByteString):
810812
def __gt__(self, __value: ReadableBuffer) -> bool: ...
811813
def __ge__(self, __value: ReadableBuffer) -> bool: ...
812814
def __alloc__(self) -> int: ...
815+
def __buffer__(self, __flags: int) -> memoryview: ...
816+
def __release_buffer__(self, __buffer: memoryview) -> None: ...
813817

814818
@final
815819
class memoryview(Sequence[int]):
@@ -871,6 +875,9 @@ class memoryview(Sequence[int]):
871875
else:
872876
def hex(self) -> str: ...
873877

878+
def __buffer__(self, __flags: int) -> memoryview: ...
879+
def __release_buffer__(self, __buffer: memoryview) -> None: ...
880+
874881
@final
875882
class bool(int):
876883
def __new__(cls, __o: object = ...) -> Self: ...

stdlib/fcntl.pyi

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import sys
22
from _typeshed import FileDescriptorLike, ReadOnlyBuffer, WriteableBuffer
33
from typing import Any, overload
4-
from typing_extensions import Literal
4+
from typing_extensions import Buffer, Literal
55

66
if sys.platform != "win32":
77
FASYNC: int
@@ -104,13 +104,19 @@ if sys.platform != "win32":
104104
def fcntl(__fd: FileDescriptorLike, __cmd: int, __arg: int = 0) -> int: ...
105105
@overload
106106
def fcntl(__fd: FileDescriptorLike, __cmd: int, __arg: str | ReadOnlyBuffer) -> bytes: ...
107+
# If arg is an int, return int
107108
@overload
108109
def ioctl(__fd: FileDescriptorLike, __request: int, __arg: int = 0, __mutate_flag: bool = True) -> int: ...
110+
# The return type works as follows:
111+
# - If arg is a read-write buffer, return int if mutate_flag is True, otherwise bytes
112+
# - If arg is a read-only buffer, return bytes (and ignore the value of mutate_flag)
113+
# We can't represent that precisely as we can't distinguish between read-write and read-only
114+
# buffers, so we add overloads for a few unambiguous cases and use Any for the rest.
109115
@overload
110-
def ioctl(__fd: FileDescriptorLike, __request: int, __arg: WriteableBuffer, __mutate_flag: Literal[True] = True) -> int: ...
116+
def ioctl(__fd: FileDescriptorLike, __request: int, __arg: bytes, __mutate_flag: bool = True) -> bytes: ...
111117
@overload
112118
def ioctl(__fd: FileDescriptorLike, __request: int, __arg: WriteableBuffer, __mutate_flag: Literal[False]) -> bytes: ...
113119
@overload
114-
def ioctl(__fd: FileDescriptorLike, __request: int, __arg: ReadOnlyBuffer, __mutate_flag: bool = True) -> bytes: ...
120+
def ioctl(__fd: FileDescriptorLike, __request: int, __arg: Buffer, __mutate_flag: bool = True) -> Any: ...
115121
def flock(__fd: FileDescriptorLike, __operation: int) -> None: ...
116122
def lockf(__fd: FileDescriptorLike, __cmd: int, __len: int = 0, __start: int = 0, __whence: int = 0) -> Any: ...

stdlib/gzip.pyi

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import _compression
22
import sys
33
import zlib
4-
from _typeshed import ReadableBuffer, StrOrBytesPath, _BufferWithLen
4+
from _typeshed import ReadableBuffer, SizedBuffer, StrOrBytesPath
55
from io import FileIO
66
from typing import Protocol, TextIO, overload
77
from typing_extensions import Literal, TypeAlias
@@ -159,9 +159,9 @@ class _GzipReader(_compression.DecompressReader):
159159
def __init__(self, fp: _ReadableFileobj) -> None: ...
160160

161161
if sys.version_info >= (3, 8):
162-
def compress(data: _BufferWithLen, compresslevel: int = 9, *, mtime: float | None = None) -> bytes: ...
162+
def compress(data: SizedBuffer, compresslevel: int = 9, *, mtime: float | None = None) -> bytes: ...
163163

164164
else:
165-
def compress(data: _BufferWithLen, compresslevel: int = 9) -> bytes: ...
165+
def compress(data: SizedBuffer, compresslevel: int = 9) -> bytes: ...
166166

167167
def decompress(data: ReadableBuffer) -> bytes: ...

stdlib/hmac.pyi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import sys
2-
from _typeshed import ReadableBuffer, _BufferWithLen
2+
from _typeshed import ReadableBuffer, SizedBuffer
33
from collections.abc import Callable
44
from types import ModuleType
55
from typing import Any, AnyStr, overload
@@ -46,4 +46,4 @@ class HMAC:
4646
def compare_digest(__a: ReadableBuffer, __b: ReadableBuffer) -> bool: ...
4747
@overload
4848
def compare_digest(__a: AnyStr, __b: AnyStr) -> bool: ...
49-
def digest(key: _BufferWithLen, msg: ReadableBuffer, digest: _DigestMod) -> bytes: ...
49+
def digest(key: SizedBuffer, msg: ReadableBuffer, digest: _DigestMod) -> bytes: ...

stdlib/imaplib.pyi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import subprocess
22
import sys
33
import time
4-
from _typeshed import ReadableBuffer, _BufferWithLen
4+
from _typeshed import ReadableBuffer, SizedBuffer
55
from builtins import list as _list # conflicts with a method named "list"
66
from collections.abc import Callable
77
from datetime import datetime
@@ -155,7 +155,7 @@ class _Authenticator:
155155
def __init__(self, mechinst: Callable[[bytes], bytes | bytearray | memoryview | str | None]) -> None: ...
156156
def process(self, data: str) -> str: ...
157157
def encode(self, inp: bytes | bytearray | memoryview) -> str: ...
158-
def decode(self, inp: str | _BufferWithLen) -> bytes: ...
158+
def decode(self, inp: str | SizedBuffer) -> bytes: ...
159159

160160
def Internaldate2tuple(resp: ReadableBuffer) -> time.struct_time | None: ...
161161
def Int2AP(num: SupportsAbs[SupportsInt]) -> bytes: ...

stdlib/mmap.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ class mmap(Iterable[int], Sized):
7676
def __iter__(self) -> Iterator[int]: ...
7777
def __enter__(self) -> Self: ...
7878
def __exit__(self, *args: Unused) -> None: ...
79+
def __buffer__(self, __flags: int) -> memoryview: ...
80+
def __release_buffer__(self, __buffer: memoryview) -> None: ...
7981

8082
if sys.version_info >= (3, 8) and sys.platform != "win32":
8183
MADV_NORMAL: int

stdlib/pickle.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ if sys.version_info >= (3, 8):
103103
def __init__(self, buffer: ReadableBuffer) -> None: ...
104104
def raw(self) -> memoryview: ...
105105
def release(self) -> None: ...
106+
def __buffer__(self, __flags: int) -> memoryview: ...
107+
def __release_buffer__(self, __buffer: memoryview) -> None: ...
106108
_BufferCallback: TypeAlias = Callable[[PickleBuffer], Any] | None
107109
def dump(
108110
obj: Any,

stdlib/smtplib.pyi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import sys
22
from _socket import _Address as _SourceAddress
3-
from _typeshed import ReadableBuffer, _BufferWithLen
3+
from _typeshed import ReadableBuffer, SizedBuffer
44
from collections.abc import Sequence
55
from email.message import Message as _Message
66
from re import Pattern
@@ -133,7 +133,7 @@ class SMTP:
133133
self,
134134
from_addr: str,
135135
to_addrs: str | Sequence[str],
136-
msg: _BufferWithLen | str,
136+
msg: SizedBuffer | str,
137137
mail_options: Sequence[str] = (),
138138
rcpt_options: Sequence[str] = (),
139139
) -> _SendErrs: ...

stdlib/typing_extensions.pyi

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,4 +398,8 @@ else:
398398
def __or__(self, right: Any) -> _SpecialForm: ...
399399
def __ror__(self, left: Any) -> _SpecialForm: ...
400400

401-
class Buffer(abc.ABC): ...
401+
@runtime_checkable
402+
class Buffer(Protocol):
403+
# Not actually a Protocol at runtime; see
404+
# https://github.com/python/typeshed/issues/10224 for why we're defining it this way
405+
def __buffer__(self, __flags: int) -> memoryview: ...

stdlib/xmlrpc/client.pyi

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import gzip
22
import http.client
33
import sys
44
import time
5-
from _typeshed import ReadableBuffer, SupportsRead, SupportsWrite, _BufferWithLen
5+
from _typeshed import ReadableBuffer, SizedBuffer, SupportsRead, SupportsWrite
66
from collections.abc import Callable, Iterable, Mapping
77
from datetime import datetime
88
from io import BytesIO
@@ -236,20 +236,20 @@ class Transport:
236236
def __init__(self, use_datetime: bool = False, use_builtin_types: bool = False) -> None: ...
237237

238238
def request(
239-
self, host: _HostType, handler: str, request_body: _BufferWithLen, verbose: bool = False
239+
self, host: _HostType, handler: str, request_body: SizedBuffer, verbose: bool = False
240240
) -> tuple[_Marshallable, ...]: ...
241241
def single_request(
242-
self, host: _HostType, handler: str, request_body: _BufferWithLen, verbose: bool = False
242+
self, host: _HostType, handler: str, request_body: SizedBuffer, verbose: bool = False
243243
) -> tuple[_Marshallable, ...]: ...
244244
def getparser(self) -> tuple[ExpatParser, Unmarshaller]: ...
245245
def get_host_info(self, host: _HostType) -> tuple[str, list[tuple[str, str]], dict[str, str]]: ...
246246
def make_connection(self, host: _HostType) -> http.client.HTTPConnection: ...
247247
def close(self) -> None: ...
248248
def send_request(
249-
self, host: _HostType, handler: str, request_body: _BufferWithLen, debug: bool
249+
self, host: _HostType, handler: str, request_body: SizedBuffer, debug: bool
250250
) -> http.client.HTTPConnection: ...
251251
def send_headers(self, connection: http.client.HTTPConnection, headers: list[tuple[str, str]]) -> None: ...
252-
def send_content(self, connection: http.client.HTTPConnection, request_body: _BufferWithLen) -> None: ...
252+
def send_content(self, connection: http.client.HTTPConnection, request_body: SizedBuffer) -> None: ...
253253
def parse_response(self, response: http.client.HTTPResponse) -> tuple[_Marshallable, ...]: ...
254254

255255
class SafeTransport(Transport):

stdlib/zipfile.pyi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import io
22
import sys
3-
from _typeshed import StrOrBytesPath, StrPath, _BufferWithLen
3+
from _typeshed import SizedBuffer, StrOrBytesPath, StrPath
44
from collections.abc import Callable, Iterable, Iterator
55
from os import PathLike
66
from types import TracebackType
@@ -179,7 +179,7 @@ class ZipFile:
179179
def writestr(
180180
self,
181181
zinfo_or_arcname: str | ZipInfo,
182-
data: _BufferWithLen | str,
182+
data: SizedBuffer | str,
183183
compress_type: int | None = None,
184184
compresslevel: int | None = None,
185185
) -> None: ...

tests/stubtest_allowlists/py3_common.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,3 +774,7 @@ pkgutil.ImpLoader.get_source
774774
pkgutil.ImpLoader.is_package
775775
pkgutil.ImpLoader.load_module
776776
pkgutil.ImpLoader.source
777+
778+
# We lie about the existence of these methods
779+
.*.__buffer__
780+
.*.__release_buffer__

0 commit comments

Comments
 (0)