Skip to content

Python 3.14: PEP-784 compression.zstd #14129

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jun 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion stdlib/@tests/stubtest_allowlists/py314.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ compression.gzip.GzipFile.readinto
compression.gzip.GzipFile.readinto1
compression.gzip.GzipFile.readinto1
compression.gzip.compress
compression.zstd
fractions.Fraction.__pow__
fractions.Fraction.__rpow__
gzip.GzipFile.readinto
Expand Down
1 change: 1 addition & 0 deletions stdlib/VERSIONS
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ _warnings: 3.0-
_weakref: 3.0-
_weakrefset: 3.0-
_winapi: 3.3-
_zstd: 3.14-
abc: 3.0-
aifc: 3.0-3.12
annotationlib: 3.14-
Expand Down
96 changes: 96 additions & 0 deletions stdlib/_zstd.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from _typeshed import ReadableBuffer
from collections.abc import Mapping
from compression.zstd import CompressionParameter, DecompressionParameter
from typing import Final, Literal, final
from typing_extensions import Self, TypeAlias

ZSTD_CLEVEL_DEFAULT: Final = 3
ZSTD_DStreamOutSize: Final = 131072
ZSTD_btlazy2: Final = 6
ZSTD_btopt: Final = 7
ZSTD_btultra: Final = 8
ZSTD_btultra2: Final = 9
ZSTD_c_chainLog: Final = 103
ZSTD_c_checksumFlag: Final = 201
ZSTD_c_compressionLevel: Final = 100
ZSTD_c_contentSizeFlag: Final = 200
ZSTD_c_dictIDFlag: Final = 202
ZSTD_c_enableLongDistanceMatching: Final = 160
ZSTD_c_hashLog: Final = 102
ZSTD_c_jobSize: Final = 401
ZSTD_c_ldmBucketSizeLog: Final = 163
ZSTD_c_ldmHashLog: Final = 161
ZSTD_c_ldmHashRateLog: Final = 164
ZSTD_c_ldmMinMatch: Final = 162
ZSTD_c_minMatch: Final = 105
ZSTD_c_nbWorkers: Final = 400
ZSTD_c_overlapLog: Final = 402
ZSTD_c_searchLog: Final = 104
ZSTD_c_strategy: Final = 107
ZSTD_c_targetLength: Final = 106
ZSTD_c_windowLog: Final = 101
ZSTD_d_windowLogMax: Final = 100
ZSTD_dfast: Final = 2
ZSTD_fast: Final = 1
ZSTD_greedy: Final = 3
ZSTD_lazy: Final = 4
ZSTD_lazy2: Final = 5

_ZstdCompressorContinue: TypeAlias = Literal[0]
_ZstdCompressorFlushBlock: TypeAlias = Literal[1]
_ZstdCompressorFlushFrame: TypeAlias = Literal[2]

@final
class ZstdCompressor:
CONTINUE: Final = 0
FLUSH_BLOCK: Final = 1
FLUSH_FRAME: Final = 2
def __init__(
self, level: int | None = None, options: Mapping[int, int] | None = None, zstd_dict: ZstdDict | None = None
) -> None: ...
def compress(
self, /, data: ReadableBuffer, mode: _ZstdCompressorContinue | _ZstdCompressorFlushBlock | _ZstdCompressorFlushFrame = 0
) -> bytes: ...
def flush(self, /, mode: _ZstdCompressorFlushBlock | _ZstdCompressorFlushFrame = 2) -> bytes: ...
@property
def last_mode(self) -> _ZstdCompressorContinue | _ZstdCompressorFlushBlock | _ZstdCompressorFlushFrame: ...

@final
class ZstdDecompressor:
def __init__(self, zstd_dict: ZstdDict | None = None, options: Mapping[int, int] | None = None) -> None: ...
def decompress(self, /, data: ReadableBuffer, max_length: int = -1) -> bytes: ...
@property
def eof(self) -> bool: ...
@property
def needs_input(self) -> bool: ...
@property
def unused_data(self) -> bytes: ...

