From 6e18f94c9fa421f2a35a43284bc02e1dec2e99ca Mon Sep 17 00:00:00 2001 From: trim21 Date: Sun, 31 May 2026 00:36:20 +0800 Subject: [PATCH] fix: use minimal Protocols for stream writer/reader type stubs The C extension only requires .write() on writer arguments and .read() on reader arguments. These methods are called via PyObject_CallMethod / duck typing - the full IO[bytes] interface is never checked. Using IO[bytes] as the type annotation is overly restrictive. Objects like opendal.File that implement .write() but not the full IO[bytes] protocol (e.g. missing mode, name, fileno, readlines, writelines, __iter__, __next__) are rejected by type checkers despite working perfectly at runtime. Add private _Writable and _Readable Protocols with only the required methods, and use them in place of IO[bytes] for stream_writer, stream_reader, copy_stream, and read_to_iter parameters. --- zstandard/__init__.pyi | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/zstandard/__init__.pyi b/zstandard/__init__.pyi index f341405f..489e15aa 100644 --- a/zstandard/__init__.pyi +++ b/zstandard/__init__.pyi @@ -6,18 +6,26 @@ import os from typing import ( - IO, BinaryIO, ByteString, Generator, Iterable, List, Optional, + Protocol, Set, Tuple, Union, ) + +class _Writable(Protocol): + def write(self, data: bytes, /) -> int: ... + + +class _Readable(Protocol): + def read(self, size: int = ..., /) -> bytes: ... + FLUSH_BLOCK: int FLUSH_FRAME: int @@ -278,15 +286,15 @@ class ZstdCompressor(object): ) -> ZstdCompressionChunker: ... def copy_stream( self, - ifh: IO[bytes], - ofh: IO[bytes], + ifh: _Readable, + ofh: _Writable, size: int = ..., read_size: int = ..., write_size: int = ..., ) -> Tuple[int, int]: ... def stream_reader( self, - source: Union[IO[bytes], ByteString], + source: Union[ByteString, _Readable], size: int = ..., read_size: int = ..., *, @@ -294,7 +302,7 @@ class ZstdCompressor(object): ) -> ZstdCompressionReader: ... def stream_writer( self, - writer: IO[bytes], + writer: _Writable, size: int = ..., write_size: int = ..., write_return_read: bool = ..., @@ -303,7 +311,7 @@ class ZstdCompressor(object): ) -> ZstdCompressionWriter: ... def read_to_iter( self, - reader: Union[IO[bytes], ByteString], + reader: Union[ByteString, _Readable], size: int = ..., read_size: int = ..., write_size: int = ..., @@ -396,25 +404,25 @@ class ZstdDecompressor(object): ) -> bytes: ... def stream_reader( self, - source: Union[IO[bytes], ByteString], + source: Union[ByteString, _Readable], read_size: int = ..., read_across_frames: bool = ..., *, - closefd=False, + closefd: bool = ..., ) -> ZstdDecompressionReader: ... def decompressobj( self, write_size: int = ..., read_across_frames: bool = False ) -> ZstdDecompressionObj: ... def read_to_iter( self, - reader: Union[IO[bytes], ByteString], + reader: Union[ByteString, _Readable], read_size: int = ..., write_size: int = ..., skip_bytes: int = ..., ) -> Generator[bytes, None, None]: ... def stream_writer( self, - writer: IO[bytes], + writer: _Writable, write_size: int = ..., write_return_read: bool = ..., *, @@ -422,8 +430,8 @@ class ZstdDecompressor(object): ) -> ZstdDecompressionWriter: ... def copy_stream( self, - ifh: IO[bytes], - ofh: IO[bytes], + ifh: _Readable, + ofh: _Writable, read_size: int = ..., write_size: int = ..., ) -> Tuple[int, int]: ...