http_client_request 0.0.1__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.
@@ -0,0 +1,48 @@
1
+ Metadata-Version: 2.1
2
+ Name: http_client_request
3
+ Version: 0.0.1
4
+ Summary: http.client request extension.
5
+ Home-page: https://github.com/ChenyangGao/python-modules/tree/main/http_client_request
6
+ License: MIT
7
+ Keywords: http.client,request
8
+ Author: ChenyangGao
9
+ Author-email: wosiwujm@gmail.com
10
+ Requires-Python: >=3.12,<4.0
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Topic :: Software Development
21
+ Classifier: Topic :: Software Development :: Libraries
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Dist: http_response (>=0.0.8)
24
+ Requires-Dist: python-argtools (>=0.0.2)
25
+ Requires-Dist: python-cookietools (>=0.0.11)
26
+ Requires-Dist: python-dicttools (>=0.0.2)
27
+ Requires-Dist: python-filewrap (>=0.2.8)
28
+ Requires-Dist: python-http_request (>=0.1.3)
29
+ Requires-Dist: yarl
30
+ Project-URL: Repository, https://github.com/ChenyangGao/python-modules/tree/main/http_client_request
31
+ Description-Content-Type: text/markdown
32
+
33
+ # http.client request extension.
34
+
35
+ ## Installation
36
+
37
+ You can install via [pypi](https://pypi.org/project/http_client_request/)
38
+
39
+ ```console
40
+ pip install -U http_client_request
41
+ ```
42
+
43
+ ## Usage
44
+
45
+ ```python
46
+ from http_client_request import request
47
+ ```
48
+
@@ -0,0 +1,224 @@
1
+ #!/usr/bin/env python3
2
+ # coding: utf-8
3
+
4
+ __author__ = "ChenyangGao <https://chenyanggao.github.io>"
5
+ __version__ = (0, 0, 1)
6
+ __all__ = ["request"]
7
+
8
+ from collections import UserString
9
+ from collections.abc import Buffer, Callable, Iterable, Mapping
10
+ from http.client import HTTPConnection, HTTPSConnection, HTTPResponse
11
+ from http.cookiejar import CookieJar
12
+ from http.cookies import SimpleCookie
13
+ from inspect import signature
14
+ from os import PathLike
15
+ from types import EllipsisType
16
+ from typing import cast, overload, Any, Final, Literal
17
+ from urllib.error import HTTPError
18
+ from urllib.parse import urljoin, urlsplit, urlunsplit
19
+
20
+ from argtools import argcount
21
+ from cookietools import cookies_to_str, extract_cookies
22
+ from dicttools import get_all_items
23
+ from filewrap import bio_chunk_iter, SupportsRead
24
+ from http_request import normalize_request_args, SupportsGeturl
25
+ from http_response import decompress_response, parse_response
26
+ from yarl import URL
27
+
28
+
29
+ type string = Buffer | str | UserString
30
+
31
+ HTTP_CONNECTION_KWARGS: Final = signature(HTTPConnection).parameters.keys()
32
+ HTTPS_CONNECTION_KWARGS: Final = signature(HTTPSConnection).parameters.keys()
33
+
34
+ if "__del__" not in HTTPConnection.__dict__:
35
+ setattr(HTTPConnection, "__del__", HTTPConnection.close)
36
+ if "__del__" not in HTTPSConnection.__dict__:
37
+ setattr(HTTPSConnection, "__del__", HTTPSConnection.close)
38
+ if "__del__" not in HTTPResponse.__dict__:
39
+ setattr(HTTPResponse, "__del__", HTTPResponse.close)
40
+
41
+
42
+ def get_host_pair(url: None | str, /) -> None | tuple[str, None | int]:
43
+ if not url:
44
+ return None
45
+ if not url.startswith(("http://", "https://")):
46
+ url = "http://" + url
47
+ urlp = urlsplit(url)
48
+ return urlp.hostname or "localhost", urlp.port
49
+
50
+
51
+ @overload
52
+ def request(
53
+ url: string | SupportsGeturl | URL,
54
+ method: string = "GET",
55
+ params: None | string | Mapping | Iterable[tuple[Any, Any]] = None,
56
+ data: Any = None,
57
+ json: Any = None,
58
+ files: None | Mapping[string, Any] | Iterable[tuple[string, Any]] = None,
59
+ headers: None | Mapping[string, string] | Iterable[tuple[string, string]] = None,
60
+ follow_redirects: bool = True,
61
+ raise_for_status: bool = True,
62
+ cookies: None | CookieJar | SimpleCookie = None,
63
+ proxies: None | str | dict[str, str] = None,
64
+ *,
65
+ parse: None | EllipsisType = None,
66
+ **request_kwargs,
67
+ ) -> HTTPResponse:
68
+ ...
69
+ @overload
70
+ def request(
71
+ url: string | SupportsGeturl | URL,
72
+ method: string = "GET",
73
+ params: None | string | Mapping | Iterable[tuple[Any, Any]] = None,
74
+ data: Any = None,
75
+ json: Any = None,
76
+ files: None | Mapping[string, Any] | Iterable[tuple[string, Any]] = None,
77
+ headers: None | Mapping[string, string] | Iterable[tuple[string, string]] = None,
78
+ follow_redirects: bool = True,
79
+ raise_for_status: bool = True,
80
+ cookies: None | CookieJar | SimpleCookie = None,
81
+ proxies: None | str | dict[str, str] = None,
82
+ *,
83
+ parse: Literal[False],
84
+ **request_kwargs,
85
+ ) -> bytes:
86
+ ...
87
+ @overload
88
+ def request(
89
+ url: string | SupportsGeturl | URL,
90
+ method: string = "GET",
91
+ params: None | string | Mapping | Iterable[tuple[Any, Any]] = None,
92
+ data: Any = None,
93
+ json: Any = None,
94
+ files: None | Mapping[string, Any] | Iterable[tuple[string, Any]] = None,
95
+ headers: None | Mapping[string, string] | Iterable[tuple[string, string]] = None,
96
+ follow_redirects: bool = True,
97
+ raise_for_status: bool = True,
98
+ cookies: None | CookieJar | SimpleCookie = None,
99
+ proxies: None | str | dict[str, str] = None,
100
+ *,
101
+ parse: Literal[True],
102
+ **request_kwargs,
103
+ ) -> bytes | str | dict | list | int | float | bool | None:
104
+ ...
105
+ @overload
106
+ def request[T](
107
+ url: string | SupportsGeturl | URL,
108
+ method: string = "GET",
109
+ params: None | string | Mapping | Iterable[tuple[Any, Any]] = None,
110
+ data: Any = None,
111
+ json: Any = None,
112
+ files: None | Mapping[string, Any] | Iterable[tuple[string, Any]] = None,
113
+ headers: None | Mapping[string, string] | Iterable[tuple[string, string]] = None,
114
+ follow_redirects: bool = True,
115
+ raise_for_status: bool = True,
116
+ cookies: None | CookieJar | SimpleCookie = None,
117
+ proxies: None | str | dict[str, str] = None,
118
+ *,
119
+ parse: Callable[[HTTPResponse, bytes], T] | Callable[[HTTPResponse], T],
120
+ **request_kwargs,
121
+ ) -> T:
122
+ ...
123
+ def request[T](
124
+ url: string | SupportsGeturl | URL,
125
+ method: string = "GET",
126
+ params: None | string | Mapping | Iterable[tuple[Any, Any]] = None,
127
+ data: Any = None,
128
+ json: Any = None,
129
+ files: None | Mapping[string, Any] | Iterable[tuple[string, Any]] = None,
130
+ headers: None | Mapping[string, string] | Iterable[tuple[string, string]] = None,
131
+ follow_redirects: bool = True,
132
+ raise_for_status: bool = True,
133
+ cookies: None | CookieJar | SimpleCookie = None,
134
+ proxies: None | str | dict[str, str] = None,
135
+ *,
136
+ parse: None | EllipsisType| bool | Callable[[HTTPResponse, bytes], T] | Callable[[HTTPResponse], T] = None,
137
+ **request_kwargs,
138
+ ) -> HTTPResponse | bytes | str | dict | list | int | float | bool | None | T:
139
+ if isinstance(proxies, str):
140
+ http_proxy = https_proxy = get_host_pair(proxies)
141
+ elif isinstance(proxies, dict):
142
+ http_proxy = get_host_pair(proxies.get("http"))
143
+ https_proxy = get_host_pair(proxies.get("https"))
144
+ if isinstance(data, PathLike):
145
+ data = bio_chunk_iter(open(data, "rb"))
146
+ elif isinstance(data, SupportsRead):
147
+ data = bio_chunk_iter(data)
148
+ request_args = normalize_request_args(
149
+ method=method,
150
+ url=url,
151
+ params=params,
152
+ data=data,
153
+ json=json,
154
+ files=files,
155
+ headers=headers,
156
+ )
157
+ url = request_args["url"]
158
+ headers_ = request_args["headers"]
159
+ need_set_cookie = "cookie" not in headers_
160
+ response_cookies = CookieJar()
161
+ connection: HTTPConnection | HTTPSConnection
162
+ while True:
163
+ if need_set_cookie:
164
+ if cookies:
165
+ headers_["cookie"] = cookies_to_str(cookies, url)
166
+ elif response_cookies:
167
+ headers_["cookie"] = cookies_to_str(response_cookies, url)
168
+ urlp = urlsplit(url)
169
+ request_kwargs["host"] = urlp.hostname
170
+ request_kwargs["port"] = urlp.port
171
+ if urlp.scheme == "https":
172
+ connection = HTTPSConnection(**dict(get_all_items(request_kwargs, *HTTPS_CONNECTION_KWARGS)))
173
+ if http_proxy:
174
+ connection.set_tunnel(*http_proxy)
175
+ else:
176
+ connection = HTTPConnection(**dict(get_all_items(request_kwargs, *HTTP_CONNECTION_KWARGS)))
177
+ if https_proxy:
178
+ connection.set_tunnel(*https_proxy)
179
+ connection.request(
180
+ request_args["method"],
181
+ urlunsplit(urlp._replace(scheme="", netloc="")),
182
+ cast(Buffer | Iterable[Buffer], request_args["data"]),
183
+ headers_,
184
+ )
185
+ response = connection.getresponse()
186
+ setattr(response, "connection", connection)
187
+ setattr(response, "url", url)
188
+ setattr(response, "cookies", response_cookies)
189
+ extract_cookies(response_cookies, url, response)
190
+ if cookies is not None:
191
+ extract_cookies(cookies, url, response) # type: ignore
192
+ status_code = response.status
193
+ if 300 <= status_code < 400 and follow_redirects:
194
+ url = request_args["url"] = urljoin(url, response.headers["location"])
195
+ if status_code == 303:
196
+ request_args["method"] = "GET"
197
+ request_args["data"] = None
198
+ continue
199
+ if status_code >= 400 and raise_for_status:
200
+ raise HTTPError(
201
+ url,
202
+ status_code,
203
+ response.reason,
204
+ response.headers,
205
+ response,
206
+ )
207
+ if parse is None:
208
+ return response
209
+ elif parse is ...:
210
+ response.close()
211
+ return response
212
+ if isinstance(parse, bool):
213
+ content = decompress_response(response.read())
214
+ if parse:
215
+ return parse_response(response, content)
216
+ return content
217
+ ac = argcount(parse)
218
+ if ac == 1:
219
+ return cast(Callable[[HTTPResponse], T], parse)(response)
220
+ else:
221
+ return cast(Callable[[HTTPResponse, bytes], T], parse)(
222
+ response, decompress_response(response.read()))
223
+
224
+ # TODO: 支持连接池
File without changes
@@ -0,0 +1,43 @@
1
+ [tool.poetry]
2
+ name = "http_client_request"
3
+ version = "0.0.1"
4
+ description = "http.client request extension."
5
+ authors = ["ChenyangGao <wosiwujm@gmail.com>"]
6
+ license = "MIT"
7
+ readme = "readme.md"
8
+ homepage = "https://github.com/ChenyangGao/python-modules/tree/main/http_client_request"
9
+ repository = "https://github.com/ChenyangGao/python-modules/tree/main/http_client_request"
10
+ keywords = ["http.client", "request"]
11
+ classifiers = [
12
+ "License :: OSI Approved :: MIT License",
13
+ "Development Status :: 5 - Production/Stable",
14
+ "Programming Language :: Python",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.12",
17
+ "Programming Language :: Python :: 3 :: Only",
18
+ "Operating System :: OS Independent",
19
+ "Intended Audience :: Developers",
20
+ "Topic :: Software Development",
21
+ "Topic :: Software Development :: Libraries",
22
+ "Topic :: Software Development :: Libraries :: Python Modules",
23
+ ]
24
+ include = [
25
+ "LICENSE",
26
+ ]
27
+
28
+ [tool.poetry.dependencies]
29
+ python = "^3.12"
30
+ http_response = ">=0.0.8"
31
+ python-argtools = ">=0.0.2"
32
+ python-cookietools = ">=0.0.11"
33
+ python-dicttools = ">=0.0.2"
34
+ python-filewrap = ">=0.2.8"
35
+ python-http_request = ">=0.1.3"
36
+ yarl = "*"
37
+
38
+ [build-system]
39
+ requires = ["poetry-core"]
40
+ build-backend = "poetry.core.masonry.api"
41
+
42
+ [[tool.poetry.packages]]
43
+ include = "http_client_request"
@@ -0,0 +1,15 @@
1
+ # http.client request extension.
2
+
3
+ ## Installation
4
+
5
+ You can install via [pypi](https://pypi.org/project/http_client_request/)
6
+
7
+ ```console
8
+ pip install -U http_client_request
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```python
14
+ from http_client_request import request
15
+ ```