@final
class ZstdDict:
def __init__(self, dict_content: bytes, /, *, is_raw: bool = False) -> None: ...
def __len__(self, /) -> int: ...
@property
def as_digested_dict(self) -> tuple[Self, int]: ...
@property
def as_prefix(self) -> tuple[Self, int]: ...
@property
def as_undigested_dict(self) -> tuple[Self, int]: ...
@property
def dict_content(self) -> bytes: ...
@property
def dict_id(self) -> int: ...

class ZstdError(Exception): ...

def finalize_dict(
custom_dict_bytes: bytes, samples_bytes: bytes, samples_sizes: tuple[int, ...], dict_size: int, compression_level: int, /
) -> bytes: ...
def get_frame_info(frame_buffer: ReadableBuffer) -> tuple[int, int]: ...
def get_frame_size(frame_buffer: ReadableBuffer) -> int: ...
def get_param_bounds(parameter: int, is_compress: bool) -> tuple[int, int]: ...
def set_parameter_types(c_parameter_type: type[CompressionParameter], d_parameter_type: type[DecompressionParameter]) -> None: ...
def train_dict(samples_bytes: bytes, samples_sizes: tuple[int, ...], dict_size: int, /) -> bytes: ...

zstd_version: Final[str]
zstd_version_number: Final[int]
11 changes: 6 additions & 5 deletions stdlib/bz2.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import sys
from _bz2 import BZ2Compressor as BZ2Compressor, BZ2Decompressor as BZ2Decompressor
from _typeshed import ReadableBuffer, StrOrBytesPath, WriteableBuffer
from collections.abc import Iterable
from typing import IO, Literal, Protocol, SupportsIndex, TextIO, overload
from io import TextIOWrapper
from typing import IO, Literal, Protocol, SupportsIndex, overload
from typing_extensions import Self, TypeAlias

if sys.version_info >= (3, 14):
Expand Down Expand Up @@ -48,7 +49,7 @@ def open(
encoding: str | None = None,
errors: str | None = None,
newline: str | None = None,
) -> TextIO: ...
) -> TextIOWrapper: ...
@overload
def open(
filename: _WritableFileobj,
Expand All @@ -66,7 +67,7 @@ def open(
encoding: str | None = None,
errors: str | None = None,
newline: str | None = None,
) -> TextIO: ...
) -> TextIOWrapper: ...
@overload
def open(
filename: StrOrBytesPath,
Expand All @@ -84,7 +85,7 @@ def open(
encoding: str | None = None,
errors: str | None = None,
newline: str | None = None,
) -> TextIO: ...
) -> TextIOWrapper: ...
@overload
def open(
filename: StrOrBytesPath | _ReadableFileobj | _WritableFileobj,
Expand All @@ -93,7 +94,7 @@ def open(
encoding: str | None = None,
errors: str | None = None,
newline: str | None = None,
) -> BZ2File | TextIO: ...
) -> BZ2File | TextIOWrapper: ...

class BZ2File(BaseStream, IO[bytes]):
def __enter__(self) -> Self: ...
Expand Down
3 changes: 2 additions & 1 deletion stdlib/compression/_common/_streams.pyi
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from _typeshed import Incomplete, WriteableBuffer
from collections.abc import Callable
from io import DEFAULT_BUFFER_SIZE, BufferedIOBase, RawIOBase
from typing import Any, Protocol
from typing import Any, Protocol, type_check_only

BUFFER_SIZE = DEFAULT_BUFFER_SIZE

@type_check_only
class _Reader(Protocol):
def read(self, n: int, /) -> bytes: ...
def seekable(self) -> bool: ...
Expand Down
87 changes: 87 additions & 0 deletions stdlib/compression/zstd/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import enum
from _typeshed import ReadableBuffer
from collections.abc import Iterable, Mapping
from compression.zstd._zstdfile import ZstdFile, open
from typing import Final, final

