python-http_request 0.1.6.4__tar.gz → 0.1.7__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.4
2
2
  Name: python-http_request
3
- Version: 0.1.6.4
3
+ Version: 0.1.7
4
4
  Summary: Python http request utils.
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -23,14 +23,13 @@ Classifier: Topic :: Software Development :: Libraries
23
23
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
24
  Requires-Dist: http_response (>=0.0.9)
25
25
  Requires-Dist: orjson
26
- Requires-Dist: python-argtools (>=0.0.2)
27
- Requires-Dist: python-asynctools (>=0.1.3)
26
+ Requires-Dist: python-asynctools (>=0.1.3.4)
28
27
  Requires-Dist: python-cookietools (>=0.1.4)
29
- Requires-Dist: python-dicttools (>=0.0.4)
28
+ Requires-Dist: python-dicttools (>=0.0.5)
30
29
  Requires-Dist: python-ensure (>=0.0.1)
31
- Requires-Dist: python-filewrap (>=0.2.8)
32
- Requires-Dist: python-texttools (>=0.0.5)
33
- Requires-Dist: python-undefined (>=0.0.3)
30
+ Requires-Dist: python-filewrap (>=0.2.9)
31
+ Requires-Dist: python-texttools (>=0.0.6)
32
+ Requires-Dist: python-undefined (>=0.0.4)
34
33
  Requires-Dist: socket_keepalive (>=0.0.1)
35
34
  Requires-Dist: yarl
36
35
  Project-URL: Homepage, https://github.com/ChenyangGao/python-modules/tree/main/python-http_request
@@ -2,7 +2,7 @@
2
2
  # encoding: utf-8
3
3
 
4
4
  __author__ = "ChenyangGao <https://chenyanggao.github.io>"
