22import sys
33import zlib
44from concurrent .futures import Executor
5- from typing import Optional , cast
5+ from typing import Any , Final , Optional , Protocol , TypedDict , cast
66
77if sys .version_info >= (3 , 12 ):
88 from collections .abc import Buffer
2424MAX_SYNC_CHUNK_SIZE = 1024
2525
2626
27+ class ZLibCompressObjProtocol (Protocol ):
28+ def compress (self , data : Buffer ) -> bytes : ...
29+ def flush (self , mode : int = ..., / ) -> bytes : ...
30+
31+
32+ class ZLibDecompressObjProtocol (Protocol ):
33+ def decompress (self , data : Buffer , max_length : int = ...) -> bytes : ...
34+ def flush (self , length : int = ..., / ) -> bytes : ...
35+
36+ @property
37+ def eof (self ) -> bool : ...
38+
39+
40+ class ZLibBackendProtocol (Protocol ):
41+ MAX_WBITS : int
42+ Z_FULL_FLUSH : int
43+ Z_SYNC_FLUSH : int
44+ Z_BEST_SPEED : int
45+ Z_FINISH : int
46+
47+ def compressobj (
48+ self ,
49+ level : int = ...,
50+ method : int = ...,
51+ wbits : int = ...,
52+ memLevel : int = ...,
53+ strategy : int = ...,
54+ zdict : Optional [Buffer ] = ...,
55+ ) -> ZLibCompressObjProtocol : ...
56+ def decompressobj (
57+ self , wbits : int = ..., zdict : Buffer = ...
58+ ) -> ZLibDecompressObjProtocol : ...
59+
60+ def compress (
61+ self , data : Buffer , / , level : int = ..., wbits : int = ...
62+ ) -> bytes : ...
63+ def decompress (
64+ self , data : Buffer , / , wbits : int = ..., bufsize : int = ...
65+ ) -> bytes : ...
66+
67+
68+ class CompressObjArgs (TypedDict , total = False ):
69+ wbits : int
70+ strategy : int
71+ level : int
72+
73+
74+ class ZLibBackendWrapper :
75+ def __init__ (self , _zlib_backend : ZLibBackendProtocol ):
76+ self ._zlib_backend : ZLibBackendProtocol = _zlib_backend
77+
78+ @property
79+ def name (self ) -> str :
80+ return getattr (self ._zlib_backend , "__name__" , "undefined" )
81+
82+ @property
83+ def MAX_WBITS (self ) -> int :
84+ return self ._zlib_backend .MAX_WBITS
85+
86+ @property
87+ def Z_FULL_FLUSH (self ) -> int :
88+ return self ._zlib_backend .Z_FULL_FLUSH
89+
90+ @property
91+ def Z_SYNC_FLUSH (self ) -> int :
92+ return self ._zlib_backend .Z_SYNC_FLUSH
93+
94+ @property
95+ def Z_BEST_SPEED (self ) -> int :
96+ return self ._zlib_backend .Z_BEST_SPEED
97+
98+ @property
99+ def Z_FINISH (self ) -> int :
100+ return self ._zlib_backend .Z_FINISH
101+
102+ def compressobj (self , * args : Any , ** kwargs : Any ) -> ZLibCompressObjProtocol :
103+ return self ._zlib_backend .compressobj (* args , ** kwargs )
104+
105+ def decompressobj (self , * args : Any , ** kwargs : Any ) -> ZLibDecompressObjProtocol :
106+ return self ._zlib_backend .decompressobj (* args , ** kwargs )
107+
108+ def compress (self , data : Buffer , * args : Any , ** kwargs : Any ) -> bytes :
109+ return self ._zlib_backend .compress (data , * args , ** kwargs )
110+
111+ def decompress (self , data : Buffer , * args : Any , ** kwargs : Any ) -> bytes :
112+ return self ._zlib_backend .decompress (data , * args , ** kwargs )
113+
114+ # Everything not explicitly listed in the Protocol we just pass through
115+ def __getattr__ (self , attrname : str ) -> Any :
116+ return getattr (self ._zlib_backend , attrname )
117+
118+
119+ ZLibBackend : ZLibBackendWrapper = ZLibBackendWrapper (zlib )
120+
121+
122+ def set_zlib_backend (new_zlib_backend : ZLibBackendProtocol ) -> None :
123+ ZLibBackend ._zlib_backend = new_zlib_backend
124+
125+
27126def encoding_to_mode (
28127 encoding : Optional [str ] = None ,
29128 suppress_deflate_header : bool = False ,
30129) -> int :
31130 if encoding == "gzip" :
32- return 16 + zlib .MAX_WBITS
131+ return 16 + ZLibBackend .MAX_WBITS
33132
34- return - zlib .MAX_WBITS if suppress_deflate_header else zlib .MAX_WBITS
133+ return - ZLibBackend .MAX_WBITS if suppress_deflate_header else ZLibBackend .MAX_WBITS
35134
36135
37136class ZlibBaseHandler :
@@ -53,7 +152,7 @@ def __init__(
53152 suppress_deflate_header : bool = False ,
54153 level : Optional [int ] = None ,
55154 wbits : Optional [int ] = None ,
56- strategy : int = zlib . Z_DEFAULT_STRATEGY ,
155+ strategy : Optional [ int ] = None ,
57156 executor : Optional [Executor ] = None ,
58157 max_sync_chunk_size : Optional [int ] = MAX_SYNC_CHUNK_SIZE ,
59158 ):
@@ -66,12 +165,15 @@ def __init__(
66165 executor = executor ,
67166 max_sync_chunk_size = max_sync_chunk_size ,
68167 )
69- if level is None :
70- self ._compressor = zlib .compressobj (wbits = self ._mode , strategy = strategy )
71- else :
72- self ._compressor = zlib .compressobj (
73- wbits = self ._mode , strategy = strategy , level = level
74- )
168+ self ._zlib_backend : Final = ZLibBackendWrapper (ZLibBackend ._zlib_backend )
169+
170+ kwargs : CompressObjArgs = {}
171+ kwargs ["wbits" ] = self ._mode
172+ if strategy is not None :
173+ kwargs ["strategy" ] = strategy
174+ if level is not None :
175+ kwargs ["level" ] = level
176+ self ._compressor = self ._zlib_backend .compressobj (** kwargs )
75177 self ._compress_lock = asyncio .Lock ()
76178
77179 def compress_sync (self , data : Buffer ) -> bytes :
@@ -100,8 +202,10 @@ async def compress(self, data: Buffer) -> bytes:
100202 )
101203 return self .compress_sync (data )
102204
103- def flush (self , mode : int = zlib .Z_FINISH ) -> bytes :
104- return self ._compressor .flush (mode )
205+ def flush (self , mode : Optional [int ] = None ) -> bytes :
206+ return self ._compressor .flush (
207+ mode if mode is not None else self ._zlib_backend .Z_FINISH
208+ )
105209
106210
107211class ZLibDecompressor (ZlibBaseHandler ):
@@ -117,7 +221,8 @@ def __init__(
117221 executor = executor ,
118222 max_sync_chunk_size = max_sync_chunk_size ,
119223 )
120- self ._decompressor = zlib .decompressobj (wbits = self ._mode )
224+ self ._zlib_backend : Final = ZLibBackendWrapper (ZLibBackend ._zlib_backend )
225+ self ._decompressor = self ._zlib_backend .decompressobj (wbits = self ._mode )
121226
122227 def decompress_sync (self , data : Buffer , max_length : int = 0 ) -> bytes :
123228 return self ._decompressor .decompress (data , max_length )
@@ -149,14 +254,6 @@ def flush(self, length: int = 0) -> bytes:
149254 def eof (self ) -> bool :
150255 return self ._decompressor .eof
151256
152- @property
153- def unconsumed_tail (self ) -> bytes :
154- return self ._decompressor .unconsumed_tail
155-
156- @property
157- def unused_data (self ) -> bytes :
158- return self ._decompressor .unused_data
159-
160257
161258class BrotliDecompressor :
162259 # Supports both 'brotlipy' and 'Brotli' packages
0 commit comments