Skip to content

Commit ff4f21e

Browse files
committed
feat: Validate compressor quality range and fail fast
1 parent 4674110 commit ff4f21e

3 files changed

Lines changed: 43 additions & 6 deletions

File tree

src/apify_client/http_compressors/_brotli.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,25 @@ class BrotliHttpCompressor(HttpCompressor):
1313

1414
content_encoding = 'br'
1515

16+
_min_quality = 0
17+
"""Lowest valid quality (fastest, least compression)."""
18+
19+
_max_quality = 11
20+
"""Highest valid quality (slowest, best compression)."""
21+
1622
def __init__(self, *, quality: int = 6) -> None:
1723
"""Initialize the brotli compressor.
1824
1925
Args:
20-
quality: Compression level, `0` (fastest) to `11` (the best compression). Defaults to `6`,
21-
a reasonable compromise between speed and compression ratio. Passed straight to the
22-
`brotli` module, which validates it.
26+
quality: Compression level, from the fastest to the best compression.
27+
28+
Raises:
29+
ValueError: If `quality` is out of the valid range.
2330
"""
31+
if not self._min_quality <= quality <= self._max_quality:
32+
raise ValueError(
33+
f'brotli quality must be between {self._min_quality} and {self._max_quality}, got {quality}.'
34+
)
2435
self._quality = quality
2536

2637
def compress(self, data: bytes) -> bytes:

src/apify_client/http_compressors/_gzip.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,25 @@ class GzipHttpCompressor(HttpCompressor):
1313

1414
content_encoding = 'gzip'
1515

16-
def __init__(self, *, quality: int = 9) -> None:
16+
_min_quality = 1
17+
"""Lowest valid quality (fastest, least compression)."""
18+
19+
_max_quality = 9
20+
"""Highest valid quality (slowest, best compression)."""
21+
22+
def __init__(self, *, quality: int = _max_quality) -> None:
1723
"""Initialize the gzip compressor.
1824
1925
Args:
20-
quality: Compression level, `1` (fastest) to `9` (the best compression). Defaults to `9`.
21-
Passed straight to the `gzip` module, which validates it.
26+
quality: Compression level, from the fastest to the best compression.
27+
28+
Raises:
29+
ValueError: If `quality` is out of the valid range.
2230
"""
31+
if not self._min_quality <= quality <= self._max_quality:
32+
raise ValueError(
33+
f'gzip quality must be between {self._min_quality} and {self._max_quality}, got {quality}.'
34+
)
2335
self._quality = quality
2436

2537
def compress(self, data: bytes) -> bytes:

tests/unit/test_http_compressors.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,20 @@ def test_brotli_compressor_round_trips_at_quality(quality: int) -> None:
6060
assert brotli.decompress(BrotliHttpCompressor(quality=quality).compress(b'payload')) == b'payload'
6161

6262

63+
@pytest.mark.parametrize('quality', [0, 10, -1])
64+
def test_gzip_compressor_rejects_out_of_range_quality(quality: int) -> None:
65+
"""Gzip compressor raises `ValueError` at construction for a quality not between `1` and `9`."""
66+
with pytest.raises(ValueError, match='gzip quality must be between 1 and 9'):
67+
GzipHttpCompressor(quality=quality)
68+
69+
70+
@pytest.mark.parametrize('quality', [-1, 12])
71+
def test_brotli_compressor_rejects_out_of_range_quality(quality: int) -> None:
72+
"""Brotli compressor raises `ValueError` at construction for a quality not between `0` and `11`."""
73+
with pytest.raises(ValueError, match='brotli quality must be between 0 and 11'):
74+
BrotliHttpCompressor(quality=quality)
75+
76+
6377
def test_gzip_compressor_round_trips_and_sets_content_encoding() -> None:
6478
"""Gzip compressor round-trips data and reports `gzip` as its content encoding."""
6579
compressor = GzipHttpCompressor()

0 commit comments

Comments
 (0)