httpr 0.2.5__cp313-cp313t-musllinux_1_2_i686.whl → 0.3.2__cp313-cp313t-musllinux_1_2_i686.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.
- httpr/__init__.py +34 -21
- httpr/httpr.cpython-313t-i386-linux-musl.so +0 -0
- httpr/httpr.pyi +5 -0
- httpr-0.3.2.dist-info/METADATA +481 -0
- httpr-0.3.2.dist-info/RECORD +9 -0
- {httpr-0.2.5.dist-info → httpr-0.3.2.dist-info}/WHEEL +1 -1
- httpr-0.2.5.dist-info/METADATA +0 -54
- httpr-0.2.5.dist-info/RECORD +0 -9
- {httpr-0.2.5.dist-info → httpr-0.3.2.dist-info}/licenses/LICENSE +0 -0
httpr/__init__.py
CHANGED
|
@@ -117,6 +117,7 @@ class Client(RClient):
|
|
|
117
117
|
verify: bool | None = True,
|
|
118
118
|
ca_cert_file: str | None = None,
|
|
119
119
|
client_pem: str | None = None,
|
|
120
|
+
client_pem_data: bytes | None = None,
|
|
120
121
|
https_only: bool | None = False,
|
|
121
122
|
http2_only: bool | None = False,
|
|
122
123
|
):
|
|
@@ -140,6 +141,8 @@ class Client(RClient):
|
|
|
140
141
|
verify: Verify SSL certificates. Default is True.
|
|
141
142
|
ca_cert_file: Path to CA certificate bundle (PEM format).
|
|
142
143
|
client_pem: Path to client certificate for mTLS (PEM format).
|
|
144
|
+
client_pem_data: Client certificate and key as bytes for mTLS (PEM format).
|
|
145
|
+
Use this instead of client_pem when you have the certificate in memory.
|
|
143
146
|
https_only: Only allow HTTPS requests. Default is False.
|
|
144
147
|
http2_only: Use HTTP/2 only (False uses HTTP/1.1). Default is False.
|
|
145
148
|
|
|
@@ -165,11 +168,18 @@ class Client(RClient):
|
|
|
165
168
|
# Client with proxy
|
|
166
169
|
client = httpr.Client(proxy="http://proxy.example.com:8080")
|
|
167
170
|
|
|
168
|
-
# Client with mTLS
|
|
171
|
+
# Client with mTLS using file path
|
|
169
172
|
client = httpr.Client(
|
|
170
173
|
client_pem="/path/to/client.pem",
|
|
171
174
|
ca_cert_file="/path/to/ca.pem",
|
|
172
175
|
)
|
|
176
|
+
|
|
177
|
+
# Client with mTLS using direct certificate data
|
|
178
|
+
cert_data = b"-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
|
|
179
|
+
client = httpr.Client(
|
|
180
|
+
client_pem_data=cert_data,
|
|
181
|
+
ca_cert_file="/path/to/ca.pem",
|
|
182
|
+
)
|
|
173
183
|
```
|
|
174
184
|
"""
|
|
175
185
|
super().__init__()
|
|
@@ -212,16 +222,16 @@ class Client(RClient):
|
|
|
212
222
|
**kwargs: Request parameters (see below).
|
|
213
223
|
|
|
214
224
|
Keyword Args:
|
|
215
|
-
params (dict[str, str]
|
|
216
|
-
headers (dict[str, str]
|
|
217
|
-
cookies (dict[str, str]
|
|
218
|
-
auth (tuple[str, str
|
|
219
|
-
auth_bearer (str
|
|
220
|
-
timeout (float
|
|
221
|
-
content (bytes
|
|
222
|
-
data (dict[str, Any]
|
|
223
|
-
json (Any
|
|
224
|
-
files (dict[str, str]
|
|
225
|
+
params (Optional[dict[str, str]]): Query parameters to append to URL.
|
|
226
|
+
headers (Optional[dict[str, str]]): Request headers (merged with client defaults).
|
|
227
|
+
cookies (Optional[dict[str, str]]): Request cookies (merged with client defaults).
|
|
228
|
+
auth (Optional[tuple[str, Optional[str]]]): Basic auth credentials (overrides client default).
|
|
229
|
+
auth_bearer (Optional[str]): Bearer token (overrides client default).
|
|
230
|
+
timeout (Optional[float]): Request timeout in seconds (overrides client default).
|
|
231
|
+
content (Optional[bytes]): Raw bytes for request body.
|
|
232
|
+
data (Optional[dict[str, Any]]): Form data for request body (application/x-www-form-urlencoded).
|
|
233
|
+
json (Optional[Any]): JSON data for request body (application/json).
|
|
234
|
+
files (Optional[dict[str, str]]): Files for multipart upload (dict mapping field names to file paths).
|
|
225
235
|
|
|
226
236
|
Returns:
|
|
227
237
|
Response object with status, headers, and body.
|
|
@@ -337,16 +347,16 @@ class Client(RClient):
|
|
|
337
347
|
**kwargs: Request parameters including body options.
|
|
338
348
|
|
|
339
349
|
Keyword Args:
|
|
340
|
-
params (dict[str, str]
|
|
341
|
-
headers (dict[str, str]
|
|
342
|
-
cookies (dict[str, str]
|
|
343
|
-
auth (tuple[str, str
|
|
344
|
-
auth_bearer (str
|
|
345
|
-
timeout (float
|
|
346
|
-
content (bytes
|
|
347
|
-
data (dict[str, Any]
|
|
348
|
-
json (Any
|
|
349
|
-
files (dict[str, str]
|
|
350
|
+
params (Optional[dict[str, str]]): Query parameters.
|
|
351
|
+
headers (Optional[dict[str, str]]): Request headers.
|
|
352
|
+
cookies (Optional[dict[str, str]]): Request cookies.
|
|
353
|
+
auth (Optional[tuple[str, Optional[str]]]): Basic auth credentials.
|
|
354
|
+
auth_bearer (Optional[str]): Bearer token.
|
|
355
|
+
timeout (Optional[float]): Request timeout.
|
|
356
|
+
content (Optional[bytes]): Raw bytes body.
|
|
357
|
+
data (Optional[dict[str, Any]]): Form-encoded body.
|
|
358
|
+
json (Optional[Any]): JSON body.
|
|
359
|
+
files (Optional[dict[str, str]]): Multipart file uploads.
|
|
350
360
|
|
|
351
361
|
Returns:
|
|
352
362
|
Response object.
|
|
@@ -787,6 +797,7 @@ def request(
|
|
|
787
797
|
verify: bool | None = True,
|
|
788
798
|
ca_cert_file: str | None = None,
|
|
789
799
|
client_pem: str | None = None,
|
|
800
|
+
client_pem_data: bytes | None = None,
|
|
790
801
|
**kwargs: Unpack[RequestParams],
|
|
791
802
|
) -> Response:
|
|
792
803
|
"""
|
|
@@ -801,6 +812,7 @@ def request(
|
|
|
801
812
|
verify: Verify SSL certificates. Default is True.
|
|
802
813
|
ca_cert_file: Path to CA certificate bundle.
|
|
803
814
|
client_pem: Path to client certificate for mTLS.
|
|
815
|
+
client_pem_data: Client certificate and key as bytes for mTLS.
|
|
804
816
|
**kwargs: Additional request parameters.
|
|
805
817
|
|
|
806
818
|
Returns:
|
|
@@ -818,6 +830,7 @@ def request(
|
|
|
818
830
|
verify=verify,
|
|
819
831
|
ca_cert_file=ca_cert_file,
|
|
820
832
|
client_pem=client_pem,
|
|
833
|
+
client_pem_data=client_pem_data,
|
|
821
834
|
) as client:
|
|
822
835
|
return client.request(method, url, **kwargs)
|
|
823
836
|
|
|
Binary file
|
httpr/httpr.pyi
CHANGED
|
@@ -28,6 +28,7 @@ class ClientRequestParams(RequestParams):
|
|
|
28
28
|
verify: bool | None
|
|
29
29
|
ca_cert_file: str | None
|
|
30
30
|
client_pem: str | None
|
|
31
|
+
client_pem_data: bytes | None
|
|
31
32
|
|
|
32
33
|
class CaseInsensitiveHeaderMap:
|
|
33
34
|
"""
|
|
@@ -286,6 +287,7 @@ class RClient:
|
|
|
286
287
|
verify: bool | None = True,
|
|
287
288
|
ca_cert_file: str | None = None,
|
|
288
289
|
client_pem: str | None = None,
|
|
290
|
+
client_pem_data: bytes | None = None,
|
|
289
291
|
https_only: bool | None = False,
|
|
290
292
|
http2_only: bool | None = False,
|
|
291
293
|
): ...
|
|
@@ -374,6 +376,7 @@ class Client(RClient):
|
|
|
374
376
|
verify: bool | None = True,
|
|
375
377
|
ca_cert_file: str | None = None,
|
|
376
378
|
client_pem: str | None = None,
|
|
379
|
+
client_pem_data: bytes | None = None,
|
|
377
380
|
https_only: bool | None = False,
|
|
378
381
|
http2_only: bool | None = False,
|
|
379
382
|
) -> None:
|
|
@@ -395,6 +398,7 @@ class Client(RClient):
|
|
|
395
398
|
verify: Verify SSL certificates. Default is True.
|
|
396
399
|
ca_cert_file: Path to CA certificate bundle (PEM format).
|
|
397
400
|
client_pem: Path to client certificate for mTLS (PEM format).
|
|
401
|
+
client_pem_data: Client certificate and key as bytes for mTLS (PEM format).
|
|
398
402
|
https_only: Only allow HTTPS requests. Default is False.
|
|
399
403
|
http2_only: Use HTTP/2 only. Default is False.
|
|
400
404
|
"""
|
|
@@ -472,6 +476,7 @@ class AsyncClient(Client):
|
|
|
472
476
|
verify: bool | None = True,
|
|
473
477
|
ca_cert_file: str | None = None,
|
|
474
478
|
client_pem: str | None = None,
|
|
479
|
+
client_pem_data: bytes | None = None,
|
|
475
480
|
https_only: bool | None = False,
|
|
476
481
|
http2_only: bool | None = False,
|
|
477
482
|
) -> None:
|
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: httpr
|
|
3
|
+
Version: 0.3.2
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: 3
|
|
6
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
13
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
14
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Requires-Dist: matplotlib ; extra == 'benchmark'
|
|
17
|
+
Requires-Dist: pandas ; extra == 'benchmark'
|
|
18
|
+
Requires-Dist: starlette ; extra == 'benchmark'
|
|
19
|
+
Requires-Dist: uvicorn ; extra == 'benchmark'
|
|
20
|
+
Requires-Dist: requests ; extra == 'benchmark'
|
|
21
|
+
Requires-Dist: httpx ; extra == 'benchmark'
|
|
22
|
+
Requires-Dist: tls-client ; extra == 'benchmark'
|
|
23
|
+
Requires-Dist: curl-cffi ; extra == 'benchmark'
|
|
24
|
+
Requires-Dist: pycurl ; extra == 'benchmark'
|
|
25
|
+
Requires-Dist: typing-extensions ; extra == 'benchmark'
|
|
26
|
+
Requires-Dist: aiohttp ; extra == 'benchmark'
|
|
27
|
+
Requires-Dist: cbor2 ; extra == 'benchmark'
|
|
28
|
+
Requires-Dist: jupyter ; extra == 'benchmark'
|
|
29
|
+
Requires-Dist: ipykernel ; extra == 'benchmark'
|
|
30
|
+
Requires-Dist: gunicorn ; extra == 'benchmark'
|
|
31
|
+
Requires-Dist: fastapi ; extra == 'benchmark'
|
|
32
|
+
Requires-Dist: certifi ; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest>=8.1.1 ; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest-asyncio>=0.25.3 ; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest-benchmark>=5.1.0 ; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest-httpbin>=2.1.0 ; extra == 'dev'
|
|
37
|
+
Requires-Dist: typing-extensions ; python_full_version < '3.12' and extra == 'dev'
|
|
38
|
+
Requires-Dist: mypy>=1.14.1 ; extra == 'dev'
|
|
39
|
+
Requires-Dist: ruff>=0.9.2 ; extra == 'dev'
|
|
40
|
+
Requires-Dist: maturin ; extra == 'dev'
|
|
41
|
+
Requires-Dist: trustme ; extra == 'dev'
|
|
42
|
+
Requires-Dist: cbor2 ; extra == 'dev'
|
|
43
|
+
Requires-Dist: go-task-bin ; extra == 'dev'
|
|
44
|
+
Requires-Dist: pre-commit ; extra == 'dev'
|
|
45
|
+
Requires-Dist: mkdocs-material[imaging] ; extra == 'docs'
|
|
46
|
+
Requires-Dist: mkdocstrings[python]>=0.27.0 ; extra == 'docs'
|
|
47
|
+
Requires-Dist: mkdocs-gen-files ; extra == 'docs'
|
|
48
|
+
Requires-Dist: mkdocs-literate-nav ; extra == 'docs'
|
|
49
|
+
Requires-Dist: mkdocs-llmstxt ; extra == 'docs'
|
|
50
|
+
Provides-Extra: benchmark
|
|
51
|
+
Provides-Extra: dev
|
|
52
|
+
Provides-Extra: docs
|
|
53
|
+
License-File: LICENSE
|
|
54
|
+
Summary: Fast HTTP client for Python
|
|
55
|
+
Keywords: python,request
|
|
56
|
+
Author: thomasht86
|
|
57
|
+
License: MIT License
|
|
58
|
+
Requires-Python: >=3.9
|
|
59
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
60
|
+
|
|
61
|
+
# httpr
|
|
62
|
+
|
|
63
|
+
**Blazing fast http-client** for Python in Rust 🦀 that can be used as drop-in replacement for `httpx` and `requests` in most cases.
|
|
64
|
+
|
|
65
|
+
- **Fast**: `httpr` is built on top of `reqwests`, which is a blazing fast http client in Rust. Check out the [benchmark](#benchmark).
|
|
66
|
+
- **Both async and sync**: `httpr` provides both a sync and async client.
|
|
67
|
+
- **Lightweight**: `httpr` is a lightweight http client with zero python-dependencies.
|
|
68
|
+
- **Async**: first-class async support.
|
|
69
|
+
- **Streaming**: supports streaming responses for efficient memory usage with large payloads.
|
|
70
|
+
- **http2**: `httpr` supports HTTP/2.
|
|
71
|
+
- **mTLS**: `httpr` supports mTLS.
|
|
72
|
+
|
|
73
|
+
## Not implemented yet
|
|
74
|
+
|
|
75
|
+
- **Fine-grained error handling**: Fine-grained error handling is not implemented yet.
|
|
76
|
+
|
|
77
|
+
## Documentation
|
|
78
|
+
|
|
79
|
+
📖 **Full documentation**: [thomasht86.github.io/httpr](https://thomasht86.github.io/httpr)
|
|
80
|
+
|
|
81
|
+
🤖 **LLM-friendly docs**: [llms.txt](https://thomasht86.github.io/httpr/llms.txt) | [llms-full.txt](https://thomasht86.github.io/httpr/llms-full.txt)
|
|
82
|
+
|
|
83
|
+
## Table of Contents
|
|
84
|
+
|
|
85
|
+
- [httpr](#httpr)
|
|
86
|
+
- [Not implemented yet](#not-implemented-yet)
|
|
87
|
+
- [Documentation](#documentation)
|
|
88
|
+
- [Table of Contents](#table-of-contents)
|
|
89
|
+
- [Installation](#installation)
|
|
90
|
+
- [Install with uv](#install-with-uv)
|
|
91
|
+
- [Install from PyPI](#install-from-pypi)
|
|
92
|
+
- [Benchmark](#benchmark)
|
|
93
|
+
- [Usage](#usage)
|
|
94
|
+
- [I. Client](#i-client)
|
|
95
|
+
- [Client methods](#client-methods)
|
|
96
|
+
- [Response object](#response-object)
|
|
97
|
+
- [Streaming responses](#streaming-responses)
|
|
98
|
+
- [Examples](#examples)
|
|
99
|
+
- [II. AsyncClient](#ii-asyncclient)
|
|
100
|
+
- [Precompiled wheels](#precompiled-wheels)
|
|
101
|
+
- [CI](#ci)
|
|
102
|
+
- [Acknowledgements](#acknowledgements)
|
|
103
|
+
|
|
104
|
+
## Installation
|
|
105
|
+
|
|
106
|
+
### Install with uv
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
uv add httpr
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
or
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
uv pip install httpr
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Install from PyPI
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
pip install -U httpr
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Benchmark
|
|
125
|
+
|
|
126
|
+

|
|
127
|
+
|
|
128
|
+
## Usage
|
|
129
|
+
|
|
130
|
+
### I. Client
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
class Client:
|
|
134
|
+
"""Initializes an HTTP client.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
auth (tuple[str, str| None] | None): Username and password for basic authentication. Default is None.
|
|
138
|
+
auth_bearer (str | None): Bearer token for authentication. Default is None.
|
|
139
|
+
params (dict[str, str] | None): Default query parameters to include in all requests. Default is None.
|
|
140
|
+
headers (dict[str, str] | None): Default headers to send with requests.
|
|
141
|
+
cookies (dict[str, str] | None): - Map of cookies to send with requests as the `Cookie` header.
|
|
142
|
+
timeout (float | None): HTTP request timeout in seconds. Default is 30.
|
|
143
|
+
cookie_store (bool | None): Enable a persistent cookie store. Received cookies will be preserved and included
|
|
144
|
+
in additional requests. Default is True.
|
|
145
|
+
referer (bool | None): Enable or disable automatic setting of the `Referer` header. Default is True.
|
|
146
|
+
proxy (str | None): Proxy URL for HTTP requests. Example: "socks5://127.0.0.1:9150". Default is None.
|
|
147
|
+
follow_redirects (bool | None): Whether to follow redirects. Default is True.
|
|
148
|
+
max_redirects (int | None): Maximum redirects to follow. Default 20. Applies if `follow_redirects` is True.
|
|
149
|
+
verify (bool | None): Verify SSL certificates. Default is True.
|
|
150
|
+
ca_cert_file (str | None): Path to CA certificate store. Default is None.
|
|
151
|
+
https_only` (bool | None): Restrict the Client to be used with HTTPS only requests. Default is `false`.
|
|
152
|
+
http2_only` (bool | None): If true - use only HTTP/2; if false - use only HTTP/1. Default is `false`.
|
|
153
|
+
|
|
154
|
+
"""
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### Client methods
|
|
158
|
+
|
|
159
|
+
The `Client` class provides a set of methods for making HTTP requests: `get`, `head`, `options`, `delete`, `post`, `put`, `patch`, each of which internally utilizes the `request()` method for execution. The parameters for these methods closely resemble those in `httpx`.
|
|
160
|
+
```python
|
|
161
|
+
def get(
|
|
162
|
+
url: str,
|
|
163
|
+
params: dict[str, str] | None = None,
|
|
164
|
+
headers: dict[str, str] | None = None,
|
|
165
|
+
cookies: dict[str, str] | None = None,
|
|
166
|
+
auth: tuple[str, str| None] | None = None,
|
|
167
|
+
auth_bearer: str | None = None,
|
|
168
|
+
timeout: float | None = 30,
|
|
169
|
+
):
|
|
170
|
+
"""Performs a GET request to the specified URL.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
url (str): The URL to which the request will be made.
|
|
174
|
+
params (dict[str, str] | None): A map of query parameters to append to the URL. Default is None.
|
|
175
|
+
headers (dict[str, str] | None): A map of HTTP headers to send with the request. Default is None.
|
|
176
|
+
cookies (dict[str, str] | None): - An optional map of cookies to send with requests as the `Cookie` header.
|
|
177
|
+
auth (tuple[str, str| None] | None): A tuple containing the username and an optional password
|
|
178
|
+
for basic authentication. Default is None.
|
|
179
|
+
auth_bearer (str | None): A string representing the bearer token for bearer token authentication. Default is None.
|
|
180
|
+
timeout (float | None): The timeout for the request in seconds. Default is 30.
|
|
181
|
+
|
|
182
|
+
"""
|
|
183
|
+
```
|
|
184
|
+
```python
|
|
185
|
+
def post(
|
|
186
|
+
url: str,
|
|
187
|
+
params: dict[str, str] | None = None,
|
|
188
|
+
headers: dict[str, str] | None = None,
|
|
189
|
+
cookies: dict[str, str] | None = None,
|
|
190
|
+
content: bytes | None = None,
|
|
191
|
+
data: dict[str, Any] | None = None,
|
|
192
|
+
json: Any | None = None,
|
|
193
|
+
files: dict[str, str] | None = None,
|
|
194
|
+
auth: tuple[str, str| None] | None = None,
|
|
195
|
+
auth_bearer: str | None = None,
|
|
196
|
+
timeout: float | None = 30,
|
|
197
|
+
):
|
|
198
|
+
"""Performs a POST request to the specified URL.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
url (str): The URL to which the request will be made.
|
|
202
|
+
params (dict[str, str] | None): A map of query parameters to append to the URL. Default is None.
|
|
203
|
+
headers (dict[str, str] | None): A map of HTTP headers to send with the request. Default is None.
|
|
204
|
+
cookies (dict[str, str] | None): - An optional map of cookies to send with requests as the `Cookie` header.
|
|
205
|
+
content (bytes | None): The content to send in the request body as bytes. Default is None.
|
|
206
|
+
data (dict[str, Any] | None): The form data to send in the request body. Default is None.
|
|
207
|
+
json (Any | None): A JSON serializable object to send in the request body. Default is None.
|
|
208
|
+
files (dict[str, str] | None): A map of file fields to file paths to be sent as multipart/form-data. Default is None.
|
|
209
|
+
auth (tuple[str, str| None] | None): A tuple containing the username and an optional password
|
|
210
|
+
for basic authentication. Default is None.
|
|
211
|
+
auth_bearer (str | None): A string representing the bearer token for bearer token authentication. Default is None.
|
|
212
|
+
timeout (float | None): The timeout for the request in seconds. Default is 30.
|
|
213
|
+
|
|
214
|
+
"""
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
#### Response object
|
|
218
|
+
|
|
219
|
+
The `Client` class returns a `Response` object that contains the following attributes and methods:
|
|
220
|
+
|
|
221
|
+
```python
|
|
222
|
+
resp.content
|
|
223
|
+
resp.cookies
|
|
224
|
+
resp.encoding
|
|
225
|
+
resp.headers
|
|
226
|
+
resp.json()
|
|
227
|
+
resp.status_code
|
|
228
|
+
resp.text
|
|
229
|
+
resp.text_markdown # html is converted to markdown text using html2text-rs
|
|
230
|
+
resp.text_plain # html is converted to plain text
|
|
231
|
+
resp.text_rich # html is converted to rich text
|
|
232
|
+
resp.url
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
#### Streaming responses
|
|
236
|
+
|
|
237
|
+
The `Client` class supports streaming responses for efficient memory usage when handling large payloads. Use the `stream()` context manager to iterate over response data without buffering the entire response in memory.
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
# Stream bytes chunks
|
|
241
|
+
with client.stream("GET", "https://example.com/large-file") as response:
|
|
242
|
+
print(f"Status: {response.status_code}")
|
|
243
|
+
for chunk in response.iter_bytes():
|
|
244
|
+
process(chunk)
|
|
245
|
+
|
|
246
|
+
# Stream text chunks
|
|
247
|
+
with client.stream("GET", "https://example.com/text") as response:
|
|
248
|
+
for text in response.iter_text():
|
|
249
|
+
print(text, end="")
|
|
250
|
+
|
|
251
|
+
# Stream line by line (useful for Server-Sent Events)
|
|
252
|
+
with client.stream("GET", "https://example.com/events") as response:
|
|
253
|
+
for line in response.iter_lines():
|
|
254
|
+
print(line.strip())
|
|
255
|
+
|
|
256
|
+
# Read entire response (if needed after checking headers)
|
|
257
|
+
with client.stream("GET", url) as response:
|
|
258
|
+
if response.status_code == 200:
|
|
259
|
+
content = response.read()
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
**StreamingResponse attributes:**
|
|
263
|
+
- `status_code` - HTTP status code
|
|
264
|
+
- `headers` - Response headers (case-insensitive)
|
|
265
|
+
- `cookies` - Response cookies
|
|
266
|
+
- `url` - Final URL after redirects
|
|
267
|
+
- `is_closed` - Whether the stream has been closed
|
|
268
|
+
- `is_consumed` - Whether the stream has been fully consumed
|
|
269
|
+
|
|
270
|
+
**StreamingResponse methods:**
|
|
271
|
+
- `iter_bytes()` - Iterate over response as bytes chunks
|
|
272
|
+
- `iter_text()` - Iterate over response as text chunks (decoded using response encoding)
|
|
273
|
+
- `iter_lines()` - Iterate over response line by line
|
|
274
|
+
- `read()` - Read entire remaining response body into memory
|
|
275
|
+
- `close()` - Close the stream and release resources
|
|
276
|
+
|
|
277
|
+
**Important notes:**
|
|
278
|
+
- Streaming must be used as a context manager (with statement)
|
|
279
|
+
- Headers, cookies, and status code are available immediately before reading the body
|
|
280
|
+
- The response body is only read when you iterate over it or call `read()`
|
|
281
|
+
- Once consumed, the stream cannot be read again
|
|
282
|
+
- Streaming is supported for all HTTP methods (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS)
|
|
283
|
+
|
|
284
|
+
#### Examples
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
import httpr
|
|
288
|
+
|
|
289
|
+
# Initialize the client
|
|
290
|
+
client = httpr.Client()
|
|
291
|
+
|
|
292
|
+
# GET request
|
|
293
|
+
resp = client.get("https://tls.peet.ws/api/all")
|
|
294
|
+
print(resp.json())
|
|
295
|
+
|
|
296
|
+
# GET request with passing params and setting timeout
|
|
297
|
+
params = {"param1": "value1", "param2": "value2"}
|
|
298
|
+
resp = client.post(url="https://httpbin.org/anything", params=params, timeout=10)
|
|
299
|
+
print(r.text)
|
|
300
|
+
|
|
301
|
+
# POST Binary Request Data
|
|
302
|
+
content = b"some_data"
|
|
303
|
+
resp = client.post(url="https://httpbin.org/anything", content=content)
|
|
304
|
+
print(r.text)
|
|
305
|
+
|
|
306
|
+
# POST Form Encoded Data
|
|
307
|
+
data = {"key1": "value1", "key2": "value2"}
|
|
308
|
+
resp = client.post(url="https://httpbin.org/anything", data=data)
|
|
309
|
+
print(r.text)
|
|
310
|
+
|
|
311
|
+
# POST JSON Encoded Data
|
|
312
|
+
json = {"key1": "value1", "key2": "value2"}
|
|
313
|
+
resp = client.post(url="https://httpbin.org/anything", json=json)
|
|
314
|
+
print(r.text)
|
|
315
|
+
|
|
316
|
+
# POST Multipart-Encoded Files
|
|
317
|
+
files = {'file1': '/home/root/file1.txt', 'file2': 'home/root/file2.txt'}
|
|
318
|
+
r = client.post("https://httpbin.org/post", files=files)
|
|
319
|
+
print(r.text)
|
|
320
|
+
|
|
321
|
+
# Authentication using user/password
|
|
322
|
+
auth = ("user", "password")
|
|
323
|
+
resp = client.post(url="https://httpbin.org/anything", auth=auth)
|
|
324
|
+
print(r.text)
|
|
325
|
+
|
|
326
|
+
# Authentication using auth bearer
|
|
327
|
+
auth_bearer = "bearerXXXXXXXXXXXXXXXXXXXX"
|
|
328
|
+
resp = client.post(url="https://httpbin.org/anything", auth_bearer=auth_bearer)
|
|
329
|
+
print(r.text)
|
|
330
|
+
|
|
331
|
+
# Using proxy or env var HTTPR_PROXY
|
|
332
|
+
resp = httpr.Client(proxy="http://127.0.0.1:8080").get("https://tls.peet.ws/api/all")
|
|
333
|
+
print(resp.json())
|
|
334
|
+
export HTTPR_PROXY="socks5://127.0.0.1:1080"
|
|
335
|
+
resp = httpr.Client().get("https://tls.peet.ws/api/all")
|
|
336
|
+
print(resp.json())
|
|
337
|
+
|
|
338
|
+
# Using custom CA certificate store: env var HTTPR_CA_BUNDLE
|
|
339
|
+
resp = httpr.Client(ca_cert_file="/cert/cacert.pem").get("https://tls.peet.ws/api/all")
|
|
340
|
+
print(resp.json())
|
|
341
|
+
resp = httpr.Client(ca_cert_file=certifi.where()).get("https://tls.peet.ws/api/all")
|
|
342
|
+
print(resp.json())
|
|
343
|
+
export HTTPR_CA_BUNDLE="/home/user/Downloads/cert.pem"
|
|
344
|
+
resp = httpr.Client().get("https://tls.peet.ws/api/all")
|
|
345
|
+
print(resp.json())
|
|
346
|
+
|
|
347
|
+
# You can also use convenience functions that use a default Client instance under the hood:
|
|
348
|
+
# httpr.get() | httpr.head() | httpr.options() | httpr.delete() | httpr.post() | httpr.patch() | httpr.put()
|
|
349
|
+
resp = httpr.get("https://httpbin.org/anything")
|
|
350
|
+
print(r.text)
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### II. AsyncClient
|
|
354
|
+
|
|
355
|
+
`httpr.AsyncClient()` is an asynchronous wrapper around the `httpr.Client` class, offering the same functions, behavior, and input arguments.
|
|
356
|
+
|
|
357
|
+
```python
|
|
358
|
+
import asyncio
|
|
359
|
+
import logging
|
|
360
|
+
|
|
361
|
+
import httpr
|
|
362
|
+
|
|
363
|
+
async def aget_text(url):
|
|
364
|
+
async with httpr.AsyncClient() as client:
|
|
365
|
+
resp = await client.get(url)
|
|
366
|
+
return resp.text
|
|
367
|
+
|
|
368
|
+
async def main():
|
|
369
|
+
urls = ["https://nytimes.com/", "https://cnn.com/", "https://abcnews.go.com/"]
|
|
370
|
+
tasks = [aget_text(u) for u in urls]
|
|
371
|
+
results = await asyncio.gather(*tasks)
|
|
372
|
+
|
|
373
|
+
if __name__ == "__main__":
|
|
374
|
+
logging.basicConfig(level=logging.INFO)
|
|
375
|
+
asyncio.run(main())
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
**Streaming with AsyncClient:**
|
|
379
|
+
|
|
380
|
+
The `AsyncClient` also supports streaming responses with the same API:
|
|
381
|
+
|
|
382
|
+
```python
|
|
383
|
+
async with httpr.AsyncClient() as client:
|
|
384
|
+
async with client.stream("GET", "https://example.com/large-file") as response:
|
|
385
|
+
for chunk in response.iter_bytes():
|
|
386
|
+
process(chunk)
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
Note: While the context manager is async, the iteration over chunks (`iter_bytes()`, `iter_text()`, `iter_lines()`) is synchronous.
|
|
390
|
+
|
|
391
|
+
## Precompiled wheels
|
|
392
|
+
|
|
393
|
+
Provides precompiled wheels for the following platforms:
|
|
394
|
+
|
|
395
|
+
- 🐧 linux: `amd64`, `aarch64`, `armv7` (aarch64 and armv7 builds are `manylinux_2_34` compatible. `ubuntu>=22.04`, `debian>=12`)
|
|
396
|
+
- 🐧 musllinux: `amd64`, `aarch64`
|
|
397
|
+
- 🪟 windows: `amd64`
|
|
398
|
+
- 🍏 macos: `amd64`, `aarch64`.
|
|
399
|
+
|
|
400
|
+
## Development
|
|
401
|
+
|
|
402
|
+
This project uses [Taskfile](https://taskfile.dev) for development workflows.
|
|
403
|
+
|
|
404
|
+
### Setup
|
|
405
|
+
|
|
406
|
+
```bash
|
|
407
|
+
# Install dependencies
|
|
408
|
+
uv sync --extra dev
|
|
409
|
+
|
|
410
|
+
# Build Rust extension (required after any .rs changes)
|
|
411
|
+
uv run maturin develop
|
|
412
|
+
|
|
413
|
+
# Add hosts entry for e2e tests (one-time setup)
|
|
414
|
+
echo '127.0.0.1 httpbun.local' | sudo tee -a /etc/hosts
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### Running Tests
|
|
418
|
+
|
|
419
|
+
```bash
|
|
420
|
+
# List all available tasks
|
|
421
|
+
task --list
|
|
422
|
+
|
|
423
|
+
# Run unit tests only
|
|
424
|
+
task test:unit
|
|
425
|
+
|
|
426
|
+
# Run e2e tests (full workflow: start httpbun → test → stop)
|
|
427
|
+
task e2e
|
|
428
|
+
|
|
429
|
+
# Run e2e tests iteratively (keep httpbun running)
|
|
430
|
+
task e2e:local
|
|
431
|
+
task test:e2e # run tests against running container
|
|
432
|
+
|
|
433
|
+
# Run all tests
|
|
434
|
+
task test
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Other Tasks
|
|
438
|
+
|
|
439
|
+
```bash
|
|
440
|
+
task dev # Build Rust extension
|
|
441
|
+
task check # Run all checks (lint + test) - use before committing
|
|
442
|
+
task lint # Run Python linters (ruff + mypy)
|
|
443
|
+
task lint:rust # Run Rust linters (fmt + clippy)
|
|
444
|
+
task lint:all # Run all linters (Python + Rust)
|
|
445
|
+
task fmt # Format Python code with ruff
|
|
446
|
+
task fmt:rust # Format Rust code
|
|
447
|
+
task fmt:all # Format all code (Python + Rust)
|
|
448
|
+
task certs # Generate SSL certificates for e2e tests
|
|
449
|
+
task httpbun:start # Start httpbun container
|
|
450
|
+
task httpbun:stop # Stop httpbun container
|
|
451
|
+
task httpbun:logs # Show container logs
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Test Structure
|
|
455
|
+
|
|
456
|
+
- `tests/unit/` - Unit tests using pytest-httpbin (fast, no Docker required)
|
|
457
|
+
- `tests/e2e/` - E2E tests using httpbun Docker container with SSL
|
|
458
|
+
|
|
459
|
+
## CI
|
|
460
|
+
|
|
461
|
+
| Job | PRs | Push to main | Tags (Release) | Manual |
|
|
462
|
+
|-----|:---:|:------------:|:--------------:|:------:|
|
|
463
|
+
| `lint` | ✓ | | | |
|
|
464
|
+
| `test` (Python 3.10-3.14) | ✓ | ✓ | ✓ | |
|
|
465
|
+
| `docs` (build) | ✓ | | | |
|
|
466
|
+
| `linux`, `musllinux`, `windows`, `macos`, `sdist` | | | ✓ | ✓ |
|
|
467
|
+
| `release` (PyPI publish) | | | ✓ | ✓ |
|
|
468
|
+
| `benchmark` | | | ✓ | ✓ |
|
|
469
|
+
|
|
470
|
+
- **PRs**: Run lint, tests across Python 3.10-3.14 matrix, and verify docs build
|
|
471
|
+
- **Push to main**: Run tests only
|
|
472
|
+
- **Tags**: Run tests, build wheels, publish stable release to PyPI, run benchmarks
|
|
473
|
+
- **Manual**: Full multi-platform wheel builds with release and benchmarks
|
|
474
|
+
|
|
475
|
+
## Acknowledgements
|
|
476
|
+
|
|
477
|
+
- [uv](https://docs.astral.sh/uv/): The package manager used, and for leading the way in the "Rust for python tools"-sphere.
|
|
478
|
+
- [primp](https://github.com/deedy5/primp): *A lot* of code is borrowed from primp, that wraps rust library `rquest` for python in a similar way. If primp supported mTLS, I would have used it instead.
|
|
479
|
+
- [reqwests](https://github.com/seanmonstar/reqwest): The rust library that powers httpr.
|
|
480
|
+
- [pyo3](https://github.com/PyO3/pyo3)
|
|
481
|
+
- [maturin](https://github.com/PyO3/maturin)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
httpr/__init__.py,sha256=rnhHEynCwQXxNquG-oxL9CTzCKWmuc1oqmi9MN85q0Y,30890
|
|
2
|
+
httpr/httpr.cpython-313t-i386-linux-musl.so,sha256=wyyXwIxr_hoMcqgRL8rbt1VnmjXDyr4BQduJruruEOA,4955805
|
|
3
|
+
httpr/httpr.pyi,sha256=JRUdElAkcTXPwGrtRuK1vC-5aS9RQ1BxbYVZA3df7oc,22865
|
|
4
|
+
httpr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
httpr-0.3.2.dist-info/METADATA,sha256=86C5MwNVmWtmnrWDI-4DpVmq_ES_UQ-97Tl5lTkhebY,18029
|
|
6
|
+
httpr-0.3.2.dist-info/WHEEL,sha256=6ADlOhKFXhEu-Gk8dFo0B1i68er1Rbvld5DrNBBvRuo,107
|
|
7
|
+
httpr-0.3.2.dist-info/licenses/LICENSE,sha256=ZPD9tCar0h91tI4v-TuZdrjDdLqzU4rzPTxtP3x--uc,1063
|
|
8
|
+
httpr.libs/libgcc_s-f5fcfe20.so.1,sha256=AgqyMrWe8E5-efp_cXekgnQn-q6zJOV7u8ZBzQVlfJc,552385
|
|
9
|
+
httpr-0.3.2.dist-info/RECORD,,
|
httpr-0.2.5.dist-info/METADATA
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: httpr
|
|
3
|
-
Version: 0.2.5
|
|
4
|
-
Classifier: Programming Language :: Rust
|
|
5
|
-
Classifier: Programming Language :: Python :: 3
|
|
6
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
7
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
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 :: Implementation :: CPython
|
|
12
|
-
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
13
|
-
Classifier: Topic :: Internet :: WWW/HTTP
|
|
14
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
15
|
-
Requires-Dist: certifi ; extra == 'dev'
|
|
16
|
-
Requires-Dist: pytest>=8.1.1 ; extra == 'dev'
|
|
17
|
-
Requires-Dist: pytest-asyncio>=0.25.3 ; extra == 'dev'
|
|
18
|
-
Requires-Dist: pytest-httpbin>=2.1.0 ; extra == 'dev'
|
|
19
|
-
Requires-Dist: typing-extensions ; python_full_version < '3.12' and extra == 'dev'
|
|
20
|
-
Requires-Dist: mypy>=1.14.1 ; extra == 'dev'
|
|
21
|
-
Requires-Dist: ruff>=0.9.2 ; extra == 'dev'
|
|
22
|
-
Requires-Dist: maturin ; extra == 'dev'
|
|
23
|
-
Requires-Dist: trustme ; extra == 'dev'
|
|
24
|
-
Requires-Dist: cbor2 ; extra == 'dev'
|
|
25
|
-
Requires-Dist: mkdocs-material[imaging] ; extra == 'docs'
|
|
26
|
-
Requires-Dist: mkdocstrings[python]>=0.27.0 ; extra == 'docs'
|
|
27
|
-
Requires-Dist: mkdocs-gen-files ; extra == 'docs'
|
|
28
|
-
Requires-Dist: mkdocs-literate-nav ; extra == 'docs'
|
|
29
|
-
Requires-Dist: mkdocs-llmstxt ; extra == 'docs'
|
|
30
|
-
Requires-Dist: matplotlib ; extra == 'benchmark'
|
|
31
|
-
Requires-Dist: pandas ; extra == 'benchmark'
|
|
32
|
-
Requires-Dist: starlette ; extra == 'benchmark'
|
|
33
|
-
Requires-Dist: uvicorn ; extra == 'benchmark'
|
|
34
|
-
Requires-Dist: requests ; extra == 'benchmark'
|
|
35
|
-
Requires-Dist: httpx ; extra == 'benchmark'
|
|
36
|
-
Requires-Dist: tls-client ; extra == 'benchmark'
|
|
37
|
-
Requires-Dist: curl-cffi ; extra == 'benchmark'
|
|
38
|
-
Requires-Dist: pycurl ; extra == 'benchmark'
|
|
39
|
-
Requires-Dist: typing-extensions ; extra == 'benchmark'
|
|
40
|
-
Requires-Dist: aiohttp ; extra == 'benchmark'
|
|
41
|
-
Requires-Dist: cbor2 ; extra == 'benchmark'
|
|
42
|
-
Requires-Dist: jupyter ; extra == 'benchmark'
|
|
43
|
-
Requires-Dist: ipykernel ; extra == 'benchmark'
|
|
44
|
-
Requires-Dist: gunicorn ; extra == 'benchmark'
|
|
45
|
-
Requires-Dist: fastapi ; extra == 'benchmark'
|
|
46
|
-
Provides-Extra: dev
|
|
47
|
-
Provides-Extra: docs
|
|
48
|
-
Provides-Extra: benchmark
|
|
49
|
-
License-File: LICENSE
|
|
50
|
-
Summary: Fast HTTP client for Python
|
|
51
|
-
Keywords: python,request
|
|
52
|
-
Author: thomasht86
|
|
53
|
-
License: MIT License
|
|
54
|
-
Requires-Python: >=3.10
|
httpr-0.2.5.dist-info/RECORD
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
httpr-0.2.5.dist-info/METADATA,sha256=8ApuM5fZ2FgK1v2uXz0O8iXoKOKrqLIwiG-eSSWPfE8,2379
|
|
2
|
-
httpr-0.2.5.dist-info/WHEEL,sha256=BiRyfV9PO5pr3rBoHoDPBfEga_p-ngzde1sw4WIMGMQ,107
|
|
3
|
-
httpr-0.2.5.dist-info/licenses/LICENSE,sha256=ZPD9tCar0h91tI4v-TuZdrjDdLqzU4rzPTxtP3x--uc,1063
|
|
4
|
-
httpr.libs/libgcc_s-f5fcfe20.so.1,sha256=AgqyMrWe8E5-efp_cXekgnQn-q6zJOV7u8ZBzQVlfJc,552385
|
|
5
|
-
httpr/__init__.py,sha256=KAqXcDD7uC6EcHPIEcsTImTl5AQjPBkYmVuRpe17IPw,30143
|
|
6
|
-
httpr/httpr.cpython-313t-i386-linux-musl.so,sha256=ABpBUaNloXyUSJt1iq-1PpnLu8UhoDHelOR1zd6uvtw,4951709
|
|
7
|
-
httpr/httpr.pyi,sha256=m63ot1LXOmgYMdIQYIrGHUp8WMVm9GW0hpkfwNeohJ8,22605
|
|
8
|
-
httpr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
httpr-0.2.5.dist-info/RECORD,,
|
|
File without changes
|