From 3bcbe05ae95d4815efac49725ac484899375a8b5 Mon Sep 17 00:00:00 2001 From: Daniel Sissman Date: Fri, 24 Apr 2026 18:17:50 -0700 Subject: [PATCH] Improved Retry Handling Added support for improved HTTP request retry handling. Incremented the WorldCat Client library version number to `0.8.2`. --- .env.example | 3 + CHANGELOG.md | 4 ++ requirements.txt | 1 + source/worldcatclient/client/__init__.py | 6 +- .../worldcatclient/configuration/__init__.py | 13 +++++ .../data/configuration/required.spec | 9 +++ source/worldcatclient/session/__init__.py | 55 +++++++++++++++++++ source/worldcatclient/version.txt | 2 +- 8 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 source/worldcatclient/configuration/__init__.py create mode 100644 source/worldcatclient/data/configuration/required.spec diff --git a/.env.example b/.env.example index 4d9b835..bf2a7aa 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,6 @@ # WorldCat API Client ID & Secret WORLDCAT_API_CLIENT_ID= WORLDCAT_API_SECRET= + +# WorldCat API Client Timeout (seconds) +WORLDCAT_API_TIMEOUT=10 diff --git a/CHANGELOG.md b/CHANGELOG.md index e15754c..9fa05fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog All notable changes to this project will be documented in this file. +## [0.8.2] - 2026-04-24 +### Added +- Support for improved HTTP request retry handling. + ## [0.8.1] - 2026-04-22 ### Added - Support for fully parsing the record payloads returned from WorldCat API search calls. diff --git a/requirements.txt b/requirements.txt index df60974..9404993 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ # WorldCat Client: Package Dependencies classicist==1.0.5 +configures>=0.9.2 enumerific>=1.1.0 fluently==0.9.1 requests==2.32.* diff --git a/source/worldcatclient/client/__init__.py b/source/worldcatclient/client/__init__.py index ed10430..f9b57b8 100644 --- a/source/worldcatclient/client/__init__.py +++ b/source/worldcatclient/client/__init__.py @@ -1,3 +1,4 @@ +from worldcatclient.configuration import secrets from worldcatclient.logging import logger from worldcatclient.enumerations import ( WorldCatScope, @@ -50,7 +51,10 @@ def __init__( self._secret: str = secret if endpoint is None: - endpoint: str = WORLDCAT_API_ENDPOINT + endpoint: str = secrets.get( + "WORLDCAT_API_ENDPOINT", + default=WORLDCAT_API_ENDPOINT, + ) elif not isinstance(endpoint, str): raise TypeError("The 'endpoint' argument must have a string value!") elif not (endpoint.startswith("http://") or endpoint.startswith("https://")): diff --git a/source/worldcatclient/configuration/__init__.py b/source/worldcatclient/configuration/__init__.py new file mode 100644 index 0000000..a320e70 --- /dev/null +++ b/source/worldcatclient/configuration/__init__.py @@ -0,0 +1,13 @@ +from configures import Secrets + +import os + +secrets = Secrets( + specification=os.path.join( + os.path.dirname(__file__), + "..", + "data", + "configuration", + "required.spec", + ), +) diff --git a/source/worldcatclient/data/configuration/required.spec b/source/worldcatclient/data/configuration/required.spec new file mode 100644 index 0000000..f86eb41 --- /dev/null +++ b/source/worldcatclient/data/configuration/required.spec @@ -0,0 +1,9 @@ +# WorldCat Client: Required Runtime Configuration Specification +# This file lists the environment variables required by the application, and defines the +# acceptable format for each variables' value. The runtime environment must provide the +# environment variables listed below, and the value of each must conform to the regexes. + +# WorldCat API Client Configuration +WORLDCAT_API_ENDPOINT=(http(s)?://[a-z0-9\-]+(?:\.[a-z0-9\-]+){1,}(\:[0-9]{1,5})?(/[\w\-\_]+)*/?) +WORLDCAT_API_TOKEN=([0-9a-f]{32}) +WORLDCAT_API_TIMEOUT=([0-9]+(\.[0-9]+)?) diff --git a/source/worldcatclient/session/__init__.py b/source/worldcatclient/session/__init__.py index 7cdd474..4df1630 100644 --- a/source/worldcatclient/session/__init__.py +++ b/source/worldcatclient/session/__init__.py @@ -23,6 +23,8 @@ import requests import json +from urllib3.util import Retry + logger = logger.getChild(__name__) @@ -67,6 +69,16 @@ def __init__( authendpoint: str = None, scopes: list[WorldCatScope] | list[str] = None, retries: int = 3, + retry_statuses: tuple[int] | list[int] | set[int] = ( + 408, + 429, + 500, + 502, + 503, + 504, + 522, + ), + backoff_factor: float | int = 1.0, **kwargs, ): super().__init__(*args, **kwargs) @@ -128,6 +140,49 @@ def __init__( self._retries: int = retries + if isinstance(retry_statuses, (tuple, list, set)): + retry_statuses = list(retry_statuses) + + for retry_status in retry_statuses: + if not isinstance(retry_status, int): + raise TypeError( + "Each entry in the 'retry_statuses' argument must have an integer value!" + ) + elif not 400 <= retry_status <= 599: + raise ValueError( + "Each entry in the 'retry_statuses' argument must have an integer value between 400-599!" + ) + else: + raise TypeError( + "The 'retry_statuses' argument must have a tuple, list or set value of integers!" + ) + + if isinstance(backoff_factor, (float, int)): + backoff_factor = float(backoff_factor) + + if not 0.1 <= backoff_factor <= 10.0: + raise ValueError( + "The 'backoff_factor' argument must have a value between 0.1 – 10.0!" + ) + else: + raise TypeError( + "The 'backoff_factor' argument must have a float or integer value!" + ) + + # Create and configure a HTTP adapter with the configured retry strategy + adapter = requests.adapters.HTTPAdapter( + max_retries=Retry( + total=retries, # Total number of retries per request + status_forcelist=retry_statuses, # Statuses to retry + backoff_factor=backoff_factor, # Exponential backoff (e.g., 0s, 2s, 4s...) + allowed_methods=["HEAD", "GET"], # Methods to retry requests for + ), + ) + + # Mount the adapters into the current session + self.mount("http://", adapter) + self.mount("https://", adapter) + def __del__(self): logger.debug("%s.__del__()", self.__class__.__name__) diff --git a/source/worldcatclient/version.txt b/source/worldcatclient/version.txt index c18d72b..53a48a1 100644 --- a/source/worldcatclient/version.txt +++ b/source/worldcatclient/version.txt @@ -1 +1 @@ -0.8.1 \ No newline at end of file +0.8.2 \ No newline at end of file