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.
Files changed (22) hide show
  1. {httpx_tenacity-0.2.0 → httpx_tenacity-0.3.0}/.gitignore +3 -0
  2. {httpx_tenacity-0.2.0 → httpx_tenacity-0.3.0}/PKG-INFO +90 -16
  3. httpx_tenacity-0.3.0/README.md +95 -0
  4. {httpx_tenacity-0.2.0 → httpx_tenacity-0.3.0}/pyproject.toml +68 -24
  5. {httpx_tenacity-0.2.0 → httpx_tenacity-0.3.0}/src/httpx_tenacity/__init__.py +9 -4
  6. httpx_tenacity-0.3.0/src/httpx_tenacity/_types.py +55 -0
  7. httpx_tenacity-0.3.0/src/httpx_tenacity/_version.py +24 -0
  8. httpx_tenacity-0.3.0/src/httpx_tenacity/httpx/__init__.py +37 -0
  9. {httpx_tenacity-0.2.0/src/httpx_tenacity → httpx_tenacity-0.3.0/src/httpx_tenacity/httpx}/async_tenacious_transport.py +39 -35
  10. {httpx_tenacity-0.2.0/src/httpx_tenacity → httpx_tenacity-0.3.0/src/httpx_tenacity/httpx}/tenacious_transport.py +39 -35
  11. {httpx_tenacity-0.2.0/src/httpx_tenacity → httpx_tenacity-0.3.0/src/httpx_tenacity/httpx}/types.py +2 -12
  12. httpx_tenacity-0.3.0/src/httpx_tenacity/httpx2/__init__.py +37 -0
  13. httpx_tenacity-0.3.0/src/httpx_tenacity/httpx2/async_tenacious_transport.py +136 -0
  14. httpx_tenacity-0.3.0/src/httpx_tenacity/httpx2/tenacious_transport.py +136 -0
  15. httpx_tenacity-0.3.0/src/httpx_tenacity/httpx2/types.py +43 -0
  16. {httpx_tenacity-0.2.0 → httpx_tenacity-0.3.0}/src/httpx_tenacity/retry_callback.py +11 -4
  17. httpx_tenacity-0.3.0/src/httpx_tenacity/retry_policy.py +131 -0
  18. {httpx_tenacity-0.2.0 → httpx_tenacity-0.3.0}/src/httpx_tenacity/wait.py +23 -10
  19. httpx_tenacity-0.2.0/README.md +0 -23
  20. httpx_tenacity-0.2.0/src/httpx_tenacity/_version.py +0 -34
  21. {httpx_tenacity-0.2.0 → httpx_tenacity-0.3.0}/LICENSE +0 -0
  22. {httpx_tenacity-0.2.0 → httpx_tenacity-0.3.0}/src/httpx_tenacity/py.typed +0 -0
@@ -242,3 +242,6 @@ cython_debug/
242
242
 
243
243
  # Hatch-VCS
244
244
  _version.py
245
+
246
+ # Amp orb portals
247
+ .amp/portals/*
@@ -1,7 +1,7 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: httpx-tenacity
3
- Version: 0.2.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.10
29
- Requires-Dist: httpx~=0.25
28
+ Requires-Python: >=3.11
30
29
  Requires-Dist: tenacity~=9.1
31
- Requires-Dist: typing-extensions~=4.6; python_version < '3.11'
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 | [![Latest PyPI Version](https://img.shields.io/pypi/v/httpx-tenacity.svg)](https://pypi.org/project/httpx-tenacity/) [![Supported Python Versions](https://img.shields.io/pypi/pyversions/httpx-tenacity.svg)](https://pypi.org/project/httpx-tenacity/) |
38
+ | | |
39
+ |------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
40
+ | Package | [![Latest PyPI Version](https://img.shields.io/pypi/v/httpx-tenacity.svg)](https://pypi.org/project/httpx-tenacity/) [![Supported Python Versions](https://img.shields.io/pypi/pyversions/httpx-tenacity.svg)](https://pypi.org/project/httpx-tenacity/) |
39
41
  | Meta | [![Apache-2.0](https://img.shields.io/pypi/l/httpx-tenacity.svg)](LICENSE) [![Code of Conduct](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg)](.github/CODE_OF_CONDUCT.md) [![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/) [![Code Style Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) [![Linting: Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) |
40
- | Automation | [![CI](https://github.com/Midnighter/httpx-tenacity/actions/workflows/main.yml/badge.svg)](https://github.com/Midnighter/httpx-tenacity/actions/workflows/main.yml) |
42
+ | Automation | [![CI](https://github.com/Midnighter/httpx-tenacity/actions/workflows/main.yml/badge.svg)](https://github.com/Midnighter/httpx-tenacity/actions/workflows/main.yml) [![codecov](https://codecov.io/github/Midnighter/httpx-tenacity/graph/badge.svg?token=H51KS4D6M4)](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/). Install it,
47
- for example, with pip.
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 | [![Latest PyPI Version](https://img.shields.io/pypi/v/httpx-tenacity.svg)](https://pypi.org/project/httpx-tenacity/) [![Supported Python Versions](https://img.shields.io/pypi/pyversions/httpx-tenacity.svg)](https://pypi.org/project/httpx-tenacity/) |
6
+ | Meta | [![Apache-2.0](https://img.shields.io/pypi/l/httpx-tenacity.svg)](LICENSE) [![Code of Conduct](https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg)](.github/CODE_OF_CONDUCT.md) [![Checked with mypy](https://www.mypy-lang.org/static/mypy_badge.svg)](https://mypy-lang.org/) [![Code Style Black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) [![Linting: Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) |
7
+ | Automation | [![CI](https://github.com/Midnighter/httpx-tenacity/actions/workflows/main.yml/badge.svg)](https://github.com/Midnighter/httpx-tenacity/actions/workflows/main.yml) [![codecov](https://codecov.io/github/Midnighter/httpx-tenacity/graph/badge.svg?token=H51KS4D6M4)](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.10"
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
- "mkdocs-material ~=9.5",
57
- "mkdocstrings[python] ~=1.0",
58
- "mkdocs-awesome-pages-plugin ~=2.9",
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-httpx", "pytest-raises"]
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 = "mkdocs build {args:--clean --strict}"
210
- serve = "mkdocs serve {args}"
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 = ["pip check", "hatch build {args:--clean}", "twine check dist/*"]
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.10", "3.11", "3.12", "3.13", "3.14"]
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.10", "3.11", "3.12", "3.13", "3.14"]
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
- """Provide top level symbols."""
16
+ """
17
+ Provide the retry policies that configure the tenacious transports.
17
18
 
18
- from .types import HTTPXHTTPTransportKeywordArguments
19
- from .async_tenacious_transport import AsyncTenaciousTransport
20
- from .tenacious_transport import TenaciousTransport
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