pyreqwest 0.9.0__cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl

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 (52) hide show
  1. pyreqwest/__init__.py +3 -0
  2. pyreqwest/__init__.pyi +1 -0
  3. pyreqwest/_pyreqwest.cpython-313-s390x-linux-gnu.so +0 -0
  4. pyreqwest/bytes/__init__.py +16 -0
  5. pyreqwest/bytes/__init__.pyi +106 -0
  6. pyreqwest/client/__init__.py +21 -0
  7. pyreqwest/client/__init__.pyi +359 -0
  8. pyreqwest/client/types.py +54 -0
  9. pyreqwest/compatibility/__init__.py +4 -0
  10. pyreqwest/compatibility/httpx/__init__.py +11 -0
  11. pyreqwest/compatibility/httpx/_internal.py +60 -0
  12. pyreqwest/compatibility/httpx/transport.py +154 -0
  13. pyreqwest/cookie/__init__.py +5 -0
  14. pyreqwest/cookie/__init__.pyi +174 -0
  15. pyreqwest/exceptions/__init__.py +193 -0
  16. pyreqwest/http/__init__.py +19 -0
  17. pyreqwest/http/__init__.pyi +344 -0
  18. pyreqwest/logging/__init__.py +7 -0
  19. pyreqwest/logging/__init__.pyi +4 -0
  20. pyreqwest/middleware/__init__.py +5 -0
  21. pyreqwest/middleware/__init__.pyi +12 -0
  22. pyreqwest/middleware/asgi/__init__.py +5 -0
  23. pyreqwest/middleware/asgi/asgi.py +168 -0
  24. pyreqwest/middleware/types.py +26 -0
  25. pyreqwest/multipart/__init__.py +5 -0
  26. pyreqwest/multipart/__init__.pyi +75 -0
  27. pyreqwest/proxy/__init__.py +5 -0
  28. pyreqwest/proxy/__init__.pyi +47 -0
  29. pyreqwest/py.typed +0 -0
  30. pyreqwest/pytest_plugin/__init__.py +8 -0
  31. pyreqwest/pytest_plugin/internal/__init__.py +0 -0
  32. pyreqwest/pytest_plugin/internal/assert_eq.py +6 -0
  33. pyreqwest/pytest_plugin/internal/assert_message.py +123 -0
  34. pyreqwest/pytest_plugin/internal/matcher.py +34 -0
  35. pyreqwest/pytest_plugin/internal/plugin.py +15 -0
  36. pyreqwest/pytest_plugin/mock.py +512 -0
  37. pyreqwest/pytest_plugin/types.py +26 -0
  38. pyreqwest/request/__init__.py +29 -0
  39. pyreqwest/request/__init__.pyi +218 -0
  40. pyreqwest/response/__init__.py +19 -0
  41. pyreqwest/response/__init__.pyi +157 -0
  42. pyreqwest/simple/__init__.py +1 -0
  43. pyreqwest/simple/request/__init__.py +21 -0
  44. pyreqwest/simple/request/__init__.pyi +29 -0
  45. pyreqwest/simple/sync_request/__init__.py +21 -0
  46. pyreqwest/simple/sync_request/__init__.pyi +29 -0
  47. pyreqwest/types.py +12 -0
  48. pyreqwest-0.9.0.dist-info/METADATA +148 -0
  49. pyreqwest-0.9.0.dist-info/RECORD +52 -0
  50. pyreqwest-0.9.0.dist-info/WHEEL +5 -0
  51. pyreqwest-0.9.0.dist-info/entry_points.txt +2 -0
  52. pyreqwest-0.9.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,218 @@
