httpx-tenacity 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {httpx_tenacity-0.2.0 → httpx_tenacity-0.3.0}/.gitignore +3 -0
- {httpx_tenacity-0.2.0 → httpx_tenacity-0.3.0}/PKG-INFO +90 -16
- httpx_tenacity-0.3.0/README.md +95 -0
- {httpx_tenacity-0.2.0 → httpx_tenacity-0.3.0}/pyproject.toml +68 -24
- {httpx_tenacity-0.2.0 → httpx_tenacity-0.3.0}/src/httpx_tenacity/__init__.py +9 -4
- httpx_tenacity-0.3.0/src/httpx_tenacity/_types.py +55 -0
- httpx_tenacity-0.3.0/src/httpx_tenacity/_version.py +24 -0
- httpx_tenacity-0.3.0/src/httpx_tenacity/httpx/__init__.py +37 -0
- {httpx_tenacity-0.2.0/src/httpx_tenacity → httpx_tenacity-0.3.0/src/httpx_tenacity/httpx}/async_tenacious_transport.py +39 -35
- {httpx_tenacity-0.2.0/src/httpx_tenacity → httpx_tenacity-0.3.0/src/httpx_tenacity/httpx}/tenacious_transport.py +39 -35
- {httpx_tenacity-0.2.0/src/httpx_tenacity → httpx_tenacity-0.3.0/src/httpx_tenacity/httpx}/types.py +2 -12
- httpx_tenacity-0.3.0/src/httpx_tenacity/httpx2/__init__.py +37 -0
- httpx_tenacity-0.3.0/src/httpx_tenacity/httpx2/async_tenacious_transport.py +136 -0
- httpx_tenacity-0.3.0/src/httpx_tenacity/httpx2/tenacious_transport.py +136 -0
- httpx_tenacity-0.3.0/src/httpx_tenacity/httpx2/types.py +43 -0
- {httpx_tenacity-0.2.0 → httpx_tenacity-0.3.0}/src/httpx_tenacity/retry_callback.py +11 -4
- httpx_tenacity-0.3.0/src/httpx_tenacity/retry_policy.py +131 -0
- {httpx_tenacity-0.2.0 → httpx_tenacity-0.3.0}/src/httpx_tenacity/wait.py +23 -10
- httpx_tenacity-0.2.0/README.md +0 -23
- httpx_tenacity-0.2.0/src/httpx_tenacity/_version.py +0 -34
- {httpx_tenacity-0.2.0 → httpx_tenacity-0.3.0}/LICENSE +0 -0
- {httpx_tenacity-0.2.0 → httpx_tenacity-0.3.0}/src/httpx_tenacity/py.typed +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: httpx-tenacity
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: A lightweight package that provides tenacious (retrying) httpx transports.
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: A lightweight package that provides tenacious (retrying) httpx and httpx2 transports.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Midnighter/httpx-tenacity
|
|
6
6
|
Project-URL: Documentation, https://midnighter.github.io/httpx-tenacity/
|
|
7
7
|
Project-URL: Source Code, https://github.com/Midnighter/httpx-tenacity
|
|
@@ -10,7 +10,7 @@ Project-URL: Download, https://pypi.org/project/httpx-tenacity/#files
|
|
|
10
10
|
Author-email: "Moritz E. Beber" <midnighter@posteo.net>
|
|
11
11
|
License-Expression: Apache-2.0
|
|
12
12
|
License-File: LICENSE
|
|
13
|
-
Keywords: httpx,retry,tenacity
|
|
13
|
+
Keywords: httpx,httpx2,retry,tenacity
|
|
14
14
|
Classifier: Development Status :: 2 - Pre-Alpha
|
|
15
15
|
Classifier: Environment :: Web Environment
|
|
16
16
|
Classifier: Intended Audience :: Developers
|
|
@@ -18,38 +18,112 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
18
18
|
Classifier: Natural Language :: English
|
|
19
19
|
Classifier: Operating System :: OS Independent
|
|
20
20
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.11
|
|
23
22
|
Classifier: Programming Language :: Python :: 3.12
|
|
24
23
|
Classifier: Programming Language :: Python :: 3.13
|
|
25
24
|
Classifier: Programming Language :: Python :: 3.14
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
26
26
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
27
27
|
Classifier: Typing :: Typed
|
|
28
|
-
Requires-Python: >=3.
|
|
29
|
-
Requires-Dist: httpx~=0.25
|
|
28
|
+
Requires-Python: >=3.11
|
|
30
29
|
Requires-Dist: tenacity~=9.1
|
|
31
|
-
|
|
30
|
+
Provides-Extra: httpx
|
|
31
|
+
Requires-Dist: httpx~=0.25; extra == 'httpx'
|
|
32
|
+
Provides-Extra: httpx2
|
|
33
|
+
Requires-Dist: httpx2~=2.10; extra == 'httpx2'
|
|
32
34
|
Description-Content-Type: text/markdown
|
|
33
35
|
|
|
34
36
|
# HTTPX Tenacity
|
|
35
37
|
|
|
36
|
-
| |
|
|
37
|
-
|
|
38
|
-
| Package | [](https://pypi.org/project/httpx-tenacity/) [](https://pypi.org/project/httpx-tenacity/)
|
|
38
|
+
| | |
|
|
39
|
+
|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
40
|
+
| Package | [](https://pypi.org/project/httpx-tenacity/) [](https://pypi.org/project/httpx-tenacity/) |
|
|
39
41
|
| Meta | [](LICENSE) [](.github/CODE_OF_CONDUCT.md) [](https://mypy-lang.org/) [](https://github.com/ambv/black) [](https://github.com/astral-sh/ruff) |
|
|
40
|
-
| Automation | [](https://github.com/Midnighter/httpx-tenacity/actions/workflows/main.yml)
|
|
42
|
+
| Automation | [](https://github.com/Midnighter/httpx-tenacity/actions/workflows/main.yml) [](https://codecov.io/github/Midnighter/httpx-tenacity) |
|
|
41
43
|
|
|
42
|
-
_A lightweight package that provides tenacious (retrying) httpx transports._
|
|
44
|
+
_A lightweight package that provides tenacious (retrying) httpx and httpx2 transports._
|
|
43
45
|
|
|
44
46
|
## Installation
|
|
45
47
|
|
|
46
|
-
The package is published on [PyPI](https://pypi.org/project/httpx-tenacity/).
|
|
47
|
-
|
|
48
|
+
The package is published on [PyPI](https://pypi.org/project/httpx-tenacity/). Plain
|
|
49
|
+
`pip install httpx-tenacity` installs only the shared core with the retry policies,
|
|
50
|
+
which contains no HTTP client. Select the HTTP client you use with an extra, `httpx` or
|
|
51
|
+
`httpx2`.
|
|
48
52
|
|
|
49
53
|
```sh
|
|
50
|
-
pip install httpx-tenacity
|
|
54
|
+
pip install 'httpx-tenacity[httpx]'
|
|
55
|
+
pip install 'httpx-tenacity[httpx2]'
|
|
51
56
|
```
|
|
52
57
|
|
|
58
|
+
To use both HTTP clients in the same process, install both extras.
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
pip install 'httpx-tenacity[httpx,httpx2]'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
There is no automatic fallback: you choose the integration explicitly by importing from
|
|
65
|
+
`httpx_tenacity.httpx` or from `httpx_tenacity.httpx2`.
|
|
66
|
+
|
|
67
|
+
### Migrating from <0.3
|
|
68
|
+
|
|
69
|
+
Two breaking changes land in 0.3.0.
|
|
70
|
+
|
|
71
|
+
First, the transports moved out of the package root and `httpx` is no longer a required
|
|
72
|
+
dependency. Install `httpx-tenacity[httpx]` instead of `httpx-tenacity` and change your
|
|
73
|
+
imports. `RetryPolicy`, `SmartRetryPolicy`, and `RetryingT` keep their import paths.
|
|
74
|
+
|
|
75
|
+
```diff
|
|
76
|
+
-from httpx_tenacity import AsyncTenaciousTransport, TenaciousTransport
|
|
77
|
+
+from httpx_tenacity.httpx import AsyncTenaciousTransport, TenaciousTransport
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The same applies to `HTTPXHTTPTransportKeywordArguments`. For HTTPX2, import the
|
|
81
|
+
transports from `httpx_tenacity.httpx2` instead. The transports' loggers follow their
|
|
82
|
+
modules and are now named `httpx_tenacity.httpx.tenacious_transport` and
|
|
83
|
+
`httpx_tenacity.httpx.async_tenacious_transport`.
|
|
84
|
+
|
|
85
|
+
Second, `TenaciousTransport.create()` and `AsyncTenaciousTransport.create()` take a
|
|
86
|
+
`policy` instead of the `max_attempts`, `multiplier`, `max_wait_seconds`,
|
|
87
|
+
`min_wait_seconds`, and `exponent_base` arguments. Pass them to `SmartRetryPolicy`.
|
|
88
|
+
|
|
89
|
+
```diff
|
|
90
|
+
-transport = TenaciousTransport.create(max_attempts=3)
|
|
91
|
+
+transport = TenaciousTransport.create(policy=SmartRetryPolicy(max_attempts=3))
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Usage
|
|
95
|
+
|
|
96
|
+
Wrap the requests of an HTTPX client with a transport that retries server-side issues
|
|
97
|
+
(HTTP 5xx errors and 429 Too Many Requests responses) and request errors:
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
import httpx
|
|
101
|
+
from httpx_tenacity import SmartRetryPolicy
|
|
102
|
+
from httpx_tenacity.httpx import TenaciousTransport
|
|
103
|
+
|
|
104
|
+
transport = TenaciousTransport.create(policy=SmartRetryPolicy(max_attempts=3))
|
|
105
|
+
|
|
106
|
+
with httpx.Client(transport=transport) as client:
|
|
107
|
+
response = client.get("https://api.example.com/data")
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
For HTTPX2, import `httpx2` instead of `httpx` and import the transports from
|
|
111
|
+
`httpx_tenacity.httpx2` instead of `httpx_tenacity.httpx`; everything else stays the
|
|
112
|
+
same. The [documentation](https://midnighter.github.io/httpx-tenacity/) shows every
|
|
113
|
+
example for both clients, including the asynchronous `AsyncTenaciousTransport`.
|
|
114
|
+
Transports, clients, and responses must all come from the same client:
|
|
115
|
+
`httpx_tenacity.httpx` works with `httpx` and `httpx_tenacity.httpx2` works with
|
|
116
|
+
`httpx2`.
|
|
117
|
+
|
|
118
|
+
### Using Both Clients Together
|
|
119
|
+
|
|
120
|
+
You can install both extras, `httpx-tenacity[httpx,httpx2]`, and use both integrations
|
|
121
|
+
in the same process. The retry policies are independent of the HTTP client, so you may
|
|
122
|
+
even share one policy between them. The HTTP objects, however, are **not
|
|
123
|
+
interchangeable**: an `httpx_tenacity.httpx` transport cannot be used with an
|
|
124
|
+
`httpx2.Client`, and an `httpx_tenacity.httpx2` transport cannot be used with an
|
|
125
|
+
`httpx.Client`.
|
|
126
|
+
|
|
53
127
|
## Copyright
|
|
54
128
|
|
|
55
129
|
- Copyright © 2026 Moritz E. Beber.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# HTTPX Tenacity
|
|
2
|
+
|
|
3
|
+
| | |
|
|
4
|
+
|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
5
|
+
| Package | [](https://pypi.org/project/httpx-tenacity/) [](https://pypi.org/project/httpx-tenacity/) |
|
|
6
|
+
| Meta | [](LICENSE) [](.github/CODE_OF_CONDUCT.md) [](https://mypy-lang.org/) [](https://github.com/ambv/black) [](https://github.com/astral-sh/ruff) |
|
|
7
|
+
| Automation | [](https://github.com/Midnighter/httpx-tenacity/actions/workflows/main.yml) [](https://codecov.io/github/Midnighter/httpx-tenacity) |
|
|
8
|
+
|
|
9
|
+
_A lightweight package that provides tenacious (retrying) httpx and httpx2 transports._
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
The package is published on [PyPI](https://pypi.org/project/httpx-tenacity/). Plain
|
|
14
|
+
`pip install httpx-tenacity` installs only the shared core with the retry policies,
|
|
15
|
+
which contains no HTTP client. Select the HTTP client you use with an extra, `httpx` or
|
|
16
|
+
`httpx2`.
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
pip install 'httpx-tenacity[httpx]'
|
|
20
|
+
pip install 'httpx-tenacity[httpx2]'
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
To use both HTTP clients in the same process, install both extras.
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
pip install 'httpx-tenacity[httpx,httpx2]'
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
There is no automatic fallback: you choose the integration explicitly by importing from
|
|
30
|
+
`httpx_tenacity.httpx` or from `httpx_tenacity.httpx2`.
|
|
31
|
+
|
|
32
|
+
### Migrating from <0.3
|
|
33
|
+
|
|
34
|
+
Two breaking changes land in 0.3.0.
|
|
35
|
+
|
|
36
|
+
First, the transports moved out of the package root and `httpx` is no longer a required
|
|
37
|
+
dependency. Install `httpx-tenacity[httpx]` instead of `httpx-tenacity` and change your
|
|
38
|
+
imports. `RetryPolicy`, `SmartRetryPolicy`, and `RetryingT` keep their import paths.
|
|
39
|
+
|
|
40
|
+
```diff
|
|
41
|
+
-from httpx_tenacity import AsyncTenaciousTransport, TenaciousTransport
|
|
42
|
+
+from httpx_tenacity.httpx import AsyncTenaciousTransport, TenaciousTransport
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The same applies to `HTTPXHTTPTransportKeywordArguments`. For HTTPX2, import the
|
|
46
|
+
transports from `httpx_tenacity.httpx2` instead. The transports' loggers follow their
|
|
47
|
+
modules and are now named `httpx_tenacity.httpx.tenacious_transport` and
|
|
48
|
+
`httpx_tenacity.httpx.async_tenacious_transport`.
|
|
49
|
+
|
|
50
|
+
Second, `TenaciousTransport.create()` and `AsyncTenaciousTransport.create()` take a
|
|
51
|
+
`policy` instead of the `max_attempts`, `multiplier`, `max_wait_seconds`,
|
|
52
|
+
`min_wait_seconds`, and `exponent_base` arguments. Pass them to `SmartRetryPolicy`.
|
|
53
|
+
|
|
54
|
+
```diff
|
|
55
|
+
-transport = TenaciousTransport.create(max_attempts=3)
|
|
56
|
+
+transport = TenaciousTransport.create(policy=SmartRetryPolicy(max_attempts=3))
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Usage
|
|
60
|
+
|
|
61
|
+
Wrap the requests of an HTTPX client with a transport that retries server-side issues
|
|
62
|
+
(HTTP 5xx errors and 429 Too Many Requests responses) and request errors:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
import httpx
|
|
66
|
+
from httpx_tenacity import SmartRetryPolicy
|
|
67
|
+
from httpx_tenacity.httpx import TenaciousTransport
|
|
68
|
+
|
|
69
|
+
transport = TenaciousTransport.create(policy=SmartRetryPolicy(max_attempts=3))
|
|
70
|
+
|
|
71
|
+
with httpx.Client(transport=transport) as client:
|
|
72
|
+
response = client.get("https://api.example.com/data")
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
For HTTPX2, import `httpx2` instead of `httpx` and import the transports from
|
|
76
|
+
`httpx_tenacity.httpx2` instead of `httpx_tenacity.httpx`; everything else stays the
|
|
77
|
+
same. The [documentation](https://midnighter.github.io/httpx-tenacity/) shows every
|
|
78
|
+
example for both clients, including the asynchronous `AsyncTenaciousTransport`.
|
|
79
|
+
Transports, clients, and responses must all come from the same client:
|
|
80
|
+
`httpx_tenacity.httpx` works with `httpx` and `httpx_tenacity.httpx2` works with
|
|
81
|
+
`httpx2`.
|
|
82
|
+
|
|
83
|
+
### Using Both Clients Together
|
|
84
|
+
|
|
85
|
+
You can install both extras, `httpx-tenacity[httpx,httpx2]`, and use both integrations
|
|
86
|
+
in the same process. The retry policies are independent of the HTTP client, so you may
|
|
87
|
+
even share one policy between them. The HTTP objects, however, are **not
|
|
88
|
+
interchangeable**: an `httpx_tenacity.httpx` transport cannot be used with an
|
|
89
|
+
`httpx2.Client`, and an `httpx_tenacity.httpx2` transport cannot be used with an
|
|
90
|
+
`httpx.Client`.
|
|
91
|
+
|
|
92
|
+
## Copyright
|
|
93
|
+
|
|
94
|
+
- Copyright © 2026 Moritz E. Beber.
|
|
95
|
+
- Free software distributed under the [Apache Software License 2.0](./LICENSE).
|
|
@@ -13,11 +13,11 @@ requires = ["hatchling", "hatch-vcs"]
|
|
|
13
13
|
[project]
|
|
14
14
|
name = "httpx-tenacity"
|
|
15
15
|
dynamic = ["version"]
|
|
16
|
-
description = "A lightweight package that provides tenacious (retrying) httpx transports."
|
|
16
|
+
description = "A lightweight package that provides tenacious (retrying) httpx and httpx2 transports."
|
|
17
17
|
authors = [{ name = "Moritz E. Beber", email = "midnighter@posteo.net" }]
|
|
18
18
|
license = "Apache-2.0"
|
|
19
19
|
readme = { "file" = "README.md", "content-type" = "text/markdown" }
|
|
20
|
-
requires-python = ">=3.
|
|
20
|
+
requires-python = ">=3.11"
|
|
21
21
|
# Please consult https://pypi.org/classifiers/ for a full list.
|
|
22
22
|
classifiers = [
|
|
23
23
|
"Development Status :: 2 - Pre-Alpha",
|
|
@@ -27,20 +27,16 @@ classifiers = [
|
|
|
27
27
|
"Natural Language :: English",
|
|
28
28
|
"Operating System :: OS Independent",
|
|
29
29
|
"Programming Language :: Python :: 3 :: Only",
|
|
30
|
-
"Programming Language :: Python :: 3.10",
|
|
31
30
|
"Programming Language :: Python :: 3.11",
|
|
32
31
|
"Programming Language :: Python :: 3.12",
|
|
33
32
|
"Programming Language :: Python :: 3.13",
|
|
34
33
|
"Programming Language :: Python :: 3.14",
|
|
34
|
+
"Programming Language :: Python :: 3.15",
|
|
35
35
|
"Topic :: Internet :: WWW/HTTP",
|
|
36
36
|
"Typing :: Typed",
|
|
37
37
|
]
|
|
38
|
-
keywords = ["httpx", "tenacity", "retry"]
|
|
39
|
-
dependencies = [
|
|
40
|
-
"httpx ~=0.25",
|
|
41
|
-
"tenacity ~=9.1",
|
|
42
|
-
"typing-extensions ~=4.6; python_version < '3.11'",
|
|
43
|
-
]
|
|
38
|
+
keywords = ["httpx", "httpx2", "tenacity", "retry"]
|
|
39
|
+
dependencies = ["tenacity ~=9.1"]
|
|
44
40
|
|
|
45
41
|
[project.urls]
|
|
46
42
|
Homepage = "https://github.com/Midnighter/httpx-tenacity"
|
|
@@ -49,16 +45,21 @@ Documentation = "https://midnighter.github.io/httpx-tenacity/"
|
|
|
49
45
|
"Bug Tracker" = "https://github.com/Midnighter/httpx-tenacity/issues"
|
|
50
46
|
Download = "https://pypi.org/project/httpx-tenacity/#files"
|
|
51
47
|
|
|
48
|
+
[project.optional-dependencies]
|
|
49
|
+
httpx = ["httpx ~=0.25"]
|
|
50
|
+
httpx2 = ["httpx2 ~=2.10"]
|
|
51
|
+
|
|
52
52
|
[dependency-groups]
|
|
53
53
|
style = ["pydoclint", "ruff"]
|
|
54
|
-
types = ["mypy", "ty"]
|
|
54
|
+
types = ["mypy", "ty", "pyrefly"]
|
|
55
55
|
docs = [
|
|
56
|
-
"
|
|
57
|
-
"mkdocstrings
|
|
58
|
-
"
|
|
59
|
-
"mike ~=2.1",
|
|
56
|
+
"zensical ~=0.1.1",
|
|
57
|
+
"mkdocstrings-python ~=2.0",
|
|
58
|
+
"mike @ git+https://github.com/squidfunk/mike.git",
|
|
60
59
|
]
|
|
61
|
-
tests = ["anyio", "pytest", "pytest-cov", "pytest-
|
|
60
|
+
tests = ["anyio", "pytest", "pytest-cov", "pytest-raises"]
|
|
61
|
+
# Pinned exactly so that CI and local development audit with the same zizmor.
|
|
62
|
+
workflows = ["zizmor ==1.32.0"]
|
|
62
63
|
|
|
63
64
|
################################################################################
|
|
64
65
|
# Tool Configuration
|
|
@@ -78,6 +79,9 @@ source = "vcs"
|
|
|
78
79
|
|
|
79
80
|
[tool.pytest.ini_options]
|
|
80
81
|
testpaths = ["tests"]
|
|
82
|
+
# Shared test helpers. Replace with a Hatch workspace member once hatch-pip-compile
|
|
83
|
+
# supports workspaces.
|
|
84
|
+
pythonpath = ["test-helpers"]
|
|
81
85
|
markers = ["raises"]
|
|
82
86
|
|
|
83
87
|
[tool.coverage.paths]
|
|
@@ -86,6 +90,7 @@ source = ["src/httpx_tenacity", "*/site-packages/httpx_tenacity"]
|
|
|
86
90
|
[tool.coverage.run]
|
|
87
91
|
branch = true
|
|
88
92
|
parallel = true
|
|
93
|
+
patch = ["subprocess"]
|
|
89
94
|
omit = ["src/httpx_tenacity/_version.py"]
|
|
90
95
|
|
|
91
96
|
[tool.coverage.report]
|
|
@@ -121,6 +126,9 @@ exclude = ["src/httpx_tenacity/_version.py"]
|
|
|
121
126
|
"PLR2004", # Magic numbers are okay in tests.
|
|
122
127
|
"FBT001", # Boolean positional arguments are fine in tests.
|
|
123
128
|
]
|
|
129
|
+
"test-helpers/*.py" = [
|
|
130
|
+
"INP001", # The helpers are a plain module on the pytest `pythonpath`, not a package.
|
|
131
|
+
]
|
|
124
132
|
"tests/pyodide/**/*.py" = [
|
|
125
133
|
"PLC0415", # We explicitly need imports inside function and ignore the '`import` should be at the top-level of a file' rule.
|
|
126
134
|
"I001", # 'Import block is un-sorted or un-formatted' ignored because we may have to import in a particular, not-alphabetical order.
|
|
@@ -128,7 +136,7 @@ exclude = ["src/httpx_tenacity/_version.py"]
|
|
|
128
136
|
|
|
129
137
|
[tool.ruff.lint.isort]
|
|
130
138
|
case-sensitive = true
|
|
131
|
-
known-first-party = ["src", "httpx_tenacity"]
|
|
139
|
+
known-first-party = ["src", "httpx_tenacity", "httpx_tenacity_test_types"]
|
|
132
140
|
lines-after-imports = 2
|
|
133
141
|
|
|
134
142
|
[tool.ruff.lint.pylint]
|
|
@@ -141,6 +149,11 @@ check-return-types = false
|
|
|
141
149
|
check-yield-types = false
|
|
142
150
|
exclude = "_version.py"
|
|
143
151
|
|
|
152
|
+
[tool.pyrefly]
|
|
153
|
+
project-includes = [
|
|
154
|
+
"src/**/*.py*",
|
|
155
|
+
]
|
|
156
|
+
|
|
144
157
|
################################################################################
|
|
145
158
|
# Hatch Environments
|
|
146
159
|
################################################################################
|
|
@@ -161,6 +174,7 @@ pip-compile-constraint = "dev"
|
|
|
161
174
|
pip-compile-hashes = true
|
|
162
175
|
pip-compile-resolver = "uv"
|
|
163
176
|
pip-compile-installer = "uv"
|
|
177
|
+
features = ["httpx", "httpx2"]
|
|
164
178
|
|
|
165
179
|
[tool.hatch.envs.no-install]
|
|
166
180
|
description = """A base environment for use cases that do not install the project."""
|
|
@@ -181,11 +195,19 @@ template = "no-install"
|
|
|
181
195
|
dependency-groups = ["style"]
|
|
182
196
|
|
|
183
197
|
[tool.hatch.envs.style.scripts]
|
|
184
|
-
docstrings = ["pydoclint src", "pydoclint tests"]
|
|
198
|
+
docstrings = ["pydoclint src", "pydoclint tests", "pydoclint test-helpers"]
|
|
185
199
|
code = "ruff check {args}"
|
|
186
200
|
format = "ruff format {args}"
|
|
187
201
|
fix = "ruff check --fix {args}"
|
|
188
|
-
check = ["docstrings", "code"]
|
|
202
|
+
check = ["docstrings", "format --check", "code"]
|
|
203
|
+
|
|
204
|
+
[tool.hatch.envs.workflows]
|
|
205
|
+
description = """Audit the GitHub workflows for security issues."""
|
|
206
|
+
template = "no-install"
|
|
207
|
+
dependency-groups = ["workflows"]
|
|
208
|
+
|
|
209
|
+
[tool.hatch.envs.workflows.scripts]
|
|
210
|
+
check = "zizmor {args} ."
|
|
189
211
|
|
|
190
212
|
[tool.hatch.envs.audit]
|
|
191
213
|
description = """Check dependencies for security vulnerabilities."""
|
|
@@ -199,15 +221,15 @@ description = """Check the static types of the codebase."""
|
|
|
199
221
|
dependency-groups = ["types"]
|
|
200
222
|
|
|
201
223
|
[tool.hatch.envs.types.scripts]
|
|
202
|
-
check = ["mypy src/httpx_tenacity", "ty check src/httpx_tenacity"]
|
|
224
|
+
check = ["mypy src/httpx_tenacity", "ty check src/httpx_tenacity", "pyrefly check"]
|
|
203
225
|
|
|
204
226
|
[tool.hatch.envs.docs]
|
|
205
227
|
description = """Build or serve the documentation."""
|
|
206
228
|
dependency-groups = ["docs"]
|
|
207
229
|
|
|
208
230
|
[tool.hatch.envs.docs.scripts]
|
|
209
|
-
build = "
|
|
210
|
-
serve = "
|
|
231
|
+
build = "zensical build {args:--clean --strict}"
|
|
232
|
+
serve = "zensical serve {args}"
|
|
211
233
|
deploy = "mike deploy {args}"
|
|
212
234
|
|
|
213
235
|
[tool.hatch.envs.distribution]
|
|
@@ -217,18 +239,25 @@ dependencies = ["pip", "twine"]
|
|
|
217
239
|
builder = true
|
|
218
240
|
|
|
219
241
|
[tool.hatch.envs.distribution.scripts]
|
|
220
|
-
check = [
|
|
242
|
+
check = [
|
|
243
|
+
"pip check",
|
|
244
|
+
"hatch build {args:--clean}",
|
|
245
|
+
"twine check dist/*",
|
|
246
|
+
"python {root}/tests/distribution/check_wheel_metadata.py dist",
|
|
247
|
+
]
|
|
221
248
|
|
|
222
249
|
[tool.hatch.envs.unit-tests]
|
|
223
250
|
description = """Run the unit tests."""
|
|
224
251
|
dependency-groups = ["tests"]
|
|
252
|
+
# The typing tests run mypy through its Python API.
|
|
253
|
+
extra-dependencies = ["mypy"]
|
|
225
254
|
|
|
226
255
|
[tool.hatch.envs.unit-tests.env-vars]
|
|
227
256
|
PYTHONDEVMODE = "1"
|
|
228
257
|
PYTHONASYNCIODEBUG = "1"
|
|
229
258
|
|
|
230
259
|
[[tool.hatch.envs.unit-tests.matrix]]
|
|
231
|
-
python = ["3.
|
|
260
|
+
python = ["3.11", "3.12", "3.13", "3.14", "3.15"]
|
|
232
261
|
|
|
233
262
|
[tool.hatch.envs.unit-tests.scripts]
|
|
234
263
|
run = "pytest {args:--cov=httpx_tenacity --cov-report=term-missing} {root}/tests/unit"
|
|
@@ -242,7 +271,22 @@ PYTHONDEVMODE = "1"
|
|
|
242
271
|
PYTHONASYNCIODEBUG = "1"
|
|
243
272
|
|
|
244
273
|
[[tool.hatch.envs.acceptance-tests.matrix]]
|
|
245
|
-
python = ["3.
|
|
274
|
+
python = ["3.11", "3.12", "3.13", "3.14", "3.15"]
|
|
246
275
|
|
|
247
276
|
[tool.hatch.envs.acceptance-tests.scripts]
|
|
248
277
|
run = "pytest {args:--cov=httpx_tenacity --cov-report=term-missing} {root}/tests/acceptance"
|
|
278
|
+
|
|
279
|
+
[tool.hatch.envs.documentation-tests]
|
|
280
|
+
description = """Run the code examples in the documentation."""
|
|
281
|
+
dependency-groups = ["tests"]
|
|
282
|
+
extra-dependencies = ["mktestdocs"]
|
|
283
|
+
|
|
284
|
+
[tool.hatch.envs.documentation-tests.env-vars]
|
|
285
|
+
PYTHONDEVMODE = "1"
|
|
286
|
+
PYTHONASYNCIODEBUG = "1"
|
|
287
|
+
|
|
288
|
+
[[tool.hatch.envs.documentation-tests.matrix]]
|
|
289
|
+
python = ["3.11", "3.12", "3.13", "3.14", "3.15"]
|
|
290
|
+
|
|
291
|
+
[tool.hatch.envs.documentation-tests.scripts]
|
|
292
|
+
run = "pytest {args:--cov=httpx_tenacity --cov-report=term-missing} {root}/tests/documentation"
|
|
@@ -13,8 +13,13 @@
|
|
|
13
13
|
# the License.
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
"""
|
|
16
|
+
"""
|
|
17
|
+
Provide the retry policies that configure the tenacious transports.
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
This package imports no HTTP client. The transports live in the integration
|
|
20
|
+
packages, `httpx_tenacity.httpx` for HTTPX and `httpx_tenacity.httpx2` for
|
|
21
|
+
HTTPX2. Each of them requires its own extra.
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from .retry_policy import RetryingT, RetryPolicy, SmartRetryPolicy
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Copyright (c) 2026 Moritz E. Beber
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
4
|
+
# use this file except in compliance with the License. You may obtain a copy of
|
|
5
|
+
# the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
11
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
12
|
+
# License for the specific language governing permissions and limitations under
|
|
13
|
+
# the License.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
"""Provide types that do not depend on an HTTP client."""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from typing import TYPE_CHECKING, Protocol, TypeAlias
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
24
|
+
from collections.abc import Mapping
|
|
25
|
+
|
|
26
|
+
# Fallback for Pyodide HTTPX. See: https://github.com/Midnighter/httpx-tenacity/pull/10#issuecomment-3241566927
|
|
27
|
+
SocketOption: TypeAlias = (
|
|
28
|
+
tuple[int, int, int]
|
|
29
|
+
| tuple[int, int, bytes | bytearray]
|
|
30
|
+
| tuple[int, int, None, int]
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# We redefine the following type from the private `_types` modules of HTTPX and
|
|
34
|
+
# HTTPX2 to avoid a dependency on them. It is identical for both clients. It would
|
|
35
|
+
# be better if they exposed it publicly, but it is unlikely to change often. Thus we
|
|
36
|
+
# consider it acceptable to redefine it here for type hinting purposes.
|
|
37
|
+
CertTypes: TypeAlias = str | tuple[str, str] | tuple[str, str, str]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Response(Protocol):
|
|
41
|
+
"""
|
|
42
|
+
Describe the parts of an HTTP response that the retry helpers read.
|
|
43
|
+
|
|
44
|
+
Responses of different HTTP clients are distinct classes. This protocol lets the
|
|
45
|
+
client-independent code accept all of them.
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def status_code(self) -> int:
|
|
51
|
+
"""The HTTP status code."""
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def headers(self) -> Mapping[str, str]:
|
|
55
|
+
"""The response headers."""
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.3.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 3, 0)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Copyright (c) 2026 Moritz E. Beber
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
4
|
+
# use this file except in compliance with the License. You may obtain a copy of
|
|
5
|
+
# the License at
|
|
6
|
+
#
|
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
11
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
12
|
+
# License for the specific language governing permissions and limitations under
|
|
13
|
+
# the License.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
Provide the HTTPX integration.
|
|
18
|
+
|
|
19
|
+
Importing this package requires the `httpx` extra, for example
|
|
20
|
+
`pip install "httpx-tenacity[httpx]"`.
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
import httpx
|
|
26
|
+
except ModuleNotFoundError as error:
|
|
27
|
+
if error.name != "httpx":
|
|
28
|
+
raise
|
|
29
|
+
msg = (
|
|
30
|
+
"The HTTPX integration requires the 'httpx' package. Install it with "
|
|
31
|
+
"`pip install 'httpx-tenacity[httpx]'`."
|
|
32
|
+
)
|
|
33
|
+
raise ModuleNotFoundError(msg, name="httpx") from error
|
|
34
|
+
|
|
35
|
+
from .types import HTTPXHTTPTransportKeywordArguments
|
|
36
|
+
from .async_tenacious_transport import AsyncTenaciousTransport
|
|
37
|
+
from .tenacious_transport import TenaciousTransport
|