Skip to content

Commit a25a6ef

Browse files
committed
docs: Tighten the request-body compression docstrings and comments
1 parent 2f15848 commit a25a6ef

3 files changed

Lines changed: 12 additions & 17 deletions

File tree

src/apify_client/http_clients/_base.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -227,11 +227,9 @@ def _compute_timeout(self, timeout: Timeout, *, attempt: int) -> int | float | N
227227
def _is_body_worth_compressing(data: str | bytes | bytearray | None) -> bool:
228228
"""Whether `_prepare_request_call` would compress this body, decided without encoding a large `str`.
229229
230-
Mirrors the rule applied in `_prepare_request_call`, which measures the threshold on the encoded
231-
bytes. A `str` therefore cannot be judged by its character count alone. That count is a lower
232-
bound on the UTF-8 length, so a `str` reaching the threshold in characters reaches it in bytes
233-
too. Below that, the encoded length decides, and such a body is under 4 KiB, so encoding it here
234-
is cheap. Any other type is passed through uncompressed.
230+
The threshold is measured on encoded bytes, so a character count alone cannot decide a `str`. It
231+
is a lower bound, so a `str` long enough in characters is long enough in bytes too. Below that the
232+
encoded length decides, and the body is then under 4 KiB, so encoding it here is cheap.
235233
"""
236234
if isinstance(data, str):
237235
return len(data) >= MIN_COMPRESSION_SIZE or len(data.encode('utf-8')) >= MIN_COMPRESSION_SIZE
@@ -275,8 +273,8 @@ def _prepare_request_call(
275273
data = self._http_compressor.compress(data)
276274
headers = self._merge_headers(headers, {'Content-Encoding': self._http_compressor.content_encoding})
277275
else:
278-
# `Content-Encoding` must always describe what was actually applied, so a value the
279-
# caller supplied is dropped rather than left to mislabel an uncompressed body.
276+
# `Content-Encoding` must describe what was actually applied, so drop a caller
277+
# value rather than let it mislabel an uncompressed body.
280278
headers = {key: value for key, value in headers.items() if key.lower() != 'content-encoding'}
281279

282280
return (headers, self._parse_params(params), data)

src/apify_client/http_clients/_impit.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -395,10 +395,9 @@ async def call(
395395
self._statistics.calls += 1
396396

397397
# Serializing and compressing a request body is CPU-bound and would block the event loop, so
398-
# offload request preparation to a worker thread whenever there is a body to compress. Bodyless
399-
# requests skip the thread hop, as they have no expensive work to move off the loop. So do raw
400-
# bodies the client sends as they are, for which the hop would cost more than preparing them
401-
# inline. The size of a `json` body is only known once serialized, so it always hops.
398+
# offload preparation to a worker thread whenever there is something to compress. A body the
399+
# client sends as it is costs less to prepare inline than the hop itself. A `json` body always
400+
# hops, as its size is only known once serialized.
402401
if json is not None or self._is_body_worth_compressing(data):
403402
prepared_headers, prepared_params, content = await asyncio.to_thread(
404403
self._prepare_request_call,

tests/unit/test_http_clients.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -467,8 +467,7 @@ def test_prepare_request_call_measures_threshold_in_bytes_not_characters(compres
467467
"""A `str` body under the threshold in characters but over it in UTF-8 bytes is still compressed."""
468468
compressor, content_encoding, decompress = compressor_case
469469
client = _ConcreteHttpClient(http_compressor=compressor)
470-
# U+00E9 (e with an acute accent) encodes to 2 bytes, so this body is half the threshold
471-
# in characters but just above it in bytes.
470+
# U+00E9 encodes to 2 bytes, so this body is under the threshold in characters but over it in bytes.
472471
body = '\u00e9' * (MIN_COMPRESSION_SIZE // 2 + 1)
473472

474473
headers, _params, data = client._prepare_request_call(data=body)
@@ -658,7 +657,7 @@ async def test_async_call_compresses_request_body_off_the_event_loop() -> None:
658657

659658

660659
async def test_async_call_compresses_a_multibyte_str_body_off_the_event_loop() -> None:
661-
"""A `str` body under the threshold in characters but over it in bytes gets compressed, so it must be offloaded."""
660+
"""A `str` body over the threshold only once encoded is still compressed, so it must be offloaded."""
662661
compressor = _ThreadRecordingCompressor()
663662
client = ImpitHttpClientAsync(token='test_token', http_compressor=compressor)
664663
client._impit_async_client = Mock(request=AsyncMock(return_value=Mock(status_code=200)))
@@ -718,13 +717,12 @@ async def test_async_call_offloads_a_body_at_the_threshold(monkeypatch: pytest.M
718717
async def test_async_call_skips_thread_offload_for_a_body_it_cannot_compress(
719718
monkeypatch: pytest.MonkeyPatch,
720719
) -> None:
721-
"""A body of a type the client passes through has nothing to compress, so deciding must not need its length."""
720+
"""A body of a type the client passes through needs no hop, and deciding that must not need its length."""
722721
client = ImpitHttpClientAsync(token='test_token')
723722
client._impit_async_client = Mock(request=AsyncMock(return_value=Mock(status_code=200)))
724723
spy = _to_thread_spy(monkeypatch)
725724

726-
# A file-like body sits outside the declared type, but `encode_key_value_store_record_value` passes
727-
# one through, so the gate must not assume every body has a length.
725+
# `encode_key_value_store_record_value` passes file-like bodies through, so the gate cannot assume a length.
728726
body: Any = BytesIO(b'x' * MIN_COMPRESSION_SIZE)
729727

730728
await client.call(method='PUT', url='https://api.test.com/endpoint', data=body)

0 commit comments

Comments
 (0)