never-primp 1.0.2__cp38-abi3-win_amd64.whl → 1.1.1__cp38-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 never-primp might be problematic. Click here for more details.
- never_primp/__init__.py +119 -15
- never_primp/never_primp.pyd +0 -0
- never_primp/never_primp.pyi +3 -18
- never_primp-1.1.1.dist-info/METADATA +813 -0
- never_primp-1.1.1.dist-info/RECORD +8 -0
- never_primp-1.0.2.dist-info/METADATA +0 -803
- never_primp-1.0.2.dist-info/RECORD +0 -8
- {never_primp-1.0.2.dist-info → never_primp-1.1.1.dist-info}/WHEEL +0 -0
- {never_primp-1.0.2.dist-info → never_primp-1.1.1.dist-info}/licenses/LICENSE +0 -0
never_primp/__init__.py
CHANGED
|
@@ -142,7 +142,7 @@ class Client(RClient):
|
|
|
142
142
|
auth_bearer: str | None = None,
|
|
143
143
|
params: dict[str, str] | None = None,
|
|
144
144
|
headers: dict[str, str] | None = None,
|
|
145
|
-
|
|
145
|
+
cookies: dict[str, str] | None = None,
|
|
146
146
|
cookie_store: bool | None = True,
|
|
147
147
|
split_cookies: bool | None = False,
|
|
148
148
|
referer: bool | None = True,
|
|
@@ -155,26 +155,29 @@ class Client(RClient):
|
|
|
155
155
|
verify: bool | None = True,
|
|
156
156
|
ca_cert_file: str | None = None,
|
|
157
157
|
https_only: bool | None = False,
|
|
158
|
+
http1_only: bool | None = False,
|
|
158
159
|
http2_only: bool | None = False,
|
|
159
|
-
# Performance optimization parameters
|
|
160
160
|
pool_idle_timeout: float | None = None,
|
|
161
161
|
pool_max_idle_per_host: int | None = None,
|
|
162
162
|
tcp_nodelay: bool | None = None,
|
|
163
163
|
tcp_keepalive: float | None = None,
|
|
164
|
-
# Retry mechanism
|
|
165
|
-
retry_count: int | None = None,
|
|
166
|
-
retry_backoff: float | None = None,
|
|
167
164
|
):
|
|
168
165
|
"""
|
|
169
166
|
Args:
|
|
170
167
|
auth: a tuple containing the username and an optional password for basic authentication. Default is None.
|
|
171
168
|
auth_bearer: a string representing the bearer token for bearer token authentication. Default is None.
|
|
172
169
|
params: a map of query parameters to append to the URL. Default is None.
|
|
173
|
-
headers: an optional map of HTTP headers
|
|
174
|
-
|
|
175
|
-
|
|
170
|
+
headers: an optional ordered map of HTTP headers with strict order preservation.
|
|
171
|
+
Headers will be sent in the exact order specified, with automatic positioning of:
|
|
172
|
+
- Host (first position)
|
|
173
|
+
- Content-Length (second position for POST/PUT/PATCH)
|
|
174
|
+
- Content-Type (third position if auto-calculated)
|
|
175
|
+
- cookie (second-to-last position)
|
|
176
|
+
- priority (last position)
|
|
176
177
|
Example: {"User-Agent": "...", "Accept": "...", "Accept-Language": "..."}
|
|
177
178
|
Note: Python 3.7+ dict maintains insertion order by default.
|
|
179
|
+
cookies: initial cookies to set for the client. These cookies will be included in all requests.
|
|
180
|
+
Can be updated later using client.cookies.update(). Default is None.
|
|
178
181
|
cookie_store: enable a persistent cookie store. Received cookies will be preserved and included
|
|
179
182
|
in additional requests. Default is True.
|
|
180
183
|
split_cookies: split cookies into multiple `cookie` headers (HTTP/2 style) instead of a single
|
|
@@ -207,11 +210,17 @@ class Client(RClient):
|
|
|
207
210
|
verify: an optional boolean indicating whether to verify SSL certificates. Default is True.
|
|
208
211
|
ca_cert_file: path to CA certificate store. Default is None.
|
|
209
212
|
https_only: restrict the Client to be used with HTTPS only requests. Default is False.
|
|
210
|
-
|
|
213
|
+
http1_only: if true - use only HTTP/1.1. Default is False.
|
|
214
|
+
http2_only: if true - use only HTTP/2. Default is False.
|
|
215
|
+
Note: http1_only and http2_only are mutually exclusive. If both are true, http1_only takes precedence.
|
|
211
216
|
"""
|
|
212
217
|
super().__init__()
|
|
213
218
|
self._cookies_jar: CookieJar | None = None
|
|
214
219
|
|
|
220
|
+
# Set initial cookies if provided
|
|
221
|
+
if cookies:
|
|
222
|
+
self.update_cookies(cookies)
|
|
223
|
+
|
|
215
224
|
@property
|
|
216
225
|
def cookies(self) -> CookieJar:
|
|
217
226
|
"""
|
|
@@ -247,6 +256,79 @@ class Client(RClient):
|
|
|
247
256
|
del self
|
|
248
257
|
|
|
249
258
|
def request(self, method: HttpMethod, url: str, **kwargs: Unpack[RequestParams]) -> Response:
|
|
259
|
+
"""
|
|
260
|
+
Send an HTTP request with support for requests-toolbelt MultipartEncoder.
|
|
261
|
+
|
|
262
|
+
Supports both native primp format and requests-toolbelt MultipartEncoder:
|
|
263
|
+
- Native: request(url, data={...}, files={...})
|
|
264
|
+
- Toolbelt: request(url, data=MultipartEncoder(...))
|
|
265
|
+
"""
|
|
266
|
+
# Check if data is a MultipartEncoder from requests-toolbelt
|
|
267
|
+
data = kwargs.get('data')
|
|
268
|
+
if data is not None and hasattr(data, 'fields') and hasattr(data, 'content_type'):
|
|
269
|
+
# This looks like a MultipartEncoder
|
|
270
|
+
# Extract fields and convert to primp format
|
|
271
|
+
converted_data = {}
|
|
272
|
+
converted_files = {}
|
|
273
|
+
|
|
274
|
+
try:
|
|
275
|
+
# MultipartEncoder.fields is a dict-like object
|
|
276
|
+
for field_name, field_value in data.fields.items():
|
|
277
|
+
if isinstance(field_value, tuple):
|
|
278
|
+
# File field: (filename, file_obj, content_type)
|
|
279
|
+
if len(field_value) >= 2:
|
|
280
|
+
filename = field_value[0]
|
|
281
|
+
file_obj = field_value[1]
|
|
282
|
+
|
|
283
|
+
# Read the file content
|
|
284
|
+
if hasattr(file_obj, 'read'):
|
|
285
|
+
file_content = file_obj.read()
|
|
286
|
+
# Reset file pointer if possible
|
|
287
|
+
if hasattr(file_obj, 'seek'):
|
|
288
|
+
try:
|
|
289
|
+
file_obj.seek(0)
|
|
290
|
+
except:
|
|
291
|
+
pass
|
|
292
|
+
else:
|
|
293
|
+
file_content = file_obj
|
|
294
|
+
|
|
295
|
+
# Add mime type if provided
|
|
296
|
+
if len(field_value) >= 3:
|
|
297
|
+
mime_type = field_value[2]
|
|
298
|
+
converted_files[field_name] = (filename, file_content, mime_type)
|
|
299
|
+
else:
|
|
300
|
+
converted_files[field_name] = (filename, file_content)
|
|
301
|
+
else:
|
|
302
|
+
# Regular field
|
|
303
|
+
if isinstance(field_value, bytes):
|
|
304
|
+
converted_data[field_name] = field_value.decode('utf-8')
|
|
305
|
+
else:
|
|
306
|
+
converted_data[field_name] = str(field_value)
|
|
307
|
+
|
|
308
|
+
# Replace data and files in kwargs
|
|
309
|
+
if converted_data:
|
|
310
|
+
kwargs['data'] = converted_data
|
|
311
|
+
else:
|
|
312
|
+
kwargs.pop('data', None)
|
|
313
|
+
|
|
314
|
+
if converted_files:
|
|
315
|
+
kwargs['files'] = converted_files
|
|
316
|
+
|
|
317
|
+
except Exception as e:
|
|
318
|
+
# If conversion fails, fall back to treating it as raw content
|
|
319
|
+
# Read the encoder as bytes and send as content
|
|
320
|
+
if hasattr(data, 'read'):
|
|
321
|
+
kwargs['content'] = data.read()
|
|
322
|
+
kwargs.pop('data', None)
|
|
323
|
+
|
|
324
|
+
# Get content type from encoder
|
|
325
|
+
if hasattr(data, 'content_type'):
|
|
326
|
+
headers = kwargs.get('headers', {})
|
|
327
|
+
if not isinstance(headers, dict):
|
|
328
|
+
headers = dict(headers)
|
|
329
|
+
headers['Content-Type'] = data.content_type
|
|
330
|
+
kwargs['headers'] = headers
|
|
331
|
+
|
|
250
332
|
return super().request(method=method, url=url, **kwargs)
|
|
251
333
|
|
|
252
334
|
def get(self, url: str, **kwargs: Unpack[RequestParams]) -> Response:
|
|
@@ -277,12 +359,12 @@ class AsyncClient(Client):
|
|
|
277
359
|
auth_bearer: str | None = None,
|
|
278
360
|
params: dict[str, str] | None = None,
|
|
279
361
|
headers: dict[str, str] | None = None,
|
|
280
|
-
|
|
362
|
+
cookies: dict[str, str] | None = None,
|
|
281
363
|
cookie_store: bool | None = True,
|
|
282
364
|
split_cookies: bool | None = False,
|
|
283
365
|
referer: bool | None = True,
|
|
284
366
|
proxy: str | None = None,
|
|
285
|
-
timeout: float | None =
|
|
367
|
+
timeout: float | None = None,
|
|
286
368
|
impersonate: IMPERSONATE | None = None,
|
|
287
369
|
impersonate_os: IMPERSONATE_OS | None = None,
|
|
288
370
|
follow_redirects: bool | None = True,
|
|
@@ -290,17 +372,39 @@ class AsyncClient(Client):
|
|
|
290
372
|
verify: bool | None = True,
|
|
291
373
|
ca_cert_file: str | None = None,
|
|
292
374
|
https_only: bool | None = False,
|
|
375
|
+
http1_only: bool | None = False,
|
|
293
376
|
http2_only: bool | None = False,
|
|
294
377
|
# Performance optimization parameters
|
|
295
378
|
pool_idle_timeout: float | None = None,
|
|
296
379
|
pool_max_idle_per_host: int | None = None,
|
|
297
380
|
tcp_nodelay: bool | None = None,
|
|
298
381
|
tcp_keepalive: float | None = None,
|
|
299
|
-
# Retry mechanism
|
|
300
|
-
retry_count: int | None = None,
|
|
301
|
-
retry_backoff: float | None = None,
|
|
302
382
|
):
|
|
303
|
-
super().__init__(
|
|
383
|
+
super().__init__(
|
|
384
|
+
auth=auth,
|
|
385
|
+
auth_bearer=auth_bearer,
|
|
386
|
+
params=params,
|
|
387
|
+
headers=headers,
|
|
388
|
+
cookies=cookies,
|
|
389
|
+
cookie_store=cookie_store,
|
|
390
|
+
split_cookies=split_cookies,
|
|
391
|
+
referer=referer,
|
|
392
|
+
proxy=proxy,
|
|
393
|
+
timeout=timeout,
|
|
394
|
+
impersonate=impersonate,
|
|
395
|
+
impersonate_os=impersonate_os,
|
|
396
|
+
follow_redirects=follow_redirects,
|
|
397
|
+
max_redirects=max_redirects,
|
|
398
|
+
verify=verify,
|
|
399
|
+
ca_cert_file=ca_cert_file,
|
|
400
|
+
https_only=https_only,
|
|
401
|
+
http1_only=http1_only,
|
|
402
|
+
http2_only=http2_only,
|
|
403
|
+
pool_idle_timeout=pool_idle_timeout,
|
|
404
|
+
pool_max_idle_per_host=pool_max_idle_per_host,
|
|
405
|
+
tcp_nodelay=tcp_nodelay,
|
|
406
|
+
tcp_keepalive=tcp_keepalive,
|
|
407
|
+
)
|
|
304
408
|
|
|
305
409
|
async def __aenter__(self) -> AsyncClient:
|
|
306
410
|
return self
|
never_primp/never_primp.pyd
CHANGED
|
Binary file
|
never_primp/never_primp.pyi
CHANGED
|
@@ -38,7 +38,6 @@ class RequestParams(TypedDict, total=False):
|
|
|
38
38
|
auth_bearer: str | None
|
|
39
39
|
params: dict[str, str] | None
|
|
40
40
|
headers: dict[str, str] | None
|
|
41
|
-
ordered_headers: dict[str, str] | None
|
|
42
41
|
cookies: dict[str, str] | None
|
|
43
42
|
timeout: float | None
|
|
44
43
|
content: bytes | None
|
|
@@ -83,12 +82,12 @@ class RClient:
|
|
|
83
82
|
auth_bearer: str | None = None,
|
|
84
83
|
params: dict[str, str] | None = None,
|
|
85
84
|
headers: dict[str, str] | None = None,
|
|
86
|
-
|
|
87
|
-
timeout: float | None = None,
|
|
85
|
+
cookies: dict[str, str] | None = None,
|
|
88
86
|
cookie_store: bool | None = True,
|
|
89
87
|
split_cookies: bool | None = False,
|
|
90
88
|
referer: bool | None = True,
|
|
91
89
|
proxy: str | None = None,
|
|
90
|
+
timeout: float | None = None,
|
|
92
91
|
impersonate: IMPERSONATE | None = None,
|
|
93
92
|
impersonate_os: IMPERSONATE_OS | None = None,
|
|
94
93
|
follow_redirects: bool | None = True,
|
|
@@ -96,24 +95,18 @@ class RClient:
|
|
|
96
95
|
verify: bool | None = True,
|
|
97
96
|
ca_cert_file: str | None = None,
|
|
98
97
|
https_only: bool | None = False,
|
|
98
|
+
http1_only: bool | None = False,
|
|
99
99
|
http2_only: bool | None = False,
|
|
100
100
|
pool_idle_timeout: float | None = None,
|
|
101
101
|
pool_max_idle_per_host: int | None = None,
|
|
102
102
|
tcp_nodelay: bool | None = None,
|
|
103
103
|
tcp_keepalive: float | None = None,
|
|
104
|
-
retry_count: int | None = None,
|
|
105
|
-
retry_backoff: float | None = None,
|
|
106
104
|
): ...
|
|
107
105
|
@property
|
|
108
106
|
def headers(self) -> dict[str, str]: ...
|
|
109
107
|
@headers.setter
|
|
110
108
|
def headers(self, headers: dict[str, str]) -> None: ...
|
|
111
109
|
def headers_update(self, headers: dict[str, str]) -> None: ...
|
|
112
|
-
@property
|
|
113
|
-
def ordered_headers(self) -> dict[str, str]: ...
|
|
114
|
-
@ordered_headers.setter
|
|
115
|
-
def ordered_headers(self, ordered_headers: dict[str, str]) -> None: ...
|
|
116
|
-
def ordered_headers_update(self, ordered_headers: dict[str, str]) -> None: ...
|
|
117
110
|
# Cookie management methods (no URL required)
|
|
118
111
|
def get_all_cookies(self) -> dict[str, str]: ...
|
|
119
112
|
def set_cookie(self, name: str, value: str, domain: str | None = None, path: str | None = None) -> None: ...
|
|
@@ -143,14 +136,6 @@ class RClient:
|
|
|
143
136
|
@split_cookies.setter
|
|
144
137
|
def split_cookies(self, split_cookies: bool | None) -> None: ...
|
|
145
138
|
@property
|
|
146
|
-
def retry_count(self) -> int | None: ...
|
|
147
|
-
@retry_count.setter
|
|
148
|
-
def retry_count(self, retry_count: int | None) -> None: ...
|
|
149
|
-
@property
|
|
150
|
-
def retry_backoff(self) -> float | None: ...
|
|
151
|
-
@retry_backoff.setter
|
|
152
|
-
def retry_backoff(self, retry_backoff: float | None) -> None: ...
|
|
153
|
-
@property
|
|
154
139
|
def proxy(self) -> str | None: ...
|
|
155
140
|
@proxy.setter
|
|
156
141
|
def proxy(self, proxy: str) -> None: ...
|