httpr 0.1.0__cp39-abi3-win_amd64.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.
Potentially problematic release.
This version of httpr might be problematic. Click here for more details.
- httpr/__init__.py +205 -0
- httpr/httpr.pyd +0 -0
- httpr/httpr.pyi +101 -0
- httpr/py.typed +0 -0
- httpr-0.1.0.dist-info/METADATA +316 -0
- httpr-0.1.0.dist-info/RECORD +8 -0
- httpr-0.1.0.dist-info/WHEEL +4 -0
- httpr-0.1.0.dist-info/licenses/LICENSE +21 -0
httpr/__init__.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import sys
|
|
5
|
+
from functools import partial
|
|
6
|
+
from typing import TYPE_CHECKING, TypedDict
|
|
7
|
+
|
|
8
|
+
if sys.version_info <= (3, 11):
|
|
9
|
+
from typing_extensions import Unpack
|
|
10
|
+
else:
|
|
11
|
+
from typing import Unpack
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
from .httpr import RClient
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from .httpr import ClientRequestParams, HttpMethod, RequestParams, Response
|
|
18
|
+
else:
|
|
19
|
+
|
|
20
|
+
class _Unpack:
|
|
21
|
+
@staticmethod
|
|
22
|
+
def __getitem__(*args, **kwargs):
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
Unpack = _Unpack()
|
|
26
|
+
RequestParams = ClientRequestParams = TypedDict
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Client(RClient):
|
|
30
|
+
"""Initializes an HTTP client"""
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
auth: tuple[str, str | None] | None = None,
|
|
35
|
+
auth_bearer: str | None = None,
|
|
36
|
+
params: dict[str, str] | None = None,
|
|
37
|
+
headers: dict[str, str] | None = None,
|
|
38
|
+
cookies: dict[str, str] | None = None,
|
|
39
|
+
cookie_store: bool | None = True,
|
|
40
|
+
referer: bool | None = True,
|
|
41
|
+
proxy: str | None = None,
|
|
42
|
+
timeout: float | None = 30,
|
|
43
|
+
follow_redirects: bool | None = True,
|
|
44
|
+
max_redirects: int | None = 20,
|
|
45
|
+
verify: bool | None = True,
|
|
46
|
+
ca_cert_file: str | None = None,
|
|
47
|
+
client_pem: str | None = None,
|
|
48
|
+
https_only: bool | None = False,
|
|
49
|
+
# http2_only: bool | None = False,
|
|
50
|
+
):
|
|
51
|
+
"""
|
|
52
|
+
Args:
|
|
53
|
+
auth: a tuple containing the username and an optional password for basic authentication. Default is None.
|
|
54
|
+
auth_bearer: a string representing the bearer token for bearer token authentication. Default is None.
|
|
55
|
+
params: a map of query parameters to append to the URL. Default is None.
|
|
56
|
+
headers: an optional map of HTTP headers to send with requests.
|
|
57
|
+
cookies: an optional map of cookies to send with requests as the `Cookie` header.
|
|
58
|
+
cookie_store: enable a persistent cookie store. Received cookies will be preserved and included
|
|
59
|
+
in additional requests. Default is True.
|
|
60
|
+
referer: automatic setting of the `Referer` header. Default is True.
|
|
61
|
+
proxy: proxy URL for HTTP requests, example: "socks5://127.0.0.1:9150". Default is None.
|
|
62
|
+
timeout: timeout for HTTP requests in seconds. Default is 30.
|
|
63
|
+
follow_redirects: a boolean to enable or disable following redirects. Default is True.
|
|
64
|
+
max_redirects: the maximum number of redirects if `follow_redirects` is True. Default is 20.
|
|
65
|
+
verify: an optional boolean indicating whether to verify SSL certificates. Default is True.
|
|
66
|
+
ca_cert_file: path to CA certificate store. Default is None.
|
|
67
|
+
client_pem: path to client certificate file. Default is None.
|
|
68
|
+
https_only: restrict the Client to be used with HTTPS only requests. Default is False.
|
|
69
|
+
http2_only: if true - use only HTTP/2, if false - use only HTTP/1. Default is False.
|
|
70
|
+
"""
|
|
71
|
+
super().__init__()
|
|
72
|
+
|
|
73
|
+
def __enter__(self) -> Client:
|
|
74
|
+
return self
|
|
75
|
+
|
|
76
|
+
def __exit__(self, *args):
|
|
77
|
+
del self
|
|
78
|
+
|
|
79
|
+
def request(self, method: HttpMethod, url: str, **kwargs: Unpack[RequestParams]) -> Response:
|
|
80
|
+
# Validate the HTTP method. Raise an exception if it's not supported.
|
|
81
|
+
if method not in ["GET", "HEAD", "OPTIONS", "DELETE", "POST", "PUT", "PATCH"]:
|
|
82
|
+
raise ValueError(f"Unsupported HTTP method: {method}")
|
|
83
|
+
return super().request(method=method, url=url, **kwargs)
|
|
84
|
+
|
|
85
|
+
def get(self, url: str, **kwargs: Unpack[RequestParams]) -> Response:
|
|
86
|
+
return self.request(method="GET", url=url, **kwargs)
|
|
87
|
+
|
|
88
|
+
def head(self, url: str, **kwargs: Unpack[RequestParams]) -> Response:
|
|
89
|
+
return self.request(method="HEAD", url=url, **kwargs)
|
|
90
|
+
|
|
91
|
+
def options(self, url: str, **kwargs: Unpack[RequestParams]) -> Response:
|
|
92
|
+
return self.request(method="OPTIONS", url=url, **kwargs)
|
|
93
|
+
|
|
94
|
+
def delete(self, url: str, **kwargs: Unpack[RequestParams]) -> Response:
|
|
95
|
+
return self.request(method="DELETE", url=url, **kwargs)
|
|
96
|
+
|
|
97
|
+
def post(self, url: str, **kwargs: Unpack[RequestParams]) -> Response:
|
|
98
|
+
return self.request(method="POST", url=url, **kwargs)
|
|
99
|
+
|
|
100
|
+
def put(self, url: str, **kwargs: Unpack[RequestParams]) -> Response:
|
|
101
|
+
return self.request(method="PUT", url=url, **kwargs)
|
|
102
|
+
|
|
103
|
+
def patch(self, url: str, **kwargs: Unpack[RequestParams]) -> Response:
|
|
104
|
+
return self.request(method="PATCH", url=url, **kwargs)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class AsyncClient(Client):
|
|
108
|
+
def __init__(self, *args, **kwargs):
|
|
109
|
+
super().__init__(*args, **kwargs)
|
|
110
|
+
|
|
111
|
+
async def __aenter__(self) -> AsyncClient:
|
|
112
|
+
return self
|
|
113
|
+
|
|
114
|
+
async def __aexit__(self, *args):
|
|
115
|
+
del self
|
|
116
|
+
|
|
117
|
+
async def _run_sync_asyncio(self, fn, *args, **kwargs):
|
|
118
|
+
loop = asyncio.get_running_loop()
|
|
119
|
+
return await loop.run_in_executor(None, partial(fn, *args, **kwargs))
|
|
120
|
+
|
|
121
|
+
async def request(self, method: HttpMethod, url: str, **kwargs: Unpack[RequestParams]): # type: ignore
|
|
122
|
+
return await self._run_sync_asyncio(super().request, method=method, url=url, **kwargs)
|
|
123
|
+
|
|
124
|
+
async def get(self, url: str, **kwargs: Unpack[RequestParams]): # type: ignore
|
|
125
|
+
return await self.request(method="GET", url=url, **kwargs)
|
|
126
|
+
|
|
127
|
+
async def head(self, url: str, **kwargs: Unpack[RequestParams]): # type: ignore
|
|
128
|
+
return await self.request(method="HEAD", url=url, **kwargs)
|
|
129
|
+
|
|
130
|
+
async def options(self, url: str, **kwargs: Unpack[RequestParams]): # type: ignore
|
|
131
|
+
return await self.request(method="OPTIONS", url=url, **kwargs)
|
|
132
|
+
|
|
133
|
+
async def delete(self, url: str, **kwargs: Unpack[RequestParams]): # type: ignore
|
|
134
|
+
return await self.request(method="DELETE", url=url, **kwargs)
|
|
135
|
+
|
|
136
|
+
async def post(self, url: str, **kwargs: Unpack[RequestParams]): # type: ignore
|
|
137
|
+
return await self.request(method="POST", url=url, **kwargs)
|
|
138
|
+
|
|
139
|
+
async def put(self, url: str, **kwargs: Unpack[RequestParams]): # type: ignore
|
|
140
|
+
return await self.request(method="PUT", url=url, **kwargs)
|
|
141
|
+
|
|
142
|
+
async def patch(self, url: str, **kwargs: Unpack[RequestParams]): # type: ignore
|
|
143
|
+
return await self.request(method="PATCH", url=url, **kwargs)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def request(
|
|
147
|
+
method: HttpMethod,
|
|
148
|
+
url: str,
|
|
149
|
+
verify: bool | None = True,
|
|
150
|
+
ca_cert_file: str | None = None,
|
|
151
|
+
client_pem: str | None = None,
|
|
152
|
+
**kwargs: Unpack[RequestParams],
|
|
153
|
+
):
|
|
154
|
+
"""
|
|
155
|
+
Args:
|
|
156
|
+
method: the HTTP method to use (e.g., "GET", "POST").
|
|
157
|
+
url: the URL to which the request will be made.
|
|
158
|
+
verify: an optional boolean indicating whether to verify SSL certificates. Default is True.
|
|
159
|
+
ca_cert_file: path to CA certificate store. Default is None.
|
|
160
|
+
client_pem: path to client certificate file. Default is None.
|
|
161
|
+
auth: a tuple containing the username and an optional password for basic authentication. Default is None.
|
|
162
|
+
auth_bearer: a string representing the bearer token for bearer token authentication. Default is None.
|
|
163
|
+
params: a map of query parameters to append to the URL. Default is None.
|
|
164
|
+
headers: an optional map of HTTP headers to send with requests. If `impersonate` is set, this will be ignored.
|
|
165
|
+
cookies: an optional map of cookies to send with requests as the `Cookie` header.
|
|
166
|
+
timeout: the timeout for the request in seconds. Default is 30.
|
|
167
|
+
content: the content to send in the request body as bytes. Default is None.
|
|
168
|
+
data: the form data to send in the request body. Default is None.
|
|
169
|
+
json: a JSON serializable object to send in the request body. Default is None.
|
|
170
|
+
files: a map of file fields to file paths to be sent as multipart/form-data. Default is None.
|
|
171
|
+
"""
|
|
172
|
+
with Client(
|
|
173
|
+
verify=verify,
|
|
174
|
+
ca_cert_file=ca_cert_file,
|
|
175
|
+
client_pem=client_pem,
|
|
176
|
+
) as client:
|
|
177
|
+
return client.request(method, url, **kwargs)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def get(url: str, **kwargs: Unpack[ClientRequestParams]):
|
|
181
|
+
return request(method="GET", url=url, **kwargs)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def head(url: str, **kwargs: Unpack[ClientRequestParams]):
|
|
185
|
+
return request(method="HEAD", url=url, **kwargs)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def options(url: str, **kwargs: Unpack[ClientRequestParams]):
|
|
189
|
+
return request(method="OPTIONS", url=url, **kwargs)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def delete(url: str, **kwargs: Unpack[ClientRequestParams]):
|
|
193
|
+
return request(method="DELETE", url=url, **kwargs)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def post(url: str, **kwargs: Unpack[ClientRequestParams]):
|
|
197
|
+
return request(method="POST", url=url, **kwargs)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def put(url: str, **kwargs: Unpack[ClientRequestParams]):
|
|
201
|
+
return request(method="PUT", url=url, **kwargs)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def patch(url: str, **kwargs: Unpack[ClientRequestParams]):
|
|
205
|
+
return request(method="PATCH", url=url, **kwargs)
|
httpr/httpr.pyd
ADDED
|
Binary file
|
httpr/httpr.pyi
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from typing import Any, Literal, TypedDict
|
|
5
|
+
|
|
6
|
+
if sys.version_info <= (3, 11):
|
|
7
|
+
from typing_extensions import Unpack
|
|
8
|
+
else:
|
|
9
|
+
from typing import Unpack
|
|
10
|
+
|
|
11
|
+
HttpMethod = Literal["GET", "HEAD", "OPTIONS", "DELETE", "POST", "PUT", "PATCH"]
|
|
12
|
+
|
|
13
|
+
class RequestParams(TypedDict, total=False):
|
|
14
|
+
auth: tuple[str, str | None] | None
|
|
15
|
+
auth_bearer: str | None
|
|
16
|
+
params: dict[str, str] | None
|
|
17
|
+
headers: dict[str, str] | None
|
|
18
|
+
cookies: dict[str, str] | None
|
|
19
|
+
timeout: float | None
|
|
20
|
+
content: bytes | None
|
|
21
|
+
data: dict[str, Any] | None
|
|
22
|
+
json: Any | None
|
|
23
|
+
files: dict[str, str] | None
|
|
24
|
+
|
|
25
|
+
class ClientRequestParams(RequestParams):
|
|
26
|
+
verify: bool | None
|
|
27
|
+
ca_cert_file: str | None
|
|
28
|
+
client_pem: str | None
|
|
29
|
+
|
|
30
|
+
class Response:
|
|
31
|
+
@property
|
|
32
|
+
def content(self) -> bytes: ...
|
|
33
|
+
@property
|
|
34
|
+
def cookies(self) -> dict[str, str]: ...
|
|
35
|
+
@property
|
|
36
|
+
def headers(self) -> dict[str, str]: ...
|
|
37
|
+
@property
|
|
38
|
+
def status_code(self) -> int: ...
|
|
39
|
+
@property
|
|
40
|
+
def url(self) -> str: ...
|
|
41
|
+
@property
|
|
42
|
+
def encoding(self) -> str: ...
|
|
43
|
+
@property
|
|
44
|
+
def text(self) -> str: ...
|
|
45
|
+
def json(self) -> Any: ...
|
|
46
|
+
@property
|
|
47
|
+
def text_markdown(self) -> str: ...
|
|
48
|
+
@property
|
|
49
|
+
def text_plain(self) -> str: ...
|
|
50
|
+
@property
|
|
51
|
+
def text_rich(self) -> str: ...
|
|
52
|
+
|
|
53
|
+
class RClient:
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
auth: tuple[str, str | None] | None = None,
|
|
57
|
+
auth_bearer: str | None = None,
|
|
58
|
+
params: dict[str, str] | None = None,
|
|
59
|
+
headers: dict[str, str] | None = None,
|
|
60
|
+
cookies: dict[str, str] | None = None,
|
|
61
|
+
timeout: float | None = None,
|
|
62
|
+
cookie_store: bool | None = True,
|
|
63
|
+
referer: bool | None = True,
|
|
64
|
+
proxy: str | None = None,
|
|
65
|
+
follow_redirects: bool | None = True,
|
|
66
|
+
max_redirects: int | None = 20,
|
|
67
|
+
verify: bool | None = True,
|
|
68
|
+
ca_cert_file: str | None = None,
|
|
69
|
+
client_pem: str | None = None,
|
|
70
|
+
https_only: bool | None = False,
|
|
71
|
+
http2_only: bool | None = False,
|
|
72
|
+
): ...
|
|
73
|
+
@property
|
|
74
|
+
def headers(self) -> dict[str, str]: ...
|
|
75
|
+
@headers.setter
|
|
76
|
+
def headers(self, headers: dict[str, str]) -> None: ...
|
|
77
|
+
@property
|
|
78
|
+
def cookies(self) -> dict[str, str]: ...
|
|
79
|
+
@cookies.setter
|
|
80
|
+
def cookies(self, cookies: dict[str, str]) -> None: ...
|
|
81
|
+
@property
|
|
82
|
+
def proxy(self) -> str | None: ...
|
|
83
|
+
@proxy.setter
|
|
84
|
+
def proxy(self, proxy: str) -> None: ...
|
|
85
|
+
def request(self, method: HttpMethod, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
|
|
86
|
+
def get(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
|
|
87
|
+
def head(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
|
|
88
|
+
def options(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
|
|
89
|
+
def delete(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
|
|
90
|
+
def post(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
|
|
91
|
+
def put(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
|
|
92
|
+
def patch(self, url: str, **kwargs: Unpack[RequestParams]) -> Response: ...
|
|
93
|
+
|
|
94
|
+
def request(method: HttpMethod, url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
|
|
95
|
+
def get(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
|
|
96
|
+
def head(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
|
|
97
|
+
def options(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
|
|
98
|
+
def delete(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
|
|
99
|
+
def post(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
|
|
100
|
+
def put(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
|
|
101
|
+
def patch(url: str, **kwargs: Unpack[ClientRequestParams]) -> Response: ...
|
httpr/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: httpr
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: 3
|
|
6
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
14
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
15
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Requires-Dist: certifi ; extra == 'dev'
|
|
18
|
+
Requires-Dist: pytest>=8.1.1 ; extra == 'dev'
|
|
19
|
+
Requires-Dist: pytest-asyncio>=0.25.3 ; extra == 'dev'
|
|
20
|
+
Requires-Dist: typing-extensions ; python_full_version < '3.12' and extra == 'dev'
|
|
21
|
+
Requires-Dist: mypy>=1.14.1 ; extra == 'dev'
|
|
22
|
+
Requires-Dist: ruff>=0.9.2 ; extra == 'dev'
|
|
23
|
+
Requires-Dist: maturin ; extra == 'dev'
|
|
24
|
+
Requires-Dist: trustme ; extra == 'dev'
|
|
25
|
+
Requires-Dist: mkdocs-material ; extra == 'docs'
|
|
26
|
+
Requires-Dist: httpr[dev] ; extra == 'scratch'
|
|
27
|
+
Requires-Dist: matplotlib ; extra == 'scratch'
|
|
28
|
+
Requires-Dist: pandas ; extra == 'scratch'
|
|
29
|
+
Requires-Dist: jupyter ; extra == 'scratch'
|
|
30
|
+
Requires-Dist: ipykernel ; extra == 'scratch'
|
|
31
|
+
Requires-Dist: httpx ; extra == 'scratch'
|
|
32
|
+
Requires-Dist: gunicorn ; extra == 'scratch'
|
|
33
|
+
Requires-Dist: uvicorn ; extra == 'scratch'
|
|
34
|
+
Requires-Dist: trustme ; extra == 'scratch'
|
|
35
|
+
Requires-Dist: starlette ; extra == 'scratch'
|
|
36
|
+
Requires-Dist: fastapi ; extra == 'scratch'
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Provides-Extra: docs
|
|
39
|
+
Provides-Extra: scratch
|
|
40
|
+
License-File: LICENSE
|
|
41
|
+
Summary: Fast HTTP client for Python
|
|
42
|
+
Keywords: python,request
|
|
43
|
+
Author: thomasht86
|
|
44
|
+
License: MIT License
|
|
45
|
+
Requires-Python: >=3.9
|
|
46
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
47
|
+
|
|
48
|
+
# httpr
|
|
49
|
+
|
|
50
|
+
**Blazing fast http-client** for Python in Rust 🦀 that can be used as drop-in replacement for `httpx` in most cases.
|
|
51
|
+
|
|
52
|
+
- **Fast**: httpr is built on top of `reqwests` which is a blazing fast http client in Rust. Check out the [benchmark](#benchmark).
|
|
53
|
+
- **Both async and sync**: httpr provides both a sync and async client.
|
|
54
|
+
- **Lightweight**: httpr is a lightweight http client with 0 python-dependencies.
|
|
55
|
+
- **Async**: first-class support for async/await.
|
|
56
|
+
- **http2**: httpr supports http2.
|
|
57
|
+
- **mTLS**: httpr supports mTLS.
|
|
58
|
+
- **Zero python dependencies**: httpr is a pure rust library with no python dependencies.
|
|
59
|
+
|
|
60
|
+
## Table of Contents
|
|
61
|
+
|
|
62
|
+
- [httpr](#httpr)
|
|
63
|
+
- [Table of Contents](#table-of-contents)
|
|
64
|
+
- [Installation](#installation)
|
|
65
|
+
- [Install with uv](#install-with-uv)
|
|
66
|
+
- [Install from PyPI](#install-from-pypi)
|
|
67
|
+
- [Benchmark](#benchmark)
|
|
68
|
+
- [Usage](#usage)
|
|
69
|
+
- [I. Client](#i-client)
|
|
70
|
+
- [Client methods](#client-methods)
|
|
71
|
+
- [Response object](#response-object)
|
|
72
|
+
- [Examples](#examples)
|
|
73
|
+
- [II. AsyncClient](#ii-asyncclient)
|
|
74
|
+
- [Precompiled wheels](#precompiled-wheels)
|
|
75
|
+
- [Acknowledgements](#acknowledgements)
|
|
76
|
+
|
|
77
|
+
## Installation
|
|
78
|
+
|
|
79
|
+
### Install with uv
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
uv add httpr
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
or
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
uv pip install httpr
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Install from PyPI
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
pip install -U httpr
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Benchmark
|
|
98
|
+
|
|
99
|
+

|
|
100
|
+
|
|
101
|
+
## Usage
|
|
102
|
+
|
|
103
|
+
### I. Client
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
class Client:
|
|
107
|
+
"""Initializes an HTTP client.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
auth (tuple[str, str| None] | None): Username and password for basic authentication. Default is None.
|
|
111
|
+
auth_bearer (str | None): Bearer token for authentication. Default is None.
|
|
112
|
+
params (dict[str, str] | None): Default query parameters to include in all requests. Default is None.
|
|
113
|
+
headers (dict[str, str] | None): Default headers to send with requests.
|
|
114
|
+
cookies (dict[str, str] | None): - Map of cookies to send with requests as the `Cookie` header.
|
|
115
|
+
timeout (float | None): HTTP request timeout in seconds. Default is 30.
|
|
116
|
+
cookie_store (bool | None): Enable a persistent cookie store. Received cookies will be preserved and included
|
|
117
|
+
in additional requests. Default is True.
|
|
118
|
+
referer (bool | None): Enable or disable automatic setting of the `Referer` header. Default is True.
|
|
119
|
+
proxy (str | None): Proxy URL for HTTP requests. Example: "socks5://127.0.0.1:9150". Default is None.
|
|
120
|
+
follow_redirects (bool | None): Whether to follow redirects. Default is True.
|
|
121
|
+
max_redirects (int | None): Maximum redirects to follow. Default 20. Applies if `follow_redirects` is True.
|
|
122
|
+
verify (bool | None): Verify SSL certificates. Default is True.
|
|
123
|
+
ca_cert_file (str | None): Path to CA certificate store. Default is None.
|
|
124
|
+
https_only` (bool | None): Restrict the Client to be used with HTTPS only requests. Default is `false`.
|
|
125
|
+
http2_only` (bool | None): If true - use only HTTP/2; if false - use only HTTP/1. Default is `false`.
|
|
126
|
+
|
|
127
|
+
"""
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
#### Client methods
|
|
131
|
+
|
|
132
|
+
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`.
|
|
133
|
+
```python
|
|
134
|
+
def get(
|
|
135
|
+
url: str,
|
|
136
|
+
params: dict[str, str] | None = None,
|
|
137
|
+
headers: dict[str, str] | None = None,
|
|
138
|
+
cookies: dict[str, str] | None = None,
|
|
139
|
+
auth: tuple[str, str| None] | None = None,
|
|
140
|
+
auth_bearer: str | None = None,
|
|
141
|
+
timeout: float | None = 30,
|
|
142
|
+
):
|
|
143
|
+
"""Performs a GET request to the specified URL.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
url (str): The URL to which the request will be made.
|
|
147
|
+
params (dict[str, str] | None): A map of query parameters to append to the URL. Default is None.
|
|
148
|
+
headers (dict[str, str] | None): A map of HTTP headers to send with the request. Default is None.
|
|
149
|
+
cookies (dict[str, str] | None): - An optional map of cookies to send with requests as the `Cookie` header.
|
|
150
|
+
auth (tuple[str, str| None] | None): A tuple containing the username and an optional password
|
|
151
|
+
for basic authentication. Default is None.
|
|
152
|
+
auth_bearer (str | None): A string representing the bearer token for bearer token authentication. Default is None.
|
|
153
|
+
timeout (float | None): The timeout for the request in seconds. Default is 30.
|
|
154
|
+
|
|
155
|
+
"""
|
|
156
|
+
```
|
|
157
|
+
```python
|
|
158
|
+
def post(
|
|
159
|
+
url: str,
|
|
160
|
+
params: dict[str, str] | None = None,
|
|
161
|
+
headers: dict[str, str] | None = None,
|
|
162
|
+
cookies: dict[str, str] | None = None,
|
|
163
|
+
content: bytes | None = None,
|
|
164
|
+
data: dict[str, Any] | None = None,
|
|
165
|
+
json: Any | None = None,
|
|
166
|
+
files: dict[str, str] | None = None,
|
|
167
|
+
auth: tuple[str, str| None] | None = None,
|
|
168
|
+
auth_bearer: str | None = None,
|
|
169
|
+
timeout: float | None = 30,
|
|
170
|
+
):
|
|
171
|
+
"""Performs a POST request to the specified URL.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
url (str): The URL to which the request will be made.
|
|
175
|
+
params (dict[str, str] | None): A map of query parameters to append to the URL. Default is None.
|
|
176
|
+
headers (dict[str, str] | None): A map of HTTP headers to send with the request. Default is None.
|
|
177
|
+
cookies (dict[str, str] | None): - An optional map of cookies to send with requests as the `Cookie` header.
|
|
178
|
+
content (bytes | None): The content to send in the request body as bytes. Default is None.
|
|
179
|
+
data (dict[str, Any] | None): The form data to send in the request body. Default is None.
|
|
180
|
+
json (Any | None): A JSON serializable object to send in the request body. Default is None.
|
|
181
|
+
files (dict[str, str] | None): A map of file fields to file paths to be sent as multipart/form-data. Default is None.
|
|
182
|
+
auth (tuple[str, str| None] | None): A tuple containing the username and an optional password
|
|
183
|
+
for basic authentication. Default is None.
|
|
184
|
+
auth_bearer (str | None): A string representing the bearer token for bearer token authentication. Default is None.
|
|
185
|
+
timeout (float | None): The timeout for the request in seconds. Default is 30.
|
|
186
|
+
|
|
187
|
+
"""
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
#### Response object
|
|
191
|
+
|
|
192
|
+
The `Client` class returns a `Response` object that contains the following attributes and methods:
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
resp.content
|
|
196
|
+
resp.cookies
|
|
197
|
+
resp.encoding
|
|
198
|
+
resp.headers
|
|
199
|
+
resp.json()
|
|
200
|
+
resp.status_code
|
|
201
|
+
resp.text
|
|
202
|
+
resp.text_markdown # html is converted to markdown text using html2text-rs
|
|
203
|
+
resp.text_plain # html is converted to plain text
|
|
204
|
+
resp.text_rich # html is converted to rich text
|
|
205
|
+
resp.url
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
#### Examples
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
import httpr
|
|
212
|
+
|
|
213
|
+
# Initialize the client
|
|
214
|
+
client = httpr.Client()
|
|
215
|
+
|
|
216
|
+
# GET request
|
|
217
|
+
resp = client.get("https://tls.peet.ws/api/all")
|
|
218
|
+
print(resp.json())
|
|
219
|
+
|
|
220
|
+
# GET request with passing params and setting timeout
|
|
221
|
+
params = {"param1": "value1", "param2": "value2"}
|
|
222
|
+
resp = client.post(url="https://httpbin.org/anything", params=params, timeout=10)
|
|
223
|
+
print(r.text)
|
|
224
|
+
|
|
225
|
+
# POST Binary Request Data
|
|
226
|
+
content = b"some_data"
|
|
227
|
+
resp = client.post(url="https://httpbin.org/anything", content=content)
|
|
228
|
+
print(r.text)
|
|
229
|
+
|
|
230
|
+
# POST Form Encoded Data
|
|
231
|
+
data = {"key1": "value1", "key2": "value2"}
|
|
232
|
+
resp = client.post(url="https://httpbin.org/anything", data=data)
|
|
233
|
+
print(r.text)
|
|
234
|
+
|
|
235
|
+
# POST JSON Encoded Data
|
|
236
|
+
json = {"key1": "value1", "key2": "value2"}
|
|
237
|
+
resp = client.post(url="https://httpbin.org/anything", json=json)
|
|
238
|
+
print(r.text)
|
|
239
|
+
|
|
240
|
+
# POST Multipart-Encoded Files
|
|
241
|
+
files = {'file1': '/home/root/file1.txt', 'file2': 'home/root/file2.txt'}
|
|
242
|
+
r = client.post("https://httpbin.org/post", files=files)
|
|
243
|
+
print(r.text)
|
|
244
|
+
|
|
245
|
+
# Authentication using user/password
|
|
246
|
+
auth = ("user", "password")
|
|
247
|
+
resp = client.post(url="https://httpbin.org/anything", auth=auth)
|
|
248
|
+
print(r.text)
|
|
249
|
+
|
|
250
|
+
# Authentication using auth bearer
|
|
251
|
+
auth_bearer = "bearerXXXXXXXXXXXXXXXXXXXX"
|
|
252
|
+
resp = client.post(url="https://httpbin.org/anything", auth_bearer=auth_bearer)
|
|
253
|
+
print(r.text)
|
|
254
|
+
|
|
255
|
+
# Using proxy or env var HTTPR_PROXY
|
|
256
|
+
resp = httpr.Client(proxy="http://127.0.0.1:8080").get("https://tls.peet.ws/api/all")
|
|
257
|
+
print(resp.json())
|
|
258
|
+
export HTTPR_PROXY="socks5://127.0.0.1:1080"
|
|
259
|
+
resp = httpr.Client().get("https://tls.peet.ws/api/all")
|
|
260
|
+
print(resp.json())
|
|
261
|
+
|
|
262
|
+
# Using custom CA certificate store: env var HTTPR_CA_BUNDLE
|
|
263
|
+
resp = httpr.Client(ca_cert_file="/cert/cacert.pem").get("https://tls.peet.ws/api/all")
|
|
264
|
+
print(resp.json())
|
|
265
|
+
resp = httpr.Client(ca_cert_file=certifi.where()).get("https://tls.peet.ws/api/all")
|
|
266
|
+
print(resp.json())
|
|
267
|
+
export HTTPR_CA_BUNDLE="/home/user/Downloads/cert.pem"
|
|
268
|
+
resp = httpr.Client().get("https://tls.peet.ws/api/all")
|
|
269
|
+
print(resp.json())
|
|
270
|
+
|
|
271
|
+
# You can also use convenience functions that use a default Client instance under the hood:
|
|
272
|
+
# httpr.get() | httpr.head() | httpr.options() | httpr.delete() | httpr.post() | httpr.patch() | httpr.put()
|
|
273
|
+
resp = httpr.get("https://httpbin.org/anything")
|
|
274
|
+
print(r.text)
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### II. AsyncClient
|
|
278
|
+
|
|
279
|
+
`httpr.AsyncClient()` is an asynchronous wrapper around the `httpr.Client` class, offering the same functions, behavior, and input arguments.
|
|
280
|
+
|
|
281
|
+
```python
|
|
282
|
+
import asyncio
|
|
283
|
+
import logging
|
|
284
|
+
|
|
285
|
+
import httpr
|
|
286
|
+
|
|
287
|
+
async def aget_text(url):
|
|
288
|
+
async with httpr.AsyncClient() as client:
|
|
289
|
+
resp = await client.get(url)
|
|
290
|
+
return resp.text
|
|
291
|
+
|
|
292
|
+
async def main():
|
|
293
|
+
urls = ["https://nytimes.com/", "https://cnn.com/", "https://abcnews.go.com/"]
|
|
294
|
+
tasks = [aget_text(u) for u in urls]
|
|
295
|
+
results = await asyncio.gather(*tasks)
|
|
296
|
+
|
|
297
|
+
if __name__ == "__main__":
|
|
298
|
+
logging.basicConfig(level=logging.INFO)
|
|
299
|
+
asyncio.run(main())
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## Precompiled wheels
|
|
303
|
+
|
|
304
|
+
Provides precompiled wheels for the following platforms:
|
|
305
|
+
|
|
306
|
+
- 🐧 linux: `amd64`, `aarch64`, `armv7` (aarch64 and armv7 builds are `manylinux_2_34` compatible. `ubuntu>=22.04`, `debian>=12`)
|
|
307
|
+
- 🐧 musllinux: `amd64`, `aarch64`
|
|
308
|
+
- 🪟 windows: `amd64`
|
|
309
|
+
- 🍏 macos: `amd64`, `aarch64`.
|
|
310
|
+
|
|
311
|
+
## Acknowledgements
|
|
312
|
+
|
|
313
|
+
- [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.
|
|
314
|
+
- [reqwests](https://github.com/seanmonstar/reqwest): The rust library that powers httpr.
|
|
315
|
+
- [pyo3](https://github.com/PyO3/pyo3)
|
|
316
|
+
- [maturin](https://github.com/PyO3/maturin)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
httpr-0.1.0.dist-info/METADATA,sha256=QCm1NtyBhldWbNhFEZwp6KaOuoUlIYVQ5f_lu6OLT28,12325
|
|
2
|
+
httpr-0.1.0.dist-info/WHEEL,sha256=R8ZEnni7m2eO5M7FTZ3FjvjlbxsclsOWT9kw6XK1Agc,94
|
|
3
|
+
httpr-0.1.0.dist-info/licenses/LICENSE,sha256=O15TIxUAXq6sSpPu0K3CJu_dT5ZzCgIk7hQtZsf0lEk,1084
|
|
4
|
+
httpr/httpr.pyi,sha256=VbROFo3ZXTacg2Fe0kvQWZ0RXXrAK08F4ivUL0cvGtY,3830
|
|
5
|
+
httpr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
httpr/__init__.py,sha256=mJu6IccmRh6mzdliCqwFnQtpsYuKYPpeTvvcuYqQBdw,8833
|
|
7
|
+
httpr/httpr.pyd,sha256=fuRxnteKqCmw5LP4iCo13HS-AY8Gjhjsv9iJRdRSwLw,4543488
|
|
8
|
+
httpr-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 deedy5
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|