python-urlopen 0.0.5__tar.gz → 0.0.5.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.
- {python_urlopen-0.0.5 → python_urlopen-0.0.5.2}/PKG-INFO +3 -1
- {python_urlopen-0.0.5 → python_urlopen-0.0.5.2}/pyproject.toml +3 -1
- {python_urlopen-0.0.5 → python_urlopen-0.0.5.2}/urlopen/__init__.py +56 -14
- {python_urlopen-0.0.5 → python_urlopen-0.0.5.2}/LICENSE +0 -0
- {python_urlopen-0.0.5 → python_urlopen-0.0.5.2}/readme.md +0 -0
- {python_urlopen-0.0.5 → python_urlopen-0.0.5.2}/urlopen/__main__.py +0 -0
- {python_urlopen-0.0.5 → python_urlopen-0.0.5.2}/urlopen/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-urlopen
|
|
3
|
-
Version: 0.0.5
|
|
3
|
+
Version: 0.0.5.2
|
|
4
4
|
Summary: Python urlopen wrapper.
|
|
5
5
|
Home-page: https://github.com/ChenyangGao/web-mount-packs/tree/main/python-module/python-urlopen
|
|
6
6
|
License: MIT
|
|
@@ -21,9 +21,11 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
21
21
|
Classifier: Topic :: Software Development
|
|
22
22
|
Classifier: Topic :: Software Development :: Libraries
|
|
23
23
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Requires-Dist: brotli
|
|
24
25
|
Requires-Dist: http_response
|
|
25
26
|
Requires-Dist: python-argtools
|
|
26
27
|
Requires-Dist: python-filewrap
|
|
28
|
+
Requires-Dist: zstandard
|
|
27
29
|
Project-URL: Repository, https://github.com/ChenyangGao/web-mount-packs/tree/main/python-module/python-urlopen
|
|
28
30
|
Description-Content-Type: text/markdown
|
|
29
31
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "python-urlopen"
|
|
3
|
-
version = "0.0.5"
|
|
3
|
+
version = "0.0.5.2"
|
|
4
4
|
description = "Python urlopen wrapper."
|
|
5
5
|
authors = ["ChenyangGao <wosiwujm@gmail.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -27,9 +27,11 @@ include = [
|
|
|
27
27
|
|
|
28
28
|
[tool.poetry.dependencies]
|
|
29
29
|
python = "^3.10"
|
|
30
|
+
brotli = "*"
|
|
30
31
|
http_response = "*"
|
|
31
32
|
python-argtools = "*"
|
|
32
33
|
python-filewrap = "*"
|
|
34
|
+
zstandard = "*"
|
|
33
35
|
|
|
34
36
|
[tool.poetry.scripts]
|
|
35
37
|
python-urlopen = "urlopen.__main__:main"
|
|
@@ -9,6 +9,7 @@ import errno
|
|
|
9
9
|
|
|
10
10
|
from collections.abc import Callable, Generator, Iterable, Mapping, Sequence
|
|
11
11
|
from copy import copy
|
|
12
|
+
from gzip import decompress as decompress_gzip
|
|
12
13
|
from http.client import HTTPResponse
|
|
13
14
|
from http.cookiejar import CookieJar
|
|
14
15
|
from inspect import isgenerator
|
|
@@ -22,18 +23,60 @@ from typing import cast, Any
|
|
|
22
23
|
from urllib.error import HTTPError
|
|
23
24
|
from urllib.parse import urlencode, urlsplit
|
|
24
25
|
from urllib.request import build_opener, HTTPCookieProcessor, HTTPSHandler, OpenerDirector, Request
|
|
26
|
+
from zlib import compressobj, DEF_MEM_LEVEL, DEFLATED, MAX_WBITS
|
|
25
27
|
|
|
26
28
|
from argtools import argcount
|
|
29
|
+
from brotli import decompress as decompress_br # type: ignore
|
|
27
30
|
from filewrap import bio_skip_iter, SupportsRead, SupportsWrite
|
|
28
31
|
from http_response import get_filename, get_length, is_chunked, is_range_request
|
|
32
|
+
from zstandard import decompress as decompress_zstd
|
|
29
33
|
|
|
30
34
|
|
|
31
35
|
if "__del__" not in HTTPResponse.__dict__:
|
|
32
36
|
setattr(HTTPResponse, "__del__", HTTPResponse.close)
|
|
37
|
+
if "__del__" not in OpenerDirector.__dict__:
|
|
38
|
+
setattr(OpenerDirector, "__del__", OpenerDirector.close)
|
|
33
39
|
|
|
34
40
|
CRE_search_charset = re_compile(r"\bcharset=(?P<charset>[^ ;]+)").search
|
|
35
41
|
|
|
36
42
|
|
|
43
|
+
def decompress_deflate(data, compresslevel=9):
|
|
44
|
+
# Fork from: https://stackoverflow.com/questions/1089662/python-inflate-and-deflate-implementations#answer-1089787
|
|
45
|
+
compress = compressobj(
|
|
46
|
+
compresslevel, # level: 0-9
|
|
47
|
+
DEFLATED, # method: must be DEFLATED
|
|
48
|
+
-MAX_WBITS, # window size in bits:
|
|
49
|
+
# -15..-8: negate, suppress header
|
|
50
|
+
# 8..15: normal
|
|
51
|
+
# 16..30: subtract 16, gzip header
|
|
52
|
+
DEF_MEM_LEVEL, # mem level: 1..8/9
|
|
53
|
+
0 # strategy:
|
|
54
|
+
# 0 = Z_DEFAULT_STRATEGY
|
|
55
|
+
# 1 = Z_FILTERED
|
|
56
|
+
# 2 = Z_HUFFMAN_ONLY
|
|
57
|
+
# 3 = Z_RLE
|
|
58
|
+
# 4 = Z_FIXED
|
|
59
|
+
)
|
|
60
|
+
deflated = compress.compress(data)
|
|
61
|
+
deflated += compress.flush()
|
|
62
|
+
return deflated
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def decompress_response(resp: HTTPResponse) -> bytes:
|
|
66
|
+
data = resp.read()
|
|
67
|
+
content_encoding = resp.headers.get("Content-Encoding")
|
|
68
|
+
match content_encoding:
|
|
69
|
+
case "gzip":
|
|
70
|
+
data = decompress_gzip(data)
|
|
71
|
+
case "deflate":
|
|
72
|
+
data = decompress_deflate(data)
|
|
73
|
+
case "br":
|
|
74
|
+
data = decompress_br(data)
|
|
75
|
+
case "zstd":
|
|
76
|
+
data = decompress_zstd(data)
|
|
77
|
+
return data
|
|
78
|
+
|
|
79
|
+
|
|
37
80
|
def urlopen(
|
|
38
81
|
url: str | Request,
|
|
39
82
|
method: str = "GET",
|
|
@@ -44,7 +87,7 @@ def urlopen(
|
|
|
44
87
|
timeout: None | int | float = None,
|
|
45
88
|
cookies: None | CookieJar = None,
|
|
46
89
|
proxy: None | tuple[str, str] = None,
|
|
47
|
-
opener: OpenerDirector =
|
|
90
|
+
opener: None | OpenerDirector = None,
|
|
48
91
|
context: None | SSLContext = None,
|
|
49
92
|
origin: None | str = None,
|
|
50
93
|
) -> HTTPResponse:
|
|
@@ -95,7 +138,7 @@ def urlopen(
|
|
|
95
138
|
if proxy:
|
|
96
139
|
req.set_proxy(*proxy)
|
|
97
140
|
if opener is None:
|
|
98
|
-
opener = build_opener()
|
|
141
|
+
opener = build_opener(HTTPSHandler(context=_create_unverified_context()))
|
|
99
142
|
if context is not None:
|
|
100
143
|
opener.add_handler(HTTPSHandler(context=context))
|
|
101
144
|
if cookies is not None:
|
|
@@ -136,17 +179,16 @@ def request(
|
|
|
136
179
|
if parse is None:
|
|
137
180
|
return resp
|
|
138
181
|
with resp:
|
|
139
|
-
if parse
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return data.decode(get_charset(content_type))
|
|
182
|
+
if isinstance(parse, bool):
|
|
183
|
+
data = decompress_response(resp)
|
|
184
|
+
if parse:
|
|
185
|
+
content_type = resp.headers.get("Content-Type", "")
|
|
186
|
+
if content_type == "application/json":
|
|
187
|
+
return loads(data)
|
|
188
|
+
elif content_type.startswith("application/json;"):
|
|
189
|
+
return loads(data.decode(get_charset(content_type)))
|
|
190
|
+
elif content_type.startswith("text/"):
|
|
191
|
+
return data.decode(get_charset(content_type))
|
|
150
192
|
return data
|
|
151
193
|
else:
|
|
152
194
|
ac = argcount(parse)
|
|
@@ -154,7 +196,7 @@ def request(
|
|
|
154
196
|
if ac == 1:
|
|
155
197
|
return parse(resp)
|
|
156
198
|
else:
|
|
157
|
-
return parse(resp, resp
|
|
199
|
+
return parse(resp, decompress_response(resp))
|
|
158
200
|
|
|
159
201
|
|
|
160
202
|
def download(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|