From c8217536d29469bd98a0cd152b74bd23acce7bc9 Mon Sep 17 00:00:00 2001 From: Victor Gavro Date: Tue, 19 May 2026 22:50:16 +0300 Subject: [PATCH] refactor: retry logging --- httpx_retries/retry.py | 19 +++++++--------- httpx_retries/transport.py | 46 +++++++++++++++++++++++++------------- 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/httpx_retries/retry.py b/httpx_retries/retry.py index 5415cae..2ea3a71 100644 --- a/httpx_retries/retry.py +++ b/httpx_retries/retry.py @@ -4,7 +4,7 @@ import random import sys import time -from collections.abc import Iterable, Mapping +from collections.abc import Iterable from email.utils import parsedate_to_datetime from enum import Enum from http import HTTPStatus @@ -226,12 +226,12 @@ def backoff_strategy(self) -> float: return min(backoff, self.max_backoff_wait) - def _calculate_sleep(self, headers: httpx.Headers | Mapping[str, str]) -> float: + def calculate_sleep(self, response: httpx.Response | Exception) -> float: """Calculate the sleep duration based on headers and backoff strategy.""" sleep_time = 0.0 # Check Retry-After header first if enabled - if self.respect_retry_after_header: - retry_after = headers.get("Retry-After", "").strip() + if isinstance(response, httpx.Response) and self.respect_retry_after_header: + retry_after = response.headers.get("Retry-After", "").strip() if retry_after: try: retry_after_sleep = min(self.parse_retry_after(retry_after), self.max_backoff_wait) @@ -251,7 +251,7 @@ def _calculate_sleep(self, headers: httpx.Headers | Mapping[str, str]) -> float: return sleep_time - def sleep(self, response: httpx.Response | Exception) -> None: + def sleep(self, response: httpx.Response | Exception | float) -> None: """ Sleep between retry attempts using the calculated duration. @@ -259,12 +259,11 @@ def sleep(self, response: httpx.Response | Exception) -> None: of the time requested. If that is not present, it will use an exponential backoff. By default, the backoff factor is 0 and this method will return immediately. """ - time_to_sleep = self._calculate_sleep(response.headers if isinstance(response, httpx.Response) else {}) - logger.debug("sleep seconds=%s", time_to_sleep) + time_to_sleep = response if isinstance(response, (int, float)) else self.calculate_sleep(response) time.sleep(time_to_sleep) self.elapsed_sleep += time_to_sleep - async def asleep(self, response: httpx.Response | Exception) -> None: + async def asleep(self, response: httpx.Response | Exception | float) -> None: """ Sleep between retry attempts asynchronously using the calculated duration. @@ -272,14 +271,12 @@ async def asleep(self, response: httpx.Response | Exception) -> None: of the time requested. If that is not present, it will use an exponential backoff. By default, the backoff factor is 0 and this method will return immediately. """ - time_to_sleep = self._calculate_sleep(response.headers if isinstance(response, httpx.Response) else {}) - logger.debug("asleep seconds=%s", time_to_sleep) + time_to_sleep = response if isinstance(response, (int, float)) else self.calculate_sleep(response) await asyncio.sleep(time_to_sleep) self.elapsed_sleep += time_to_sleep def increment(self) -> "Retry": """Return a new Retry instance with the attempt count incremented.""" - logger.debug("increment retry=%s new_attempts_made=%s", self, self.attempts_made + 1) return self.__class__( total=self.total, max_backoff_wait=self.max_backoff_wait, diff --git a/httpx_retries/transport.py b/httpx_retries/transport.py index 46e762e..fac2781 100644 --- a/httpx_retries/transport.py +++ b/httpx_retries/transport.py @@ -85,16 +85,12 @@ def handle_request(self, request: httpx.Request) -> httpx.Response: if self._sync_transport is None: raise RuntimeError("Synchronous request received but no sync transport available") - logger.debug("handle_request started request=%s", request) - if self.retry.is_retryable_method(request.method): send_method = partial(self._sync_transport.handle_request) response = self._retry_operation(request, send_method) else: response = self._sync_transport.handle_request(request) - logger.debug("handle_request finished request=%s response=%s", request, response) - return response async def handle_async_request(self, request: httpx.Request) -> httpx.Response: @@ -109,16 +105,12 @@ async def handle_async_request(self, request: httpx.Request) -> httpx.Response: if self._async_transport is None: raise RuntimeError("Async request received but no async transport available") - logger.debug("handle_async_request started request=%s", request) - if self.retry.is_retryable_method(request.method): send_method = partial(self._async_transport.handle_async_request) response = await self._retry_operation_async(request, send_method) else: response = await self._async_transport.handle_async_request(request) - logger.debug("handle_async_request finished request=%s response=%s", request, response) - return response def _retry_operation( @@ -134,9 +126,7 @@ def _retry_operation( if isinstance(response, httpx.Response): response.close() - logger.debug("_retry_operation retrying request=%s response=%s retry=%s", request, response, retry) - retry = retry.increment() - retry.sleep(response) + retry = self._retry_increment(request, response, retry) try: response = send_method(request) except Exception as e: @@ -149,6 +139,19 @@ def _retry_operation( if retry.is_exhausted() or not retry.is_retryable_status_code(response.status_code): return response + def _retry_increment(self, request: httpx.Request, response: httpx.Response | Exception, retry: Retry) -> Retry: + time_to_sleep = retry.calculate_sleep(response) + logger.debug( + "retry request=%s response=%s retry=%s sleep=%s", + request, + response, + retry, + time_to_sleep, + ) + retry = retry.increment() + retry.sleep(time_to_sleep) + return retry + async def _retry_operation_async( self, request: httpx.Request, @@ -162,11 +165,7 @@ async def _retry_operation_async( if isinstance(response, httpx.Response): await response.aclose() - logger.debug( - "_retry_operation_async retrying request=%s response=%s retry=%s", request, response, retry - ) - retry = retry.increment() - await retry.asleep(response) + retry = await self._retry_increment_async(request, response, retry) try: response = await send_method(request) except Exception as e: @@ -178,3 +177,18 @@ async def _retry_operation_async( if retry.is_exhausted() or not retry.is_retryable_status_code(response.status_code): return response + + async def _retry_increment_async( + self, request: httpx.Request, response: httpx.Response | Exception, retry: Retry + ) -> Retry: + time_to_sleep = retry.calculate_sleep(response) + logger.debug( + "retry request=%s response=%s retry=%s sleep=%s", + request, + response, + retry, + time_to_sleep, + ) + retry = retry.increment() + await retry.asleep(time_to_sleep) + return retry