http_client_request 0.1.0__tar.gz → 0.1.2__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.
- {http_client_request-0.1.0 → http_client_request-0.1.2}/PKG-INFO +3 -5
- {http_client_request-0.1.0 → http_client_request-0.1.2}/http_client_request/__init__.py +41 -114
- {http_client_request-0.1.0 → http_client_request-0.1.2}/pyproject.toml +3 -5
- {http_client_request-0.1.0 → http_client_request-0.1.2}/http_client_request/py.typed +0 -0
- {http_client_request-0.1.0 → http_client_request-0.1.2}/readme.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: http_client_request
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: http.client request extension.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: http.client,request
|
|
@@ -21,13 +21,11 @@ Classifier: Topic :: Software Development
|
|
|
21
21
|
Classifier: Topic :: Software Development :: Libraries
|
|
22
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
23
|
Requires-Dist: http_response (>=0.0.9)
|
|
24
|
-
Requires-Dist: python-argtools (>=0.0.2)
|
|
25
24
|
Requires-Dist: python-cookietools (>=0.1.4)
|
|
26
|
-
Requires-Dist: python-dicttools (>=0.0.
|
|
25
|
+
Requires-Dist: python-dicttools (>=0.0.5)
|
|
27
26
|
Requires-Dist: python-filewrap (>=0.2.9)
|
|
28
27
|
Requires-Dist: python-http_request (>=0.1.6)
|
|
29
|
-
Requires-Dist: python-
|
|
30
|
-
Requires-Dist: python-undefined (>=0.0.3)
|
|
28
|
+
Requires-Dist: python-undefined (>=0.0.4)
|
|
31
29
|
Requires-Dist: socket_keepalive (>=0.0.1)
|
|
32
30
|
Requires-Dist: urllib3
|
|
33
31
|
Requires-Dist: yarl
|
|
@@ -4,14 +4,12 @@
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
6
|
__author__ = "ChenyangGao <https://chenyanggao.github.io>"
|
|
7
|
-
__version__ = (0, 1,
|
|
7
|
+
__version__ = (0, 1, 2)
|
|
8
8
|
__all__ = [
|
|
9
9
|
"CONNECTION_POOL", "HTTPConnection", "HTTPSConnection", "HTTPResponse",
|
|
10
10
|
"ConnectionPool", "request",
|
|
11
11
|
]
|
|
12
12
|
|
|
13
|
-
import socket
|
|
14
|
-
|
|
15
13
|
from array import array
|
|
16
14
|
from collections import defaultdict, deque, UserString
|
|
17
15
|
from collections.abc import Buffer, Callable, Iterable, Mapping
|
|
@@ -21,25 +19,21 @@ from http.client import (
|
|
|
21
19
|
)
|
|
22
20
|
from http.cookiejar import CookieJar
|
|
23
21
|
from http.cookies import BaseCookie
|
|
24
|
-
from io import BufferedReader
|
|
25
22
|
from inspect import signature
|
|
26
23
|
from os import PathLike
|
|
27
24
|
from select import select
|
|
28
|
-
from socket import socket
|
|
29
|
-
from ssl import SSLWantReadError
|
|
25
|
+
from socket import socket
|
|
30
26
|
from types import EllipsisType
|
|
31
27
|
from typing import cast, overload, Any, Final, Literal
|
|
32
28
|
from urllib.error import HTTPError
|
|
33
29
|
from urllib.parse import urljoin, urlsplit, urlunsplit, ParseResult, SplitResult
|
|
34
30
|
from warnings import warn
|
|
35
31
|
|
|
36
|
-
from argtools import argcount
|
|
37
32
|
from cookietools import cookies_to_str, extract_cookies
|
|
38
33
|
from dicttools import get_all_items
|
|
39
34
|
from filewrap import SupportsRead
|
|
40
35
|
from http_request import normalize_request_args, SupportsGeturl
|
|
41
|
-
from http_response import decompress_response, parse_response
|
|
42
|
-
from property import funcproperty
|
|
36
|
+
from http_response import decompress_response, parse_response, get_length
|
|
43
37
|
from socket_keepalive import socket_keepalive
|
|
44
38
|
from urllib3 import HTTPResponse as Urllib3HTTPResponse, HTTPHeaderDict
|
|
45
39
|
from undefined import undefined, Undefined
|
|
@@ -70,7 +64,7 @@ def is_ipv6(host: str, /) -> bool:
|
|
|
70
64
|
return False
|
|
71
65
|
|
|
72
66
|
|
|
73
|
-
def sock_buf_readable(sock:
|
|
67
|
+
def sock_buf_readable(sock: socket, /) -> bool:
|
|
74
68
|
rlist, *_ = select([sock], (), (), 0)
|
|
75
69
|
return bool(rlist)
|
|
76
70
|
|
|
@@ -80,6 +74,8 @@ try:
|
|
|
80
74
|
from termios import FIONREAD
|
|
81
75
|
|
|
82
76
|
def sock_bufsize(sock, /) -> int:
|
|
77
|
+
if sock is None:
|
|
78
|
+
return 0
|
|
83
79
|
sock_size = array("i", [0])
|
|
84
80
|
ioctl(sock, FIONREAD, sock_size)
|
|
85
81
|
return sock_size[0]
|
|
@@ -87,6 +83,8 @@ except ImportError:
|
|
|
87
83
|
from ctypes import byref, c_ulong, WinDLL # type: ignore
|
|
88
84
|
ws2_32 = WinDLL("ws2_32")
|
|
89
85
|
def sock_bufsize(sock, /) -> int:
|
|
86
|
+
if sock is None:
|
|
87
|
+
return 0
|
|
90
88
|
FIONREAD = 0x4004667f
|
|
91
89
|
b = c_ulong(0)
|
|
92
90
|
ws2_32.ioctlsocket(sock.fileno(), FIONREAD, byref(b))
|
|
@@ -102,70 +100,22 @@ class HTTPResponse(BaseHTTPResponse):
|
|
|
102
100
|
def __del__(self, /):
|
|
103
101
|
self.close()
|
|
104
102
|
|
|
105
|
-
@funcproperty
|
|
106
|
-
def _fp(self, /) -> BufferedReader:
|
|
107
|
-
return self.fp
|
|
108
|
-
|
|
109
|
-
@property
|
|
110
|
-
def buffer_size(self, /) -> int:
|
|
111
|
-
fp = self._fp
|
|
112
|
-
sock = fp.raw._sock
|
|
113
|
-
sock.setblocking(False)
|
|
114
|
-
try:
|
|
115
|
-
cache_size = len(fp.peek() or b"")
|
|
116
|
-
while True:
|
|
117
|
-
buffer_size = cache_size + sock_bufsize(sock)
|
|
118
|
-
if cache_size == (cache_size := len(fp.peek() or b"")):
|
|
119
|
-
return buffer_size
|
|
120
|
-
except (BlockingIOError, SSLWantReadError):
|
|
121
|
-
return 0
|
|
122
|
-
finally:
|
|
123
|
-
sock.setblocking(True)
|
|
124
|
-
|
|
125
|
-
@property
|
|
126
|
-
def unbuffer_size(self, /) -> int:
|
|
127
|
-
method = self.__dict__.get("method", "").upper()
|
|
128
|
-
content_length = self.length
|
|
129
|
-
if method == "HEAD" or content_length == 0:
|
|
130
|
-
return 0
|
|
131
|
-
if content_length:
|
|
132
|
-
return content_length - self.tell() - self.buffer_size
|
|
133
|
-
sock = self._fp.raw._sock
|
|
134
|
-
if sock_bufsize(sock) == sock.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF):
|
|
135
|
-
return -1
|
|
136
|
-
else:
|
|
137
|
-
return 0
|
|
138
|
-
|
|
139
|
-
@property
|
|
140
|
-
def unread_size(self, /) -> int:
|
|
141
|
-
method = self.__dict__.get("method", "").upper()
|
|
142
|
-
content_length = self.length
|
|
143
|
-
if method == "HEAD" or content_length == 0:
|
|
144
|
-
return 0
|
|
145
|
-
sock = self._fp.raw._sock
|
|
146
|
-
if content_length:
|
|
147
|
-
return content_length - self.tell()
|
|
148
|
-
elif sock_bufsize(sock) == sock.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF):
|
|
149
|
-
return -1
|
|
150
|
-
else:
|
|
151
|
-
return self.buffer_size
|
|
152
|
-
|
|
153
103
|
def _close_conn(self, /):
|
|
154
|
-
fp = self.
|
|
104
|
+
fp = self.fp
|
|
155
105
|
setattr(self, "fp", None)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
106
|
+
pool = getattr(self, "pool", None)
|
|
107
|
+
connection = getattr(self, "connection", None)
|
|
108
|
+
if pool and connection and not (
|
|
109
|
+
200 <= self.status < 300 and
|
|
110
|
+
(length := self.length) and
|
|
111
|
+
length - self._pos - len(fp.peek()) - sock_bufsize(connection.sock) > 1024 * 1024 * 10
|
|
112
|
+
):
|
|
113
|
+
try:
|
|
114
|
+
self.read()
|
|
115
|
+
except OSError:
|
|
116
|
+
pass
|
|
117
|
+
pool.return_connection(connection)
|
|
118
|
+
else:
|
|
169
119
|
fp.close()
|
|
170
120
|
|
|
171
121
|
def get_urllib3_response(self, /) -> Urllib3HTTPResponse:
|
|
@@ -311,35 +261,8 @@ class ConnectionPool:
|
|
|
311
261
|
except IndexError:
|
|
312
262
|
break
|
|
313
263
|
con.timeout = timeout
|
|
314
|
-
|
|
315
|
-
resp = con.response
|
|
316
|
-
if con.state == "Idle" or not sock:
|
|
317
|
-
pass
|
|
318
|
-
elif con.state == "Request-sent" or getattr(sock, "_closed"):
|
|
264
|
+
if con.state != "Idle" or getattr(con.sock, "_closed"):
|
|
319
265
|
con.close()
|
|
320
|
-
elif resp and 0 < resp.unbuffer_size <= 1024 * 1024:
|
|
321
|
-
try:
|
|
322
|
-
resp.read()
|
|
323
|
-
except (ConnectionResetError, BrokenPipeError):
|
|
324
|
-
con.close()
|
|
325
|
-
else:
|
|
326
|
-
try:
|
|
327
|
-
sock.setblocking(False)
|
|
328
|
-
except OSError:
|
|
329
|
-
con.close()
|
|
330
|
-
else:
|
|
331
|
-
try:
|
|
332
|
-
if Socket.recv(sock, 1):
|
|
333
|
-
con.close()
|
|
334
|
-
except BlockingIOError:
|
|
335
|
-
pass
|
|
336
|
-
except (ConnectionResetError, BrokenPipeError):
|
|
337
|
-
con.close()
|
|
338
|
-
try:
|
|
339
|
-
if not getattr(sock, "_closed"):
|
|
340
|
-
sock.setblocking(True)
|
|
341
|
-
except OSError:
|
|
342
|
-
pass
|
|
343
266
|
return con
|
|
344
267
|
if url.scheme == "https":
|
|
345
268
|
return HTTPSConnection(url.hostname or "localhost", url.port, timeout=timeout)
|
|
@@ -440,7 +363,7 @@ def request[T](
|
|
|
440
363
|
proxies: None | str | dict[str, str] = None,
|
|
441
364
|
pool: None | Undefined | ConnectionPool = undefined,
|
|
442
365
|
*,
|
|
443
|
-
parse: Callable[[HTTPResponse, bytes], T]
|
|
366
|
+
parse: Callable[[HTTPResponse, bytes], T],
|
|
444
367
|
**request_kwargs,
|
|
445
368
|
) -> T:
|
|
446
369
|
...
|
|
@@ -458,7 +381,7 @@ def request[T](
|
|
|
458
381
|
proxies: None | str | dict[str, str] = None,
|
|
459
382
|
pool: None | Undefined | ConnectionPool = undefined,
|
|
460
383
|
*,
|
|
461
|
-
parse: None | EllipsisType| bool | Callable[[HTTPResponse, bytes], T]
|
|
384
|
+
parse: None | EllipsisType| bool | Callable[[HTTPResponse, bytes], T] = None,
|
|
462
385
|
**request_kwargs,
|
|
463
386
|
) -> HTTPResponse | bytes | str | dict | list | int | float | bool | None | T:
|
|
464
387
|
if pool is undefined:
|
|
@@ -561,8 +484,10 @@ def request[T](
|
|
|
561
484
|
if status_code == 303:
|
|
562
485
|
method = "GET"
|
|
563
486
|
body = None
|
|
487
|
+
response.read()
|
|
564
488
|
continue
|
|
565
489
|
elif status_code >= 400 and raise_for_status:
|
|
490
|
+
setattr(response, "content", response.read())
|
|
566
491
|
raise HTTPError(
|
|
567
492
|
url,
|
|
568
493
|
status_code,
|
|
@@ -571,21 +496,23 @@ def request[T](
|
|
|
571
496
|
response,
|
|
572
497
|
)
|
|
573
498
|
if parse is None:
|
|
499
|
+
if method == "HEAD":
|
|
500
|
+
response.read()
|
|
574
501
|
return response
|
|
575
502
|
elif parse is ...:
|
|
576
|
-
|
|
503
|
+
try:
|
|
504
|
+
if (method == "HEAD" or
|
|
505
|
+
(length := get_length(response)) is not None and length <= 10485760
|
|
506
|
+
):
|
|
507
|
+
response.read()
|
|
508
|
+
finally:
|
|
509
|
+
response.close()
|
|
577
510
|
return response
|
|
511
|
+
content = decompress_response(response.read(), response)
|
|
578
512
|
if isinstance(parse, bool):
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
ac = argcount(parse)
|
|
584
|
-
if ac == 1:
|
|
585
|
-
return cast(Callable[[HTTPResponse], T], parse)(response)
|
|
586
|
-
else:
|
|
587
|
-
content = decompress_response(response.read(), response)
|
|
588
|
-
return cast(Callable[[HTTPResponse, bytes], T], parse)(
|
|
589
|
-
response, content)
|
|
513
|
+
if not parse:
|
|
514
|
+
return content
|
|
515
|
+
parse = cast(Callable, parse_response)
|
|
516
|
+
return parse(response, content)
|
|
590
517
|
|
|
591
518
|
# TODO: 实现异步请求,非阻塞模式(sock.setblocking(False)),对于响应体的数据加载,使用 select 模块进行通知
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "http_client_request"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "http.client request extension."
|
|
5
5
|
authors = ["ChenyangGao <wosiwujm@gmail.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -28,13 +28,11 @@ include = [
|
|
|
28
28
|
[tool.poetry.dependencies]
|
|
29
29
|
python = "^3.12"
|
|
30
30
|
http_response = ">=0.0.9"
|
|
31
|
-
python-argtools = ">=0.0.2"
|
|
32
31
|
python-cookietools = ">=0.1.4"
|
|
33
|
-
python-dicttools = ">=0.0.
|
|
32
|
+
python-dicttools = ">=0.0.5"
|
|
34
33
|
python-filewrap = ">=0.2.9"
|
|
35
34
|
python-http_request = ">=0.1.6"
|
|
36
|
-
python-
|
|
37
|
-
python-undefined = ">=0.0.3"
|
|
35
|
+
python-undefined = ">=0.0.4"
|
|
38
36
|
socket_keepalive = ">=0.0.1"
|
|
39
37
|
urllib3 = "*"
|
|
40
38
|
yarl = "*"
|
|
File without changes
|
|
File without changes
|