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 CHANGED
@@ -2,13 +2,13 @@
2
2
  # encoding: utf-8
3
3
 
4
4
  __author__ = "ChenyangGao <https://chenyanggao.github.io>"
5
- __version__ = (0, 0, 6)
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, Buffer, SupportsRead
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
- CRE_URL_SCHEME: Final = re_compile(r"^(?i:[a-z][a-z0-9.+-]*)://")
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, /) -> 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 CRE_URL_SCHEME.match(url) is None:
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 netloc:
45
- netloc = "localhost"
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, /) -> 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 CRE_URL_SCHEME.match(url) is None:
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.netloc:
57
- repl["path"] = "localhost"
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, /) -> Buffer:
80
- if isinstance(s, Buffer):
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
- headers[b"Content-Disposition"] += b'; filename="%s"' % quote(filename).encode("ascii")
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
- headers[b"Content-Disposition"] += b'; filename="%s"' % quote(filename).encode("ascii")
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.6
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,<4.0
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.1)
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,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.8.1
2
+ Generator: poetry-core 1.9.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -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,,