pathapi-httpx 0.1.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.
@@ -0,0 +1,41 @@
1
+ # Category rule: nothing dot-prefixed at the repo root gets tracked except the
2
+ # files re-included below (a category beats a list). Root-anchored on purpose;
3
+ # nested dotfiles are governed by the specific rules further down.
4
+ /.*
5
+ !/.gitignore
6
+ !/.gitattributes
7
+ !/.github/
8
+
9
+ # Private agent config. No trailing slash on `.agents` on purpose: it may be a
10
+ # symlink, which git treats as a file, so a directory-only `.agents/` would not
11
+ # match it. These stay even though /.* covers the root: they also catch nested
12
+ # copies. *.local.* is an unshared, machine- or user-specific override and is
13
+ # never committed (it is also excluded from the build in pyproject.toml).
14
+ .agents
15
+ *.local.*
16
+ CLAUDE*
17
+ .claude
18
+
19
+ # Build/dist output and generated docs site
20
+ build/
21
+ dist/
22
+ site/
23
+
24
+ # Python
25
+ __pycache__/
26
+ *.py[cod]
27
+ .pytest_cache/
28
+ .hypothesis/
29
+ .mypy_cache/
30
+ .ruff_cache/
31
+ .coverage*
32
+ htmlcov/
33
+ *.egg-info/
34
+ .venv/
35
+ .pyvenv/
36
+
37
+ # OS/editor
38
+ .DS_Store
39
+ Thumbs.db
40
+ .idea/
41
+ .vscode/
@@ -0,0 +1,31 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2026-10-17
11
+
12
+ ### Added
13
+
14
+ - `pathapi_httpx.HttpxTransport` and `pathapi_httpx.AsyncHttpxTransport`, the
15
+ HTTP transports of `pathapi.http` over `httpx.Client` and `httpx.AsyncClient`,
16
+ and `pathapi_httpx.__version__`. They need Python 3.9 or newer,
17
+ `pathapi>=0.1.2,<0.2` and `httpx>=0.28.0,<0.29`.
18
+ - The distribution declares the `httpx` entry of the `pathapi.http_transports`
19
+ and of the `pathapi.http_async_transports` groups. Installing it is all that
20
+ `pathapi.http` needs to send through them:
21
+ `HTTPClient(HttpxTransport(client))`, `AsyncHTTPClient(AsyncHttpxTransport())`,
22
+ `PATHAPI_HTTP_TRANSPORT=httpx` and `PATHAPI_HTTP_ASYNC_TRANSPORT=httpx` use
23
+ them, `pathapi.http.get_transport("httpx")` and
24
+ `pathapi.http.get_async_transport("httpx")` load them, and
25
+ `pathapi.http.transport_names()` and `async_transport_names()` list them.
26
+ - The tests `pathapi` runs over its own transports run over these
27
+ (`python -m pathapi.http.testing --http-transports httpx --http-async-transports httpx`)
28
+ as part of the test suite.
29
+
30
+ [Unreleased]: https://github.com/jose-pr/pathapi-httpx/compare/v0.1.0...HEAD
31
+ [0.1.0]: https://github.com/jose-pr/pathapi-httpx/releases/tag/v0.1.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Jose A
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,172 @@
1
+ Metadata-Version: 2.5
2
+ Name: pathapi-httpx
3
+ Version: 0.1.0
4
+ Summary: The httpx HTTP transports for pathapi: a synchronous one and a native async one, with HTTP/2, your own httpx client and its mounts
5
+ Project-URL: Homepage, https://github.com/jose-pr/pathapi-httpx/
6
+ Project-URL: Documentation, https://jose-pr.github.io/pathapi-httpx/
7
+ Project-URL: Issues, https://github.com/jose-pr/pathapi-httpx/issues
8
+ Author: Jose A
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.9
22
+ Requires-Dist: httpx<0.29,>=0.28.0
23
+ Requires-Dist: pathapi<0.2,>=0.1.2
24
+ Provides-Extra: dev
25
+ Requires-Dist: black; extra == 'dev'
26
+ Requires-Dist: build; extra == 'dev'
27
+ Requires-Dist: cryptography; extra == 'dev'
28
+ Requires-Dist: hatchling; extra == 'dev'
29
+ Requires-Dist: pytest; extra == 'dev'
30
+ Requires-Dist: pytest-cov; extra == 'dev'
31
+ Requires-Dist: twine; extra == 'dev'
32
+ Provides-Extra: docs
33
+ Requires-Dist: mkdocs; extra == 'docs'
34
+ Requires-Dist: mkdocs-material; extra == 'docs'
35
+ Requires-Dist: mkdocstrings[python]; extra == 'docs'
36
+ Description-Content-Type: text/markdown
37
+
38
+ # pathapi-httpx
39
+
40
+ [![Version](https://img.shields.io/pypi/v/pathapi-httpx.svg)](https://pypi.org/project/pathapi-httpx/)
41
+ [![Python versions](https://img.shields.io/pypi/pyversions/pathapi-httpx.svg)](https://pypi.org/project/pathapi-httpx/)
42
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jose-pr/pathapi-httpx/blob/main/LICENSE)
43
+ [![Docs](https://img.shields.io/badge/docs-latest-blue.svg)](https://jose-pr.github.io/pathapi-httpx/)
44
+ [![CI](https://img.shields.io/github/actions/workflow/status/jose-pr/pathapi-httpx/test.yml)](https://github.com/jose-pr/pathapi-httpx/actions/workflows/test.yml)
45
+
46
+ **The `httpx` HTTP transports for [`pathapi`](https://github.com/jose-pr/pathapi).**
47
+ `pathapi.http` sends requests with a transport built on the standard library,
48
+ and from an event loop with that transport on worker threads. Install this
49
+ distribution and `HTTPClient` sends through an `httpx.Client`, and
50
+ `AsyncHTTPClient` through an `httpx.AsyncClient` on the caller's loop, with
51
+ nothing to configure. It is documented at
52
+ <https://jose-pr.github.io/pathapi-httpx/>.
53
+
54
+ ## Features
55
+
56
+ | You want | Built-in transports | These transports |
57
+ | --- | --- | --- |
58
+ | Requests from an event loop without a worker thread per request | No: `ThreadedTransport` runs the standard-library transport on threads | Yes: `AsyncHttpxTransport` |
59
+ | HTTP/2 | No | Yes: `HttpxTransport(http2=True)` with `httpx[http2]` |
60
+ | Your own `httpx.Client` or `httpx.AsyncClient`: mounted transports, limits, proxies, `auth` | No | Yes: `HttpxTransport(client)` |
61
+ | `SSL_CERT_FILE`, `SSL_CERT_DIR` and the certifi bundle | The system trust store, or an `ssl_context=` | Yes |
62
+ | Proxies from the environment, `proxy=` and `mounts=` | `http://` proxies through a provider | Yes, as `httpx` does them |
63
+
64
+ - **Credentials belong to one server.** The `auth` of a client is sent to the
65
+ server of the first request and to no other, and the clients of `pathapi.http`
66
+ never take an `Authorization` header to another server on a redirect.
67
+ - **Exactly the headers it is given.** A client's default headers and cookies
68
+ are not added: `HTTPClient` and `AsyncHTTPClient` own the headers of a request.
69
+ - **No third-party text in an error.** A failure of `httpx` is a
70
+ `TimeoutError`, `ConnectionError`, `ssl.SSLError`, `ValueError` or
71
+ `OSError(EIO)`; what the library says, which can quote a URL, is never passed on.
72
+ - **The same promises as the built-in transports.** The tests `pathapi` runs
73
+ over its own transports run over these
74
+ (`python -m pathapi.http.testing --http-transports httpx --http-async-transports httpx`).
75
+
76
+ ## Installation
77
+
78
+ ```bash
79
+ pip install pathapi-httpx
80
+ ```
81
+
82
+ Requires `pathapi` and `httpx`, which pip installs with it. HTTP/2 needs
83
+ `pip install "httpx[http2]"`.
84
+
85
+ ## Quick start
86
+
87
+ **Send through an `httpx.Client` from threads or a script:**
88
+
89
+ ```python
90
+ import httpx
91
+ from pathapi.http import HTTPClient
92
+ from pathapi_httpx import HttpxTransport
93
+
94
+ with httpx.Client() as session:
95
+ with HTTPClient(HttpxTransport(session), timeout=(3, 30)) as client:
96
+ reply = client.request("GET", "http://files.example.org/reports/today.txt")
97
+ print(reply.read().decode())
98
+ ```
99
+
100
+ The `httpx.Client` stays yours: neither the client nor the transport closes it.
101
+
102
+ **Send from an event loop** with `httpx.AsyncClient` on that loop:
103
+
104
+ ```python
105
+ import asyncio
106
+
107
+ from pathapi.http import AsyncHTTPClient
108
+ from pathapi_httpx import AsyncHttpxTransport
109
+
110
+
111
+ async def main():
112
+ async with AsyncHttpxTransport() as transport:
113
+ async with AsyncHTTPClient(transport) as client:
114
+ replies = await asyncio.gather(
115
+ *(
116
+ client.request("GET", f"http://files.example.org/reports/{day}.txt")
117
+ for day in ("mon", "tue", "wed")
118
+ )
119
+ )
120
+ print([len(await reply.read()) for reply in replies])
121
+
122
+
123
+ asyncio.run(main())
124
+ ```
125
+
126
+ **Choose them for the whole process.** `PATHAPI_HTTP_TRANSPORT=httpx` and
127
+ `PATHAPI_HTTP_ASYNC_TRANSPORT=httpx` make every `HTTPClient` and
128
+ `AsyncHTTPClient` that was given no transport send through a new `httpx` client:
129
+
130
+ ```bash
131
+ PATHAPI_HTTP_ASYNC_TRANSPORT=httpx python -c "from pathapi.http import AsyncHTTPClient; print(type(AsyncHTTPClient().transport))"
132
+ ```
133
+
134
+ ## API overview
135
+
136
+ | Name | Purpose |
137
+ | --- | --- |
138
+ | `pathapi_httpx.HttpxTransport` | The transport: `send()` and `close()` over an `httpx.Client` |
139
+ | `pathapi_httpx.AsyncHttpxTransport` | The async transport: `await send()` and `await aclose()` over an `httpx.AsyncClient` |
140
+ | `pathapi_httpx.__version__` | The installed version |
141
+
142
+ `pathapi.http` finds the classes through the `httpx` entries of its
143
+ `pathapi.http_transports` and `pathapi.http_async_transports` entry-point
144
+ groups, which this distribution declares. The reference is in the
145
+ [API header](https://github.com/jose-pr/pathapi-httpx/blob/main/src/pathapi_httpx/AGENTS.md)
146
+ that ships with the package.
147
+
148
+ ## Development
149
+
150
+ ```bash
151
+ python -m venv .venv/3.14-posix-x86_64
152
+ .venv/3.14-posix-x86_64/bin/python -m pip install -e ".[dev]"
153
+ .venv/3.14-posix-x86_64/bin/python -m pytest -q -rs
154
+ ```
155
+
156
+ `pathapi` 0.1.2 is a dependency; while it is not on PyPI, install it
157
+ editable from its checkout first (`pip install -e ../pathapi`). The test run
158
+ includes `python -m pathapi.http.testing --http-transports httpx
159
+ --http-async-transports httpx`, the tests `pathapi` runs over its own transports.
160
+
161
+ ### Releasing
162
+
163
+ This project follows [Semantic Versioning](https://semver.org/) and keeps a
164
+ [`CHANGELOG.md`](CHANGELOG.md). Pushing a tag matching `v*` triggers the release
165
+ workflow: test gate → build (checking the tag names the version built) → a strict
166
+ docs build as a gate → GitHub release → publish. The release workflow never deploys
167
+ the docs site itself: for a final release its last job dispatches the docs workflow
168
+ at the tag, which owns every Pages deploy.
169
+
170
+ ## License
171
+
172
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,135 @@
1
+ # pathapi-httpx
2
+
3
+ [![Version](https://img.shields.io/pypi/v/pathapi-httpx.svg)](https://pypi.org/project/pathapi-httpx/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/pathapi-httpx.svg)](https://pypi.org/project/pathapi-httpx/)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jose-pr/pathapi-httpx/blob/main/LICENSE)
6
+ [![Docs](https://img.shields.io/badge/docs-latest-blue.svg)](https://jose-pr.github.io/pathapi-httpx/)
7
+ [![CI](https://img.shields.io/github/actions/workflow/status/jose-pr/pathapi-httpx/test.yml)](https://github.com/jose-pr/pathapi-httpx/actions/workflows/test.yml)
8
+
9
+ **The `httpx` HTTP transports for [`pathapi`](https://github.com/jose-pr/pathapi).**
10
+ `pathapi.http` sends requests with a transport built on the standard library,
11
+ and from an event loop with that transport on worker threads. Install this
12
+ distribution and `HTTPClient` sends through an `httpx.Client`, and
13
+ `AsyncHTTPClient` through an `httpx.AsyncClient` on the caller's loop, with
14
+ nothing to configure. It is documented at
15
+ <https://jose-pr.github.io/pathapi-httpx/>.
16
+
17
+ ## Features
18
+
19
+ | You want | Built-in transports | These transports |
20
+ | --- | --- | --- |
21
+ | Requests from an event loop without a worker thread per request | No: `ThreadedTransport` runs the standard-library transport on threads | Yes: `AsyncHttpxTransport` |
22
+ | HTTP/2 | No | Yes: `HttpxTransport(http2=True)` with `httpx[http2]` |
23
+ | Your own `httpx.Client` or `httpx.AsyncClient`: mounted transports, limits, proxies, `auth` | No | Yes: `HttpxTransport(client)` |
24
+ | `SSL_CERT_FILE`, `SSL_CERT_DIR` and the certifi bundle | The system trust store, or an `ssl_context=` | Yes |
25
+ | Proxies from the environment, `proxy=` and `mounts=` | `http://` proxies through a provider | Yes, as `httpx` does them |
26
+
27
+ - **Credentials belong to one server.** The `auth` of a client is sent to the
28
+ server of the first request and to no other, and the clients of `pathapi.http`
29
+ never take an `Authorization` header to another server on a redirect.
30
+ - **Exactly the headers it is given.** A client's default headers and cookies
31
+ are not added: `HTTPClient` and `AsyncHTTPClient` own the headers of a request.
32
+ - **No third-party text in an error.** A failure of `httpx` is a
33
+ `TimeoutError`, `ConnectionError`, `ssl.SSLError`, `ValueError` or
34
+ `OSError(EIO)`; what the library says, which can quote a URL, is never passed on.
35
+ - **The same promises as the built-in transports.** The tests `pathapi` runs
36
+ over its own transports run over these
37
+ (`python -m pathapi.http.testing --http-transports httpx --http-async-transports httpx`).
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ pip install pathapi-httpx
43
+ ```
44
+
45
+ Requires `pathapi` and `httpx`, which pip installs with it. HTTP/2 needs
46
+ `pip install "httpx[http2]"`.
47
+
48
+ ## Quick start
49
+
50
+ **Send through an `httpx.Client` from threads or a script:**
51
+
52
+ ```python
53
+ import httpx
54
+ from pathapi.http import HTTPClient
55
+ from pathapi_httpx import HttpxTransport
56
+
57
+ with httpx.Client() as session:
58
+ with HTTPClient(HttpxTransport(session), timeout=(3, 30)) as client:
59
+ reply = client.request("GET", "http://files.example.org/reports/today.txt")
60
+ print(reply.read().decode())
61
+ ```
62
+
63
+ The `httpx.Client` stays yours: neither the client nor the transport closes it.
64
+
65
+ **Send from an event loop** with `httpx.AsyncClient` on that loop:
66
+
67
+ ```python
68
+ import asyncio
69
+
70
+ from pathapi.http import AsyncHTTPClient
71
+ from pathapi_httpx import AsyncHttpxTransport
72
+
73
+
74
+ async def main():
75
+ async with AsyncHttpxTransport() as transport:
76
+ async with AsyncHTTPClient(transport) as client:
77
+ replies = await asyncio.gather(
78
+ *(
79
+ client.request("GET", f"http://files.example.org/reports/{day}.txt")
80
+ for day in ("mon", "tue", "wed")
81
+ )
82
+ )
83
+ print([len(await reply.read()) for reply in replies])
84
+
85
+
86
+ asyncio.run(main())
87
+ ```
88
+
89
+ **Choose them for the whole process.** `PATHAPI_HTTP_TRANSPORT=httpx` and
90
+ `PATHAPI_HTTP_ASYNC_TRANSPORT=httpx` make every `HTTPClient` and
91
+ `AsyncHTTPClient` that was given no transport send through a new `httpx` client:
92
+
93
+ ```bash
94
+ PATHAPI_HTTP_ASYNC_TRANSPORT=httpx python -c "from pathapi.http import AsyncHTTPClient; print(type(AsyncHTTPClient().transport))"
95
+ ```
96
+
97
+ ## API overview
98
+
99
+ | Name | Purpose |
100
+ | --- | --- |
101
+ | `pathapi_httpx.HttpxTransport` | The transport: `send()` and `close()` over an `httpx.Client` |
102
+ | `pathapi_httpx.AsyncHttpxTransport` | The async transport: `await send()` and `await aclose()` over an `httpx.AsyncClient` |
103
+ | `pathapi_httpx.__version__` | The installed version |
104
+
105
+ `pathapi.http` finds the classes through the `httpx` entries of its
106
+ `pathapi.http_transports` and `pathapi.http_async_transports` entry-point
107
+ groups, which this distribution declares. The reference is in the
108
+ [API header](https://github.com/jose-pr/pathapi-httpx/blob/main/src/pathapi_httpx/AGENTS.md)
109
+ that ships with the package.
110
+
111
+ ## Development
112
+
113
+ ```bash
114
+ python -m venv .venv/3.14-posix-x86_64
115
+ .venv/3.14-posix-x86_64/bin/python -m pip install -e ".[dev]"
116
+ .venv/3.14-posix-x86_64/bin/python -m pytest -q -rs
117
+ ```
118
+
119
+ `pathapi` 0.1.2 is a dependency; while it is not on PyPI, install it
120
+ editable from its checkout first (`pip install -e ../pathapi`). The test run
121
+ includes `python -m pathapi.http.testing --http-transports httpx
122
+ --http-async-transports httpx`, the tests `pathapi` runs over its own transports.
123
+
124
+ ### Releasing
125
+
126
+ This project follows [Semantic Versioning](https://semver.org/) and keeps a
127
+ [`CHANGELOG.md`](CHANGELOG.md). Pushing a tag matching `v*` triggers the release
128
+ workflow: test gate → build (checking the tag names the version built) → a strict
129
+ docs build as a gate → GitHub release → publish. The release workflow never deploys
130
+ the docs site itself: for a final release its last job dispatches the docs workflow
131
+ at the tag, which owns every Pages deploy.
132
+
133
+ ## License
134
+
135
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,23 @@
1
+ # The transports
2
+
3
+ `HttpxTransport` implements the HTTP transport contract of `pathapi.http`:
4
+ `send()` makes one request with an `httpx.Client` and returns an
5
+ `HTTPResponse`, and `close()` closes a client the transport made.
6
+ `AsyncHttpxTransport` implements the async contract: `await send()` makes one
7
+ request with an `httpx.AsyncClient` on the running event loop and returns an
8
+ `AsyncHTTPResponse`, and `await aclose()` closes a client the transport made.
9
+ `pathapi.http` finds them through the `httpx` entries of its
10
+ `pathapi.http_transports` and `pathapi.http_async_transports` entry-point
11
+ groups, so `PATHAPI_HTTP_TRANSPORT=httpx`,
12
+ `PATHAPI_HTTP_ASYNC_TRANSPORT=httpx`, `pathapi.http.get_transport("httpx")` and
13
+ `pathapi.http.get_async_transport("httpx")` name them.
14
+
15
+ ::: pathapi_httpx.HttpxTransport
16
+ options:
17
+ show_if_no_docstring: true
18
+ filters: ["!^_"]
19
+
20
+ ::: pathapi_httpx.AsyncHttpxTransport
21
+ options:
22
+ show_if_no_docstring: true
23
+ filters: ["!^_"]
@@ -0,0 +1,20 @@
1
+ # Benchmarks
2
+
3
+ `benchmarks/bench.py` times a batch of concurrent requests through
4
+ `AsyncHTTPClient` against a loopback server that answers each request after a
5
+ fixed delay: with `ThreadedTransport`, the default, which waits for each reply
6
+ on a worker thread, and with `AsyncHttpxTransport`, which waits on the event
7
+ loop. It also times `HTTPClient` with `HttpxTransport` and the built-in
8
+ transport on the same sequential requests.
9
+
10
+ ```bash
11
+ python benchmarks/bench.py # run the benchmarks
12
+ python benchmarks/bench.py --help # options; runs nothing
13
+ python benchmarks/bench.py --save # also write benchmarks/results/<name>.json
14
+ ```
15
+
16
+ Compare two runs on their medians, and only between runs from the same machine
17
+ and interpreter. The result schema is described in
18
+ [`benchmarks/README.md`](https://github.com/jose-pr/pathapi-httpx/blob/main/benchmarks/README.md).
19
+ A run on a developer machine is a sanity check; performance claims come from CI
20
+ runs.
@@ -0,0 +1,3 @@
1
+ # Changelog
2
+
3
+ --8<-- "CHANGELOG.md"
@@ -0,0 +1,47 @@
1
+ # pathapi-httpx
2
+
3
+ The **`httpx` HTTP transports for [`pathapi`](https://jose-pr.github.io/pathapi/)**.
4
+ `pathapi.http` sends requests with a transport built on the standard
5
+ library, and from an event loop with that transport on worker threads. Install
6
+ this distribution and `HTTPClient` sends through an `httpx.Client`, and
7
+ `AsyncHTTPClient` through an `httpx.AsyncClient` on the caller's loop, with
8
+ nothing to configure.
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ pip install pathapi-httpx
14
+ ```
15
+
16
+ HTTP/2 needs `pip install "httpx[http2]"`.
17
+
18
+ ## 30-second tour
19
+
20
+ ```python
21
+ import asyncio
22
+
23
+ from pathapi.http import AsyncHTTPClient
24
+ from pathapi_httpx import AsyncHttpxTransport
25
+
26
+
27
+ async def main():
28
+ async with AsyncHttpxTransport() as transport:
29
+ async with AsyncHTTPClient(transport) as client:
30
+ reply = await client.request(
31
+ "GET", "http://files.example.org/reports/today.txt"
32
+ )
33
+ print((await reply.read()).decode())
34
+
35
+
36
+ asyncio.run(main())
37
+ ```
38
+
39
+ `PATHAPI_HTTP_TRANSPORT=httpx` and `PATHAPI_HTTP_ASYNC_TRANSPORT=httpx` make
40
+ every `HTTPClient` and `AsyncHTTPClient` that was given no transport send
41
+ through a new `httpx` client.
42
+
43
+ ## Learn more
44
+
45
+ - [The transports](api/transport.md): `HttpxTransport` and `AsyncHttpxTransport`, their arguments and what they guarantee.
46
+ - [Benchmarks](benchmarks.md): concurrent requests through the thread-backed default and `AsyncHttpxTransport` on loopback.
47
+ - [Changelog](changelog.md)
@@ -0,0 +1,48 @@
1
+ """Fetch several files at once from an event loop with `AsyncHttpxTransport`.
2
+
3
+ The script starts a small server on the loopback interface, so it needs no
4
+ network: an `AsyncHTTPClient` over `AsyncHttpxTransport` sends the requests
5
+ together on the running loop, each from the same pool of connections.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import asyncio
11
+ import http.server
12
+ import threading
13
+
14
+ from pathapi.http import AsyncHTTPClient
15
+ from pathapi_httpx import AsyncHttpxTransport
16
+
17
+
18
+ class Handler(http.server.BaseHTTPRequestHandler):
19
+ def do_GET(self):
20
+ body = f"contents of {self.path}\n".encode()
21
+ self.send_response(200)
22
+ self.send_header("Content-Length", str(len(body)))
23
+ self.end_headers()
24
+ self.wfile.write(body)
25
+
26
+ def log_message(self, *args):
27
+ pass
28
+
29
+
30
+ async def main(base: str) -> None:
31
+ async with AsyncHttpxTransport() as transport:
32
+ async with AsyncHTTPClient(transport, timeout=(3, 30)) as client:
33
+ names = ("a.txt", "b.txt", "c.txt")
34
+ replies = await asyncio.gather(
35
+ *(client.request("GET", f"{base}/{name}") for name in names)
36
+ )
37
+ for reply in replies:
38
+ print((await reply.read()).decode(), end="")
39
+
40
+
41
+ if __name__ == "__main__":
42
+ server = http.server.ThreadingHTTPServer(("127.0.0.1", 0), Handler)
43
+ threading.Thread(target=server.serve_forever, daemon=True).start()
44
+ try:
45
+ asyncio.run(main(f"http://127.0.0.1:{server.server_port}"))
46
+ finally:
47
+ server.shutdown()
48
+ server.server_close()
@@ -0,0 +1,52 @@
1
+ site_name: pathapi-httpx
2
+ site_description: The httpx HTTP transports for pathapi
3
+ site_url: https://jose-pr.github.io/pathapi-httpx/
4
+ repo_url: https://github.com/jose-pr/pathapi-httpx
5
+ repo_name: jose-pr/pathapi-httpx
6
+ edit_uri: edit/main/docs/
7
+
8
+ # Constrain the scan to docs/ so pymdownx.snippets + strict nav don't trip on
9
+ # repo-level markdown.
10
+ docs_dir: docs
11
+
12
+ theme:
13
+ name: material
14
+ palette:
15
+ - media: "(prefers-color-scheme: light)"
16
+ scheme: default
17
+ toggle:
18
+ icon: material/brightness-7
19
+ name: Switch to dark mode
20
+ - media: "(prefers-color-scheme: dark)"
21
+ scheme: slate
22
+ toggle:
23
+ icon: material/brightness-4
24
+ name: Switch to light mode
25
+ features:
26
+ - navigation.sections
27
+ - content.code.copy
28
+
29
+ nav:
30
+ - Home: index.md
31
+ - Benchmarks: benchmarks.md
32
+ - API Reference:
33
+ - The transports: api/transport.md
34
+ - Changelog: changelog.md
35
+
36
+ plugins:
37
+ - search
38
+ - mkdocstrings:
39
+ handlers:
40
+ python:
41
+ options:
42
+ show_source: true
43
+ show_root_heading: true
44
+ merge_init_into_class: true
45
+ show_if_no_docstring: true
46
+
47
+ markdown_extensions:
48
+ - admonition
49
+ - tables
50
+ - pymdownx.snippets:
51
+ base_path: ["."]
52
+ - pymdownx.superfences