From 29ef3bddbf4ce737ac3b946a2e2bc472d9849aeb Mon Sep 17 00:00:00 2001 From: HardMax71 Date: Thu, 10 Sep 2026 20:34:00 +0200 Subject: [PATCH 1/3] docs: describe what RetryHandlerOption.delay and max_delay actually do The option stores delay as max_delay and the handler adds it to every backoff; the caps are backoff_max and MAX_DELAY. The RetryHandler class docstring listed four constructor parameters that do not exist. --- .../options/retry_handler_option.py | 13 +++++- .../kiota_http/middleware/retry_handler.py | 45 +++++++------------ 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/packages/http/httpx/kiota_http/middleware/options/retry_handler_option.py b/packages/http/httpx/kiota_http/middleware/options/retry_handler_option.py index a2ed09ea..b9f794bf 100644 --- a/packages/http/httpx/kiota_http/middleware/options/retry_handler_option.py +++ b/packages/http/httpx/kiota_http/middleware/options/retry_handler_option.py @@ -2,7 +2,15 @@ class RetryHandlerOption(RequestOption): - """The retry request option class + """Options for the retry handler. + + Args: + delay (float): base delay in seconds that the handler adds to every computed + backoff, see ``RetryHandler.get_delay_time``. It is not a cap. Accepted range + 0 to ``MAX_DELAY``. Exposed as ``max_delay``. Defaults to ``DEFAULT_DELAY``. + max_retries (int): number of retries after the first attempt, 0 to + ``MAX_MAX_RETRIES``. Exposed as ``max_retry``. + should_retry (bool): whether the handler retries at all. """ # Default maxRetries value @@ -14,7 +22,8 @@ class RetryHandlerOption(RequestOption): # Default delay value in seconds DEFAULT_DELAY: float = 3.0 - # Default maximum delay value in seconds + # Largest accepted delay value in seconds. The handler also skips the retry when the + # computed delay reaches this value. MAX_DELAY: float = 180.0 # Default value for should retry diff --git a/packages/http/httpx/kiota_http/middleware/retry_handler.py b/packages/http/httpx/kiota_http/middleware/retry_handler.py index 2c06ee2b..f16a70c4 100644 --- a/packages/http/httpx/kiota_http/middleware/retry_handler.py +++ b/packages/http/httpx/kiota_http/middleware/retry_handler.py @@ -16,32 +16,16 @@ class RetryHandler(BaseMiddleware): - """ - Middleware that allows us to specify the retry policy for all requests - Retry configuration. - :param int max_retries: - Maximum number of retries to allow. Takes precedence over other counts. - Set to ``0`` to fail on the first retry. - :param iterable retry_on_status_codes: - A set of integer HTTP status codes that we should force a retry on. - A retry is initiated if the request method is in ``allowed_methods`` - and the response status code is in ``RETRY STATUS CODES``. - :param float retry_backoff_factor: - A backoff factor to apply between attempts after the second try - (most errors are resolved immediately by a second try without a - delay). - The request will sleep for:: - {backoff factor} * (2 ** ({retry number} - 1)) - seconds. If the backoff_factor is 0.1, then :func:`.sleep` will sleep - for [0.0s, 0.2s, 0.4s, ...] between retries. It will never be longer - than :attr:`RetryHandler.MAXIMUM_BACKOFF`. - By default, backoff is set to 0.5. - :param int retry_time_limit: - The maximum cumulative time in seconds that total retries should take. - The cumulative retry time and retry-after value for each request retry - will be evaluated against this value; if the cumulative retry time plus - the retry-after value is greater than the retry_time_limit, the failed - response will be immediately returned, else the request retry continues. + """Retries a request on 429, 503 and 504 (``DEFAULT_RETRY_STATUS_CODES``) for the + methods in ``DEFAULT_ALLOWED_METHODS``, using the ``RetryHandlerOption`` of the + request or the one given to the constructor: ``max_retry`` (retries after the first + attempt), ``max_delay`` (a base delay, see ``get_delay_time``) and ``should_retry``. + + The delay before a retry comes from ``get_delay_time``: a ``Retry-After`` response + header is used as is; otherwise ``backoff_factor * 2 ** (retry_count - 1)``, plus up + to one second of jitter, plus the option's ``max_delay``, capped at + ``MAXIMUM_BACKOFF`` (120 seconds). No retry happens when the delay reaches + ``RetryHandlerOption.MAX_DELAY`` (180 seconds). """ DEFAULT_BACKOFF_FACTOR: float = 0.5 @@ -170,10 +154,11 @@ def check_retry_valid(self, retry_count, options): return False def get_delay_time(self, retry_count, response=None, delay=RetryHandlerOption.DEFAULT_DELAY): - """ - Get the time in seconds to delay between retry attempts. - Respects a retry-after header in the response if provided - If no retry-after response header, it defaults to exponential backoff + """Get the time in seconds to delay before the next attempt. + + A ``Retry-After`` response header is returned as is, without a cap. Otherwise the + exponential backoff for ``retry_count`` plus ``delay`` (the option's ``max_delay``, + a base delay added to every attempt) is returned, capped at ``backoff_max``. """ retry_after = self._get_retry_after(response) if retry_after: From 5e79aa847550ee6eed94b5b5b8c67b45be452c88 Mon Sep 17 00:00:00 2001 From: HardMax71 Date: Thu, 10 Sep 2026 20:44:47 +0200 Subject: [PATCH 2/3] docs: say that a zero Retry-After falls through to the backoff, and name the jitter --- .../middleware/options/retry_handler_option.py | 2 +- .../httpx/kiota_http/middleware/retry_handler.py | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/http/httpx/kiota_http/middleware/options/retry_handler_option.py b/packages/http/httpx/kiota_http/middleware/options/retry_handler_option.py index b9f794bf..80cf5f39 100644 --- a/packages/http/httpx/kiota_http/middleware/options/retry_handler_option.py +++ b/packages/http/httpx/kiota_http/middleware/options/retry_handler_option.py @@ -23,7 +23,7 @@ class RetryHandlerOption(RequestOption): DEFAULT_DELAY: float = 3.0 # Largest accepted delay value in seconds. The handler also skips the retry when the - # computed delay reaches this value. + # computed delay is this value or more. MAX_DELAY: float = 180.0 # Default value for should retry diff --git a/packages/http/httpx/kiota_http/middleware/retry_handler.py b/packages/http/httpx/kiota_http/middleware/retry_handler.py index f16a70c4..64026c1a 100644 --- a/packages/http/httpx/kiota_http/middleware/retry_handler.py +++ b/packages/http/httpx/kiota_http/middleware/retry_handler.py @@ -22,10 +22,10 @@ class RetryHandler(BaseMiddleware): attempt), ``max_delay`` (a base delay, see ``get_delay_time``) and ``should_retry``. The delay before a retry comes from ``get_delay_time``: a ``Retry-After`` response - header is used as is; otherwise ``backoff_factor * 2 ** (retry_count - 1)``, plus up - to one second of jitter, plus the option's ``max_delay``, capped at - ``MAXIMUM_BACKOFF`` (120 seconds). No retry happens when the delay reaches - ``RetryHandlerOption.MAX_DELAY`` (180 seconds). + header above zero is used as parsed, without a cap; otherwise + ``backoff_factor * 2 ** (retry_count - 1)``, plus up to one second of jitter, plus + the option's ``max_delay``, capped at ``MAXIMUM_BACKOFF`` (120 seconds). No retry + happens when the delay is ``RetryHandlerOption.MAX_DELAY`` (180 seconds) or more. """ DEFAULT_BACKOFF_FACTOR: float = 0.5 @@ -156,9 +156,11 @@ def check_retry_valid(self, retry_count, options): def get_delay_time(self, retry_count, response=None, delay=RetryHandlerOption.DEFAULT_DELAY): """Get the time in seconds to delay before the next attempt. - A ``Retry-After`` response header is returned as is, without a cap. Otherwise the - exponential backoff for ``retry_count`` plus ``delay`` (the option's ``max_delay``, - a base delay added to every attempt) is returned, capped at ``backoff_max``. + A ``Retry-After`` response header that parses to more than zero seconds is + returned without a cap; zero or a missing header falls through to the backoff. + The backoff is ``backoff_factor * 2 ** (retry_count - 1)``, plus up to one second + of jitter, plus ``delay`` (the option's ``max_delay``, a base delay added to every + attempt), capped at ``backoff_max``. """ retry_after = self._get_retry_after(response) if retry_after: From 50c8e23d6159a765337b9925834028e55dedab07 Mon Sep 17 00:00:00 2001 From: HardMax71 Date: Thu, 10 Sep 2026 20:49:13 +0200 Subject: [PATCH 3/3] docs: name backoff_max as the cap, MAXIMUM_BACKOFF is only its default --- packages/http/httpx/kiota_http/middleware/retry_handler.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/http/httpx/kiota_http/middleware/retry_handler.py b/packages/http/httpx/kiota_http/middleware/retry_handler.py index 64026c1a..37ef9b12 100644 --- a/packages/http/httpx/kiota_http/middleware/retry_handler.py +++ b/packages/http/httpx/kiota_http/middleware/retry_handler.py @@ -24,8 +24,9 @@ class RetryHandler(BaseMiddleware): The delay before a retry comes from ``get_delay_time``: a ``Retry-After`` response header above zero is used as parsed, without a cap; otherwise ``backoff_factor * 2 ** (retry_count - 1)``, plus up to one second of jitter, plus - the option's ``max_delay``, capped at ``MAXIMUM_BACKOFF`` (120 seconds). No retry - happens when the delay is ``RetryHandlerOption.MAX_DELAY`` (180 seconds) or more. + the option's ``max_delay``, capped at ``backoff_max`` (``MAXIMUM_BACKOFF``, 120 + seconds, by default). No retry happens when the delay is + ``RetryHandlerOption.MAX_DELAY`` (180 seconds) or more. """ DEFAULT_BACKOFF_FACTOR: float = 0.5