Coverage for /pythoncovmergedfiles/medio/medio/src/aiohttp/aiohttp/compression_utils.py: 75%
68 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-27 06:09 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-27 06:09 +0000
1import asyncio
2import zlib
3from concurrent.futures import Executor
4from typing import Optional, cast
6try:
7 try:
8 import brotlicffi as brotli
9 except ImportError:
10 import brotli
12 HAS_BROTLI = True
13except ImportError: # pragma: no cover
14 HAS_BROTLI = False
16MAX_SYNC_CHUNK_SIZE = 1024
19def encoding_to_mode(
20 encoding: Optional[str] = None,
21 suppress_deflate_header: bool = False,
22) -> int:
23 if encoding == "gzip":
24 return 16 + zlib.MAX_WBITS
26 return -zlib.MAX_WBITS if suppress_deflate_header else zlib.MAX_WBITS
29class ZlibBaseHandler:
30 def __init__(
31 self,
32 mode: int,
33 executor: Optional[Executor] = None,
34 max_sync_chunk_size: Optional[int] = MAX_SYNC_CHUNK_SIZE,
35 ):
36 self._mode = mode
37 self._executor = executor
38 self._max_sync_chunk_size = max_sync_chunk_size
41class ZLibCompressor(ZlibBaseHandler):
42 def __init__(
43 self,
44 encoding: Optional[str] = None,
45 suppress_deflate_header: bool = False,
46 level: Optional[int] = None,
47 wbits: Optional[int] = None,
48 strategy: int = zlib.Z_DEFAULT_STRATEGY,
49 executor: Optional[Executor] = None,
50 max_sync_chunk_size: Optional[int] = MAX_SYNC_CHUNK_SIZE,
51 ):
52 super().__init__(
53 mode=encoding_to_mode(encoding, suppress_deflate_header)
54 if wbits is None
55 else wbits,
56 executor=executor,
57 max_sync_chunk_size=max_sync_chunk_size,
58 )
59 if level is None:
60 self._compressor = zlib.compressobj(wbits=self._mode, strategy=strategy)
61 else:
62 self._compressor = zlib.compressobj(
63 wbits=self._mode, strategy=strategy, level=level
64 )
66 def compress_sync(self, data: bytes) -> bytes:
67 return self._compressor.compress(data)
69 async def compress(self, data: bytes) -> bytes:
70 if (
71 self._max_sync_chunk_size is not None
72 and len(data) > self._max_sync_chunk_size
73 ):
74 return await asyncio.get_event_loop().run_in_executor(
75 self._executor, self.compress_sync, data
76 )
77 return self.compress_sync(data)
79 def flush(self, mode: int = zlib.Z_FINISH) -> bytes:
80 return self._compressor.flush(mode)
83class ZLibDecompressor(ZlibBaseHandler):
84 def __init__(
85 self,
86 encoding: Optional[str] = None,
87 suppress_deflate_header: bool = False,
88 executor: Optional[Executor] = None,
89 max_sync_chunk_size: Optional[int] = MAX_SYNC_CHUNK_SIZE,
90 ):
91 super().__init__(
92 mode=encoding_to_mode(encoding, suppress_deflate_header),
93 executor=executor,
94 max_sync_chunk_size=max_sync_chunk_size,
95 )
96 self._decompressor = zlib.decompressobj(wbits=self._mode)
98 def decompress_sync(self, data: bytes, max_length: int = 0) -> bytes:
99 return self._decompressor.decompress(data, max_length)
101 async def decompress(self, data: bytes, max_length: int = 0) -> bytes:
102 if (
103 self._max_sync_chunk_size is not None
104 and len(data) > self._max_sync_chunk_size
105 ):
106 return await asyncio.get_event_loop().run_in_executor(
107 self._executor, self.decompress_sync, data, max_length
108 )
109 return self.decompress_sync(data, max_length)
111 def flush(self, length: int = 0) -> bytes:
112 return (
113 self._decompressor.flush(length)
114 if length > 0
115 else self._decompressor.flush()
116 )
118 @property
119 def eof(self) -> bool:
120 return self._decompressor.eof
122 @property
123 def unconsumed_tail(self) -> bytes:
124 return self._decompressor.unconsumed_tail
126 @property
127 def unused_data(self) -> bytes:
128 return self._decompressor.unused_data
131class BrotliDecompressor:
132 # Supports both 'brotlipy' and 'Brotli' packages
133 # since they share an import name. The top branches
134 # are for 'brotlipy' and bottom branches for 'Brotli'
135 def __init__(self) -> None:
136 if not HAS_BROTLI:
137 raise RuntimeError(
138 "The brotli decompression is not available. "
139 "Please install `Brotli` module"
140 )
141 self._obj = brotli.Decompressor()
143 def decompress_sync(self, data: bytes) -> bytes:
144 if hasattr(self._obj, "decompress"):
145 return cast(bytes, self._obj.decompress(data))
146 return cast(bytes, self._obj.process(data))
148 def flush(self) -> bytes:
149 if hasattr(self._obj, "flush"):
150 return cast(bytes, self._obj.flush())
151 return b""