Main classes and functions¶
aresilient ¶
aresilient - Resilient HTTP request library with automatic retry logic.
This package provides resilient HTTP request functionality with automatic retry logic and backoff strategies. Built on top of the modern httpx library, it simplifies handling transient failures in HTTP communications, making your applications more robust and fault-tolerant.
Key Features
- Automatic retry logic for transient HTTP errors (429, 500, 502, 503, 504)
- Multiple backoff strategies: Exponential, Linear, Fibonacci, Constant, and custom
- Optional jitter to prevent thundering herd problems
- Retry-After header support (both integer seconds and HTTP-date formats)
- Complete HTTP method support (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)
- Full async support for high-performance applications
- Configurable timeout, retry attempts, backoff factors, and jitter
- Enhanced error handling with detailed exception information
- Callback/Event system for observability (logging, metrics, alerting)
- Context manager API for managing request sessions
Example
>>> from aresilient import get
>>> from aresilient.backoff import LinearBackoff
>>> from aresilient.core.config import ClientConfig
>>> # Use default exponential backoff
>>> response = get("https://api.example.com/data") # doctest: +SKIP
>>> # Use linear backoff strategy
>>> response = get(
... "https://api.example.com/data",
... config=ClientConfig(backoff_strategy=LinearBackoff(base_delay=1.0)),
... ) # doctest: +SKIP
>>> # Use context manager for multiple requests
>>> from aresilient import ResilientClient
>>> with ResilientClient(config=ClientConfig(max_retries=5)) as client: # doctest: +SKIP
... response1 = client.get("https://api.example.com/data1")
... response2 = client.post("https://api.example.com/data2", json={"key": "value"})
...
aresilient.AsyncResilientClient ¶
Asynchronous context manager for resilient HTTP requests.
This class provides an async context manager interface for making multiple HTTP requests with shared retry configuration. The client automatically manages the lifecycle of the underlying httpx.AsyncClient and applies consistent retry logic across all requests.
Two usage patterns are supported:
Scenario 1 - Two context managers (external lifecycle management):
The httpx.AsyncClient is created and managed by an outer async with
block, and passed into AsyncResilientClient. AsyncResilientClient
does not close the underlying client when it exits, leaving full control
to the caller. Use this pattern when you need to share a single
httpx.AsyncClient across multiple AsyncResilientClient instances,
or when you need to configure the client with headers, auth, proxies, etc.
and want explicit lifecycle control.
.. code-block:: python
import httpx
from aresilient import AsyncResilientClient
from aresilient.core.config import ClientConfig
async with httpx.AsyncClient(headers={"Authorization": "Bearer token"}) as http_client:
async with AsyncResilientClient(
client=http_client, config=ClientConfig(max_retries=5)
) as client:
response = await client.get("https://api.example.com/data1")
# http_client is closed here by the outer ``async with`` block
Scenario 2 - Single context manager (AsyncResilientClient manages lifecycle):
An httpx.AsyncClient instance is passed inline (or omitted, in which
case a default client is created). AsyncResilientClient enters and
closes the underlying client automatically when the async with block
exits. Use this pattern for the simplest usage when you don't need to
reuse the httpx.AsyncClient outside the async with block.
.. code-block:: python
import httpx
from aresilient import AsyncResilientClient
from aresilient.core.config import ClientConfig
async with AsyncResilientClient(
client=httpx.AsyncClient(), config=ClientConfig(max_retries=5)
) as client:
response = await client.get("https://api.example.com/data1")
# httpx.AsyncClient is closed here by AsyncResilientClient
# Equivalent shorthand (AsyncResilientClient creates a default client):
async with AsyncResilientClient(config=ClientConfig(max_retries=5)) as client:
response = await client.get("https://api.example.com/data1")
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
ClientConfig | None
|
Optional ClientConfig instance for retry configuration.
If |
None
|
client
|
AsyncClient | None
|
Optional httpx.AsyncClient instance to use for requests.
If |
None
|
Example
>>> import asyncio
>>> from aresilient import AsyncResilientClient
>>> from aresilient.core.config import ClientConfig
>>> async def main(): # doctest: +SKIP
... async with AsyncResilientClient(config=ClientConfig(max_retries=5)) as client:
... response1 = await client.get("https://api.example.com/data1")
... response2 = await client.post(
... "https://api.example.com/data2", json={"key": "value"}
... )
...
>>> asyncio.run(main()) # doctest: +SKIP
# Client automatically closed after context exits
Note
All HTTP method calls (get, post, put, delete, patch, head, options, request)
use the resilience parameters defined in the config passed to the constructor.
aresilient.AsyncResilientClient.__aenter__
async
¶
__aenter__() -> Self
Enter the async context manager.
If the underlying httpx.AsyncClient is not yet open, it is
entered and its lifecycle is managed by this context manager (closed
on exit). If the client is already open (e.g. managed by an outer
async with block), AsyncResilientClient uses it without
closing it on exit.
Returns:
| Type | Description |
|---|---|
Self
|
The AsyncResilientClient instance for making requests. |
aresilient.AsyncResilientClient.__aexit__
async
¶
__aexit__(
exc_type: type[BaseException] | None,
exc_val: BaseException | None,
exc_tb: TracebackType | None,
) -> None
Exit the async context manager and close the underlying httpx client if this context manager opened it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exc_type
|
type[BaseException] | None
|
Exception type if an exception occurred. |
required |
exc_val
|
BaseException | None
|
Exception value if an exception occurred. |
required |
exc_tb
|
TracebackType | None
|
Exception traceback if an exception occurred. |
required |
aresilient.AsyncResilientClient.delete
async
¶
delete(url: str, **kwargs: Any) -> Response
Send an HTTP DELETE request with automatic retry logic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The URL to send the DELETE request to. |
required |
**kwargs
|
Any
|
Additional keyword arguments (see request() method). |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
An httpx.Response object containing the server's HTTP response. |
Example
>>> import asyncio
>>> from aresilient import AsyncResilientClient
>>> async def main(): # doctest: +SKIP
... async with AsyncResilientClient() as client:
... response = await client.delete("https://api.example.com/data")
...
>>> asyncio.run(main()) # doctest: +SKIP
aresilient.AsyncResilientClient.get
async
¶
get(url: str, **kwargs: Any) -> Response
Send an HTTP GET request with automatic retry logic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The URL to send the GET request to. |
required |
**kwargs
|
Any
|
Additional keyword arguments (see request() method). |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
An httpx.Response object containing the server's HTTP response. |
Example
>>> import asyncio
>>> from aresilient import AsyncResilientClient
>>> async def main(): # doctest: +SKIP
... async with AsyncResilientClient() as client:
... response = await client.get("https://api.example.com/data")
...
>>> asyncio.run(main()) # doctest: +SKIP
aresilient.AsyncResilientClient.head
async
¶
head(url: str, **kwargs: Any) -> Response
Send an HTTP HEAD request with automatic retry logic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The URL to send the HEAD request to. |
required |
**kwargs
|
Any
|
Additional keyword arguments (see request() method). |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
An httpx.Response object containing the server's HTTP response. |
Example
>>> import asyncio
>>> from aresilient import AsyncResilientClient
>>> async def main(): # doctest: +SKIP
... async with AsyncResilientClient() as client:
... response = await client.head("https://api.example.com/data")
...
>>> asyncio.run(main()) # doctest: +SKIP
aresilient.AsyncResilientClient.options
async
¶
options(url: str, **kwargs: Any) -> Response
Send an HTTP OPTIONS request with automatic retry logic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The URL to send the OPTIONS request to. |
required |
**kwargs
|
Any
|
Additional keyword arguments (see request() method). |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
An httpx.Response object containing the server's HTTP response. |
Example
>>> import asyncio
>>> from aresilient import AsyncResilientClient
>>> async def main(): # doctest: +SKIP
... async with AsyncResilientClient() as client:
... response = await client.options("https://api.example.com/data")
...
>>> asyncio.run(main()) # doctest: +SKIP
aresilient.AsyncResilientClient.patch
async
¶
patch(url: str, **kwargs: Any) -> Response
Send an HTTP PATCH request with automatic retry logic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The URL to send the PATCH request to. |
required |
**kwargs
|
Any
|
Additional keyword arguments (see request() method). |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
An httpx.Response object containing the server's HTTP response. |
Example
>>> import asyncio
>>> from aresilient import AsyncResilientClient
>>> async def main(): # doctest: +SKIP
... async with AsyncResilientClient() as client:
... response = await client.patch(
... "https://api.example.com/data", json={"key": "value"}
... )
...
>>> asyncio.run(main()) # doctest: +SKIP
aresilient.AsyncResilientClient.post
async
¶
post(url: str, **kwargs: Any) -> Response
Send an HTTP POST request with automatic retry logic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The URL to send the POST request to. |
required |
**kwargs
|
Any
|
Additional keyword arguments (see request() method). |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
An httpx.Response object containing the server's HTTP response. |
Example
>>> import asyncio
>>> from aresilient import AsyncResilientClient
>>> async def main(): # doctest: +SKIP
... async with AsyncResilientClient() as client:
... response = await client.post(
... "https://api.example.com/data", json={"key": "value"}
... )
...
>>> asyncio.run(main()) # doctest: +SKIP
aresilient.AsyncResilientClient.put
async
¶
put(url: str, **kwargs: Any) -> Response
Send an HTTP PUT request with automatic retry logic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The URL to send the PUT request to. |
required |
**kwargs
|
Any
|
Additional keyword arguments (see request() method). |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
An httpx.Response object containing the server's HTTP response. |
Example
>>> import asyncio
>>> from aresilient import AsyncResilientClient
>>> async def main(): # doctest: +SKIP
... async with AsyncResilientClient() as client:
... response = await client.put(
... "https://api.example.com/data", json={"key": "value"}
... )
...
>>> asyncio.run(main()) # doctest: +SKIP
aresilient.AsyncResilientClient.request
async
¶
request(method: str, url: str, **kwargs: Any) -> Response
Send an HTTP request with automatic retry logic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
str
|
HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, etc.). |
required |
url
|
str
|
The URL to send the request to. |
required |
**kwargs
|
Any
|
Additional keyword arguments passed to httpx.AsyncClient.request(). |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
An httpx.Response object containing the server's HTTP response. |
Raises:
| Type | Description |
|---|---|
HttpRequestError
|
If the request fails after all retries. |
Example
>>> import asyncio
>>> from aresilient import AsyncResilientClient
>>> async def main(): # doctest: +SKIP
... async with AsyncResilientClient() as client:
... response = await client.request("GET", "https://api.example.com/data")
...
>>> asyncio.run(main()) # doctest: +SKIP
aresilient.HttpRequestError ¶
Bases: RuntimeError
Exception raised when an HTTP request fails.
This exception captures comprehensive details about failed HTTP requests, including the request method, URL, status code, and the full response object when available. It supports exception chaining to preserve the original cause of the error.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
str
|
The HTTP method used for the request (e.g., 'GET', 'POST'). |
required |
url
|
str
|
The target URL that was requested. |
required |
message
|
str
|
A descriptive error message explaining the failure. |
required |
status_code
|
int | None
|
The HTTP status code returned by the server, if the
request reached the server. Defaults to |
None
|
response
|
Response | None
|
The complete httpx.Response object containing headers,
body, and other response details. Defaults to |
None
|
cause
|
BaseException | None
|
The original exception that caused this error, used for
exception chaining. Defaults to |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
method |
The HTTP method used for the request. |
|
url |
The target URL that was requested. |
|
status_code |
The HTTP status code, if available. |
|
response |
The full response object, if available. |
Examples:
Raising an error for a failed GET request:
>>> from aresilient import HttpRequestError
>>> raise HttpRequestError(
... method="GET",
... url="https://api.example.com/data",
... message="Request failed with status 404",
... status_code=404,
... ) # doctest: +SKIP
aresilient.ResilientClient ¶
Synchronous context manager for resilient HTTP requests.
This class provides a context manager interface for making multiple HTTP requests with shared retry configuration. The client automatically manages the lifecycle of the underlying httpx.Client and applies consistent retry logic across all requests.
Two usage patterns are supported:
Scenario 1 - Two context managers (external lifecycle management):
The httpx.Client is created and managed by an outer with block,
and passed into ResilientClient. ResilientClient does not close
the underlying client when it exits, leaving full control to the caller.
Use this pattern when you need to share a single httpx.Client across
multiple ResilientClient instances, or when you need to configure the
httpx.Client with headers, auth, proxies, etc. and want explicit
lifecycle control.
.. code-block:: python
import httpx
from aresilient import ResilientClient
from aresilient.core.config import ClientConfig
with httpx.Client(headers={"Authorization": "Bearer token"}) as http_client:
with ResilientClient(
client=http_client, config=ClientConfig(max_retries=5)
) as client:
response = client.get("https://api.example.com/data1")
# http_client is closed here by the outer ``with`` block
Scenario 2 - Single context manager (ResilientClient manages lifecycle):
An httpx.Client instance is passed inline (or omitted, in which case a
default client is created). ResilientClient enters and closes the
underlying client automatically when the with block exits.
Use this pattern for the simplest usage when you don't need to reuse the
httpx.Client outside the with block.
.. code-block:: python
import httpx
from aresilient import ResilientClient
from aresilient.core.config import ClientConfig
with ResilientClient(
client=httpx.Client(), config=ClientConfig(max_retries=5)
) as client:
response = client.get("https://api.example.com/data1")
# httpx.Client is closed here by ResilientClient
# Equivalent shorthand (ResilientClient creates a default client):
with ResilientClient(config=ClientConfig(max_retries=5)) as client:
response = client.get("https://api.example.com/data1")
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
ClientConfig | None
|
Optional ClientConfig instance for retry configuration.
If |
None
|
client
|
Client | None
|
Optional httpx.Client instance to use for requests.
If |
None
|
Example
>>> from aresilient import ResilientClient
>>> from aresilient.core.config import ClientConfig
>>> with ResilientClient(config=ClientConfig(max_retries=5)) as client: # doctest: +SKIP
... response1 = client.get("https://api.example.com/data1")
... response2 = client.post("https://api.example.com/data2", json={"key": "value"})
...
# Client automatically closed after context exits
Note
All HTTP method calls (get, post, put, delete, patch, head, options, request)
use the resilience parameters defined in the config passed to the constructor.
aresilient.ResilientClient.__enter__ ¶
__enter__() -> Self
Enter the context manager.
If the underlying httpx.Client is not yet open, it is entered and
its lifecycle is managed by this context manager (closed on exit).
If the client is already open (e.g. managed by an outer with
block), ResilientClient uses it without closing it on exit.
Returns:
| Type | Description |
|---|---|
Self
|
The ResilientClient instance for making requests. |
aresilient.ResilientClient.__exit__ ¶
__exit__(
exc_type: type[BaseException] | None,
exc_val: BaseException | None,
exc_tb: TracebackType | None,
) -> None
Exit the context manager and close the underlying httpx client if this context manager opened it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
exc_type
|
type[BaseException] | None
|
Exception type if an exception occurred. |
required |
exc_val
|
BaseException | None
|
Exception value if an exception occurred. |
required |
exc_tb
|
TracebackType | None
|
Exception traceback if an exception occurred. |
required |
aresilient.ResilientClient.delete ¶
delete(url: str, **kwargs: Any) -> Response
Send an HTTP DELETE request with automatic retry logic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The URL to send the DELETE request to. |
required |
**kwargs
|
Any
|
Additional keyword arguments (see request() method). |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
An httpx.Response object containing the server's HTTP response. |
Example
>>> from aresilient import ResilientClient
>>> with ResilientClient() as client: # doctest: +SKIP
... response = client.delete("https://api.example.com/data")
...
aresilient.ResilientClient.get ¶
get(url: str, **kwargs: Any) -> Response
Send an HTTP GET request with automatic retry logic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The URL to send the GET request to. |
required |
**kwargs
|
Any
|
Additional keyword arguments (see request() method). |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
An httpx.Response object containing the server's HTTP response. |
Example
>>> from aresilient import ResilientClient
>>> with ResilientClient() as client: # doctest: +SKIP
... response = client.get("https://api.example.com/data")
...
aresilient.ResilientClient.head ¶
head(url: str, **kwargs: Any) -> Response
Send an HTTP HEAD request with automatic retry logic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The URL to send the HEAD request to. |
required |
**kwargs
|
Any
|
Additional keyword arguments (see request() method). |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
An httpx.Response object containing the server's HTTP response. |
Example
>>> from aresilient import ResilientClient
>>> with ResilientClient() as client: # doctest: +SKIP
... response = client.head("https://api.example.com/data")
...
aresilient.ResilientClient.options ¶
options(url: str, **kwargs: Any) -> Response
Send an HTTP OPTIONS request with automatic retry logic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The URL to send the OPTIONS request to. |
required |
**kwargs
|
Any
|
Additional keyword arguments (see request() method). |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
An httpx.Response object containing the server's HTTP response. |
Example
>>> from aresilient import ResilientClient
>>> with ResilientClient() as client: # doctest: +SKIP
... response = client.options("https://api.example.com/data")
...
aresilient.ResilientClient.patch ¶
patch(url: str, **kwargs: Any) -> Response
Send an HTTP PATCH request with automatic retry logic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The URL to send the PATCH request to. |
required |
**kwargs
|
Any
|
Additional keyword arguments (see request() method). |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
An httpx.Response object containing the server's HTTP response. |
Example
>>> from aresilient import ResilientClient
>>> with ResilientClient() as client: # doctest: +SKIP
... response = client.patch("https://api.example.com/data", json={"key": "value"})
...
aresilient.ResilientClient.post ¶
post(url: str, **kwargs: Any) -> Response
Send an HTTP POST request with automatic retry logic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The URL to send the POST request to. |
required |
**kwargs
|
Any
|
Additional keyword arguments (see request() method). |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
An httpx.Response object containing the server's HTTP response. |
Example
>>> from aresilient import ResilientClient
>>> with ResilientClient() as client: # doctest: +SKIP
... response = client.post("https://api.example.com/data", json={"key": "value"})
...
aresilient.ResilientClient.put ¶
put(url: str, **kwargs: Any) -> Response
Send an HTTP PUT request with automatic retry logic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
url
|
str
|
The URL to send the PUT request to. |
required |
**kwargs
|
Any
|
Additional keyword arguments (see request() method). |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
An httpx.Response object containing the server's HTTP response. |
Example
>>> from aresilient import ResilientClient
>>> with ResilientClient() as client: # doctest: +SKIP
... response = client.put("https://api.example.com/data", json={"key": "value"})
...
aresilient.ResilientClient.request ¶
request(method: str, url: str, **kwargs: Any) -> Response
Send an HTTP request with automatic retry logic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
method
|
str
|
HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, etc.). |
required |
url
|
str
|
The URL to send the request to. |
required |
**kwargs
|
Any
|
Additional keyword arguments passed to httpx.Client.request(). |
{}
|
Returns:
| Type | Description |
|---|---|
Response
|
An httpx.Response object containing the server's HTTP response. |
Raises:
| Type | Description |
|---|---|
HttpRequestError
|
If the request fails after all retries. |
Example
>>> from aresilient import ResilientClient
>>> with ResilientClient() as client: # doctest: +SKIP
... response = client.request("GET", "https://api.example.com/data")
...