python-http_request 0.0.6__py3-none-any.whl → 0.0.7.1__py3-none-any.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.
- http_request/__init__.py +55 -23
- {python_http_request-0.0.6.dist-info → python_http_request-0.0.7.1.dist-info}/METADATA +7 -8
- python_http_request-0.0.7.1.dist-info/RECORD +7 -0
- {python_http_request-0.0.6.dist-info → python_http_request-0.0.7.1.dist-info}/WHEEL +1 -1
- python_http_request-0.0.6.dist-info/RECORD +0 -7
- {python_http_request-0.0.6.dist-info → python_http_request-0.0.7.1.dist-info}/LICENSE +0 -0
http_request/__init__.py
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
# encoding: utf-8
|
|
3
3
|
|
|
4
4
|
__author__ = "ChenyangGao <https://chenyanggao.github.io>"
|
|
5
|
-
__version__ = (0, 0,
|
|
5
|
+
__version__ = (0, 0, 7)
|
|
6
6
|
__all__ = [
|
|
7
7
|
"SupportsGeturl", "url_origin", "complete_url", "cookies_str_to_dict", "headers_str_to_dict",
|
|
8
8
|
"encode_multipart_data", "encode_multipart_data_async",
|
|
9
9
|
]
|
|
10
10
|
|
|
11
|
-
from collections.abc import AsyncIterable, AsyncIterator, ItemsView, Iterable, Iterator, Mapping
|
|
11
|
+
from collections.abc import AsyncIterable, AsyncIterator, Buffer, ItemsView, Iterable, Iterator, Mapping
|
|
12
12
|
from itertools import chain
|
|
13
13
|
from mimetypes import guess_type
|
|
14
14
|
from os import fsdecode
|
|
@@ -19,14 +19,14 @@ from urllib.parse import quote, urlsplit, urlunsplit
|
|
|
19
19
|
from uuid import uuid4
|
|
20
20
|
|
|
21
21
|
from asynctools import ensure_aiter, async_chain
|
|
22
|
-
from filewrap import bio_chunk_iter, bio_chunk_async_iter,
|
|
22
|
+
from filewrap import bio_chunk_iter, bio_chunk_async_iter, SupportsRead
|
|
23
23
|
from integer_tool import int_to_bytes
|
|
24
24
|
from texttools import text_to_dict
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
AnyStr = TypeVar("AnyStr", bytes, str, covariant=True)
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
CRE_URL_SCHEME_match: Final = re_compile(r"(?i:[a-z][a-z0-9.+-]*)://").match
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
@runtime_checkable
|
|
@@ -34,27 +34,41 @@ class SupportsGeturl(Protocol[AnyStr]):
|
|
|
34
34
|
def geturl(self) -> AnyStr: ...
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
def url_origin(url: str,
|
|
38
|
-
if url.startswith("
|
|
37
|
+
def url_origin(url: str, /, default_port: int = 0) -> str:
|
|
38
|
+
if url.startswith("/"):
|
|
39
|
+
url = "http://localhost" + url
|
|
40
|
+
elif url.startswith("//"):
|
|
41
|
+
url = "http:" + url
|
|
42
|
+
elif url.startswith("://"):
|
|
39
43
|
url = "http" + url
|
|
40
|
-
elif
|
|
44
|
+
elif not CRE_URL_SCHEME_match(url):
|
|
41
45
|
url = "http://" + url
|
|
42
46
|
urlp = urlsplit(url)
|
|
43
|
-
scheme, netloc = urlp.scheme, urlp.netloc
|
|
44
|
-
if not
|
|
45
|
-
netloc = "
|
|
47
|
+
scheme, netloc = urlp.scheme or "http", urlp.netloc or "localhost"
|
|
48
|
+
if default_port and not urlp.port:
|
|
49
|
+
netloc = netloc.removesuffix(":") + f":{default_port}"
|
|
46
50
|
return f"{scheme}://{netloc}"
|
|
47
51
|
|
|
48
52
|
|
|
49
|
-
def complete_url(url: str,
|
|
50
|
-
if url.startswith("
|
|
53
|
+
def complete_url(url: str, /, default_port: int = 0) -> str:
|
|
54
|
+
if url.startswith("/"):
|
|
55
|
+
url = "http://localhost" + url
|
|
56
|
+
elif url.startswith("//"):
|
|
57
|
+
url = "http:" + url
|
|
58
|
+
elif url.startswith("://"):
|
|
51
59
|
url = "http" + url
|
|
52
|
-
elif
|
|
60
|
+
elif not CRE_URL_SCHEME_match(url):
|
|
53
61
|
url = "http://" + url
|
|
54
62
|
urlp = urlsplit(url)
|
|
55
63
|
repl = {"query": "", "fragment": ""}
|
|
56
|
-
if not urlp.
|
|
57
|
-
repl["
|
|
64
|
+
if not urlp.scheme:
|
|
65
|
+
repl["scheme"] = "http"
|
|
66
|
+
netloc = urlp.netloc
|
|
67
|
+
if not netloc:
|
|
68
|
+
netloc = "localhost"
|
|
69
|
+
if default_port and not urlp.port:
|
|
70
|
+
netloc = netloc.removesuffix(":") + f":{default_port}"
|
|
71
|
+
repl["netloc"] = netloc
|
|
58
72
|
return urlunsplit(urlp._replace(**repl)).rstrip("/")
|
|
59
73
|
|
|
60
74
|
|
|
@@ -76,9 +90,13 @@ def headers_str_to_dict(
|
|
|
76
90
|
return text_to_dict(headers.strip(), kv_sep, entry_sep)
|
|
77
91
|
|
|
78
92
|
|
|
79
|
-
def ensure_bytes(s, /) ->
|
|
80
|
-
if isinstance(s,
|
|
93
|
+
def ensure_bytes(s, /) -> bytes:
|
|
94
|
+
if isinstance(s, bytes):
|
|
81
95
|
return s
|
|
96
|
+
elif isinstance(s, memoryview):
|
|
97
|
+
return s.tobytes()
|
|
98
|
+
elif isinstance(s, Buffer):
|
|
99
|
+
return bytes(s)
|
|
82
100
|
if isinstance(s, int):
|
|
83
101
|
return int_to_bytes(s)
|
|
84
102
|
elif isinstance(s, str):
|
|
@@ -93,9 +111,13 @@ def encode_multipart_data(
|
|
|
93
111
|
data: None | Mapping[str, Any] = None,
|
|
94
112
|
files: None | Mapping[str, Buffer | SupportsRead[Buffer] | Iterable[Buffer]] = None,
|
|
95
113
|
boundary: None | str = None,
|
|
114
|
+
file_suffix: str = "",
|
|
96
115
|
) -> tuple[dict, Iterator[Buffer]]:
|
|
97
116
|
if not boundary:
|
|
98
117
|
boundary = uuid4().hex
|
|
118
|
+
suffix = bytes(file_suffix, "ascii")
|
|
119
|
+
if suffix and not suffix.startswith(b"."):
|
|
120
|
+
suffix = b"." + suffix
|
|
99
121
|
headers = {"Content-Type": f"multipart/form-data; boundary={boundary}"}
|
|
100
122
|
|
|
101
123
|
def encode_data(data) -> Iterator[Buffer]:
|
|
@@ -140,15 +162,18 @@ def encode_multipart_data(
|
|
|
140
162
|
elif hasattr(file, "read"):
|
|
141
163
|
file = bio_chunk_iter(file)
|
|
142
164
|
if not filename:
|
|
143
|
-
path = getattr(file, name, None)
|
|
165
|
+
path = getattr(file, "name", None)
|
|
144
166
|
if path:
|
|
145
167
|
filename = basename(path)
|
|
146
168
|
if b"Content-Type" not in headers:
|
|
147
169
|
headers[b"Content-Type"] = ensure_bytes(guess_type(fsdecode(filename))[0] or b"application/octet-stream")
|
|
148
170
|
if filename:
|
|
149
|
-
|
|
171
|
+
name = bytes(quote(filename), "ascii")
|
|
172
|
+
if not name.endswith(suffix):
|
|
173
|
+
name += suffix
|
|
174
|
+
headers[b"Content-Disposition"] += b'; filename="%s"' % name
|
|
150
175
|
else:
|
|
151
|
-
headers[b"Content-Disposition"] += b'; filename="%032x"' % uuid4().int
|
|
176
|
+
headers[b"Content-Disposition"] += b'; filename="%032x%s"' % (uuid4().int, suffix)
|
|
152
177
|
yield boundary_line
|
|
153
178
|
for entry in headers.items():
|
|
154
179
|
yield b"%s: %s\r\n" % entry
|
|
@@ -167,9 +192,13 @@ def encode_multipart_data_async(
|
|
|
167
192
|
data: None | Mapping[str, Any] = None,
|
|
168
193
|
files: None | Mapping[str, Buffer | SupportsRead[Buffer] | Iterable[Buffer] | AsyncIterable[Buffer]] = None,
|
|
169
194
|
boundary: None | str = None,
|
|
195
|
+
file_suffix: str = "",
|
|
170
196
|
) -> tuple[dict, AsyncIterator[Buffer]]:
|
|
171
197
|
if not boundary:
|
|
172
198
|
boundary = uuid4().hex
|
|
199
|
+
suffix = bytes(file_suffix, "ascii")
|
|
200
|
+
if suffix and not suffix.startswith(b"."):
|
|
201
|
+
suffix = b"." + suffix
|
|
173
202
|
headers = {"Content-Type": f"multipart/form-data; boundary={boundary}"}
|
|
174
203
|
|
|
175
204
|
async def encode_data(data) -> AsyncIterator[Buffer]:
|
|
@@ -214,7 +243,7 @@ def encode_multipart_data_async(
|
|
|
214
243
|
elif hasattr(file, "read"):
|
|
215
244
|
file = bio_chunk_async_iter(file)
|
|
216
245
|
if not filename:
|
|
217
|
-
path = getattr(file, name, None)
|
|
246
|
+
path = getattr(file, "name", None)
|
|
218
247
|
if path:
|
|
219
248
|
filename = basename(path)
|
|
220
249
|
if b"Content-Type" not in headers:
|
|
@@ -222,9 +251,12 @@ def encode_multipart_data_async(
|
|
|
222
251
|
else:
|
|
223
252
|
file = ensure_aiter(file)
|
|
224
253
|
if filename:
|
|
225
|
-
|
|
254
|
+
name = bytes(quote(filename), "ascii")
|
|
255
|
+
if not name.endswith(suffix):
|
|
256
|
+
name += suffix
|
|
257
|
+
headers[b"Content-Disposition"] += b'; filename="%s"' % name
|
|
226
258
|
else:
|
|
227
|
-
headers[b"Content-Disposition"] += b'; filename="%032x"' % uuid4().int
|
|
259
|
+
headers[b"Content-Disposition"] += b'; filename="%032x%s"' % (uuid4().int, suffix)
|
|
228
260
|
yield boundary_line
|
|
229
261
|
for entry in headers.items():
|
|
230
262
|
yield b"%s: %s\r\n" % entry
|
|
@@ -1,30 +1,29 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-http_request
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.7.1
|
|
4
4
|
Summary: Python http response utils.
|
|
5
5
|
Home-page: https://github.com/ChenyangGao/web-mount-packs/tree/main/python-module/python-http_request
|
|
6
6
|
License: MIT
|
|
7
7
|
Keywords: http,request
|
|
8
8
|
Author: ChenyangGao
|
|
9
9
|
Author-email: wosiwujm@gmail.com
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.12,<4.0
|
|
11
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
13
13
|
Classifier: License :: OSI Approved :: MIT License
|
|
14
14
|
Classifier: Operating System :: OS Independent
|
|
15
15
|
Classifier: Programming Language :: Python
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
19
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
19
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
20
|
Classifier: Topic :: Software Development
|
|
22
21
|
Classifier: Topic :: Software Development :: Libraries
|
|
23
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
-
Requires-Dist: integer_tool
|
|
25
|
-
Requires-Dist: python-asynctools
|
|
26
|
-
Requires-Dist: python-filewrap (>=0.
|
|
27
|
-
Requires-Dist: python-texttools
|
|
23
|
+
Requires-Dist: integer_tool (>=0.0.2)
|
|
24
|
+
Requires-Dist: python-asynctools (>=0.0.10)
|
|
25
|
+
Requires-Dist: python-filewrap (>=0.2.6)
|
|
26
|
+
Requires-Dist: python-texttools (>=0.0.3)
|
|
28
27
|
Project-URL: Repository, https://github.com/ChenyangGao/web-mount-packs/tree/main/python-module/python-http_request
|
|
29
28
|
Description-Content-Type: text/markdown
|
|
30
29
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
|
|
2
|
+
http_request/__init__.py,sha256=kF6myD_k_IErV9JaoDT9dNaJO5Kd72EjDbwYDswohxk,10464
|
|
3
|
+
http_request/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
python_http_request-0.0.7.1.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
|
|
5
|
+
python_http_request-0.0.7.1.dist-info/METADATA,sha256=jHqEf5N1uINz6-kK3_qgq2_PyzVfbhFL6t9BmSFGE8Q,1481
|
|
6
|
+
python_http_request-0.0.7.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
7
|
+
python_http_request-0.0.7.1.dist-info/RECORD,,
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
|
|
2
|
-
http_request/__init__.py,sha256=pfKYsbd6y5B3HYkGwbIzLi5dN5RTEqTKZE5qLGnVPoM,9223
|
|
3
|
-
http_request/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
python_http_request-0.0.6.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
|
|
5
|
-
python_http_request-0.0.6.dist-info/METADATA,sha256=v7O-Y-qv1lb4xvO_vN7uaT1DFLfv4zfUiAJze73hG_o,1497
|
|
6
|
-
python_http_request-0.0.6.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
7
|
-
python_http_request-0.0.6.dist-info/RECORD,,
|
|
File without changes
|