Skip to content

Commit 5b0789f

Browse files
austindjschefflkaxil
authored
Release v1.4.0 (#30)
* Add Python 3.13 support, drop 3.8 (#29) * Fix optional dependencies to avoid forcing installation of both httpx and requests (#26) - Move httpx and requests from core dependencies to optional extras - Add 'all' extra for users who want both libraries - Update README to reflect new installation instructions - Update noxfile to install both extras for testing Previously, both httpx and requests were always installed even when users only wanted one library. Now users can install only what they need: - retryhttp[all] for both libraries - retryhttp[httpx] for httpx only - retryhttp[requests] for requests only * Add support for aiohttp (#28) Co-authored-by: Austin de Coup-Crank <austindcc@gmail.com> * Release v1.4.0 --------- Co-authored-by: Jens Scheffler <95105677+jscheffl@users.noreply.github.com> Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
1 parent 8e8ef50 commit 5b0789f

File tree

11 files changed

+78
-34
lines changed

11 files changed

+78
-34
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- name: Setup Nox
1818
uses: wntrblm/nox@2024.04.15
1919
with:
20-
python-versions: "3.8, 3.9, 3.10, 3.11, 3.12"
20+
python-versions: "3.9, 3.10, 3.11, 3.12, 3.13"
2121

2222
- name: Run Nox
2323
run: nox -t test

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,22 @@ Several HTTP errors are often transient, and might succeed if retried:
1919

2020
This project aims to simplify retrying these, by extending [`tenacity`](https://tenacity.readthedocs.io/) with custom retry and wait strategies, as well as a custom decorator. Defaults are sensible for most use cases, but are fully customizable.
2121

22-
Supports both [`requests`](https://docs.python-requests.org/en/latest/index.html) and [`httpx`](https://python-httpx.org/) natively, but could be customized to use with any library that raises exceptions for the conditions listed above.
22+
Supports both [`requests`](https://docs.python-requests.org/en/latest/index.html), [`httpx`](https://python-httpx.org/) and [`aiohttp`](https://docs.aiohttp.org/) natively, but could be customized to use with any library that raises exceptions for the conditions listed above.
2323

2424
## Install
2525

2626
Install from PyPI:
2727

2828
```sh
29-
pip install retryhttp # Supports both HTTPX and requests
29+
pip install retryhttp # Supports HTTPX, requests and aiohttp
3030
```
3131

3232
You can also install support for only HTTPX or requests, if you would rather not install unnecessary dependencies:
3333

3434
```sh
3535
pip install retryhttp[httpx] # Supports only HTTPX
3636
pip install retryhttp[requests] # Supports only requests
37+
pip install retryhttp[aiohttp] # Supports only aiohttp
3738
```
3839

3940
Or, install the latest development snapshot from git:

docs/changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## v1.4.0
4+
5+
* Added support for `aiohttp` ([#28])(https://github.com/austind/retryhttp/pull/28)
6+
* Dropped support for Python 3.8, added support for Python 3.13 ([#29](https://github.com/austind/retryhttp/pull/29))
7+
* Bugfix: Prevent installing both `requests` and `httpx` unless specified ([#26](https://github.com/austind/retryhttp/pull/26))
8+
39
## v1.3.3
410

511
* Bugfix: Pass error types to wait strategy in retry decorator ([#25](https://github.com/austind/retryhttp/pull/25))

docs/guide.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## Overview
22

3-
`retryhttp` makes it easy to retry potentially transient HTTP errors when using `httpx` or `requests`.
3+
`retryhttp` makes it easy to retry potentially transient HTTP errors when using `httpx`, `requests` or `aiohttp`.
44

55
!!! note
66
Errors that you can safely retry vary from service to service.
@@ -83,4 +83,4 @@ Under the hood, RetryHTTP is a convenience layer on top of the excellent retry l
8383

8484
`tenacity` works by adding a decorator to functions that might fail. This decorator is configured with retry, wait, and stop strategies that configure what conditions to retry, how long to wait between retries, and when to stop retrying, respectively. Failures could be a raised exception, or a configurable return value. See [`tenacity` documentation](https://tenacity.readthedocs.io/en/latest/index.html) for details.
8585

86-
`retryhttp` provides new retry and stop strategies for potentially transient error conditions raised by `httpx` and `requests`. To make things as convenient as possible, `retryhttp` also provides a [new decorator][retryhttp.retry] that wraps [`tenacity.retry`](https://tenacity.readthedocs.io/en/latest/api.html#tenacity.retry) with sensible defaults, which are all customizable.
86+
`retryhttp` provides new retry and stop strategies for potentially transient error conditions raised by `httpx`, `requests` and `aiohttp`. To make things as convenient as possible, `retryhttp` also provides a [new decorator][retryhttp.retry] that wraps [`tenacity.retry`](https://tenacity.readthedocs.io/en/latest/api.html#tenacity.retry) with sensible defaults, which are all customizable.

docs/index.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ Several HTTP errors are often transient, and might succeed if retried:
1717

1818
This project aims to simplify retrying these, by extending [`tenacity`](https://tenacity.readthedocs.io/) with custom retry and wait strategies, as well as a custom decorator. Defaults are sensible for most use cases, but are fully customizable.
1919

20-
Supports exceptions raised by both [`requests`](https://docs.python-requests.org/en/latest/index.html) and [`httpx`](https://python-httpx.org/).
20+
Supports exceptions raised by both [`requests`](https://docs.python-requests.org/en/latest/index.html), [`httpx`](https://python-httpx.org/) and [`aiohttp`](https://docs.aiohttp.org/).
2121

2222
## Install
2323

2424
Install from PyPI:
2525

2626
```sh
27-
# Supports both HTTPX and requests
27+
# Supports HTTPX, requests and aiohttp
2828
pip install retryhttp
2929
```
3030

@@ -33,6 +33,7 @@ You can also install support for only HTTPX or requests:
3333
```sh
3434
pip install retryhttp[httpx] # Supports only HTTPX
3535
pip install retryhttp[requests] # Supports only requests
36+
pip install retryhttp[aiohttp] # Supports only aiohttp
3637
```
3738

3839
Or, install the latest development snapshot from git:

noxfile.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import nox
22

33

4-
@nox.session(python=["3.8", "3.9", "3.10", "3.11", "3.12"], tags=["test"])
4+
@nox.session(python=["3.9", "3.10", "3.11", "3.12", "3.13"], tags=["test"])
55
def test(session: nox.Session) -> None:
6-
session.install(".", ".[dev]")
6+
session.install(".[all,dev]")
77
session.run("pytest", "-n", "4")

pyproject.toml

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11

22
[project]
33
name = "retryhttp"
4-
version = "1.3.3"
4+
version = "1.4.0"
55
description = "Retry potentially transient HTTP errors in Python."
66
license = {file = "LICENSE"}
77
readme = "README.md"
8-
requires-python = ">=3.8"
8+
requires-python = ">=3.9"
99
authors = [
1010
{name = "Austin de Coup-Crank", email = "austindcc@gmail.com"},
1111
]
@@ -19,31 +19,35 @@ classifiers = [
1919
"Programming Language :: Python",
2020
"Programming Language :: Python :: 3",
2121
"Programming Language :: Python :: 3 :: Only",
22-
"Programming Language :: Python :: 3.8",
2322
"Programming Language :: Python :: 3.9",
2423
"Programming Language :: Python :: 3.10",
2524
"Programming Language :: Python :: 3.11",
2625
"Programming Language :: Python :: 3.12",
26+
"Programming Language :: Python :: 3.13",
2727
"Topic :: Internet :: WWW/HTTP",
2828
"Topic :: Utilities",
2929
]
3030
dependencies = [
31-
"httpx",
3231
"pydantic",
33-
"requests",
34-
"types-requests",
3532
"tenacity"
3633
]
3734

3835

3936
[project.optional-dependencies]
4037
httpx = [
4138
"httpx",
42-
"tenacity",
4339
]
4440
requests = [
4541
"requests",
4642
"types-requests",
43+
]
44+
all = [
45+
"httpx",
46+
"requests",
47+
"types-requests",
48+
]
49+
aiohttp = [
50+
"aiohttp",
4751
"tenacity",
4852
]
4953
dev = [

requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
httpx
22
tenacity
33
pydantic
4+
aiohttp
5+
requests
6+
types-requests

retryhttp/_retry.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,11 @@ def retry(
8787
- `httpx.WriteError`
8888
- `requests.ConnectionError`
8989
- `requests.exceptions.ChunkedEncodingError`
90+
- `aiohttp.ClientConnectionError`
9091
- Timeouts:
9192
- `httpx.TimeoutException`
9293
- `requests.Timeout`
94+
- `aiohttp.ServerTimeoutError`
9395
9496
Args:
9597
max_attempt_number: Total times to attempt a request. Includes the first attempt
@@ -112,11 +114,13 @@ def retry(
112114
- `httpx.WriteError`
113115
- `requests.ConnectError`
114116
- `requests.exceptions.ChunkedEncodingError`
117+
- `aiohttp.ClientConnectionError`
115118
timeouts: One or more exceptions that will trigger `wait_timeouts` if
116119
`retry_timeouts` is `True`. Defaults to:
117120
118121
- `httpx.TimeoutException`
119122
- `requests.Timeout`
123+
- `aiohttp.ServerTimeoutError`
120124
121125
Returns:
122126
Decorated function.
@@ -181,6 +185,7 @@ class retry_if_network_error(retry_if_exception_type):
181185
- `httpx.WriteError`
182186
- `requests.ConnectionError`
183187
- `requests.exceptions.ChunkedEncodingError`
188+
- `aiohttp.ClientConnectionError`
184189
185190
"""
186191

@@ -237,6 +242,8 @@ class retry_if_timeout(retry_if_exception_type):
237242
- `httpx.ReadTimeout`
238243
- `httpx.WriteTimeout`
239244
- `requests.Timeout`
245+
- `aiohttp.ClientConnectionError`
246+
240247
"""
241248

242249
def __init__(

retryhttp/_utils.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
_HTTPX_INSTALLED = False
88
_REQUESTS_INSTALLED = False
9+
_AIOHTTP_INSTALLED = False
910

1011
try:
1112
import httpx
@@ -21,6 +22,13 @@
2122
except ImportError:
2223
pass
2324

25+
try:
26+
import aiohttp
27+
28+
_AIOHTTP_INSTALLED = True
29+
except ImportError:
30+
pass
31+
2432

2533
def get_default_network_errors() -> Tuple[Type[BaseException], ...]:
2634
"""Get all network errors to use by default.
@@ -51,6 +59,8 @@ def get_default_network_errors() -> Tuple[Type[BaseException], ...]:
5159
requests.exceptions.ChunkedEncodingError,
5260
]
5361
)
62+
if _AIOHTTP_INSTALLED:
63+
exceptions.append(aiohttp.ClientConnectionError)
5464
return tuple(exceptions)
5565

5666

@@ -66,6 +76,8 @@ def get_default_timeouts() -> Tuple[Type[BaseException], ...]:
6676
exceptions.append(httpx.TimeoutException)
6777
if _REQUESTS_INSTALLED:
6878
exceptions.append(requests.Timeout)
79+
if _AIOHTTP_INSTALLED:
80+
exceptions.append(aiohttp.ServerTimeoutError)
6981
return tuple(exceptions)
7082

7183

@@ -81,6 +93,8 @@ def get_default_http_status_exceptions() -> Tuple[Type[BaseException], ...]:
8193
exceptions.append(httpx.HTTPStatusError)
8294
if _REQUESTS_INSTALLED:
8395
exceptions.append(requests.HTTPError)
96+
if _AIOHTTP_INSTALLED:
97+
exceptions.append(aiohttp.ClientResponseError)
8498
return tuple(exceptions)
8599

86100

@@ -99,6 +113,8 @@ def is_rate_limited(exc: Optional[BaseException]) -> bool:
99113
exceptions = get_default_http_status_exceptions()
100114
if isinstance(exc, exceptions) and hasattr(exc, "response"):
101115
return exc.response.status_code == 429
116+
if isinstance(exc, exceptions) and hasattr(exc, "status"): # for aiohttp.ClientResponseError
117+
return exc.status == 429
102118
return False
103119

104120

@@ -124,6 +140,8 @@ def is_server_error(
124140
exceptions = get_default_http_status_exceptions()
125141
if isinstance(exc, exceptions) and hasattr(exc, "response"):
126142
return exc.response.status_code in status_codes
143+
if isinstance(exc, exceptions) and hasattr(exc, "status"): # for aiohttp.ClientResponseError
144+
return exc.status in status_codes
127145
return False
128146

129147

0 commit comments

Comments
 (0)