1
+ from datetime import timedelta
2
+ from typing import Any, Self
3
+
4
+ from pyreqwest.bytes import Bytes
5
+ from pyreqwest.http import HeaderMap, Url
6
+ from pyreqwest.middleware.types import Middleware, SyncMiddleware
7
+ from pyreqwest.multipart import FormBuilder
8
+ from pyreqwest.response import Response, SyncResponse
9
+ from pyreqwest.types import ExtensionsType, FormParams, HeadersType, QueryParams, Stream, SyncStream
10
+
11
+ class Request:
12
+ @property
13
+ def method(self) -> str:
14
+ """Get the HTTP method. (e.g. GET, POST)."""
15
+
16
+ @method.setter
17
+ def method(self, value: str) -> None:
18
+ """Set the HTTP method."""
19
+
20
+ @property
21
+ def url(self) -> Url:
22
+ """Get the url."""
23
+
24
+ @url.setter
25
+ def url(self, value: Url | str) -> None:
26
+ """Set the url."""
27
+
28
+ @property
29
+ def headers(self) -> HeaderMap:
30
+ """Get the headers. This is not a copy. Modifying it modifies the request."""
31
+
32
+ @headers.setter
33
+ def headers(self, headers: HeadersType) -> None:
34
+ """Replace headers. Given value is copied."""
35
+
36
+ @property
37
+ def body(self) -> "RequestBody | None":
38
+ """Get the body."""
39
+
40
+ @body.setter
41
+ def body(self, value: "RequestBody | None") -> None:
42
+ """Set the body or remove body."""
43
+
44
+ @property
45
+ def extensions(self) -> dict[str, Any]:
46
+ """Arbitrary per-request data storage. Useful for passing through data to middleware and response."""
47
+
48
+ @extensions.setter
49
+ def extensions(self, value: ExtensionsType) -> None:
50
+ """Replace extensions. Given value is shallow copied."""
51
+
52
+ def copy(self) -> Self:
53
+ """Copy the request. Byte-bodies are zero-copied. Stream bodies are re-created via their own copy logic."""
54
+
55
+ def __copy__(self) -> Self: ...
56
+ def repr_full(self) -> str:
57
+ """Verbose repr including non-sensitive headers and body summary."""
58
+
59
+ @classmethod
60
+ def from_request_and_body(cls, request: Self, body: "RequestBody | None") -> Self:
61
+ """Clone request with a new body instance."""
62
+
63
+ class ConsumedRequest(Request):
64
+ """Request that will fully read the response body when sent."""
65
+
66
+ async def send(self) -> Response:
67
+ """Execute the request returning a Response with fully read response body."""
68
+
69
+ class StreamRequest(Request):
70
+ """Request whose response body is streamed."""
71
+
72
+ async def __aenter__(self) -> Response:
73
+ """Execute the request returning a Response with streaming response body."""
74
+
75
+ async def __aexit__(self, *args: object, **kwargs: Any) -> None:
76
+ """Close streaming response."""
77
+
78
+ @property
79
+ def read_buffer_limit(self) -> int:
80
+ """Max bytes buffered when reading streamed body."""
81
+
82
+ class SyncConsumedRequest(Request):
83
+ """Synchronous request that will fully read the response body when sent."""
84
+
85
+ def send(self) -> SyncResponse:
86
+ """Execute the request returning a Response with fully read response body."""
87
+
88
+ class SyncStreamRequest(Request):
89
+ """Synchronous request whose response body is streamed."""
90
+
91
+ def __enter__(self) -> SyncResponse:
92
+ """Execute the request returning a Response with streaming response body."""
93
+
94
+ def __exit__(self, *args: object, **kwargs: Any) -> None:
95
+ """Close streaming response."""
96
+
97
+ @property
98
+ def read_buffer_limit(self) -> int:
99
+ """Max bytes buffered when reading streamed body."""
100
+
101
+ class RequestBody:
102
+ """Represents request body content (bytes, text, or async stream). Bodies are single-use."""
103
+
104
+ @staticmethod
105
+ def from_text(body: str) -> "RequestBody":
106
+ """Create body from text."""
107
+
108
+ @staticmethod
109
+ def from_bytes(body: bytes | bytearray | memoryview) -> "RequestBody":
110
+ """Create body from raw bytes."""
111
+
112
+ @staticmethod
113
+ def from_stream(stream: Stream) -> "RequestBody":
114
+ """Create body from async byte stream."""
115
+
116
+ def copy_bytes(self) -> Bytes | None:
117
+ """Return bytes zero-copy. None for stream."""
118
+
119
+ def get_stream(self) -> Stream | None:
120
+ """Return underlying stream if streaming body else None."""
121
+
122
+ def __copy__(self) -> Self:
123
+ """Copy body (Zero-copied bytes. Stream supplies its own copy)."""
124
+
125
+ class BaseRequestBuilder:
126
+ def error_for_status(self, enable: bool) -> Self:
127
+ """Enable automatic HTTP error raising (4xx/5xx)."""
128
+
129
+ def header(self, name: str, value: str) -> Self:
130
+ """Append single header value."""
131
+
132
+ def headers(self, headers: HeadersType) -> Self:
133
+ """Merge multiple headers (mapping or sequence)."""
134
+
135
+ def basic_auth(self, username: str, password: str | None) -> Self:
136
+ """Add Basic Authorization header."""
137
+
138
+ def bearer_auth(self, token: str) -> Self:
139
+ """Add Bearer token Authorization header."""
140
+
141
+ def body_bytes(self, body: bytes | bytearray | memoryview) -> Self:
142
+ """Set body from raw bytes."""
143
+
144
+ def body_text(self, body: str) -> Self:
145
+ """Set body from text."""
146
+
147
+ def body_json(self, body: Any) -> Self:
148
+ """Serialize body as JSON. Sets Content-Type header."""
149
+
150
+ def query(self, query: QueryParams) -> Self:
151
+ """Add/merge query parameters."""
152
+
153
+ def timeout(self, timeout: timedelta) -> Self:
154
+ """Set per-request total timeout."""
155
+
156
+ def multipart(self, multipart: FormBuilder) -> Self:
157
+ """Attach multipart form body builder."""
158
+
159
+ def form(self, form: FormParams) -> Self:
160
+ """Set application/x-www-form-urlencoded body."""
161
+
162
+ def extensions(self, extensions: ExtensionsType) -> Self:
163
+ """Arbitrary per-request data storage. Useful for passing through data to middleware and response."""
164
+
165
+ def streamed_read_buffer_limit(self, value: int) -> Self:
166
+ """Max bytes buffered when reading streamed body."""
167
+
168
+ @staticmethod
169
+ def default_streamed_read_buffer_limit() -> int:
170
+ """Default max bytes buffered when reading streamed body."""
171
+
172
+ class RequestBuilder(BaseRequestBuilder):
173
+ """Request builder. Use `build()` or `build_streamed()` to create the request to send."""
174
+
175
+ def build(self) -> ConsumedRequest:
176
+ """Build request that full reads the response body on send()."""
177
+
178
+ def build_streamed(self) -> StreamRequest:
179
+ """Build request whose response body is streamed."""
180
+
181
+ def body_stream(self, stream: Stream) -> Self:
182
+ """Set streaming request body."""
183
+
184
+ def with_middleware(self, middleware: Middleware) -> Self:
185
+ """Use a middleware component (added after client level middlewares, executed in chain order)."""
186
+
187
+ class SyncRequestBuilder(BaseRequestBuilder):
188
+ """Synchronous request builder. Use `build()` or `build_streamed()` to create the request to send."""
189
+
190
+ def build(self) -> SyncConsumedRequest:
191
+ """Build request that full reads the response body on send()."""
192
+
193
+ def build_streamed(self) -> SyncStreamRequest:
194
+ """Build request whose response body is streamed."""
195
+
196
+ def body_stream(self, stream: SyncStream) -> Self:
197
+ """Set streaming request body."""
198
+
199
+ def with_middleware(self, middleware: SyncMiddleware) -> Self:
200
+ """Use a middleware component (added after client level middlewares, executed in chain order)."""
201
+
202
+ class OneOffRequestBuilder(BaseRequestBuilder):
203
+ """One-off request builder. Use `send()` to execute the request."""
204
+
205
+ async def send(self) -> Response:
206
+ """Execute the request returning a Response with fully read response body."""
207
+
208
+ def with_middleware(self, middleware: Middleware) -> Self:
209
+ """Use a middleware component."""
210
+
211
+ class SyncOneOffRequestBuilder(BaseRequestBuilder):
212
+ """Synchronous one-off request builder. Use `send()` to execute the request."""
213
+
214
+ def send(self) -> SyncResponse:
215
+ """Execute the request returning a Response with fully read response body."""
216
+
217
+ def with_middleware(self, middleware: SyncMiddleware) -> Self:
218
+ """Use a middleware component."""
@@ -0,0 +1,19 @@
1
+ """Response classes and builders."""
2
+
3
+ from pyreqwest._pyreqwest.response import (
4
+ BaseResponse,
5
+ Response,
6
+ ResponseBodyReader,
7
+ ResponseBuilder,
8
+ SyncResponse,
9
+ SyncResponseBodyReader,
10
+ )
11
+
12
+ __all__ = [
13
+ "BaseResponse",
14
+ "Response",
15
+ "SyncResponse",
16
+ "ResponseBuilder",
17
+ "ResponseBodyReader",
18
+ "SyncResponseBodyReader",
19
+ ]
@@ -0,0 +1,157 @@
1
+ from typing import Any, Self
2
+
3
+ from pyreqwest.bytes import Bytes
4
+ from pyreqwest.http import HeaderMap, Mime
5
+ from pyreqwest.types import ExtensionsType, HeadersType, Stream
6
+
7
+ class BaseResponse:
8
+ @property
9
+ def status(self) -> int:
10
+ """HTTP status code (e.g. 200, 404)."""
11
+
12
+ @status.setter
13
+ def status(self, value: int) -> None:
14
+ """Set HTTP status code."""
15
+
16
+ @property
17
+ def headers(self) -> HeaderMap:
18
+ """Get the headers. This is not a copy. Modifying it modifies the response.
19
+ You can also use `get_header` or `get_header_all` to access headers.
20
+ """
21
+
22
+ @headers.setter
23
+ def headers(self, headers: HeadersType) -> None:
24
+ """Replace headers. Given value is copied."""
25
+
26
+ @property
27
+ def extensions(self) -> dict[str, Any]:
28
+ """Arbitrary per-request data storage. This is the data that was passed via request and middlewares.
29
+ This is not a copy. Modifying it modifies the response.
30
+ """
31
+
32
+ @extensions.setter
33
+ def extensions(self, value: ExtensionsType) -> None:
34
+ """Replace extensions. Given value is shallow copied."""
35
+
36
+ @property
37
+ def version(self) -> str:
38
+ """Used HTTP version (e.g. 'HTTP/1.1', 'HTTP/2.0')."""
39
+
40
+ @version.setter
41
+ def version(self, value: str) -> None:
42
+ """Set HTTP version."""
43
+
44
+ def error_for_status(self) -> None:
45
+ """Raise StatusError for 4xx/5xx."""
46
+
47
+ def get_header(self, key: str) -> str | None:
48
+ """Return first matching header value else None (case-insensitive)."""
49
+
50
+ def get_header_all(self, key: str) -> list[str]:
51
+ """Return all values for header name (case-insensitive). Empty if absent."""
52
+
53
+ def content_type_mime(self) -> Mime | None:
54
+ """Parsed Content-Type header as Mime or None if absent."""
55
+
56
+ class Response(BaseResponse):
57
+ """Asynchronous response with optionally streamed body."""
58
+
59
+ async def bytes(self) -> Bytes:
60
+ """Return entire body as bytes (cached after first read)."""
61
+
62
+ async def json(self) -> Any:
63
+ """Decode body as JSON (underlying bytes cached after first read). Uses serde for decoding.
64
+ User can provide custom deserializer via `ClientBuilder.json_handler`.
65
+ """
66
+
67
+ async def text(self) -> str:
68
+ """Decode body to text (underlying bytes cached after first read). Uses charset from Content-Type."""
69
+
70
+ @property
71
+ def body_reader(self) -> "ResponseBodyReader":
72
+ """Access streaming reader. Using bytes(), json() or text() is not allowed after reading body partially."""
73
+
74
+ class SyncResponse(BaseResponse):
75
+ """Synchronous response variant."""
76
+
77
+ def bytes(self) -> Bytes:
78
+ """Return entire body as bytes (cached after first read)."""
79
+
80
+ def json(self) -> Any:
81
+ """Decode body as JSON (underlying bytes cached after first read). Uses serde for decoding.
82
+ User can provide custom deserializer via `SyncClientBuilder.json_handler`.
83
+ """
84
+
85
+ def text(self) -> str:
86
+ """Decode body to text (underlying bytes cached after first read). Uses charset from Content-Type."""
87
+
88
+ @property
89
+ def body_reader(self) -> "SyncResponseBodyReader":
90
+ """Access streaming reader. Using bytes(), json() or text() is not allowed after reading body partially."""
91
+
92
+ class ResponseBuilder:
93
+ """Programmatic response construction (for testing, middlewares, manual responses)."""
94
+
95
+ def __init__(self) -> None:
96
+ """Create empty response builder (defaults: 200, HTTP/1.1, empty headers/body)."""
97
+
98
+ async def build(self) -> Response:
99
+ """Build asynchronous response."""
100
+
101
+ def build_sync(self) -> SyncResponse:
102
+ """Build synchronous response (disallows async streams)."""
103
+
104
+ def status(self, value: int) -> Self:
105
+ """Set status code."""
106
+
107
+ def version(self, value: str) -> Self:
108
+ """Set HTTP version string."""
109
+
110
+ def header(self, name: str, value: str) -> Self:
111
+ """Append single header value (multiple allowed)."""
112
+
113
+ def headers(self, headers: HeadersType) -> Self:
114
+ """Merge multiple headers (mapping or sequence)."""
115
+
116
+ def extensions(self, extensions: ExtensionsType) -> Self:
117
+ """Set extensions."""
118
+
119
+ def body_bytes(self, body: bytes | bytearray | memoryview) -> Self:
120
+ """Set fixed byte body (zero-copied where possible)."""
121
+
122
+ def body_text(self, body: str) -> Self:
123
+ """Set text body (UTF-8 encoded)."""
124
+
125
+ def body_json(self, body: Any) -> Self:
126
+ """Serialize body to JSON (sets Content-Type). Uses serde for serialization."""
127
+
128
+ def body_stream(self, stream: Stream) -> Self:
129
+ """Set streaming body. `build_sync` can not be mixed with async streams."""
130
+
131
+ def copy(self) -> Self:
132
+ """Copy builder."""
133
+ def __copy__(self) -> Self: ...
134
+
135
+ class ResponseBodyReader:
136
+ """Streaming body reader."""
137
+
138
+ async def bytes(self) -> Bytes:
139
+ """Read remaining stream fully and return bytes (caches)."""
140
+
141
+ async def read(self, amount: int = ...) -> Bytes | None:
142
+ """Read up to amount bytes (or default chunk size) from stream. None when stream is exhausted."""
143
+
144
+ async def read_chunk(self) -> Bytes | None:
145
+ """Return next raw chunk. Sizes are arbitrary and depend on OS. None when stream is exhausted."""
146
+
147
+ class SyncResponseBodyReader:
148
+ """Streaming body reader."""
149
+
150
+ def bytes(self) -> Bytes:
151
+ """Read remaining stream fully and return bytes (caches)."""
152
+
153
+ def read(self, amount: int = ...) -> Bytes | None:
154
+ """Read up to amount bytes (or default chunk size) from stream. None when stream is exhausted."""
155
+
156
+ def read_chunk(self) -> Bytes | None:
157
+ """Return next raw chunk. Sizes are arbitrary and depend on OS. None when stream is exhausted."""
@@ -0,0 +1 @@
1
+ """Simple interfaces for doing one-off requests."""
@@ -0,0 +1,21 @@
1
+ """Simple async interfaces for doing one-off requests."""
2
+
3
+ from pyreqwest._pyreqwest.simple.request import (
4
+ pyreqwest_delete,
5
+ pyreqwest_get,
6
+ pyreqwest_head,
7
+ pyreqwest_patch,
8
+ pyreqwest_post,
9
+ pyreqwest_put,
10
+ pyreqwest_request,
11
+ )
12
+
13
+ __all__ = [
14
+ "pyreqwest_request",
15
+ "pyreqwest_get",
16
+ "pyreqwest_post",
17
+ "pyreqwest_put",
18
+ "pyreqwest_patch",
19
+ "pyreqwest_delete",
20
+ "pyreqwest_head",
21
+ ]
@@ -0,0 +1,29 @@
1
+ from pyreqwest.http import Url
2
+ from pyreqwest.request import OneOffRequestBuilder
3
+
4
+ def pyreqwest_request(method: str, url: Url | str) -> OneOffRequestBuilder:
5
+ """Create a simple request with the given HTTP method and URL.
6
+
7
+ Returns a request builder, which will allow setting headers and the request body before sending.
8
+
9
+ NOTE: This is only recommended for simple scripting use-cases. Usually, the client should be reused for multiple
10
+ requests to benefit from connection pooling and other optimizations (via ClientBuilder).
11
+ """
12
+
13
+ def pyreqwest_get(url: Url | str) -> OneOffRequestBuilder:
14
+ """Same as `pyreqwest_request("GET", url)`."""
15
+
16
+ def pyreqwest_post(url: Url | str) -> OneOffRequestBuilder:
17
+ """Same as `pyreqwest_request("POST", url)`."""
18
+
19
+ def pyreqwest_put(url: Url | str) -> OneOffRequestBuilder:
20
+ """Same as `pyreqwest_request("PUT", url)`."""
21
+
22
+ def pyreqwest_patch(url: Url | str) -> OneOffRequestBuilder:
23
+ """Same as `pyreqwest_request("PATCH", url)`."""
24
+
25
+ def pyreqwest_delete(url: Url | str) -> OneOffRequestBuilder:
26
+ """Same as `pyreqwest_request("DELETE", url)`."""
27
+
28
+ def pyreqwest_head(url: Url | str) -> OneOffRequestBuilder:
29
+ """Same as `pyreqwest_request("HEAD", url)`."""
@@ -0,0 +1,21 @@
1
+ """Simple sync interfaces for doing one-off requests."""
2
+
3
+ from pyreqwest._pyreqwest.simple.sync_request import (
4
+ pyreqwest_delete,
5
+ pyreqwest_get,
6
+ pyreqwest_head,
7
+ pyreqwest_patch,
8
+ pyreqwest_post,
9
+ pyreqwest_put,
10
+ pyreqwest_request,
11
+ )
12
+
13
+ __all__ = [
14
+ "pyreqwest_request",
15
+ "pyreqwest_get",
16
+ "pyreqwest_post",
17
+ "pyreqwest_put",
18
+ "pyreqwest_patch",
19
+ "pyreqwest_delete",
20
+ "pyreqwest_head",
21
+ ]
@@ -0,0 +1,29 @@
1
+ from pyreqwest.http import Url
2
+ from pyreqwest.request import SyncOneOffRequestBuilder
3
+
4
+ def pyreqwest_request(method: str, url: Url | str) -> SyncOneOffRequestBuilder:
5
+ """Create a simple request with the given HTTP method and URL.
6
+
7
+ Returns a request builder, which will allow setting headers and the request body before sending.
8
+
9
+ NOTE: This is only recommended for simple scripting use-cases. Usually, the client should be reused for multiple
10
+ requests to benefit from connection pooling and other optimizations (via ClientBuilder).
11
+ """
12
+
13
+ def pyreqwest_get(url: Url | str) -> SyncOneOffRequestBuilder:
14
+ """Same as `pyreqwest_request("GET", url)`."""
15
+
16
+ def pyreqwest_post(url: Url | str) -> SyncOneOffRequestBuilder:
17
+ """Same as `pyreqwest_request("POST", url)`."""
18
+
19
+ def pyreqwest_put(url: Url | str) -> SyncOneOffRequestBuilder:
20
+ """Same as `pyreqwest_request("PUT", url)`."""
21
+
22
+ def pyreqwest_patch(url: Url | str) -> SyncOneOffRequestBuilder:
23
+ """Same as `pyreqwest_request("PATCH", url)`."""
24
+
25
+ def pyreqwest_delete(url: Url | str) -> SyncOneOffRequestBuilder:
26
+ """Same as `pyreqwest_request("DELETE", url)`."""
27
+
28
+ def pyreqwest_head(url: Url | str) -> SyncOneOffRequestBuilder:
29
+ """Same as `pyreqwest_request("HEAD", url)`."""
pyreqwest/types.py ADDED
@@ -0,0 +1,12 @@
1
+ """Common types and interfaces used in the library."""
2
+
3
+ from collections.abc import AsyncIterable, Iterable, Mapping, Sequence
4
+ from typing import Any, TypeAlias
5
+
6
+ HeadersType: TypeAlias = Mapping[str, str] | Sequence[tuple[str, str]]
7
+ QueryParams: TypeAlias = Mapping[str, Any] | Sequence[tuple[str, Any]]
8
+ FormParams: TypeAlias = Mapping[str, Any] | Sequence[tuple[str, Any]]
9
+ ExtensionsType: TypeAlias = Mapping[str, Any] | Sequence[tuple[str, Any]]
10
+
11
+ SyncStream: TypeAlias = Iterable[bytes] | Iterable[bytearray] | Iterable[memoryview]
12
+ Stream: TypeAlias = AsyncIterable[bytes] | AsyncIterable[bytearray] | AsyncIterable[memoryview] | SyncStream
@@ -0,0 +1,148 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyreqwest
3
+ Version: 0.9.0
4
+ Classifier: Development Status :: 4 - Beta
5
+ Classifier: Programming Language :: Python
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: Programming Language :: Python :: 3 :: Only
8
+ Classifier: Programming Language :: Python :: 3.11
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Classifier: Programming Language :: Python :: 3.14
12
+ Classifier: Programming Language :: Python :: Implementation :: CPython
13
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
14
+ Classifier: Programming Language :: Python :: Implementation :: GraalPy
15
+ Classifier: Programming Language :: Rust
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: Intended Audience :: Information Technology
18
+ Classifier: Operating System :: POSIX :: Linux
19
+ Classifier: Operating System :: Microsoft :: Windows
20
+ Classifier: Operating System :: MacOS
21
+ Classifier: Typing :: Typed
22
+ License-File: LICENSE
23
+ Summary: Powerful and fast Rust based HTTP client
24
+ Author-email: Markus Sintonen <pyreqwest@gmail.com>
25
+ Requires-Python: >=3.11
26
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
27
+ Project-URL: Homepage, https://github.com/MarkusSintonen/pyreqwest
28
+ Project-URL: Source, https://github.com/MarkusSintonen/pyreqwest
29
+
30
+ <p align="center">
31
+ <img width="250" alt="logo" src="https://raw.githubusercontent.com/MarkusSintonen/pyreqwest/refs/heads/main/docs/logo.png" />
32
+ </p>
33
+
34
+ ---
35
+ [![codecov](https://codecov.io/github/markussintonen/pyreqwest/graph/badge.svg?token=OET0CMZYOH)](https://codecov.io/github/markussintonen/pyreqwest)
36
+ ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pyreqwest)
37
+
38
+ pyreqwest - Powerful and fast Rust based HTTP client. Built on top of and inspired by [reqwest](https://github.com/seanmonstar/reqwest).
39
+
40
+ ## Why
41
+
42
+ - No reinvention of the wheel - built on top of widely used reqwest and other Rust HTTP crates
43
+ - Secure and fast - no C-extension code, no Python code/dependencies, no `unsafe` code
44
+ - Ergonomic and easy to use - similar API as in reqwest, fully type-annotated
45
+ - Testing ergonomics - mocking included, can also connect into ASGI apps
46
+
47
+ Using this is a good choice when:
48
+
49
+ - You care about throughput and latency, especially in high concurrency scenarios
50
+ - You want a single solution to serve all your HTTP client needs
51
+
52
+ This is not a good choice when:
53
+
54
+ - You want a pure Python solution allowing debugging of the HTTP client internals
55
+ - You use alternative Python implementations or Python version older than 3.11
56
+
57
+ ## Features
58
+
59
+ - High performance, see [notes](https://github.com/MarkusSintonen/pyreqwest/blob/main/docs/performance.md) and [benchmarks](https://github.com/MarkusSintonen/pyreqwest/blob/main/docs/benchmarks.md)
60
+ - Asynchronous and synchronous HTTP clients
61
+ - Customizable via middlewares and custom JSON serializers
62
+ - Ergonomic as `reqwest`
63
+ - HTTP/1.1 and HTTP/2 support (also HTTP/3 when it [stabilizes](https://docs.rs/reqwest/latest/reqwest/#unstable-features))
64
+ - Mocking and testing utilities (can also connect to ASGI apps)
65
+ - Fully type-safe with Python type hints
66
+ - Full test coverage
67
+ - Free threading, see [notes](https://github.com/MarkusSintonen/pyreqwest/blob/main/docs/performance.md#python-313-free-threading)
68
+
69
+ ### Standard HTTP features you would expect
70
+
71
+ - HTTPS support (using [rustls](https://github.com/rustls/rustls))
72
+ - Request and response body streaming
73
+ - Connection pooling
74
+ - JSON, URLs, Headers, Cookies etc. (all serializers in Rust)
75
+ - Automatic decompression (zstd, gzip, brotli, deflate)
76
+ - Automatic response decoding (charset detection)
77
+ - Multipart form support
78
+ - Proxy support
79
+ - Redirects
80
+ - Timeouts
81
+ - Authentication (Basic, Bearer)
82
+ - Cookie management
83
+
84
+ ## Quickstart
85
+
86
+ ```python
87
+ # uv add pyreqwest
88
+
89
+ from pyreqwest.client import ClientBuilder, SyncClientBuilder
90
+
91
+ async def example_async():
92
+ async with ClientBuilder().error_for_status(True).build() as client:
93
+ response = await client.get("https://httpbun.com/get").query({"q": "val"}).build().send()
94
+ print(await response.json())
95
+
96
+ def example_sync():
97
+ with SyncClientBuilder().error_for_status(True).build() as client:
98
+ print(client.get("https://httpbun.com/get").query({"q": "val"}).build().send().json())
99
+ ```
100
+
101
+ Context manager usage is optional, but recommended. Also `close()` methods are available.
102
+
103
+ #### Mocking in pytest
104
+
105
+ ```python
106
+ from pyreqwest.client import ClientBuilder
107
+ from pyreqwest.pytest_plugin import ClientMocker
108
+
109
+ async def test_client(client_mocker: ClientMocker) -> None:
110
+ client_mocker.get(path="/api").with_body_text("Hello Mock")
111
+
112
+ async with ClientBuilder().build() as client:
113
+ response = await client.get("http://example.invalid/api").build().send()
114
+ assert response.status == 200 and await response.text() == "Hello Mock"
115
+ assert client_mocker.get_call_count() == 1
116
+ ```
117
+
118
+ Manual mocking is available via `ClientMocker.create_mocker(MonkeyPatch)`.
119
+
120
+ #### Simple request interface
121
+
122
+ This is only recommended for simple use-cases such as scripts. Usually, the full client API should be used which reuses
123
+ connections and has other optimizations.
124
+
125
+ ```python
126
+ # Sync example
127
+ from pyreqwest.simple.sync_request import pyreqwest_get
128
+ response = pyreqwest_get("https://httpbun.com/get").query({"q": "val"}).send()
129
+ print(response.json())
130
+ ```
131
+
132
+ ```python
133
+ # Async example
134
+ from pyreqwest.simple.request import pyreqwest_get
135
+ response = await pyreqwest_get("https://httpbun.com/get").query({"q": "val"}).send()
136
+ print(await response.json())
137
+ ```
138
+
139
+ ## Documentation
140
+
141
+ See [docs](https://markussintonen.github.io/pyreqwest/pyreqwest.html)
142
+
143
+ See [examples](https://github.com/MarkusSintonen/pyreqwest/tree/main/examples)
144
+
145
+ ## Compatibility with other libraries
146
+
147
+ See [compatibility docs](https://markussintonen.github.io/pyreqwest/pyreqwest/compatibility.html)
148
+