import _zstd
from _zstd import ZstdCompressor, ZstdDecompressor, ZstdDict, ZstdError, get_frame_size, zstd_version

__all__ = (
# compression.zstd
"COMPRESSION_LEVEL_DEFAULT",
"compress",
"CompressionParameter",
"decompress",
"DecompressionParameter",
"finalize_dict",
"get_frame_info",
"Strategy",
"train_dict",
# compression.zstd._zstdfile
"open",
"ZstdFile",
# _zstd
"get_frame_size",
"zstd_version",
"zstd_version_info",
"ZstdCompressor",
"ZstdDecompressor",
"ZstdDict",
"ZstdError",
)

zstd_version_info: Final[tuple[int, int, int]]
COMPRESSION_LEVEL_DEFAULT: Final = _zstd.ZSTD_CLEVEL_DEFAULT

class FrameInfo:
decompressed_size: int
dictionary_id: int
def __init__(self, decompressed_size: int, dictionary_id: int) -> None: ...

def get_frame_info(frame_buffer: ReadableBuffer) -> FrameInfo: ...
def train_dict(samples: Iterable[ReadableBuffer], dict_size: int) -> ZstdDict: ...
def finalize_dict(zstd_dict: ZstdDict, /, samples: Iterable[ReadableBuffer], dict_size: int, level: int) -> ZstdDict: ...
def compress(
data: ReadableBuffer, level: int | None = None, options: Mapping[int, int] | None = None, zstd_dict: ZstdDict | None = None
) -> bytes: ...
def decompress(data: ReadableBuffer, zstd_dict: ZstdDict | None = None, options: Mapping[int, int] | None = None) -> bytes: ...
@final
class CompressionParameter(enum.IntEnum):
compression_level = _zstd.ZSTD_c_compressionLevel
window_log = _zstd.ZSTD_c_windowLog
hash_log = _zstd.ZSTD_c_hashLog
chain_log = _zstd.ZSTD_c_chainLog
search_log = _zstd.ZSTD_c_searchLog
min_match = _zstd.ZSTD_c_minMatch
target_length = _zstd.ZSTD_c_targetLength
strategy = _zstd.ZSTD_c_strategy
enable_long_distance_matching = _zstd.ZSTD_c_enableLongDistanceMatching
ldm_hash_log = _zstd.ZSTD_c_ldmHashLog
ldm_min_match = _zstd.ZSTD_c_ldmMinMatch
ldm_bucket_size_log = _zstd.ZSTD_c_ldmBucketSizeLog
ldm_hash_rate_log = _zstd.ZSTD_c_ldmHashRateLog
content_size_flag = _zstd.ZSTD_c_contentSizeFlag
checksum_flag = _zstd.ZSTD_c_checksumFlag
dict_id_flag = _zstd.ZSTD_c_dictIDFlag
nb_workers = _zstd.ZSTD_c_nbWorkers
job_size = _zstd.ZSTD_c_jobSize
overlap_log = _zstd.ZSTD_c_overlapLog
def bounds(self) -> tuple[int, int]: ...

@final
class DecompressionParameter(enum.IntEnum):
window_log_max = _zstd.ZSTD_d_windowLogMax
def bounds(self) -> tuple[int, int]: ...

@final
class Strategy(enum.IntEnum):
fast = _zstd.ZSTD_fast
dfast = _zstd.ZSTD_dfast
greedy = _zstd.ZSTD_greedy
lazy = _zstd.ZSTD_lazy
lazy2 = _zstd.ZSTD_lazy2
btlazy2 = _zstd.ZSTD_btlazy2
btopt = _zstd.ZSTD_btopt
btultra = _zstd.ZSTD_btultra
btultra2 = _zstd.ZSTD_btultra2
117 changes: 117 additions & 0 deletions stdlib/compression/zstd/_zstdfile.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
from _typeshed import ReadableBuffer, StrOrBytesPath, SupportsWrite, WriteableBuffer
from collections.abc import Mapping
from compression._common import _streams
from compression.zstd import ZstdDict
from io import TextIOWrapper, _WrappedBuffer
from typing import Literal, overload, type_check_only
from typing_extensions import TypeAlias

