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 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] | None): Query parameters to append to URL.
216
- headers (dict[str, str] | None): Request headers (merged with client defaults).
217
- cookies (dict[str, str] | None): Request cookies (merged with client defaults).
218
- auth (tuple[str, str | None] | None): Basic auth credentials (overrides client default).
219
- auth_bearer (str | None): Bearer token (overrides client default).
220
- timeout (float | None): Request timeout in seconds (overrides client default).
221
- content (bytes | None): Raw bytes for request body.
222
- data (dict[str, Any] | None): Form data for request body (application/x-www-form-urlencoded).
223
- json (Any | None): JSON data for request body (application/json).
224
- files (dict[str, str] | None): Files for multipart upload (dict mapping field names to file paths).
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] | None): Query parameters.
341
- headers (dict[str, str] | None): Request headers.
342
- cookies (dict[str, str] | None): Request cookies.
343
- auth (tuple[str, str | None] | None): Basic auth credentials.
344
- auth_bearer (str | None): Bearer token.
345
- timeout (float | None): Request timeout.
346
- content (bytes | None): Raw bytes body.
347
- data (dict[str, Any] | None): Form-encoded body.
348
- json (Any | None): JSON body.
349
- files (dict[str, str] | None): Multipart file uploads.
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
+ ![](https://thomasht86.github.io/httpr/benchmark.jpg)
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,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: maturin (1.10.2)
2
+ Generator: maturin (1.11.5)
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp313-cp313t-musllinux_1_2_i686
@@ -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
@@ -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,,