5
- __version__ = (0, 1, 6)
5
+ __version__ = (0, 1, 7)
6
6
  __all__ = [
7
7
  "SupportsGeturl", "url_origin", "complete_url", "ensure_ascii_url",
8
8
  "urlencode", "cookies_str_to_dict", "headers_str_to_dict_by_lines",
@@ -258,7 +258,7 @@ def encode_multipart_data(
258
258
  case [filename, value, file_type]:
259
259
  if file_type:
260
260
  headers[b"content-type"] = ensure_bytes_(file_type)
261
- case [filename, value, file_type, file_headers, *rest]:
261
+ case [filename, value, file_type, file_headers, *_]:
262
262
  for k, v in iter_items(file_headers):
263
263
  headers[ensure_bytes_(k).lower()] = ensure_bytes_(v)
264
264
  if file_type:
@@ -353,7 +353,7 @@ def encode_multipart_data_async(
353
353
  case [filename, value, file_type]:
354
354
  if file_type:
355
355
  headers[b"content-type"] = ensure_bytes_(file_type)
356
- case [filename, value, file_type, file_headers, *rest]:
356
+ case [filename, value, file_type, file_headers, *_]:
357
357
  for k, v in iter_items(file_headers):
358
358
  headers[ensure_bytes_(k).lower()] = ensure_bytes_(v)
359
359
  if file_type:
@@ -21,13 +21,13 @@ from typing import cast, overload, Any, Literal
21
21
  from urllib.parse import urljoin, urlsplit, urlunsplit
22
22
  from warnings import warn
23
23
 
24
- from argtools import argcount
25
24
  from cookietools import update_cookies, cookies_to_str
26
25
  from ensure import ensure_bytes, ensure_str
27
26
  from dicttools import iter_items
28
27
  from filewrap import bio_chunk_iter, bio_chunk_async_iter, SupportsRead
29
28
  from http_response import (
30
29
  get_status_code, headers_get, decompress_response, parse_response,
30
+ get_length,
31
31
  )
32
32
  from socket_keepalive import socket_keepalive
33
33
  from yarl import URL
@@ -143,6 +143,28 @@ async def call_async_close(response, /):
143
143
  pass
144
144
 
145
145
 
146
+ def finalize(resp, /, maxsize: int = 0, method="GET"):
147
+ try:
148
+ if (method == "HEAD" or
149
+ maxsize <= 0 or
150
+ (length := get_length(resp)) is not None and length <= maxsize
151
+ ):
152
+ call_read(resp)
153
+ finally:
154
+ call_close(resp)
155
+
156
+
157
+ async def async_finalize(resp, /, maxsize: int = 0, method="GET"):
158
+ try:
159
+ if (method == "HEAD" or
160
+ maxsize <= 0 or
161
+ (length := get_length(resp)) is not None and length <= maxsize
162
+ ):
163
+ await call_async_read(resp)
164
+ finally:
165
+ await call_async_close(resp)
166
+
167
+
146
168
  def urlopen(
147
169
  url: str,
148
170
  method: str = "GET",
@@ -277,7 +299,7 @@ def request_sync[Response, T](
277
299
  urlopen: Callable[..., Response] = urlopen, # type: ignore
278
300
  dont_decompress: None | bool = None,
279
301
  *,
280
- parse: Callable[[Response, bytes], T] | Callable[[Response], T],
302
+ parse: Callable[[Response, bytes], T],
281
303
  **request_kwargs,
282
304
  ) -> T:
283
305
  ...
@@ -295,7 +317,7 @@ def request_sync[Response, T](
295
317
  urlopen: Callable[..., Response] = urlopen, # type: ignore
296
318
  dont_decompress: None | bool = None,
297
319
  *,
298
- parse: None | EllipsisType| bool | Callable[[Response, bytes], T] | Callable[[Response], T] = None,
320
+ parse: None | EllipsisType| bool | Callable[[Response, bytes], T] = None,
299
321
  **request_kwargs,
300
322
  ) -> Response | bytes | str | dict | list | int | float | bool | None | T:
301
323
  if isinstance(data, PathLike):
@@ -342,61 +364,57 @@ def request_sync[Response, T](
342
364
  update_cookies(cookies, base_cookies) # type: ignore
343
365
  update_cookies(response_cookies, base_cookies)
344
366
  status_code = get_status_code(response)
345
- if status_code >= 400 and raise_for_status:
367
+ if 300 <= status_code < 400:
368
+ if follow_redirects:
369
+ location = headers_get(response, "location")
370
+ if location and not isinstance(location, (Buffer, UserString, str)):
371
+ location = location[0]
372
+ if location:
373
+ location = ensure_str(location)
374
+ request_url = request_kwargs["url"] = urljoin(request_url, location)
375
+ if body and status_code in (307, 308):
376
+ if isinstance(body, SupportsRead):
377
+ try:
378
+ body.seek(0) # type: ignore
379
+ request_kwargs["data"] = bio_chunk_iter(body)
380
+ except Exception:
381
+ warn(f"unseekable-stream: {body!r}")
382
+ elif not isinstance(body, Buffer):
383
+ warn(f"failed to resend request body: {body!r}, when {status_code} redirects")
384
+ else:
385
+ if status_code == 303:
386
+ request_kwargs["method"] = "GET"
387
+ body = None
388
+ request_kwargs["data"] = None
389
+ finalize(response)
390
+ del response
391
+ continue
392
+ elif raise_for_status and status_code >= 400:
393
+ finalize(response)
346
394
  raise HTTPError(
347
395
  url=request_kwargs["url"],
348
396
  status=status_code,
349
397
  method=request_kwargs["method"],
350
398
  response=response,
351
399
  )
352
- elif 300 <= status_code < 400 and follow_redirects:
353
- location = headers_get(response, "location")
354
- if location and not isinstance(location, (Buffer, UserString, str)):
355
- location = location[0]
356
- if location:
357
- location = ensure_str(location)
358
- request_url = request_kwargs["url"] = urljoin(request_url, location)
359
- if body and status_code in (307, 308):
360
- if isinstance(body, SupportsRead):
361
- try:
362
- body.seek(0) # type: ignore
363
- request_kwargs["data"] = bio_chunk_iter(body)
364
- except Exception:
365
- warn(f"unseekable-stream: {body!r}")
366
- elif not isinstance(body, Buffer):
367
- warn(f"failed to resend request body: {body!r}, when {status_code} redirects")
368
- else:
369
- if status_code == 303:
370
- request_kwargs["method"] = "GET"
371
- body = None
372
- request_kwargs["data"] = None
373
- if request_kwargs["method"] != "HEAD":
374
- call_read(response)
375
- call_close(response)
376
- del response
377
- continue
378
400
  if parse is None:
401
+ if request_kwargs["method"] == "HEAD":
402
+ finalize(response)
403
+ return response
404
+ elif parse is ...:
405
+ finalize(response, 10485760, method=request_kwargs["method"])
379
406
  return response
380
407
  try:
381
- if parse is ...:
382
- return response
383
- if isinstance(parse, bool):
384
- ac = 2
385
- if parse:
386
- parse = cast(Callable[[Response, bytes], T], parse_response)
387
- else:
388
- parse = lambda _, content: content
389
- else:
390
- ac = argcount(parse)
391
- if ac == 1:
392
- return cast(Callable[[Response], T], parse)(response)
393
- else:
394
- content = call_read(response)
395
- if not dont_decompress:
396
- content = decompress_response(content, response)
397
- return cast(Callable[[Response, bytes], T], parse)(response, content)
408
+ content = call_read(response)
398
409
  finally:
399
410
  call_close(response)
411
+ if not dont_decompress:
412
+ content = decompress_response(content, response)
413
+ if isinstance(parse, bool):
414
+ if not parse:
415
+ return content
416
+ parse = cast(Callable, parse_response)
417
+ return parse(response, content)
400
418
 
401
419
 
402
420
  @overload
@@ -471,7 +489,7 @@ async def request_async[Response, T](
471
489
  urlopen: Callable[..., Response] = urlopen_async, # type: ignore
472
490
  dont_decompress: None | bool = None,
473
491
  *,
474
- parse: Callable[[Response, bytes], T] | Callable[[Response], T],
492
+ parse: Callable[[Response, bytes], T],
475
493
  **request_kwargs,
476
494
  ) -> T:
477
495
  ...
@@ -489,7 +507,7 @@ async def request_async[Response, T](
489
507
  urlopen: Callable[..., Response] = urlopen_async, # type: ignore
490
508
  dont_decompress: None | bool = None,
491
509
  *,
492
- parse: None | EllipsisType| bool | Callable[[Response, bytes], T] | Callable[[Response], T] = None,
510
+ parse: None | EllipsisType| bool | Callable[[Response, bytes], T] = None,
493
511
  **request_kwargs,
494
512
  ) -> Response | bytes | str | dict | list | int | float | bool | None | T:
495
513
  if isinstance(data, PathLike):
@@ -539,62 +557,61 @@ async def request_async[Response, T](
539
557
  update_cookies(cookies, base_cookies) # type: ignore
540
558
  update_cookies(response_cookies, base_cookies)
541
559
  status_code = get_status_code(response)
542
- if status_code >= 400 and raise_for_status:
560
+ if 300 <= status_code < 400:
561
+ if follow_redirects:
562
+ location = headers_get(response, "location")
563
+ if location and not isinstance(location, (Buffer, UserString, str)):
564
+ location = location[0]
565
+ if location:
566
+ location = ensure_str(location)
567
+ request_url = request_kwargs["url"] = urljoin(request_url, location)
568
+ if body and status_code in (307, 308):
569
+ if isinstance(body, SupportsRead):
570
+ try:
571
+ from asynctools import ensure_async
572
+ await ensure_async(body.seek)(0) # type: ignore
573
+ request_kwargs["data"] = bio_chunk_async_iter(body)
574
+ except Exception:
575
+ warn(f"unseekable-stream: {body!r}")
576
+ elif not isinstance(body, Buffer):
577
+ warn(f"failed to resend request body: {body!r}, when {status_code} redirects")
578
+ else:
579
+ if status_code == 303:
580
+ request_kwargs["method"] = "GET"
581
+ body = None
582
+ request_kwargs["data"] = None
583
+ await async_finalize(response)
584
+ del response
585
+ continue
586
+ elif raise_for_status and status_code >= 400:
587
+ await async_finalize(response)
543
588
  raise HTTPError(
544
589
  url=request_kwargs["url"],
545
590
  status=status_code,
546
591
  method=request_kwargs["method"],
547
592
  response=response,
548
593
  )
549
- elif 300 <= status_code < 400 and follow_redirects:
550
- location = headers_get(response, "location")
551
- if location and not isinstance(location, (Buffer, UserString, str)):
552
- location = location[0]
553
- if location:
554
- location = ensure_str(location)
555
- request_url = request_kwargs["url"] = urljoin(request_url, location)
556
- if body and status_code in (307, 308):
557
- if isinstance(body, SupportsRead):
558
- try:
559
- from asynctools import ensure_async
560
- await ensure_async(body.seek)(0) # type: ignore
561
- request_kwargs["data"] = bio_chunk_async_iter(body)
562
- except Exception:
563
- warn(f"unseekable-stream: {body!r}")
564
- elif not isinstance(body, Buffer):
565
- warn(f"failed to resend request body: {body!r}, when {status_code} redirects")
566
- else:
567
- if status_code == 303:
568
- request_kwargs["method"] = "GET"
569
- body = None
570
- request_kwargs["data"] = None
571
- if request_kwargs["method"] != "HEAD":
572
- await call_async_read(response)
573
- await call_async_close(response)
574
- del response
575
- continue
576
594
  if parse is None:
595
+ if request_kwargs["method"] == "HEAD":
596
+ await async_finalize(response)
597
+ return response
598
+ elif parse is ...:
599
+ await async_finalize(response, 10485760, method=request_kwargs["method"])
577
600
  return response
578
601
  try:
579
- if parse is ...:
580
- return response
581
- if isinstance(parse, bool):
582
- ac = 2
583
- if parse:
584
- parse = cast(Callable[[Response, bytes], T], parse_response)
585
- else:
586
- parse = lambda _, content: content
587
- else:
588
- ac = argcount(parse)
589
- if ac == 1:
590
- return cast(Callable[[Response], T], parse)(response)
591
- else:
592
- content = await call_async_read(response)
593
- if not dont_decompress:
594
- content = decompress_response(content, response)
595
- return cast(Callable[[Response, bytes], T], parse)(response, content)
602
+ content = await call_async_read(response)
596
603
  finally:
597
604
  await call_async_close(response)
605
+ if not dont_decompress:
606
+ content = decompress_response(content, response)
607
+ if isinstance(parse, bool):
608
+ if not parse:
609
+ return content
610
+ parse = cast(Callable, parse_response)
611
+ ret = parse(response, content)
612
+ if isawaitable(ret):
613
+ ret = await ret
614
+ return ret
598
615
 
599
616
 
600
617
  @overload
@@ -612,7 +629,7 @@ def request[Response, T](
612
629
  urlopen: None | Callable[..., Response] = None,
613
630
  dont_decompress: None | bool = None,
614
631
  *,
615
- parse: None | EllipsisType | bool | Callable[[Response, bytes], T] | Callable[[Response], T] = None,
632
+ parse: None | EllipsisType | bool | Callable[[Response, bytes], T] = None,
616
633
  async_: Literal[False] = False,
617
634
  **request_kwargs,
618
635
  ) -> Response | bytes | str | dict | list | int | float | bool | None | T:
@@ -632,7 +649,7 @@ def request[Response, T](
632
649
  urlopen: None | Callable[..., Response] = None,
633
650
  dont_decompress: None | bool = None,
634
651
  *,
635
- parse: None | EllipsisType | bool | Callable[[Response, bytes], T] | Callable[[Response, bytes], Awaitable[T]] | Callable[[Response], T] | Callable[[Response], Awaitable[T]] = None,
652
+ parse: None | EllipsisType | bool | Callable[[Response, bytes], T] | Callable[[Response, bytes], Awaitable[T]] = None,
636
653
  async_: Literal[True],
637
654
  **request_kwargs,
638
655
  ) -> Awaitable[Response | bytes | str | dict | list | int | float | bool | None | T]:
@@ -651,7 +668,7 @@ def request[Response, T](
651
668
  urlopen: None | Callable[..., Response] = None,
652
669
  dont_decompress: None | bool = None,
653
670
  *,
654
- parse: None | EllipsisType | bool | Callable[[Response, bytes], T] | Callable[[Response, bytes], Awaitable[T]] | Callable[[Response], T] | Callable[[Response], Awaitable[T]] = None,
671
+ parse: None | EllipsisType | bool | Callable[[Response, bytes], T] | Callable[[Response, bytes], Awaitable[T]] = None,
655
672
  async_: Literal[False, True] = False,
656
673
  **request_kwargs,
657
674
  ) -> Response | bytes | str | dict | list | int | float | bool | None | T | Awaitable[Response | bytes | str | dict | list | int | float | bool | None | T]:
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-http_request"
3
- version = "0.1.6.4"
3
+ version = "0.1.7"
4
4
  description = "Python http request utils."
5
5
  authors = ["ChenyangGao <wosiwujm@gmail.com>"]
6
6
  license = "MIT"
@@ -29,14 +29,13 @@ include = [
29
29
  python = "^3.12"
30
30
  http_response = ">=0.0.9"
31
31
  orjson = "*"
32
- python-argtools = ">=0.0.2"
33
- python-asynctools = ">=0.1.3"
32
+ python-asynctools = ">=0.1.3.4"
34
33
  python-cookietools = ">=0.1.4"
35
- python-dicttools = ">=0.0.4"
34
+ python-dicttools = ">=0.0.5"
36
35
  python-ensure = ">=0.0.1"
37
- python-filewrap = ">=0.2.8"
38
- python-texttools = ">=0.0.5"
39
- python-undefined = ">=0.0.3"
36
+ python-filewrap = ">=0.2.9"
37
+ python-texttools = ">=0.0.6"
38
+ python-undefined = ">=0.0.4"
40
39
  socket_keepalive = ">=0.0.1"
41
40
  yarl = "*"
42
41