python-http_request 0.1.2__tar.gz → 0.1.4__tar.gz
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.
- {python_http_request-0.1.2 → python_http_request-0.1.4}/PKG-INFO +3 -3
- {python_http_request-0.1.2 → python_http_request-0.1.4}/http_request/__init__.py +53 -40
- {python_http_request-0.1.2 → python_http_request-0.1.4}/pyproject.toml +3 -3
- {python_http_request-0.1.2 → python_http_request-0.1.4}/LICENSE +0 -0
- {python_http_request-0.1.2 → python_http_request-0.1.4}/http_request/py.typed +0 -0
- {python_http_request-0.1.2 → python_http_request-0.1.4}/readme.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-http_request
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Python http response utils.
|
|
5
5
|
Home-page: https://github.com/ChenyangGao/python-modules/tree/main/python-http_request
|
|
6
6
|
License: MIT
|
|
@@ -20,10 +20,10 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
20
20
|
Classifier: Topic :: Software Development
|
|
21
21
|
Classifier: Topic :: Software Development :: Libraries
|
|
22
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
-
Requires-Dist: http_response (>=0.0.
|
|
23
|
+
Requires-Dist: http_response (>=0.0.9)
|
|
24
24
|
Requires-Dist: orjson
|
|
25
25
|
Requires-Dist: python-asynctools (>=0.1.3)
|
|
26
|
-
Requires-Dist: python-dicttools (>=0.0.
|
|
26
|
+
Requires-Dist: python-dicttools (>=0.0.4)
|
|
27
27
|
Requires-Dist: python-ensure (>=0.0.1)
|
|
28
28
|
Requires-Dist: python-filewrap (>=0.2.8)
|
|
29
29
|
Requires-Dist: python-texttools (>=0.0.4)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# encoding: utf-8
|
|
3
3
|
|
|
4
4
|
__author__ = "ChenyangGao <https://chenyanggao.github.io>"
|
|
5
|
-
__version__ = (0, 1,
|
|
5
|
+
__version__ = (0, 1, 4)
|
|
6
6
|
__all__ = [
|
|
7
7
|
"SupportsGeturl", "url_origin", "complete_url", "ensure_ascii_url",
|
|
8
8
|
"urlencode", "cookies_str_to_dict", "headers_str_to_dict_by_lines",
|
|
@@ -12,7 +12,7 @@ __all__ = [
|
|
|
12
12
|
|
|
13
13
|
from collections import UserString
|
|
14
14
|
from collections.abc import (
|
|
15
|
-
AsyncIterable, AsyncIterator, Buffer, Iterable, Iterator,
|
|
15
|
+
AsyncIterable, AsyncIterator, Buffer, Callable, Iterable, Iterator,
|
|
16
16
|
Mapping, Sequence,
|
|
17
17
|
)
|
|
18
18
|
from decimal import Decimal
|
|
@@ -35,7 +35,7 @@ from yarl import URL
|
|
|
35
35
|
|
|
36
36
|
from asynctools import async_map
|
|
37
37
|
from dicttools import dict_map, iter_items
|
|
38
|
-
from ensure import ensure_bytes, ensure_buffer, ensure_str
|
|
38
|
+
from ensure import ensure_bytes as ensure_bytes_, ensure_buffer, ensure_str
|
|
39
39
|
from filewrap import bio_chunk_iter, bio_chunk_async_iter, SupportsRead
|
|
40
40
|
from http_response import get_charset, get_mimetype
|
|
41
41
|
from orjson import dumps as json_dumps
|
|
@@ -51,7 +51,7 @@ CRE_URL_SCHEME_match: Final = re_compile(r"(?i:[a-z][a-z0-9.+-]*)://").match
|
|
|
51
51
|
class RequestArgs(TypedDict):
|
|
52
52
|
method: str
|
|
53
53
|
url: str
|
|
54
|
-
data: Buffer | Iterable[Buffer] | AsyncIterable[Buffer]
|
|
54
|
+
data: None | Buffer | Iterable[Buffer] | AsyncIterable[Buffer]
|
|
55
55
|
headers: dict[str, str]
|
|
56
56
|
|
|
57
57
|
|
|
@@ -82,7 +82,7 @@ def complete_url(
|
|
|
82
82
|
url: str,
|
|
83
83
|
/,
|
|
84
84
|
default_port: int = 0,
|
|
85
|
-
|
|
85
|
+
params: Any = None,
|
|
86
86
|
) -> str:
|
|
87
87
|
if url.startswith("/"):
|
|
88
88
|
url = "http://localhost" + url
|
|
@@ -90,15 +90,12 @@ def complete_url(
|
|
|
90
90
|
url = "http:" + url
|
|
91
91
|
elif url.startswith("://"):
|
|
92
92
|
url = "http" + url
|
|
93
|
-
if not (
|
|
93
|
+
if not (params or default_port):
|
|
94
94
|
if not CRE_URL_SCHEME_match(url):
|
|
95
95
|
url = "http://" + url
|
|
96
96
|
return url
|
|
97
97
|
urlp = urlparse(url)
|
|
98
|
-
|
|
99
|
-
repl = {"params": "", "query": "", "fragment": ""}
|
|
100
|
-
else:
|
|
101
|
-
repl = {}
|
|
98
|
+
repl = {}
|
|
102
99
|
if not urlp.scheme:
|
|
103
100
|
repl["scheme"] = "http"
|
|
104
101
|
netloc = urlp.netloc
|
|
@@ -108,6 +105,10 @@ def complete_url(
|
|
|
108
105
|
netloc = netloc.removesuffix(":") + f":{default_port}"
|
|
109
106
|
if netloc != urlp.netloc:
|
|
110
107
|
repl["netloc"] = netloc
|
|
108
|
+
if params and (params := urlencode(params)):
|
|
109
|
+
if query := urlp.query:
|
|
110
|
+
params = query + "&" + params
|
|
111
|
+
repl["query"] = params
|
|
111
112
|
if not repl:
|
|
112
113
|
return url
|
|
113
114
|
return urlunparse(urlp._replace(**repl)).rstrip("/")
|
|
@@ -200,6 +201,7 @@ def encode_multipart_data(
|
|
|
200
201
|
files: None | Mapping[string, Any] | Iterable[tuple[string, Any]] = None,
|
|
201
202
|
boundary: None | str = None,
|
|
202
203
|
file_suffix: str = "",
|
|
204
|
+
ensure_bytes: bool = False,
|
|
203
205
|
*,
|
|
204
206
|
async_: Literal[False] = False,
|
|
205
207
|
) -> tuple[dict, Iterator[Buffer]]:
|
|
@@ -210,6 +212,7 @@ def encode_multipart_data(
|
|
|
210
212
|
files: None | Mapping[string, Any] | Iterable[tuple[string, Any]] = None,
|
|
211
213
|
boundary: None | str = None,
|
|
212
214
|
file_suffix: str = "",
|
|
215
|
+
ensure_bytes: bool = False,
|
|
213
216
|
*,
|
|
214
217
|
async_: Literal[True],
|
|
215
218
|
) -> tuple[dict, AsyncIterator[Buffer]]:
|
|
@@ -219,9 +222,14 @@ def encode_multipart_data(
|
|
|
219
222
|
files: None | Mapping[string, Any] | Iterable[tuple[string, Any]] = None,
|
|
220
223
|
boundary: None | str = None,
|
|
221
224
|
file_suffix: str = "",
|
|
225
|
+
ensure_bytes: bool = False,
|
|
222
226
|
*,
|
|
223
227
|
async_: bool = False,
|
|
224
228
|
) -> tuple[dict, Iterator[Buffer]] | tuple[dict, AsyncIterator[Buffer]]:
|
|
229
|
+
if ensure_bytes:
|
|
230
|
+
ensure_value: Callable = ensure_bytes_
|
|
231
|
+
else:
|
|
232
|
+
ensure_value = ensure_buffer
|
|
225
233
|
if async_:
|
|
226
234
|
return encode_multipart_data_async(data, files, boundary)
|
|
227
235
|
|
|
@@ -234,7 +242,7 @@ def encode_multipart_data(
|
|
|
234
242
|
boundary_bytes = bytes(boundary)
|
|
235
243
|
boundary = str(boundary_bytes, "latin-1")
|
|
236
244
|
boundary_line = b"--%s\r\n" % boundary_bytes
|
|
237
|
-
suffix =
|
|
245
|
+
suffix = ensure_bytes_(file_suffix)
|
|
238
246
|
if suffix and not suffix.startswith(b"."):
|
|
239
247
|
suffix = b"." + suffix
|
|
240
248
|
|
|
@@ -249,12 +257,12 @@ def encode_multipart_data(
|
|
|
249
257
|
pass
|
|
250
258
|
case [_, value, file_type]:
|
|
251
259
|
if file_type:
|
|
252
|
-
headers[b"content-type"] =
|
|
260
|
+
headers[b"content-type"] = ensure_bytes_(file_type)
|
|
253
261
|
case [_, value, file_type, file_headers, *rest]:
|
|
254
262
|
for k, v in iter_items(file_headers):
|
|
255
|
-
headers[
|
|
263
|
+
headers[ensure_bytes_(k).lower()] = ensure_bytes_(v)
|
|
256
264
|
if file_type:
|
|
257
|
-
headers[b"content-type"] =
|
|
265
|
+
headers[b"content-type"] = ensure_bytes_(file_type)
|
|
258
266
|
if isinstance(value, (PathLike, SupportsRead)):
|
|
259
267
|
is_file = True
|
|
260
268
|
if isinstance(value, PathLike):
|
|
@@ -265,15 +273,15 @@ def encode_multipart_data(
|
|
|
265
273
|
file = value
|
|
266
274
|
value = bio_chunk_iter(file)
|
|
267
275
|
if not filename:
|
|
268
|
-
filename =
|
|
276
|
+
filename = ensure_bytes_(basename(getattr(file, "name", b"") or b""))
|
|
269
277
|
elif isinstance(value, Buffer):
|
|
270
278
|
pass
|
|
271
279
|
elif isinstance(value, (str, UserString)):
|
|
272
|
-
value =
|
|
280
|
+
value = ensure_bytes_(value)
|
|
273
281
|
elif isinstance(value, Iterable):
|
|
274
|
-
value = map(
|
|
282
|
+
value = map(ensure_value, value)
|
|
275
283
|
else:
|
|
276
|
-
value =
|
|
284
|
+
value = ensure_value(value)
|
|
277
285
|
if is_file:
|
|
278
286
|
if filename:
|
|
279
287
|
filename = bytes(quote(filename), "ascii")
|
|
@@ -282,7 +290,7 @@ def encode_multipart_data(
|
|
|
282
290
|
else:
|
|
283
291
|
filename = bytes(uuid4().hex, "ascii") + suffix
|
|
284
292
|
if b"content-type" not in headers:
|
|
285
|
-
headers[b"content-type"] =
|
|
293
|
+
headers[b"content-type"] = ensure_bytes_(
|
|
286
294
|
guess_type(str(filename, "latin-1"))[0] or b"application/octet-stream")
|
|
287
295
|
headers[b"content-disposition"] += b'; filename="%s"' % filename
|
|
288
296
|
yield boundary_line
|
|
@@ -313,7 +321,12 @@ def encode_multipart_data_async(
|
|
|
313
321
|
files: None | Mapping[string, Any] | Iterable[tuple[string, Any]] = None,
|
|
314
322
|
boundary: None | str = None,
|
|
315
323
|
file_suffix: str = "",
|
|
324
|
+
ensure_bytes: bool = False,
|
|
316
325
|
) -> tuple[dict, AsyncIterator[Buffer]]:
|
|
326
|
+
if ensure_bytes:
|
|
327
|
+
ensure_value: Callable = ensure_bytes_
|
|
328
|
+
else:
|
|
329
|
+
ensure_value = ensure_buffer
|
|
317
330
|
if not boundary:
|
|
318
331
|
boundary = uuid4().hex
|
|
319
332
|
boundary_bytes = bytes(boundary, "ascii")
|
|
@@ -323,7 +336,7 @@ def encode_multipart_data_async(
|
|
|
323
336
|
boundary_bytes = bytes(boundary)
|
|
324
337
|
boundary = str(boundary_bytes, "latin-1")
|
|
325
338
|
boundary_line = b"--%s\r\n" % boundary_bytes
|
|
326
|
-
suffix =
|
|
339
|
+
suffix = ensure_bytes_(file_suffix)
|
|
327
340
|
if suffix and not suffix.startswith(b"."):
|
|
328
341
|
suffix = b"." + suffix
|
|
329
342
|
|
|
@@ -338,12 +351,12 @@ def encode_multipart_data_async(
|
|
|
338
351
|
pass
|
|
339
352
|
case [_, value, file_type]:
|
|
340
353
|
if file_type:
|
|
341
|
-
headers[b"content-type"] =
|
|
354
|
+
headers[b"content-type"] = ensure_bytes_(file_type)
|
|
342
355
|
case [_, value, file_type, file_headers, *rest]:
|
|
343
356
|
for k, v in iter_items(file_headers):
|
|
344
|
-
headers[
|
|
357
|
+
headers[ensure_bytes_(k).lower()] = ensure_bytes_(v)
|
|
345
358
|
if file_type:
|
|
346
|
-
headers[b"content-type"] =
|
|
359
|
+
headers[b"content-type"] = ensure_bytes_(file_type)
|
|
347
360
|
if isinstance(value, (PathLike, SupportsRead)):
|
|
348
361
|
is_file = True
|
|
349
362
|
if isinstance(value, PathLike):
|
|
@@ -354,15 +367,15 @@ def encode_multipart_data_async(
|
|
|
354
367
|
file = value
|
|
355
368
|
value = bio_chunk_async_iter(file)
|
|
356
369
|
if not filename:
|
|
357
|
-
filename =
|
|
370
|
+
filename = ensure_bytes_(basename(getattr(file, "name", b"") or b""))
|
|
358
371
|
elif isinstance(value, Buffer):
|
|
359
372
|
pass
|
|
360
373
|
elif isinstance(value, (str, UserString)):
|
|
361
|
-
value =
|
|
374
|
+
value = ensure_bytes_(value)
|
|
362
375
|
elif isinstance(value, Iterable):
|
|
363
|
-
value = async_map(
|
|
376
|
+
value = async_map(ensure_value, value)
|
|
364
377
|
else:
|
|
365
|
-
value =
|
|
378
|
+
value = ensure_value(value)
|
|
366
379
|
if is_file:
|
|
367
380
|
if filename:
|
|
368
381
|
filename = bytes(quote(filename), "ascii")
|
|
@@ -371,7 +384,7 @@ def encode_multipart_data_async(
|
|
|
371
384
|
else:
|
|
372
385
|
filename = bytes(uuid4().hex, "ascii") + suffix
|
|
373
386
|
if b"content-type" not in headers:
|
|
374
|
-
headers[b"content-type"] =
|
|
387
|
+
headers[b"content-type"] = ensure_bytes_(
|
|
375
388
|
guess_type(str(filename, "latin-1"))[0] or b"application/octet-stream")
|
|
376
389
|
headers[b"content-disposition"] += b'; filename="%s"' % filename
|
|
377
390
|
yield boundary_line
|
|
@@ -432,20 +445,20 @@ def normalize_request_args(
|
|
|
432
445
|
files: None | Mapping[string, Any] | Iterable[tuple[string, Any]] = None,
|
|
433
446
|
headers: None | Mapping[string, Any] | Iterable[tuple[string, Any]] = None,
|
|
434
447
|
ensure_ascii: bool = True,
|
|
448
|
+
ensure_bytes: bool = False,
|
|
435
449
|
*,
|
|
436
450
|
async_: bool = False,
|
|
437
451
|
) -> RequestArgs:
|
|
452
|
+
if ensure_bytes:
|
|
453
|
+
ensure_value: Callable = ensure_bytes_
|
|
454
|
+
else:
|
|
455
|
+
ensure_value = ensure_buffer
|
|
438
456
|
method = ensure_str(method).upper()
|
|
439
457
|
if isinstance(url, SupportsGeturl):
|
|
440
458
|
url = url.geturl()
|
|
441
459
|
elif isinstance(url, URL):
|
|
442
460
|
url = str(url)
|
|
443
|
-
url = complete_url(ensure_str(url))
|
|
444
|
-
if params and (params := urlencode(params, ensure_ascii=ensure_ascii)):
|
|
445
|
-
urlp = urlparse(url)
|
|
446
|
-
if query := urlp.query:
|
|
447
|
-
params = query + "&" + params
|
|
448
|
-
url = urlunparse(urlp._replace(query=params))
|
|
461
|
+
url = complete_url(ensure_str(url), params=params)
|
|
449
462
|
if ensure_ascii:
|
|
450
463
|
url = ensure_ascii_url(url)
|
|
451
464
|
headers_ = dict_map(
|
|
@@ -469,12 +482,12 @@ def normalize_request_args(
|
|
|
469
482
|
elif isinstance(data, (str, UserString)):
|
|
470
483
|
data = data.encode(charset)
|
|
471
484
|
elif isinstance(data, AsyncIterable):
|
|
472
|
-
data = async_map(
|
|
485
|
+
data = async_map(ensure_value, data)
|
|
473
486
|
elif isinstance(data, Iterator):
|
|
474
487
|
if async_:
|
|
475
|
-
data = async_map(
|
|
488
|
+
data = async_map(ensure_value, data)
|
|
476
489
|
else:
|
|
477
|
-
data = map(
|
|
490
|
+
data = map(ensure_value, data)
|
|
478
491
|
elif mimetype == "application/json":
|
|
479
492
|
if charset == "utf-8":
|
|
480
493
|
data = json_dumps(data, default=json_default)
|
|
@@ -492,12 +505,12 @@ def normalize_request_args(
|
|
|
492
505
|
if isinstance(json, Buffer):
|
|
493
506
|
data = json
|
|
494
507
|
elif isinstance(json, AsyncIterable):
|
|
495
|
-
data = async_map(
|
|
508
|
+
data = async_map(ensure_value, json)
|
|
496
509
|
elif isinstance(json, Iterator):
|
|
497
510
|
if async_:
|
|
498
|
-
data = async_map(
|
|
511
|
+
data = async_map(ensure_value, json)
|
|
499
512
|
else:
|
|
500
|
-
data = map(
|
|
513
|
+
data = map(ensure_value, json)
|
|
501
514
|
elif charset == "utf-8":
|
|
502
515
|
data = json_dumps(json, default=json_default)
|
|
503
516
|
else:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "python-http_request"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.4"
|
|
4
4
|
description = "Python http response utils."
|
|
5
5
|
authors = ["ChenyangGao <wosiwujm@gmail.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -27,10 +27,10 @@ include = [
|
|
|
27
27
|
|
|
28
28
|
[tool.poetry.dependencies]
|
|
29
29
|
python = "^3.12"
|
|
30
|
-
http_response = ">=0.0.
|
|
30
|
+
http_response = ">=0.0.9"
|
|
31
31
|
orjson = "*"
|
|
32
32
|
python-asynctools = ">=0.1.3"
|
|
33
|
-
python-dicttools = ">=0.0.
|
|
33
|
+
python-dicttools = ">=0.0.4"
|
|
34
34
|
python-ensure = ">=0.0.1"
|
|
35
35
|
python-filewrap = ">=0.2.8"
|
|
36
36
|
python-texttools = ">=0.0.4"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|