From 8874107aa4e18b7208e3a90fcd1bfd86c50789b5 Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Mon, 27 Jul 2026 13:13:07 +0200 Subject: [PATCH] Add pyreqwest transport --- src/httpx2/httpx2/__init__.py | 2 + src/httpx2/httpx2/_transports/__init__.py | 3 + src/httpx2/httpx2/_transports/pyreqwest.py | 129 +++++++++++++++++++++ src/httpx2/pyproject.toml | 3 + tests/httpx2/test_pyreqwest_transport.py | 38 ++++++ uv.lock | 66 ++++++++++- 6 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 src/httpx2/httpx2/_transports/pyreqwest.py create mode 100644 tests/httpx2/test_pyreqwest_transport.py diff --git a/src/httpx2/httpx2/__init__.py b/src/httpx2/httpx2/__init__.py index 3c43a988..c95a3ed1 100644 --- a/src/httpx2/httpx2/__init__.py +++ b/src/httpx2/httpx2/__init__.py @@ -23,6 +23,7 @@ "AsyncByteStream", "AsyncClient", "AsyncHTTPTransport", + "AsyncPyreqwestTransport", "Auth", "BaseTransport", "BasicAuth", @@ -59,6 +60,7 @@ "ProtocolError", "Proxy", "ProxyError", + "PyreqwestTransport", "put", "query", "QueryParams", diff --git a/src/httpx2/httpx2/_transports/__init__.py b/src/httpx2/httpx2/_transports/__init__.py index 6a83e1d3..1fbec3c8 100644 --- a/src/httpx2/httpx2/_transports/__init__.py +++ b/src/httpx2/httpx2/_transports/__init__.py @@ -2,6 +2,7 @@ from .base import AsyncBaseTransport, BaseTransport from .default import AsyncHTTPTransport, HTTPTransport from .mock import MockTransport +from .pyreqwest import AsyncPyreqwestTransport, PyreqwestTransport from .wsgi import WSGITransport __all__ = [ @@ -11,5 +12,7 @@ "AsyncHTTPTransport", "HTTPTransport", "MockTransport", + "AsyncPyreqwestTransport", + "PyreqwestTransport", "WSGITransport", ] diff --git a/src/httpx2/httpx2/_transports/pyreqwest.py b/src/httpx2/httpx2/_transports/pyreqwest.py new file mode 100644 index 00000000..981a3fc5 --- /dev/null +++ b/src/httpx2/httpx2/_transports/pyreqwest.py @@ -0,0 +1,129 @@ +from __future__ import annotations + +import typing +from collections.abc import AsyncIterator, Iterator +from contextlib import AsyncExitStack, ExitStack + +from .._models import Request, Response +from .._types import AsyncByteStream, SyncByteStream +from .base import AsyncBaseTransport, BaseTransport + +if typing.TYPE_CHECKING: # pragma: no cover + from pyreqwest.client import Client as PyreqwestClient, SyncClient as PyreqwestSyncClient + from pyreqwest.response import Response as PyreqwestResponse, SyncResponse as PyreqwestSyncResponse + +__all__ = ["AsyncPyreqwestTransport", "PyreqwestTransport"] + + +def _load_pyreqwest_sync_client_builder() -> typing.Any: + try: + from pyreqwest.client import SyncClientBuilder + except ImportError as exc: # pragma: no cover + msg = "Using 'PyreqwestTransport' requires installing the 'pyreqwest' package." + raise RuntimeError(msg) from exc + return SyncClientBuilder + + +def _load_pyreqwest_async_client_builder() -> typing.Any: + try: + from pyreqwest.client import ClientBuilder + except ImportError as exc: # pragma: no cover + msg = "Using 'AsyncPyreqwestTransport' requires installing the 'pyreqwest' package." + raise RuntimeError(msg) from exc + return ClientBuilder + + +def _http_version(version: str) -> bytes: + if version == "HTTP/2.0": + version = "HTTP/2" + return version.encode("ascii", errors="ignore") + + +class _PyreqwestStream(SyncByteStream): + def __init__(self, response: PyreqwestSyncResponse, exit_stack: ExitStack) -> None: + self._reader = response.body_reader + self._exit_stack = exit_stack + + def __iter__(self) -> Iterator[bytes]: + while (chunk := self._reader.read()) is not None: + yield bytes(chunk) + + def close(self) -> None: + self._exit_stack.close() + + +class _AsyncPyreqwestStream(AsyncByteStream): + def __init__(self, response: PyreqwestResponse, exit_stack: AsyncExitStack) -> None: + self._reader = response.body_reader + self._exit_stack = exit_stack + + async def __aiter__(self) -> AsyncIterator[bytes]: + while (chunk := await self._reader.read()) is not None: + yield bytes(chunk) + + async def aclose(self) -> None: + await self._exit_stack.aclose() + + +class PyreqwestTransport(BaseTransport): + """A sync transport backed by pyreqwest/reqwest.""" + + def __init__(self, client: PyreqwestSyncClient | None = None, *, close_client: bool = True) -> None: + SyncClientBuilder = _load_pyreqwest_sync_client_builder() + self._client = client or SyncClientBuilder().follow_redirects(False).default_cookie_store(False).build() + self._close_client = client is None or close_client + + def handle_request(self, request: Request) -> Response: + content = request.read() + builder = self._client.request(request.method, str(request.url)).headers(request.headers.multi_items()) + if content: + builder = builder.body_bytes(content) + + exit_stack = ExitStack() + try: + response = exit_stack.enter_context(builder.build_streamed()) + return Response( + response.status, + headers=list(response.headers.items()), + stream=_PyreqwestStream(response, exit_stack), + extensions={"http_version": _http_version(response.version)}, + ) + except BaseException: + exit_stack.close() + raise + + def close(self) -> None: + if self._close_client: + self._client.close() + + +class AsyncPyreqwestTransport(AsyncBaseTransport): + """An async transport backed by pyreqwest/reqwest.""" + + def __init__(self, client: PyreqwestClient | None = None, *, close_client: bool = True) -> None: + ClientBuilder = _load_pyreqwest_async_client_builder() + self._client = client or ClientBuilder().follow_redirects(False).default_cookie_store(False).build() + self._close_client = client is None or close_client + + async def handle_async_request(self, request: Request) -> Response: + content = await request.aread() + builder = self._client.request(request.method, str(request.url)).headers(request.headers.multi_items()) + if content: + builder = builder.body_bytes(content) + + exit_stack = AsyncExitStack() + try: + response = await exit_stack.enter_async_context(builder.build_streamed()) + return Response( + response.status, + headers=list(response.headers.items()), + stream=_AsyncPyreqwestStream(response, exit_stack), + extensions={"http_version": _http_version(response.version)}, + ) + except BaseException: + await exit_stack.aclose() + raise + + async def aclose(self) -> None: + if self._close_client: + await self._client.close() diff --git a/src/httpx2/pyproject.toml b/src/httpx2/pyproject.toml index 8fb99957..40b1e9c4 100644 --- a/src/httpx2/pyproject.toml +++ b/src/httpx2/pyproject.toml @@ -64,6 +64,9 @@ cli = [ http2 = [ "h2>=3,<5", ] +pyreqwest = [ + "pyreqwest>=0.12; python_version >= '3.11'", +] socks = [ "socksio==1.*", ] diff --git a/tests/httpx2/test_pyreqwest_transport.py b/tests/httpx2/test_pyreqwest_transport.py new file mode 100644 index 00000000..a2f3442b --- /dev/null +++ b/tests/httpx2/test_pyreqwest_transport.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +import asyncio + +import pytest + +import httpx2 + +pytest.importorskip("pyreqwest") + + +def test_pyreqwest_transport(server: object) -> None: + with httpx2.Client(transport=httpx2.PyreqwestTransport()) as client: + response = client.get(str(server.url)) # type: ignore[attr-defined] + + assert response.status_code == 200 + assert response.text == "Hello, world!" + assert response.http_version == "HTTP/1.1" + + +def test_async_pyreqwest_transport(server: object) -> None: + async def run() -> httpx2.Response: + async with httpx2.AsyncClient(transport=httpx2.AsyncPyreqwestTransport()) as client: + return await client.get(str(server.url)) # type: ignore[attr-defined] + + response = asyncio.run(run()) + + assert response.status_code == 200 + assert response.text == "Hello, world!" + assert response.http_version == "HTTP/1.1" + + +def test_pyreqwest_transport_request_body(server: object) -> None: + with httpx2.Client(transport=httpx2.PyreqwestTransport()) as client: + response = client.post(str(server.url.copy_with(path="/echo_body")), content=b"hello") # type: ignore[attr-defined] + + assert response.status_code == 200 + assert response.content == b"hello" diff --git a/uv.lock b/uv.lock index af58e71f..8cd864c7 100644 --- a/uv.lock +++ b/uv.lock @@ -1381,6 +1381,9 @@ cli = [ http2 = [ { name = "h2" }, ] +pyreqwest = [ + { name = "pyreqwest", marker = "python_full_version >= '3.11'" }, +] socks = [ { name = "socksio" }, ] @@ -1401,6 +1404,7 @@ requires-dist = [ { name = "httpcore2", editable = "src/httpcore2" }, { name = "idna", specifier = ">=3.18" }, { name = "pygments", marker = "extra == 'cli'", specifier = "==2.*" }, + { name = "pyreqwest", marker = "python_full_version >= '3.11' and extra == 'pyreqwest'", specifier = ">=0.12" }, { name = "rich", marker = "extra == 'cli'", specifier = ">=10,<16" }, { name = "socksio", marker = "extra == 'socks'", specifier = "==1.*" }, { name = "truststore", specifier = ">=0.10" }, @@ -1408,7 +1412,7 @@ requires-dist = [ { name = "wsproto", marker = "extra == 'ws'", specifier = ">=1.2" }, { name = "zstandard", marker = "python_full_version < '3.14' and extra == 'zstd'", specifier = ">=0.18.0" }, ] -provides-extras = ["brotli", "cli", "http2", "socks", "ws", "zstd"] +provides-extras = ["brotli", "cli", "http2", "pyreqwest", "socks", "ws", "zstd"] [[package]] name = "hyperframe" @@ -2745,6 +2749,66 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" }, ] +[[package]] +name = "pyreqwest" +version = "0.12.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/41/66/83daa8c9f14298b7f834f97df2a0b39a0b0ecee5b0fdabfcd56babe65422/pyreqwest-0.12.0.tar.gz", hash = "sha256:87df245c4ccbeadbba441902bc2d4d051bfacc3bd026f6d301edb3c12e7668e1", size = 4599597, upload-time = "2026-05-09T10:36:30.39Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/97/41961935282c105e8883e6f39d580852dbe129d60cd64f3f5975d3337ff9/pyreqwest-0.12.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:42896bba3722773841f8b9e998125a44f27f31bb0d6828956f4c2b7f272d3c80", size = 4077686, upload-time = "2026-05-09T10:36:27.198Z" }, + { url = "https://files.pythonhosted.org/packages/b1/17/a0d773b9d2ee094548084895a77382587d14d08f0744b179d80ed683c562/pyreqwest-0.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:29d8e7f33100fa81a8a5ecf11d96b954e6db0d6013d81b307eaa266716c63453", size = 3855419, upload-time = "2026-05-09T10:36:28.788Z" }, + { url = "https://files.pythonhosted.org/packages/d8/38/2a497dd5ef7b0c33088c564eff83e0816bce17642fc0f4566fbf50da29ed/pyreqwest-0.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5fdcab660434da3005dd014f2bbe05073e936ffcc71039e6aaf75e829867c93", size = 3910413, upload-time = "2026-05-09T10:37:02.067Z" }, + { url = "https://files.pythonhosted.org/packages/13/ec/0ea894472c98998b7337cecebc9d545510a3f2147b5269d014151315594f/pyreqwest-0.12.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ee3508746fa9c0c97b09c2d67d8cebf356cb970690cb79e1a6d211a47eea950", size = 3441116, upload-time = "2026-05-09T10:36:33.483Z" }, + { url = "https://files.pythonhosted.org/packages/8e/5a/17869ebc60ccd5d992559630af01dd3f37fea40fafacf30997d0b1834f96/pyreqwest-0.12.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c27a05ade182aa0ad9e1c85368934f8e485254c408b85ee804431da5384a1998", size = 3820813, upload-time = "2026-05-09T10:36:51.576Z" }, + { url = "https://files.pythonhosted.org/packages/57/77/144b4d628dfafebd8de8d28d9a14301d8eeb90c4dc53b38ecb273a380a75/pyreqwest-0.12.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff977173863e10dc23cd236487b7587aaff3a024cf298ef9a16bf5d575098642", size = 3869529, upload-time = "2026-05-09T10:36:01.639Z" }, + { url = "https://files.pythonhosted.org/packages/ed/e5/31e98f456a9db3c102ed6dfba57fb4e4b7ebb48289b7ac707d8fd6768559/pyreqwest-0.12.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7885ca34081c05b4fe5d898a6463614260004c9d93767a6d999a4750d753ff30", size = 3767975, upload-time = "2026-05-09T10:36:36.345Z" }, + { url = "https://files.pythonhosted.org/packages/46/71/1403a5fd7acba6b1ab2d3d092c255f5b0527cfe95ce3de36b091e7749ece/pyreqwest-0.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92c2a0799bc1eff8f729ff145296aecad1f5c5bf9e863b22bf219239e01a8380", size = 4140007, upload-time = "2026-05-09T10:37:14.33Z" }, + { url = "https://files.pythonhosted.org/packages/f0/1c/e19b7649ead9009566004323ac4ca61324f6e7dc98c66f47f28dfcbb652e/pyreqwest-0.12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ad01d9ff63ebc670fe556d18dea068c8bc811e1909a3c5184c76131bf08e10f3", size = 4089667, upload-time = "2026-05-09T10:36:18.239Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a1/f8b4b3b19eef339b1932d9e0621bd3f3a319e11314c82b26094d5e71d672/pyreqwest-0.12.0-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:86f1125f1c87bf2474609bc2b091bbb20e4b46f5687a754c8b1a72c3445f8abc", size = 3712695, upload-time = "2026-05-09T10:36:50.068Z" }, + { url = "https://files.pythonhosted.org/packages/55/44/9b37073742b88efbf112b4ef1d04d8a39744770247df15e0bbd15a1a7145/pyreqwest-0.12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a38639623ae5e78a9ee825b066768bbfade1ca0366e68ff84dc943fd41979bbd", size = 4384832, upload-time = "2026-05-09T10:37:06.313Z" }, + { url = "https://files.pythonhosted.org/packages/db/8e/8ae5de73f64f2936fc530f112c61ed215e2b4551ddb3063b94941cd7d929/pyreqwest-0.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:089fb8f84a02a76d5e49cd261281f77af51e8ac5788ad8bb52e9d1bbd34c5c9c", size = 3883948, upload-time = "2026-05-09T10:36:19.485Z" }, + { url = "https://files.pythonhosted.org/packages/77/ad/72b4558e2f22212d66afca537449da4ac443bccb0f4fb0ce24354188019d/pyreqwest-0.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:52713aad835c3ecb39521c29ad474775fbfb0b26bf5722b7daef45b301970eb9", size = 3878621, upload-time = "2026-05-09T10:37:05.081Z" }, + { url = "https://files.pythonhosted.org/packages/4b/54/a3cb529023975165569e6e07f5007ab9698bcfa6d4b6dc44b83f825e527d/pyreqwest-0.12.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0e9dcfcf3b6a02da10db8eb7ebddf4b319a29d54c591e3db2f0e6d01b7fadd1a", size = 4074985, upload-time = "2026-05-09T10:36:54.456Z" }, + { url = "https://files.pythonhosted.org/packages/19/2d/e13e87ee6eb1469c68d64f1196c065e5542906b9bf342c0d51e2978357ae/pyreqwest-0.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3d7c4c106d26d943a04bb672168dabd4f63047c944ad423d780aef72e97c6ce4", size = 3855681, upload-time = "2026-05-09T10:36:09.088Z" }, + { url = "https://files.pythonhosted.org/packages/2a/64/f64887d0fa8e28dbe6c92afe8adba3e5821a041aa7947f31648db9bc867f/pyreqwest-0.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ed9d66a834b18502b7d4026fff3596be56a62e9146f380784942c54ff947c34", size = 3917695, upload-time = "2026-05-09T10:36:32.071Z" }, + { url = "https://files.pythonhosted.org/packages/66/58/79f00f05ee384d9d2a58f587f7220bca542771ad5d0701e50371a11b1877/pyreqwest-0.12.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:737f254a3e880b6e50284106526a9b56219cc2600527ba418e5658dcaf5f3587", size = 3443079, upload-time = "2026-05-09T10:36:10.443Z" }, + { url = "https://files.pythonhosted.org/packages/1e/f0/b1961f59e60b1f6048c2a5b2bdba607ce010e4b2d843fad23d12be3e8f01/pyreqwest-0.12.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:16f579a9cf8a54750a6bec70268495bd5fab8047dc667bad3f3712e171b48bf0", size = 3830881, upload-time = "2026-05-09T10:36:37.842Z" }, + { url = "https://files.pythonhosted.org/packages/91/4d/c3648848aa5ab733201b548f7eaf9f675d9f7d8d3cd8b48fdc35af2bb91a/pyreqwest-0.12.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:211707a376c6ac366bec5b7fe0bba122cec9da7bdef2f8d64130879066ea0300", size = 3866819, upload-time = "2026-05-09T10:36:34.752Z" }, + { url = "https://files.pythonhosted.org/packages/58/23/a97802e2bdbf69b8bb59f451b3bf3ae7938f49e0f40e0fddc53e1a3c7d2b/pyreqwest-0.12.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1d61f2ee81ed1e9d9062c54bc8b8d998c0856eea05d818240ec9ccc5a2c15b0", size = 3763624, upload-time = "2026-05-09T10:36:53.186Z" }, + { url = "https://files.pythonhosted.org/packages/ed/50/b0cbc0d89f2b2eda6cdbb85cd565ba9fb18fcb5ce267d267adafa5f3416e/pyreqwest-0.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c056dc5e835d4764e1f9ac7bb4d80e5a2ec705c3e4704ab7440b772f72a5a320", size = 4146394, upload-time = "2026-05-09T10:36:22.554Z" }, + { url = "https://files.pythonhosted.org/packages/d2/9f/2576fa7178061bf9080275b890418467f5e8231a3d2fb55611d53e7d8c51/pyreqwest-0.12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:978e28842372e5d16a01c3abe2738294362f4e8e9b2d9e53e8e587bdb6eefbd0", size = 4098452, upload-time = "2026-05-09T10:36:03.555Z" }, + { url = "https://files.pythonhosted.org/packages/34/b8/cae3706afa03a7d5630d2972a69103f218321ecf906550dac58b9c4c3712/pyreqwest-0.12.0-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:43793fb68b4e526cc7e9b8f77b0a34be733b09e6d53257168e3fb1b1a6e05aff", size = 3715271, upload-time = "2026-05-09T10:36:56.256Z" }, + { url = "https://files.pythonhosted.org/packages/bd/96/756cbf38636e4d3e085a82a9a3c0255380795e5e8d23d62fd652a74ea5c9/pyreqwest-0.12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5797997820a5160d677d791c764836ca9fe1457ba65a22c622306619bf491bf1", size = 4394492, upload-time = "2026-05-09T10:37:07.596Z" }, + { url = "https://files.pythonhosted.org/packages/d9/05/1a038cd2b1b00080688036bf4bc0dbbf95ff26aed040aeef9d2e1ab90ee1/pyreqwest-0.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:17a5183fd813f9857015ba231c9d71ef6600dff40def032d5765dc3068ac6256", size = 3876049, upload-time = "2026-05-09T10:36:07.822Z" }, + { url = "https://files.pythonhosted.org/packages/c0/60/cdd0b2b8d1883b64de91127d1d05aae86d53cfc353d00568b04d73d85c4e/pyreqwest-0.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:aa84203d97e24656cd28ee9a51bbc9719da5e699d52d0dd31d8cb26ca2d48d38", size = 3870185, upload-time = "2026-05-09T10:37:17.434Z" }, + { url = "https://files.pythonhosted.org/packages/07/91/bfd9a69902502e1fd26eb222aba7a9a633d7e49be8033e63fcc4d10b65c1/pyreqwest-0.12.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3761e5096e98fed792baa127f739e4ef89cbb62bdd839ce289fbe9b5b7034953", size = 4079512, upload-time = "2026-05-09T10:37:18.774Z" }, + { url = "https://files.pythonhosted.org/packages/9a/af/671ac67a49d4f65a4b86ee75f4362ddccf1bb90c263c93c0f4ef653911d0/pyreqwest-0.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1cc863a8ba984feb6a6b98bb411acca544d5c5695e929578c61ee92a9c2a25ac", size = 3858409, upload-time = "2026-05-09T10:36:13.207Z" }, + { url = "https://files.pythonhosted.org/packages/67/48/bb70839cfa1a6980941e8bdeae0f117924234f607f6c6ffc79d520c6fede/pyreqwest-0.12.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa3dfa27ad694084da2c891b7c08de3186ed9a96d31b7f32db55ea1412bbf9c0", size = 3921613, upload-time = "2026-05-09T10:36:11.778Z" }, + { url = "https://files.pythonhosted.org/packages/f0/1c/c547e58d2be8043fce4a093c4513c2346333076536758e0d22cf9569fb85/pyreqwest-0.12.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7950aad440737a0e714c02d25b9ef77c1bda89e77c68322359dfd896e6f3616d", size = 3442967, upload-time = "2026-05-09T10:37:15.629Z" }, + { url = "https://files.pythonhosted.org/packages/50/52/14c80f10d7d6b2dfa335472ad9b41664e1eea99c5b8c1d692e55c82f33fe/pyreqwest-0.12.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcb23311f364db53c96758bb4d96170779e89804df6c6ba413d940fe16834d21", size = 3830709, upload-time = "2026-05-09T10:37:11.654Z" }, + { url = "https://files.pythonhosted.org/packages/bc/53/e2a4af42e60251519828de345dcf8b7b7a1bbedbd76ef0b13d0178838861/pyreqwest-0.12.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38cbadb941a83daa1b1abb7cc46e45d653932b68e0a18c3d1f11fa0657a3a51", size = 3870163, upload-time = "2026-05-09T10:36:40.994Z" }, + { url = "https://files.pythonhosted.org/packages/b5/cd/e746bb01435f07b226455f7d0fa3c2eba1a0b98246b411a58e1d85c4a5fd/pyreqwest-0.12.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab515cc53be81e453b2b2b48c671451101faa1992c031fe8b3d4474ee3903a0b", size = 3767501, upload-time = "2026-05-09T10:36:39.148Z" }, + { url = "https://files.pythonhosted.org/packages/cf/4b/8c37fdfb0326445b2dfc3a00d9de2fc89735bd4c2c3131a51e19cfbeba4f/pyreqwest-0.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7add2883452a99db1b1113a1952741a884806819af4612d9d91ee41cfc3b8b4e", size = 4151395, upload-time = "2026-05-09T10:36:24.34Z" }, + { url = "https://files.pythonhosted.org/packages/67/ae/933b04aefafa20d4735d4f2b71b3aab1638fefe8d1a29374cd944fad1baa/pyreqwest-0.12.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:5df972a69ffac298d81ee3d158d16f6a478005ca69eb8157f3f26b8542fee7d6", size = 4100188, upload-time = "2026-05-09T10:36:14.905Z" }, + { url = "https://files.pythonhosted.org/packages/09/bd/cb8efbaf62f69d7672f90a0d9b1928eef790ecc0274e964b0aa25823e967/pyreqwest-0.12.0-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:970aa6ea23a82d8a16cc7cd18a71397f1596aaecd77a23300e06bcbf6d3bd71a", size = 3714998, upload-time = "2026-05-09T10:36:45.253Z" }, + { url = "https://files.pythonhosted.org/packages/ef/ec/0602f6842ab204c59e6a52b7c8e70adc03fdd9e873abf3bc4095d645c398/pyreqwest-0.12.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d8bc8c3529265ab309679960f5e4364ab6bfa481c59146b5087ca086765b4e95", size = 4397663, upload-time = "2026-05-09T10:36:42.261Z" }, + { url = "https://files.pythonhosted.org/packages/42/07/44872ef091a39c43187879dc115540586620d3b0afcc44145f4f7844f962/pyreqwest-0.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:0017fcfdbe965de1b1fab549e37903c7c9d42e57c458d8f862b35d9375b9b97c", size = 3881150, upload-time = "2026-05-09T10:36:16.617Z" }, + { url = "https://files.pythonhosted.org/packages/fa/81/1548af3f48f1fc285f007e063cac8f4395750b2bec57b20b8ad177a80e1b/pyreqwest-0.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:f83368c0bcb9ab1ca840169ac037fadb3671e5408dab085ff32aa7c276b9ecb9", size = 3876204, upload-time = "2026-05-09T10:36:57.938Z" }, + { url = "https://files.pythonhosted.org/packages/f1/96/2b94d738f3045d075f29b1589f9b4fd86d58483b055fefdbd9afe21f8933/pyreqwest-0.12.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:96948f828a91d1bf201a3d6a87fd84a6fcbfb87b7d859d986e3983b7f2e25408", size = 4072031, upload-time = "2026-05-09T10:36:59.171Z" }, + { url = "https://files.pythonhosted.org/packages/25/93/6d6c89933ec9b8f5717354a2b89a185183c95d53050c21ef892aa53f2e70/pyreqwest-0.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7eeb157e836593f46145c9b2bd8c1e014f534e5d112493e236f13fa80b6a40db", size = 3844517, upload-time = "2026-05-09T10:36:05.032Z" }, + { url = "https://files.pythonhosted.org/packages/10/55/fc2152326c468d36c66b209d3756ad441fa0a515d51f13f57424a15a3f4a/pyreqwest-0.12.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94c0e5b510dbd3b6c15fd84d3d634f9984981efe2900f5dea0c5c47e84846084", size = 3909407, upload-time = "2026-05-09T10:36:06.541Z" }, + { url = "https://files.pythonhosted.org/packages/61/52/1b3bfd850ad97b0395d6aad3de8982ac7edbd9c2c3fd2f5db94f60047da2/pyreqwest-0.12.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:52e48cc8ea89380fe638ea5eeaa89aeb51131db4ec2f002e4eba277753e5b549", size = 3434817, upload-time = "2026-05-09T10:36:43.952Z" }, + { url = "https://files.pythonhosted.org/packages/ab/7c/4bd39ec0761d349009a57b89c5ba2f8d98c17e734692d5b121c2cbeedc1a/pyreqwest-0.12.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbf8ac435bad4af32f9821dd0a264713f53918e36c438f99ac949a83d5c93ff0", size = 3818957, upload-time = "2026-05-09T10:36:25.759Z" }, + { url = "https://files.pythonhosted.org/packages/23/67/0fe1e78a4004f16996383240d2de732101c9369ec3f80ce397052cfe8f5c/pyreqwest-0.12.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6e0cd8cc70991eb633f48d72b6f7eb95ca824efb6b99e3a96778284a99627f4e", size = 3864215, upload-time = "2026-05-09T10:36:20.781Z" }, + { url = "https://files.pythonhosted.org/packages/40/ca/63760095e1fdeff55461209fb6b83c924d07b6fb821727ffedeb8054c36d/pyreqwest-0.12.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d79900cb355b71500a7c901275e8c1906a1a24f2749fd0c26e843249b83678e", size = 3756697, upload-time = "2026-05-09T10:36:46.556Z" }, + { url = "https://files.pythonhosted.org/packages/fc/ad/92a92d0e16cdb9e5f93b8fe515fc4244718c2afa2ac7b3bffedd59d3ba18/pyreqwest-0.12.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9787cf6ecf59b66899d3d07724abdf5ef9e35e4207529d26904fd02e2fc0dcd", size = 4141781, upload-time = "2026-05-09T10:37:00.773Z" }, + { url = "https://files.pythonhosted.org/packages/ed/8b/2719b2c7f4edfd33021ebe6d79876353b0c5adab2c528a2ac45d24befe5e/pyreqwest-0.12.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:2d1456837b86c8bbf817c4cb21340054f90bab2e8a6185b7d88aa5d81a6ca8fa", size = 4089345, upload-time = "2026-05-09T10:37:09.06Z" }, + { url = "https://files.pythonhosted.org/packages/a4/4c/125801c849e967c418cdf176d88b04d4325435699cbc40c8b566f38190ae/pyreqwest-0.12.0-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b3944dbce6560281b730e62b33c4e670afb7d5a2230fa381cf237a5da052fc96", size = 3707394, upload-time = "2026-05-09T10:36:48.312Z" }, + { url = "https://files.pythonhosted.org/packages/24/69/441e8212d1ae9b73ec0f585a945396982f57f7a329b86e50e0657fa5a196/pyreqwest-0.12.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:9621293fbf13594f1aee6b7e00bb2f3984c74ac567021713813ecaedbfc3e592", size = 4388221, upload-time = "2026-05-09T10:37:12.998Z" }, + { url = "https://files.pythonhosted.org/packages/65/19/253b64bddc714eff668172f07a85ea0ac183bc074813c3c69e59bc4c443b/pyreqwest-0.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:1f432a45e53487a24dbc462062bb242260349438554c50f7ed4c0b917da2912d", size = 3872727, upload-time = "2026-05-09T10:37:10.43Z" }, + { url = "https://files.pythonhosted.org/packages/7a/6a/87ee9ea0ce4ac9817dcaac4b16ab1be71886024c9bbc744a72341a7752b2/pyreqwest-0.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:b5fd5c6412e66fe46ee1e9b84d4c640f9e3aabe5c57ed10db7050a513a45319c", size = 3862797, upload-time = "2026-05-09T10:37:03.314Z" }, +] + [[package]] name = "pytest" version = "9.1.1"