from _zstd import ZstdCompressor, _ZstdCompressorFlushBlock, _ZstdCompressorFlushFrame

__all__ = ("ZstdFile", "open")

_ReadBinaryMode: TypeAlias = Literal["r", "rb"]
_WriteBinaryMode: TypeAlias = Literal["w", "wb", "x", "xb", "a", "ab"]
_ReadTextMode: TypeAlias = Literal["rt"]
_WriteTextMode: TypeAlias = Literal["wt", "xt", "at"]

@type_check_only
class _FileBinaryRead(_streams._Reader):
def close(self) -> None: ...

@type_check_only
class _FileBinaryWrite(SupportsWrite[bytes]):
def close(self) -> None: ...

class ZstdFile(_streams.BaseStream):
FLUSH_BLOCK = ZstdCompressor.FLUSH_BLOCK
FLUSH_FRAME = ZstdCompressor.FLUSH_FRAME

@overload
def __init__(
self,
file: StrOrBytesPath | _FileBinaryRead,
/,
mode: _ReadBinaryMode = "r",
*,
level: None = None,
options: Mapping[int, int] | None = None,
zstd_dict: ZstdDict | None = None,
) -> None: ...
@overload
def __init__(
self,
file: StrOrBytesPath | _FileBinaryWrite,
/,
mode: _WriteBinaryMode,
*,
level: int | None = None,
options: Mapping[int, int] | None = None,
zstd_dict: ZstdDict | None = None,
) -> None: ...
def write(self, data: ReadableBuffer, /) -> int: ...
def flush(self, mode: _ZstdCompressorFlushBlock | _ZstdCompressorFlushFrame = 1) -> bytes: ... # type: ignore[override]
def read(self, size: int | None = -1) -> bytes: ...
def read1(self, size: int | None = -1) -> bytes: ...
def readinto(self, b: WriteableBuffer) -> int: ...
def readinto1(self, b: WriteableBuffer) -> int: ...
def readline(self, size: int | None = -1) -> bytes: ...
def seek(self, offset: int, whence: int = 0) -> int: ...
def peek(self, size: int = -1) -> bytes: ...
@property
def name(self) -> str | bytes: ...
@property
def mode(self) -> Literal["rb", "wb"]: ...

@overload
def open(
file: StrOrBytesPath | _FileBinaryRead,
/,
mode: _ReadBinaryMode = "rb",
*,
level: None = None,
options: Mapping[int, int] | None = None,
zstd_dict: ZstdDict | None = None,
encoding: str | None = None,
errors: str | None = None,
newline: str | None = None,
) -> ZstdFile: ...
@overload
def open(
file: StrOrBytesPath | _FileBinaryWrite,
/,
mode: _WriteBinaryMode,
*,
level: int | None = None,
options: Mapping[int, int] | None = None,
zstd_dict: ZstdDict | None = None,
encoding: str | None = None,
errors: str | None = None,
newline: str | None = None,
) -> ZstdFile: ...
@overload
def open(
file: StrOrBytesPath | _WrappedBuffer,
/,
mode: _ReadTextMode,
*,
level: None = None,
options: Mapping[int, int] | None = None,
zstd_dict: ZstdDict | None = None,
encoding: str | None = None,
errors: str | None = None,
newline: str | None = None,
) -> TextIOWrapper: ...
@overload
def open(
file: StrOrBytesPath | _WrappedBuffer,
/,
mode: _WriteTextMode,
*,
level: int | None = None,
options: Mapping[int, int] | None = None,
zstd_dict: ZstdDict | None = None,
encoding: str | None = None,
errors: str | None = None,
newline: str | None = None,
) -> TextIOWrapper: ...
Loading