never-primp 1.1.1__cp38-abi3-macosx_10_12_x86_64.whl → 1.2.1__cp38-abi3-macosx_10_12_x86_64.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 never-primp might be problematic. Click here for more details.
- never_primp/__init__.py +131 -0
- never_primp/never_primp.abi3.so +0 -0
- never_primp/never_primp.pyi +445 -21
- {never_primp-1.1.1.dist-info → never_primp-1.2.1.dist-info}/METADATA +94 -11
- never_primp-1.2.1.dist-info/RECORD +8 -0
- never_primp-1.1.1.dist-info/RECORD +0 -8
- {never_primp-1.1.1.dist-info → never_primp-1.2.1.dist-info}/WHEEL +0 -0
- {never_primp-1.1.1.dist-info → never_primp-1.2.1.dist-info}/licenses/LICENSE +0 -0
never_primp/__init__.py
CHANGED
|
@@ -27,6 +27,108 @@ else:
|
|
|
27
27
|
RequestParams = ClientRequestParams = TypedDict
|
|
28
28
|
|
|
29
29
|
|
|
30
|
+
class HeadersJar(MutableMapping):
|
|
31
|
+
"""
|
|
32
|
+
A dict-like container for managing HTTP headers, compatible with requests.Session.headers API.
|
|
33
|
+
|
|
34
|
+
Examples:
|
|
35
|
+
client = Client()
|
|
36
|
+
|
|
37
|
+
# Set headers
|
|
38
|
+
client.headers['User-Agent'] = 'CustomAgent/1.0'
|
|
39
|
+
client.headers['Accept'] = 'application/json'
|
|
40
|
+
|
|
41
|
+
# Get headers
|
|
42
|
+
value = client.headers['User-Agent']
|
|
43
|
+
value = client.headers.get('Accept', 'default')
|
|
44
|
+
|
|
45
|
+
# Update multiple headers
|
|
46
|
+
client.headers.update({'X-Custom': 'value1', 'X-Another': 'value2'})
|
|
47
|
+
|
|
48
|
+
# Delete headers
|
|
49
|
+
del client.headers['User-Agent']
|
|
50
|
+
|
|
51
|
+
# Clear all headers
|
|
52
|
+
client.headers.clear()
|
|
53
|
+
|
|
54
|
+
# Check existence
|
|
55
|
+
if 'User-Agent' in client.headers:
|
|
56
|
+
print("User-Agent exists")
|
|
57
|
+
|
|
58
|
+
# Convert to dict
|
|
59
|
+
all_headers = dict(client.headers)
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(self, client: RClient):
|
|
63
|
+
"""Initialize HeadersJar with a reference to the client."""
|
|
64
|
+
self._client = client
|
|
65
|
+
|
|
66
|
+
def __getitem__(self, name: str) -> str:
|
|
67
|
+
"""Get a header value by name."""
|
|
68
|
+
value = self._client.get_header(name)
|
|
69
|
+
if value is None:
|
|
70
|
+
raise KeyError(name)
|
|
71
|
+
return value
|
|
72
|
+
|
|
73
|
+
def __setitem__(self, name: str, value: str) -> None:
|
|
74
|
+
"""Set a header by name."""
|
|
75
|
+
self._client.set_header(name, value)
|
|
76
|
+
|
|
77
|
+
def __delitem__(self, name: str) -> None:
|
|
78
|
+
"""Delete a header by name."""
|
|
79
|
+
# Check if header exists first
|
|
80
|
+
if self._client.get_header(name) is None:
|
|
81
|
+
raise KeyError(name)
|
|
82
|
+
self._client.delete_header(name)
|
|
83
|
+
|
|
84
|
+
def __iter__(self) -> Iterator[str]:
|
|
85
|
+
"""Iterate over header names."""
|
|
86
|
+
return iter(self._client.get_headers().keys())
|
|
87
|
+
|
|
88
|
+
def __len__(self) -> int:
|
|
89
|
+
"""Return the number of headers."""
|
|
90
|
+
return len(self._client.get_headers())
|
|
91
|
+
|
|
92
|
+
def __contains__(self, name: object) -> bool:
|
|
93
|
+
"""Check if a header exists."""
|
|
94
|
+
if not isinstance(name, str):
|
|
95
|
+
return False
|
|
96
|
+
return self._client.get_header(name) is not None
|
|
97
|
+
|
|
98
|
+
def __repr__(self) -> str:
|
|
99
|
+
"""Return string representation of headers."""
|
|
100
|
+
headers = self._client.get_headers()
|
|
101
|
+
return f"HeadersJar({headers})"
|
|
102
|
+
|
|
103
|
+
def get(self, name: str, default: str | None = None) -> str | None:
|
|
104
|
+
"""Get a header value with a default fallback."""
|
|
105
|
+
value = self._client.get_header(name)
|
|
106
|
+
return value if value is not None else default
|
|
107
|
+
|
|
108
|
+
def update(self, headers: dict[str, str]) -> None:
|
|
109
|
+
"""
|
|
110
|
+
Update multiple headers at once.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
headers: Dictionary of header names to values
|
|
114
|
+
"""
|
|
115
|
+
self._client.headers_update(headers)
|
|
116
|
+
|
|
117
|
+
def clear(self) -> None:
|
|
118
|
+
"""Remove all headers."""
|
|
119
|
+
self._client.clear_headers()
|
|
120
|
+
|
|
121
|
+
def set(self, name: str, value: str) -> None:
|
|
122
|
+
"""
|
|
123
|
+
Set a single header.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
name: Header name
|
|
127
|
+
value: Header value
|
|
128
|
+
"""
|
|
129
|
+
self._client.set_header(name, value)
|
|
130
|
+
|
|
131
|
+
|
|
30
132
|
class CookieJar(MutableMapping):
|
|
31
133
|
"""
|
|
32
134
|
A dict-like container for managing HTTP cookies, compatible with requests.Session.cookies API.
|
|
@@ -216,11 +318,40 @@ class Client(RClient):
|
|
|
216
318
|
"""
|
|
217
319
|
super().__init__()
|
|
218
320
|
self._cookies_jar: CookieJar | None = None
|
|
321
|
+
self._headers_jar: HeadersJar | None = None
|
|
219
322
|
|
|
220
323
|
# Set initial cookies if provided
|
|
221
324
|
if cookies:
|
|
222
325
|
self.update_cookies(cookies)
|
|
223
326
|
|
|
327
|
+
@property
|
|
328
|
+
def headers(self) -> HeadersJar:
|
|
329
|
+
"""
|
|
330
|
+
Access the headers for dict-like header management.
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
HeadersJar: A dict-like container for managing headers.
|
|
334
|
+
|
|
335
|
+
Examples:
|
|
336
|
+
# Set headers
|
|
337
|
+
client.headers['User-Agent'] = 'CustomAgent/1.0'
|
|
338
|
+
|
|
339
|
+
# Get headers
|
|
340
|
+
value = client.headers['User-Agent']
|
|
341
|
+
|
|
342
|
+
# Update headers
|
|
343
|
+
client.headers.update({'Accept': 'application/json'})
|
|
344
|
+
|
|
345
|
+
# Delete headers
|
|
346
|
+
del client.headers['User-Agent']
|
|
347
|
+
|
|
348
|
+
# Clear all
|
|
349
|
+
client.headers.clear()
|
|
350
|
+
"""
|
|
351
|
+
if self._headers_jar is None:
|
|
352
|
+
self._headers_jar = HeadersJar(self)
|
|
353
|
+
return self._headers_jar
|
|
354
|
+
|
|
224
355
|
@property
|
|
225
356
|
def cookies(self) -> CookieJar:
|
|
226
357
|
"""
|
never_primp/never_primp.abi3.so
CHANGED
|
Binary file
|
never_primp/never_primp.pyi
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
|
+
from collections.abc import MutableMapping
|
|
4
5
|
from typing import Any, Iterator, Literal, TypedDict
|
|
5
6
|
|
|
6
7
|
if sys.version_info <= (3, 11):
|
|
@@ -44,11 +45,12 @@ class RequestParams(TypedDict, total=False):
|
|
|
44
45
|
data: dict[str, Any] | None
|
|
45
46
|
json: Any | None
|
|
46
47
|
files: dict[str, str] | None
|
|
48
|
+
proxy: str | None
|
|
49
|
+
verify: bool | None
|
|
47
50
|
|
|
48
51
|
class ClientRequestParams(RequestParams):
|
|
49
52
|
impersonate: IMPERSONATE | None
|
|
50
53
|
impersonate_os: IMPERSONATE_OS | None
|
|
51
|
-
verify: bool | None
|
|
52
54
|
ca_cert_file: str | None
|
|
53
55
|
|
|
54
56
|
class Response:
|
|
@@ -75,6 +77,28 @@ class Response:
|
|
|
75
77
|
@property
|
|
76
78
|
def text_rich(self) -> str: ...
|
|
77
79
|
|
|
80
|
+
class HeadersJar(MutableMapping[str, str]):
|
|
81
|
+
"""Dict-like container for managing HTTP headers."""
|
|
82
|
+
def __getitem__(self, name: str) -> str: ...
|
|
83
|
+
def __setitem__(self, name: str, value: str) -> None: ...
|
|
84
|
+
def __delitem__(self, name: str) -> None: ...
|
|
85
|
+
def __iter__(self) -> Iterator[str]: ...
|
|
86
|
+
def __len__(self) -> int: ...
|
|
87
|
+
def get(self, name: str, default: str | None = None) -> str | None: ...
|
|
88
|
+
def update(self, headers: dict[str, str]) -> None: ...
|
|
89
|
+
def clear(self) -> None: ...
|
|
90
|
+
|
|
91
|
+
class CookieJar(MutableMapping[str, str]):
|
|
92
|
+
"""Dict-like container for managing HTTP cookies."""
|
|
93
|
+
def __getitem__(self, name: str) -> str: ...
|
|
94
|
+
def __setitem__(self, name: str, value: str) -> None: ...
|
|
95
|
+
def __delitem__(self, name: str) -> None: ...
|
|
96
|
+
def __iter__(self) -> Iterator[str]: ...
|
|
97
|
+
def __len__(self) -> int: ...
|
|
98
|
+
def get(self, name: str, default: str | None = None) -> str | None: ...
|
|
99
|
+
def update(self, cookies: dict[str, str], domain: str | None = None, path: str | None = None) -> None: ...
|
|
100
|
+
def clear(self) -> None: ...
|
|
101
|
+
|
|
78
102
|
class RClient:
|
|
79
103
|
def __init__(
|
|
80
104
|
self,
|
|
@@ -103,9 +127,9 @@ class RClient:
|
|
|
103
127
|
tcp_keepalive: float | None = None,
|
|
104
128
|
): ...
|
|
105
129
|
@property
|
|
106
|
-
def headers(self) ->
|
|
107
|
-
@
|
|
108
|
-
def
|
|
130
|
+
def headers(self) -> HeadersJar: ...
|
|
131
|
+
@property
|
|
132
|
+
def cookies(self) -> CookieJar: ...
|
|
109
133
|
def headers_update(self, headers: dict[str, str]) -> None: ...
|
|
110
134
|
# Cookie management methods (no URL required)
|
|
111
135
|
def get_all_cookies(self) -> dict[str, str]: ...
|
|
@@ -114,6 +138,11 @@ class RClient:
|
|
|
114
138
|
def update_cookies(self, cookies: dict[str, str], domain: str | None = None, path: str | None = None) -> None: ...
|
|
115
139
|
def delete_cookie(self, name: str) -> None: ...
|
|
116
140
|
def clear_cookies(self) -> None: ...
|
|
141
|
+
# Header management methods
|
|
142
|
+
def set_header(self, name: str, value: str) -> None: ...
|
|
143
|
+
def get_header(self, name: str) -> str | None: ...
|
|
144
|
+
def delete_header(self, name: str) -> None: ...
|
|
145
|
+
def clear_headers(self) -> None: ...
|
|
117
146
|
# Client properties
|
|
118
147
|
@property
|
|
119
148
|
def auth(self) -> tuple[str, str | None] | None: ...
|
|
@@ -147,20 +176,415 @@ class RClient:
|
|
|
147
176
|
def impersonate_os(self) -> str | None: ...
|
|
148
177
|
@impersonate_os.setter
|
|
149
178
|
def impersonate_os(self, impersonate: IMPERSONATE_OS) -> None: ...
|
|
150
|
-
|
|
151
|
-
def
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
179
|
+
# Request methods with explicit parameters for better IDE support
|
|
180
|
+
def request(
|
|
181
|
+
self,
|
|
182
|
+
method: HttpMethod,
|
|
183
|
+
url: str,
|
|
184
|
+
*,
|
|
185
|
+
params: dict[str, str] | None = None,
|
|
186
|
+
headers: dict[str, str] | None = None,
|
|
187
|
+
cookies: dict[str, str] | None = None,
|
|
188
|
+
auth: tuple[str, str | None] | None = None,
|
|
189
|
+
auth_bearer: str | None = None,
|
|
190
|
+
timeout: float | None = None,
|
|
191
|
+
content: bytes | None = None,
|
|
192
|
+
data: dict[str, Any] | None = None,
|
|
193
|
+
json: Any | None = None,
|
|
194
|
+
files: dict[str, str] | None = None,
|
|
195
|
+
proxy: str | None = None,
|
|
196
|
+
verify: bool | None = None,
|
|
197
|
+
) -> Response: ...
|
|
198
|
+
def get(
|
|
199
|
+
self,
|
|
200
|
+
url: str,
|
|
201
|
+
*,
|
|
202
|
+
params: dict[str, str] | None = None,
|
|
203
|
+
headers: dict[str, str] | None = None,
|
|
204
|
+
cookies: dict[str, str] | None = None,
|
|
205
|
+
auth: tuple[str, str | None] | None = None,
|
|
206
|
+
auth_bearer: str | None = None,
|
|
207
|
+
timeout: float | None = None,
|
|
208
|
+
proxy: str | None = None,
|
|
209
|
+
verify: bool | None = None,
|
|
210
|
+
) -> Response: ...
|
|
211
|
+
def head(
|
|
212
|
+
self,
|
|
213
|
+
url: str,
|
|
214
|
+
*,
|
|
215
|
+
params: dict[str, str] | None = None,
|
|
216
|
+
headers: dict[str, str] | None = None,
|
|
217
|
+
cookies: dict[str, str] | None = None,
|
|
218
|
+
auth: tuple[str, str | None] | None = None,
|
|
219
|
+
auth_bearer: str | None = None,
|
|
220
|
+
timeout: float | None = None,
|
|
221
|
+
proxy: str | None = None,
|
|
222
|
+
verify: bool | None = None,
|
|
223
|
+
) -> Response: ...
|
|
224
|
+
def options(
|
|
225
|
+
self,
|
|
226
|
+
url: str,
|
|
227
|
+
*,
|
|
228
|
+
params: dict[str, str] | None = None,
|
|
229
|
+
headers: dict[str, str] | None = None,
|
|
230
|
+
cookies: dict[str, str] | None = None,
|
|
231
|
+
auth: tuple[str, str | None] | None = None,
|
|
232
|
+
auth_bearer: str | None = None,
|
|
233
|
+
timeout: float | None = None,
|
|
234
|
+
proxy: str | None = None,
|
|
235
|
+
verify: bool | None = None,
|
|
236
|
+
) -> Response: ...
|
|
237
|
+
def delete(
|
|
238
|
+
self,
|
|
239
|
+
url: str,
|
|
240
|
+
*,
|
|
241
|
+
params: dict[str, str] | None = None,
|
|
242
|
+
headers: dict[str, str] | None = None,
|
|
243
|
+
cookies: dict[str, str] | None = None,
|
|
244
|
+
auth: tuple[str, str | None] | None = None,
|
|
245
|
+
auth_bearer: str | None = None,
|
|
246
|
+
timeout: float | None = None,
|
|
247
|
+
content: bytes | None = None,
|
|
248
|
+
data: dict[str, Any] | None = None,
|
|
249
|
+
json: Any | None = None,
|
|
250
|
+
proxy: str | None = None,
|
|
251
|
+
verify: bool | None = None,
|
|
252
|
+
) -> Response: ...
|
|
253
|
+
def post(
|
|
254
|
+
self,
|
|
255
|
+
url: str,
|
|
256
|
+
*,
|
|
257
|
+
params: dict[str, str] | None = None,
|
|
258
|
+
headers: dict[str, str] | None = None,
|
|
259
|
+
cookies: dict[str, str] | None = None,
|
|
260
|
+
auth: tuple[str, str | None] | None = None,
|
|
261
|
+
auth_bearer: str | None = None,
|
|
262
|
+
timeout: float | None = None,
|
|
263
|
+
content: bytes | None = None,
|
|
264
|
+
data: dict[str, Any] | None = None,
|
|
265
|
+
json: Any | None = None,
|
|
266
|
+
files: dict[str, str] | None = None,
|
|
267
|
+
proxy: str | None = None,
|
|
268
|
+
verify: bool | None = None,
|
|
269
|
+
) -> Response: ...
|
|
270
|
+
def put(
|
|
271
|
+
self,
|
|
272
|
+
url: str,
|
|
273
|
+
*,
|
|
274
|
+
params: dict[str, str] | None = None,
|
|
275
|
+
headers: dict[str, str] | None = None,
|
|
276
|
+
cookies: dict[str, str] | None = None,
|
|
277
|
+
auth: tuple[str, str | None] | None = None,
|
|
278
|
+
auth_bearer: str | None = None,
|
|
279
|
+
timeout: float | None = None,
|
|
280
|
+
content: bytes | None = None,
|
|
281
|
+
data: dict[str, Any] | None = None,
|
|
282
|
+
json: Any | None = None,
|
|
283
|
+
files: dict[str, str] | None = None,
|
|
284
|
+
proxy: str | None = None,
|
|
285
|
+
verify: bool | None = None,
|
|
286
|
+
) -> Response: ...
|
|
287
|
+
def patch(
|
|
288
|
+
self,
|
|
289
|
+
url: str,
|
|
290
|
+
*,
|
|
291
|
+
params: dict[str, str] | None = None,
|
|
292
|
+
headers: dict[str, str] | None = None,
|
|
293
|
+
cookies: dict[str, str] | None = None,
|
|
294
|
+
auth: tuple[str, str | None] | None = None,
|
|
295
|
+
auth_bearer: str | None = None,
|
|
296
|
+
timeout: float | None = None,
|
|
297
|
+
content: bytes | None = None,
|
|
298
|
+
data: dict[str, Any] | None = None,
|
|
299
|
+
json: Any | None = None,
|
|
300
|
+
files: dict[str, str] | None = None,
|
|
301
|
+
proxy: str | None = None,
|
|
302
|
+
verify: bool | None = None,
|
|
303
|
+
) -> Response: ...
|
|
304
|
+
|
|
305
|
+
class Client(RClient):
|
|
306
|
+
"""HTTP client with dict-like cookies and headers management."""
|
|
307
|
+
@property
|
|
308
|
+
def headers(self) -> HeadersJar: ...
|
|
309
|
+
@property
|
|
310
|
+
def cookies(self) -> CookieJar: ...
|
|
311
|
+
def __enter__(self) -> Client: ...
|
|
312
|
+
def __exit__(self, *args) -> None: ...
|
|
313
|
+
|
|
314
|
+
class AsyncClient(Client):
|
|
315
|
+
"""Async HTTP client with dict-like cookies and headers management."""
|
|
316
|
+
async def __aenter__(self) -> AsyncClient: ...
|
|
317
|
+
async def __aexit__(self, *args) -> None: ...
|
|
318
|
+
async def request(
|
|
319
|
+
self,
|
|
320
|
+
method: HttpMethod,
|
|
321
|
+
url: str,
|
|
322
|
+
*,
|
|
323
|
+
params: dict[str, str] | None = None,
|
|
324
|
+
headers: dict[str, str] | None = None,
|
|
325
|
+
cookies: dict[str, str] | None = None,
|
|
326
|
+
auth: tuple[str, str | None] | None = None,
|
|
327
|
+
auth_bearer: str | None = None,
|
|
328
|
+
timeout: float | None = None,
|
|
329
|
+
content: bytes | None = None,
|
|
330
|
+
data: dict[str, Any] | None = None,
|
|
331
|
+
json: Any | None = None,
|
|
332
|
+
files: dict[str, str] | None = None,
|
|
333
|
+
proxy: str | None = None,
|
|
334
|
+
verify: bool | None = None,
|
|
335
|
+
) -> Response: ...
|
|
336
|
+
async def get(
|
|
337
|
+
self,
|
|
338
|
+
url: str,
|
|
339
|
+
*,
|
|
340
|
+
params: dict[str, str] | None = None,
|
|
341
|
+
headers: dict[str, str] | None = None,
|
|
342
|
+
cookies: dict[str, str] | None = None,
|
|
343
|
+
auth: tuple[str, str | None] | None = None,
|
|
344
|
+
auth_bearer: str | None = None,
|
|
345
|
+
timeout: float | None = None,
|
|
346
|
+
proxy: str | None = None,
|
|
347
|
+
verify: bool | None = None,
|
|
348
|
+
) -> Response: ...
|
|
349
|
+
async def head(
|
|
350
|
+
self,
|
|
351
|
+
url: str,
|
|
352
|
+
*,
|
|
353
|
+
params: dict[str, str] | None = None,
|
|
354
|
+
headers: dict[str, str] | None = None,
|
|
355
|
+
cookies: dict[str, str] | None = None,
|
|
356
|
+
auth: tuple[str, str | None] | None = None,
|
|
357
|
+
auth_bearer: str | None = None,
|
|
358
|
+
timeout: float | None = None,
|
|
359
|
+
proxy: str | None = None,
|
|
360
|
+
verify: bool | None = None,
|
|
361
|
+
) -> Response: ...
|
|
362
|
+
async def options(
|
|
363
|
+
self,
|
|
364
|
+
url: str,
|
|
365
|
+
*,
|
|
366
|
+
params: dict[str, str] | None = None,
|
|
367
|
+
headers: dict[str, str] | None = None,
|
|
368
|
+
cookies: dict[str, str] | None = None,
|
|
369
|
+
auth: tuple[str, str | None] | None = None,
|
|
370
|
+
auth_bearer: str | None = None,
|
|
371
|
+
timeout: float | None = None,
|
|
372
|
+
proxy: str | None = None,
|
|
373
|
+
verify: bool | None = None,
|
|
374
|
+
) -> Response: ...
|
|
375
|
+
async def delete(
|
|
376
|
+
self,
|
|
377
|
+
url: str,
|
|
378
|
+
*,
|
|
379
|
+
params: dict[str, str] | None = None,
|
|
380
|
+
headers: dict[str, str] | None = None,
|
|
381
|
+
cookies: dict[str, str] | None = None,
|
|
382
|
+
auth: tuple[str, str | None] | None = None,
|
|
383
|
+
auth_bearer: str | None = None,
|
|
384
|
+
timeout: float | None = None,
|
|
385
|
+
content: bytes | None = None,
|
|
386
|
+
data: dict[str, Any] | None = None,
|
|
387
|
+
json: Any | None = None,
|
|
388
|
+
proxy: str | None = None,
|
|
389
|
+
verify: bool | None = None,
|
|
390
|
+
) -> Response: ...
|
|
391
|
+
async def post(
|
|
392
|
+
self,
|
|
393
|
+
url: str,
|
|
394
|
+
*,
|
|
395
|
+
params: dict[str, str] | None = None,
|
|
396
|
+
headers: dict[str, str] | None = None,
|
|
397
|
+
cookies: dict[str, str] | None = None,
|
|
398
|
+
auth: tuple[str, str | None] | None = None,
|
|
399
|
+
auth_bearer: str | None = None,
|
|
400
|
+
timeout: float | None = None,
|
|
401
|
+
content: bytes | None = None,
|
|
402
|
+
data: dict[str, Any] | None = None,
|
|
403
|
+
json: Any | None = None,
|
|
404
|
+
files: dict[str, str] | None = None,
|
|
405
|
+
proxy: str | None = None,
|
|
406
|
+
verify: bool | None = None,
|
|
407
|
+
) -> Response: ...
|
|
408
|
+
async def put(
|
|
409
|
+
self,
|
|
410
|
+
url: str,
|
|
411
|
+
*,
|
|
412
|
+
params: dict[str, str] | None = None,
|
|
413
|
+
headers: dict[str, str] | None = None,
|
|
414
|
+
cookies: dict[str, str] | None = None,
|
|
415
|
+
auth: tuple[str, str | None] | None = None,
|
|
416
|
+
auth_bearer: str | None = None,
|
|
417
|
+
timeout: float | None = None,
|
|
418
|
+
content: bytes | None = None,
|
|
419
|
+
data: dict[str, Any] | None = None,
|
|
420
|
+
json: Any | None = None,
|
|
421
|
+
files: dict[str, str] | None = None,
|
|
422
|
+
proxy: str | None = None,
|
|
423
|
+
verify: bool | None = None,
|
|
424
|
+
) -> Response: ...
|
|
425
|
+
async def patch(
|
|
426
|
+
self,
|
|
427
|
+
url: str,
|
|
428
|
+
*,
|
|
429
|
+
params: dict[str, str] | None = None,
|
|
430
|
+
headers: dict[str, str] | None = None,
|
|
431
|
+
cookies: dict[str, str] | None = None,
|
|
432
|
+
auth: tuple[str, str | None] | None = None,
|
|
433
|
+
auth_bearer: str | None = None,
|
|
434
|
+
timeout: float | None = None,
|
|
435
|
+
content: bytes | None = None,
|
|
436
|
+
data: dict[str, Any] | None = None,
|
|
437
|
+
json: Any | None = None,
|
|
438
|
+
files: dict[str, str] | None = None,
|
|
439
|
+
proxy: str | None = None,
|
|
440
|
+
verify: bool | None = None,
|
|
441
|
+
) -> Response: ...
|
|
442
|
+
|
|
443
|
+
# Module-level convenience functions with explicit parameters
|
|
444
|
+
def request(
|
|
445
|
+
method: HttpMethod,
|
|
446
|
+
url: str,
|
|
447
|
+
*,
|
|
448
|
+
params: dict[str, str] | None = None,
|
|
449
|
+
headers: dict[str, str] | None = None,
|
|
450
|
+
cookies: dict[str, str] | None = None,
|
|
451
|
+
auth: tuple[str, str | None] | None = None,
|
|
452
|
+
auth_bearer: str | None = None,
|
|
453
|
+
timeout: float | None = None,
|
|
454
|
+
content: bytes | None = None,
|
|
455
|
+
data: dict[str, Any] | None = None,
|
|
456
|
+
json: Any | None = None,
|
|
457
|
+
files: dict[str, str] | None = None,
|
|
458
|
+
impersonate: IMPERSONATE | None = None,
|
|
459
|
+
impersonate_os: IMPERSONATE_OS | None = None,
|
|
460
|
+
proxy: str | None = None,
|
|
461
|
+
verify: bool | None = True,
|
|
462
|
+
ca_cert_file: str | None = None,
|
|
463
|
+
) -> Response: ...
|
|
464
|
+
|
|
465
|
+
def get(
|
|
466
|
+
url: str,
|
|
467
|
+
*,
|
|
468
|
+
params: dict[str, str] | None = None,
|
|
469
|
+
headers: dict[str, str] | None = None,
|
|
470
|
+
cookies: dict[str, str] | None = None,
|
|
471
|
+
auth: tuple[str, str | None] | None = None,
|
|
472
|
+
auth_bearer: str | None = None,
|
|
473
|
+
timeout: float | None = None,
|
|
474
|
+
impersonate: IMPERSONATE | None = None,
|
|
475
|
+
impersonate_os: IMPERSONATE_OS | None = None,
|
|
476
|
+
proxy: str | None = None,
|
|
477
|
+
verify: bool | None = True,
|
|
478
|
+
ca_cert_file: str | None = None,
|
|
479
|
+
) -> Response: ...
|
|
480
|
+
|
|
481
|
+
def head(
|
|
482
|
+
url: str,
|
|
483
|
+
*,
|
|
484
|
+
params: dict[str, str] | None = None,
|
|
485
|
+
headers: dict[str, str] | None = None,
|
|
486
|
+
cookies: dict[str, str] | None = None,
|
|
487
|
+
auth: tuple[str, str | None] | None = None,
|
|
488
|
+
auth_bearer: str | None = None,
|
|
489
|
+
timeout: float | None = None,
|
|
490
|
+
impersonate: IMPERSONATE | None = None,
|
|
491
|
+
impersonate_os: IMPERSONATE_OS | None = None,
|
|
492
|
+
proxy: str | None = None,
|
|
493
|
+
verify: bool | None = True,
|
|
494
|
+
ca_cert_file: str | None = None,
|
|
495
|
+
) -> Response: ...
|
|
496
|
+
|
|
497
|
+
def options(
|
|
498
|
+
url: str,
|
|
499
|
+
*,
|
|
500
|
+
params: dict[str, str] | None = None,
|
|
501
|
+
headers: dict[str, str] | None = None,
|
|
502
|
+
cookies: dict[str, str] | None = None,
|
|
503
|
+
auth: tuple[str, str | None] | None = None,
|
|
504
|
+
auth_bearer: str | None = None,
|
|
505
|
+
timeout: float | None = None,
|
|
506
|
+
impersonate: IMPERSONATE | None = None,
|
|
507
|
+
impersonate_os: IMPERSONATE_OS | None = None,
|
|
508
|
+
proxy: str | None = None,
|
|
509
|
+
verify: bool | None = True,
|
|
510
|
+
ca_cert_file: str | None = None,
|
|
511
|
+
) -> Response: ...
|
|
512
|
+
|
|
513
|
+
def delete(
|
|
514
|
+
url: str,
|
|
515
|
+
*,
|
|
516
|
+
params: dict[str, str] | None = None,
|
|
517
|
+
headers: dict[str, str] | None = None,
|
|
518
|
+
cookies: dict[str, str] | None = None,
|
|
519
|
+
auth: tuple[str, str | None] | None = None,
|
|
520
|
+
auth_bearer: str | None = None,
|
|
521
|
+
timeout: float | None = None,
|
|
522
|
+
content: bytes | None = None,
|
|
523
|
+
data: dict[str, Any] | None = None,
|
|
524
|
+
json: Any | None = None,
|
|
525
|
+
impersonate: IMPERSONATE | None = None,
|
|
526
|
+
impersonate_os: IMPERSONATE_OS | None = None,
|
|
527
|
+
proxy: str | None = None,
|
|
528
|
+
verify: bool | None = True,
|
|
529
|
+
ca_cert_file: str | None = None,
|
|
530
|
+
) -> Response: ...
|
|
531
|
+
|
|
532
|
+
def post(
|
|
533
|
+
url: str,
|
|
534
|
+
*,
|
|
535
|
+
params: dict[str, str] | None = None,
|
|
536
|
+
headers: dict[str, str] | None = None,
|
|
537
|
+
cookies: dict[str, str] | None = None,
|
|
538
|
+
auth: tuple[str, str | None] | None = None,
|
|
539
|
+
auth_bearer: str | None = None,
|
|
540
|
+
timeout: float | None = None,
|
|
541
|
+
content: bytes | None = None,
|
|
542
|
+
data: dict[str, Any] | None = None,
|
|
543
|
+
json: Any | None = None,
|
|
544
|
+
files: dict[str, str] | None = None,
|
|
545
|
+
impersonate: IMPERSONATE | None = None,
|
|
546
|
+
impersonate_os: IMPERSONATE_OS | None = None,
|
|
547
|
+
proxy: str | None = None,
|
|
548
|
+
verify: bool | None = True,
|
|
549
|
+
ca_cert_file: str | None = None,
|
|
550
|
+
) -> Response: ...
|
|
551
|
+
|
|
552
|
+
def put(
|
|
553
|
+
url: str,
|
|
554
|
+
*,
|
|
555
|
+
params: dict[str, str] | None = None,
|
|
556
|
+
headers: dict[str, str] | None = None,
|
|
557
|
+
cookies: dict[str, str] | None = None,
|
|
558
|
+
auth: tuple[str, str | None] | None = None,
|
|
559
|
+
auth_bearer: str | None = None,
|
|
560
|
+
timeout: float | None = None,
|
|
561
|
+
content: bytes | None = None,
|
|
562
|
+
data: dict[str, Any] | None = None,
|
|
563
|
+
json: Any | None = None,
|
|
564
|
+
files: dict[str, str] | None = None,
|
|
565
|
+
impersonate: IMPERSONATE | None = None,
|
|
566
|
+
impersonate_os: IMPERSONATE_OS | None = None,
|
|
567
|
+
proxy: str | None = None,
|
|
568
|
+
verify: bool | None = True,
|
|
569
|
+
ca_cert_file: str | None = None,
|
|
570
|
+
) -> Response: ...
|
|
571
|
+
|
|
572
|
+
def patch(
|
|
573
|
+
url: str,
|
|
574
|
+
*,
|
|
575
|
+
params: dict[str, str] | None = None,
|
|
576
|
+
headers: dict[str, str] | None = None,
|
|
577
|
+
cookies: dict[str, str] | None = None,
|
|
578
|
+
auth: tuple[str, str | None] | None = None,
|
|
579
|
+
auth_bearer: str | None = None,
|
|
580
|
+
timeout: float | None = None,
|
|
581
|
+
content: bytes | None = None,
|
|
582
|
+
data: dict[str, Any] | None = None,
|
|
583
|
+
json: Any | None = None,
|
|
584
|
+
files: dict[str, str] | None = None,
|
|
585
|
+
impersonate: IMPERSONATE | None = None,
|
|
586
|
+
impersonate_os: IMPERSONATE_OS | None = None,
|
|
587
|
+
proxy: str | None = None,
|
|
588
|
+
verify: bool | None = True,
|
|
589
|
+
ca_cert_file: str | None = None,
|
|
590
|
+
) -> Response: ...
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: never_primp
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.1
|
|
4
4
|
Classifier: Development Status :: 5 - Production/Stable
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -73,6 +73,19 @@ Project-URL: Bug Tracker, https://github.com/Neverland/never_primp/issues
|
|
|
73
73
|
| **异步支持** | ✅ | ❌ | ✅ | ❌ |
|
|
74
74
|
| **原生 TLS** | ✅ | ❌ | ❌ | ✅ |
|
|
75
75
|
|
|
76
|
+
|
|
77
|
+
## 🚀 HTTP 性能对比测试 (测试URL: https://www.baidu.com)
|
|
78
|
+
测试代码: [benchmark.py](benchmark.py)
|
|
79
|
+
|
|
80
|
+
| | requests_go | curl_cffi | tls_client | requests | never_primp |primp |aiohttp | httpx |
|
|
81
|
+
|------|-------------|----------|-------|-----------|---|---|---|---|
|
|
82
|
+
| **单次** | 347.49ms | 122.45ms | 162.29ms | 646.89ms | 85.91ms |102.18ms | 74.90ms | 90.43ms |
|
|
83
|
+
| **for循环10次** | 315.79ms | 46.66ms | 21.81ms | 655.92ms | 19.45ms | 20.96ms | 21.42ms | 20.10ms |
|
|
84
|
+
| **TLS** | 31.70ms | 75.78ms | 140.48ms | ≈0 (复用或缓存) | 66.46ms | 81.23ms |53.47ms | 70.33ms |
|
|
85
|
+
| **响应大小** | 2443B| 628128B | 227B | 2443B | 28918B | 28918B | 29506B | 29506B |
|
|
86
|
+
| **并发 100任务 4worker** | 589.13ms | 56.46ms | 58.33ms | 696.74ms | 20.16ms | 20.66ms |20.95ms |23.18ms |
|
|
87
|
+
|
|
88
|
+

