http_client_request 0.0.3__tar.gz → 0.0.5__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: http_client_request
3
- Version: 0.0.3
3
+ Version: 0.0.5
4
4
  Summary: http.client request extension.
5
5
  Home-page: https://github.com/ChenyangGao/python-modules/tree/main/http_client_request
6
6
  License: MIT
@@ -2,7 +2,7 @@
2
2
  # coding: utf-8
3
3
 
4
4
  __author__ = "ChenyangGao <https://chenyanggao.github.io>"
5
- __version__ = (0, 0, 3)
5
+ __version__ = (0, 0, 5)
6
6
  __all__ = ["ConnectionPool", "request"]
7
7
 
8
8
  from collections import defaultdict, deque, UserString
@@ -12,16 +12,17 @@ from http.cookiejar import CookieJar
12
12
  from http.cookies import SimpleCookie
13
13
  from inspect import signature
14
14
  from os import PathLike
15
- from socket import socket, MSG_DONTWAIT
15
+ from socket import socket
16
16
  from types import EllipsisType
17
17
  from typing import cast, overload, Any, Final, Literal
18
18
  from urllib.error import HTTPError
19
19
  from urllib.parse import urljoin, urlsplit, urlunsplit, ParseResult, SplitResult
20
+ from warnings import warn
20
21
 
21
22
  from argtools import argcount
22
23
  from cookietools import cookies_to_str, extract_cookies
23
24
  from dicttools import get_all_items
24
- from filewrap import bio_chunk_iter, SupportsRead
25
+ from filewrap import SupportsRead
25
26
  from http_request import normalize_request_args, SupportsGeturl
26
27
  from http_response import decompress_response, parse_response
27
28
  from undefined import undefined, Undefined
@@ -90,6 +91,10 @@ class ConnectionPool:
90
91
  for con in dq:
91
92
  con.close()
92
93
 
94
+ def __repr__(self, /) -> str:
95
+ cls = type(self)
96
+ return f"{cls.__module__}.{cls.__qualname__}({self.pool!r})"
97
+
93
98
  def get_connection(
94
99
  self,
95
100
  /,
@@ -110,11 +115,18 @@ class ConnectionPool:
110
115
  con = dq.popleft()
111
116
  except IndexError:
112
117
  break
113
- try:
114
- if not con.sock or getattr(con.sock, "_closed") or socket.recv(con.sock, 1, MSG_DONTWAIT):
115
- con.connect()
116
- except BlockingIOError:
117
- pass
118
+ sock = con.sock
119
+ if not sock or getattr(sock, "_closed"):
120
+ con.connect()
121
+ else:
122
+ sock.setblocking(False)
123
+ try:
124
+ if socket.recv(sock, 1):
125
+ con.connect()
126
+ except BlockingIOError:
127
+ pass
128
+ finally:
129
+ sock.setblocking(True)
118
130
  con.timeout = timeout
119
131
  return con
120
132
  if url.scheme == "https":
@@ -248,23 +260,32 @@ def request[T](
248
260
  https_proxy = get_host_pair(proxies.get("https"))
249
261
  else:
250
262
  http_proxy = https_proxy = None
263
+ body: Any
251
264
  if isinstance(data, PathLike):
252
- data = bio_chunk_iter(open(data, "rb"))
253
- elif isinstance(data, SupportsRead):
254
- data = bio_chunk_iter(data)
255
- request_args = normalize_request_args(
256
- method=method,
257
- url=url,
258
- params=params,
259
- data=data,
260
- json=json,
261
- files=files,
262
- headers=headers,
263
- )
265
+ data = open(data, "rb")
266
+ if isinstance(data, SupportsRead):
267
+ request_args = normalize_request_args(
268
+ method=method,
269
+ url=url,
270
+ params=params,
271
+ headers=headers,
272
+ )
273
+ body = data
274
+ else:
275
+ request_args = normalize_request_args(
276
+ method=method,
277
+ url=url,
278
+ params=params,
279
+ data=data,
280
+ files=files,
281
+ json=json,
282
+ headers=headers,
283
+ )
284
+ body = request_args["data"]
264
285
  method = request_args["method"]
265
286
  url = request_args["url"]
266
- body = cast(None | Buffer | Iterable[Buffer], request_args["data"])
267
287
  headers_ = request_args["headers"]
288
+ headers_.setdefault("connection", "keep-alive")
268
289
  need_set_cookie = "cookie" not in headers_
269
290
  response_cookies = CookieJar()
270
291
  connection: HTTPConnection | HTTPSConnection
@@ -294,7 +315,7 @@ def request[T](
294
315
  headers_,
295
316
  )
296
317
  response = connection.getresponse()
297
- if pool:
318
+ if pool and headers_.get("connection") == "keep-alive":
298
319
  setattr(response, "pool", pool)
299
320
  setattr(response, "connection", connection)
300
321
  setattr(response, "url", url)
@@ -304,11 +325,21 @@ def request[T](
304
325
  extract_cookies(cookies, url, response) # type: ignore
305
326
  status_code = response.status
306
327
  if 300 <= status_code < 400 and follow_redirects:
307
- url = request_args["url"] = urljoin(url, response.headers["location"])
308
- if status_code == 303:
309
- method = "GET"
310
- body = None
311
- continue
328
+ if location := response.headers.get("location"):
329
+ url = request_args["url"] = urljoin(url, location)
330
+ if body and status_code in (307, 308):
331
+ if isinstance(body, SupportsRead):
332
+ try:
333
+ body.seek(0) # type: ignore
334
+ except Exception:
335
+ warn(f"unseekable-stream: {body!r}")
336
+ elif not isinstance(body, Buffer):
337
+ warn(f"failed to resend request body: {body!r}, when {status_code} redirects")
338
+ else:
339
+ if status_code == 303:
340
+ method = "GET"
341
+ body = None
342
+ continue
312
343
  elif status_code >= 400 and raise_for_status:
313
344
  raise HTTPError(
314
345
  url,
@@ -335,3 +366,4 @@ def request[T](
335
366
  return cast(Callable[[HTTPResponse, bytes], T], parse)(
336
367
  response, content)
337
368
 
369
+ # TODO: 实现异步请求,非阻塞模式(sock.setblocking(False)),对于响应体的数据加载,使用 select 模块进行通知
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "http_client_request"
3
- version = "0.0.3"
3
+ version = "0.0.5"
4
4
  description = "http.client request extension."
5
5
  authors = ["ChenyangGao <wosiwujm@gmail.com>"]
6
6
  license = "MIT"