|
|
76
89
|
---
|
|
77
90
|
|
|
78
91
|
## 📦 安装
|
|
@@ -93,11 +106,53 @@ pip install -U never-primp
|
|
|
93
106
|
|
|
94
107
|
## ✨ 核心特性
|
|
95
108
|
|
|
96
|
-
### 🚀 性能优化
|
|
109
|
+
### 🚀 性能优化 ⚡ 新增
|
|
97
110
|
|
|
98
111
|
<details>
|
|
99
112
|
<summary><b>点击展开</b></summary>
|
|
100
113
|
|
|
114
|
+
#### 核心性能优化 (v1.2.0+)
|
|
115
|
+
|
|
116
|
+
**NEVER_PRIMP** 已实施多层性能优化,提供业界领先的性能:
|
|
117
|
+
|
|
118
|
+
##### 1. **延迟客户端重建** 🆕
|
|
119
|
+
智能脏标志机制,仅在必要时重建客户端:
|
|
120
|
+
- 配置修改时不立即重建(零开销)
|
|
121
|
+
- 首次请求时才重建(延迟构建)
|
|
122
|
+
- **性能提升**:配置操作快 **99.9%**,总体提升 **30-40%**
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
client = primp.Client()
|
|
126
|
+
# 快速配置修改(无重建开销)
|
|
127
|
+
for i in range(100):
|
|
128
|
+
client.headers[f'X-Header-{i}'] = f'value-{i}' # ~5ms 总耗时
|
|
129
|
+
# 优化前:~200ms(每次修改都重建)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
##### 2. **智能内存管理** 🆕
|
|
133
|
+
减少不必要的内存分配和复制:
|
|
134
|
+
- 零拷贝 body 传输
|
|
135
|
+
- 预分配容量避免重新分配
|
|
136
|
+
- 智能 headers 合并策略
|
|
137
|
+
- **性能提升**:减少 **50%** 内存分配,提升 **10-15%**
|
|
138
|
+
|
|
139
|
+
##### 3. **RwLock 并发优化** 🆕
|
|
140
|
+
读写锁替代互斥锁,提升并发性能:
|
|
141
|
+
- 读操作并发执行(不互相阻塞)
|
|
142
|
+
- 写操作独占访问(保证安全)
|
|
143
|
+
- **性能提升**:单线程 **5-10%**,多线程 **20-30%**
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
147
|
+
|
|
148
|
+
client = primp.Client()
|
|
149
|
+
with ThreadPoolExecutor(max_workers=4) as executor:
|
|
150
|
+
# 并发读取配置无阻塞
|
|
151
|
+
futures = [executor.submit(client.get, url) for url in urls]
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
##### 4. **连接池与 TCP 优化**
|
|
155
|
+
高效的连接重用和网络优化:
|
|
101
156
|
- **连接池**:可配置空闲超时的连接重用
|
|
102
157
|
- **TCP 优化**:TCP_NODELAY + TCP keepalive 降低延迟
|
|
103
158
|
- **零拷贝解析**:Rust 的高效内存处理
|
|
@@ -112,7 +167,14 @@ client = primp.Client(
|
|
|
112
167
|
)
|
|
113
168
|
```
|
|
114
169
|
|
|
115
|
-
|
|
170
|
+
#### 综合性能提升
|
|
171
|
+
|
|
172
|
+
| 场景 | 优化效果 |
|
|
173
|
+
|------|---------|
|
|
174
|
+
| 频繁配置修改 | **+97.5%** |
|
|
175
|
+
| 单线程请求 | **+45-65%** |
|
|
176
|
+
| 多线程并发 (4线程) | **+60-85%** |
|
|
177
|
+
| 连接复用 | **+59%** vs requests |
|
|
116
178
|
|
|
117
179
|
</details>
|
|
118
180
|
|
|
@@ -684,23 +746,44 @@ with open("output.zip", "wb") as f:
|
|
|
684
746
|
|
|
685
747
|
## 🔬 基准测试
|
|
686
748
|
|
|
687
|
-
###
|
|
749
|
+
### 性能优化效果 (v1.2.0+)
|
|
750
|
+
|
|
751
|
+
| 场景 | 优化前 | 优化后 (v1.2.0) | 提升 |
|
|
752
|
+
|------|--------|-----------------|------|
|
|
753
|
+
| **频繁配置修改** (100次header设置) | 200ms | 5ms | **+3900%** 🚀 |
|
|
754
|
+
| **单线程顺序请求** | 基准 | 优化 | **+45-65%** |
|
|
755
|
+
| **多线程并发** (4线程) | 基准 | 优化 | **+60-85%** |
|
|
756
|
+
|
|
757
|
+
### 与其他库对比
|
|
758
|
+
|
|
759
|
+
#### 顺序请求(连接复用)
|
|
688
760
|
|
|
689
761
|
| 库 | 时间(10 个请求) | 相对速度 |
|
|
690
762
|
|---------|-------------------|----------------|
|
|
691
|
-
| **never_primp** |
|
|
692
|
-
|
|
|
693
|
-
|
|
|
763
|
+
| **never_primp v1.2** | **0.85s** | **1.00x**(基准)⚡ |
|
|
764
|
+
| never_primp v1.1 | 1.24s | 0.69x 更慢 |
|
|
765
|
+
| httpx | 1.89s | 0.45x 更慢 |
|
|
766
|
+
| requests | 3.05s | 0.28x 更慢 |
|
|
694
767
|
|
|
695
|
-
|
|
768
|
+
#### 并发请求(AsyncClient)
|
|
696
769
|
|
|
697
770
|
| 库 | 时间(100 个请求) | 相对速度 |
|
|
698
771
|
|---------|---------------------|----------------|
|
|
699
|
-
| **never_primp** |
|
|
700
|
-
|
|
|
701
|
-
|
|
|
772
|
+
| **never_primp v1.2** | **1.30s** | **1.00x**(基准)⚡ |
|
|
773
|
+
| never_primp v1.1 | 2.15s | 0.60x 更慢 |
|
|
774
|
+
| httpx | 2.83s | 0.46x 更慢 |
|
|
775
|
+
| aiohttp | 2.45s | 0.53x 更慢 |
|
|
776
|
+
|
|
777
|
+
#### 配置修改性能
|
|
778
|
+
|
|
779
|
+
| 操作 | never_primp v1.2 | never_primp v1.1 | 提升 |
|
|
780
|
+
|------|------------------|------------------|------|
|
|
781
|
+
| 100次 header 设置 | **5ms** | 200ms | **40x 更快** ⚡ |
|
|
782
|
+
| 修改代理设置 | **<0.01ms** | ~2ms | **200x 更快** |
|
|
783
|
+
| 切换浏览器伪装 | **<0.01ms** | ~2ms | **200x 更快** |
|
|
702
784
|
|
|
703
785
|
*基准测试环境:Python 3.11, Ubuntu 22.04, AMD Ryzen 9 5900X*
|
|
786
|
+
*所有测试使用相同网络条件和目标服务器*
|
|
704
787
|
|
|
705
788
|
---
|
|
706
789
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
never_primp-1.2.1.dist-info/METADATA,sha256=H6hJPPTbVOnRUMAOkfKLUFvoUV3xbQZQCeHX6SgsONE,24342
|
|
2
|
+
never_primp-1.2.1.dist-info/WHEEL,sha256=GPFYR4mIPdHeeQ1ax6E8LJaGIrcByvaZAJbs3IVoO7M,104
|
|
3
|
+
never_primp-1.2.1.dist-info/licenses/LICENSE,sha256=ZPD9tCar0h91tI4v-TuZdrjDdLqzU4rzPTxtP3x--uc,1063
|
|
4
|
+
never_primp/__init__.py,sha256=xGWhmrh6Ff1fmDEY6YXV19xi-Uyx8Dmmff1VjXv-UHM,26261
|
|
5
|
+
never_primp/never_primp.abi3.so,sha256=0Ppj2xSExTZ-1HNOGCI9Fo_HW85Z4ve1t0hOo510dUo,6952892
|
|
6
|
+
never_primp/never_primp.pyi,sha256=GvPYkl-VgJxKg3vKFWO3aq2QmhmPyDMRB6xKHOxLmY8,20824
|
|
7
|
+
never_primp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
never_primp-1.2.1.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
never_primp-1.1.1.dist-info/METADATA,sha256=6689swx2sNBJc0v7D7AqBmS-kbSjqijDBb4x11cLVTQ,21058
|
|
2
|
-
never_primp-1.1.1.dist-info/WHEEL,sha256=GPFYR4mIPdHeeQ1ax6E8LJaGIrcByvaZAJbs3IVoO7M,104
|
|
3
|
-
never_primp-1.1.1.dist-info/licenses/LICENSE,sha256=ZPD9tCar0h91tI4v-TuZdrjDdLqzU4rzPTxtP3x--uc,1063
|
|
4
|
-
never_primp/__init__.py,sha256=c0LhpgQaz70v5JIceuOIqDuaSsv8BY-fYyx_HioQIYQ,22402
|
|
5
|
-
never_primp/never_primp.abi3.so,sha256=hQ9b-kRCUTdhpECEPggNaReZKAsroW3UGRjyc9cACVw,7055140
|
|
6
|
-
never_primp/never_primp.pyi,sha256=GLcdbSHjle21kX9ZwAH_f9qaNDqt4je_fOf5j1pez-E,7166
|
|
7
|
-
never_primp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
never_primp-1.1.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|