python-urlopen 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.
- {python_urlopen-0.1.0 → python_urlopen-0.1.2}/PKG-INFO +5 -5
- {python_urlopen-0.1.0 → python_urlopen-0.1.2}/pyproject.toml +5 -5
- {python_urlopen-0.1.0 → python_urlopen-0.1.2}/urlopen/__init__.py +59 -33
- {python_urlopen-0.1.0 → python_urlopen-0.1.2}/LICENSE +0 -0
- {python_urlopen-0.1.0 → python_urlopen-0.1.2}/readme.md +0 -0
- {python_urlopen-0.1.0 → python_urlopen-0.1.2}/urlopen/__main__.py +0 -0
- {python_urlopen-0.1.0 → python_urlopen-0.1.2}/urlopen/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-urlopen
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Python urlopen wrapper.
|
|
5
5
|
Home-page: https://github.com/ChenyangGao/python-modules/tree/main/python-urlopen
|
|
6
6
|
License: MIT
|
|
@@ -21,12 +21,12 @@ Classifier: Topic :: Software Development
|
|
|
21
21
|
Classifier: Topic :: Software Development :: Libraries
|
|
22
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
23
|
Requires-Dist: brotli
|
|
24
|
-
Requires-Dist: http_response (>=0.0.
|
|
24
|
+
Requires-Dist: http_response (>=0.0.6)
|
|
25
25
|
Requires-Dist: python-argtools (>=0.0.2)
|
|
26
|
-
Requires-Dist: python-
|
|
27
|
-
Requires-Dist: python-
|
|
26
|
+
Requires-Dist: python-cookietools (>=0.0.8)
|
|
27
|
+
Requires-Dist: python-dicttools (>=0.0.2)
|
|
28
28
|
Requires-Dist: python-filewrap (>=0.2.8)
|
|
29
|
-
Requires-Dist: python-http_request (>=0.0
|
|
29
|
+
Requires-Dist: python-http_request (>=0.1.0)
|
|
30
30
|
Requires-Dist: python-undefined (>=0.0.3)
|
|
31
31
|
Requires-Dist: yarl
|
|
32
32
|
Requires-Dist: zstandard
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "python-urlopen"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "Python urlopen wrapper."
|
|
5
5
|
authors = ["ChenyangGao <wosiwujm@gmail.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -28,12 +28,12 @@ include = [
|
|
|
28
28
|
[tool.poetry.dependencies]
|
|
29
29
|
python = "^3.12"
|
|
30
30
|
brotli = "*"
|
|
31
|
-
http_response = ">=0.0.
|
|
31
|
+
http_response = ">=0.0.6"
|
|
32
32
|
python-argtools = ">=0.0.2"
|
|
33
|
-
python-
|
|
34
|
-
python-
|
|
33
|
+
python-cookietools = ">=0.0.8"
|
|
34
|
+
python-dicttools = ">=0.0.2"
|
|
35
35
|
python-filewrap = ">=0.2.8"
|
|
36
|
-
python-http_request = ">=0.0
|
|
36
|
+
python-http_request = ">=0.1.0"
|
|
37
37
|
python-undefined = ">=0.0.3"
|
|
38
38
|
yarl = "*"
|
|
39
39
|
zstandard = "*"
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
# coding: utf-8
|
|
3
3
|
|
|
4
4
|
__author__ = "ChenyangGao <https://chenyanggao.github.io>"
|
|
5
|
-
__version__ = (0, 1,
|
|
5
|
+
__version__ = (0, 1, 2)
|
|
6
6
|
__all__ = ["urlopen", "request", "download"]
|
|
7
7
|
|
|
8
8
|
import errno
|
|
9
9
|
|
|
10
10
|
from collections import UserString
|
|
11
|
-
from collections.abc import Buffer, Callable, Generator, Iterable, Mapping
|
|
11
|
+
from collections.abc import Buffer, Callable, Generator, Iterable, Mapping
|
|
12
|
+
from copy import copy
|
|
12
13
|
from gzip import decompress as decompress_gzip
|
|
13
14
|
from http.client import HTTPResponse
|
|
14
15
|
from http.cookiejar import CookieJar
|
|
@@ -28,8 +29,8 @@ from urllib.request import (
|
|
|
28
29
|
from zlib import compressobj, DEF_MEM_LEVEL, DEFLATED, MAX_WBITS
|
|
29
30
|
|
|
30
31
|
from argtools import argcount
|
|
32
|
+
from cookietools import cookies_dict_to_str
|
|
31
33
|
from dicttools import iter_items
|
|
32
|
-
from ensure import ensure_buffer
|
|
33
34
|
from filewrap import bio_skip_iter, bio_chunk_iter, SupportsRead, SupportsWrite
|
|
34
35
|
from http_request import normalize_request_args, SupportsGeturl
|
|
35
36
|
from http_response import (
|
|
@@ -47,7 +48,9 @@ if "__del__" not in HTTPResponse.__dict__:
|
|
|
47
48
|
if "__del__" not in OpenerDirector.__dict__:
|
|
48
49
|
setattr(OpenerDirector, "__del__", OpenerDirector.close)
|
|
49
50
|
|
|
50
|
-
|
|
51
|
+
_cookies = CookieJar()
|
|
52
|
+
_opener: OpenerDirector = build_opener(HTTPSHandler(context=_create_unverified_context()), HTTPCookieProcessor(_cookies))
|
|
53
|
+
setattr(_opener, "cookies", _cookies)
|
|
51
54
|
|
|
52
55
|
|
|
53
56
|
if getdefaulttimeout() is None:
|
|
@@ -101,16 +104,17 @@ def decompress_response(response: HTTPResponse, /) -> bytes:
|
|
|
101
104
|
def urlopen(
|
|
102
105
|
url: string | SupportsGeturl | URL | Request,
|
|
103
106
|
method: string = "GET",
|
|
104
|
-
params: None | string | Mapping |
|
|
107
|
+
params: None | string | Mapping | Iterable[tuple[Any, Any]] = None,
|
|
105
108
|
data: Any = None,
|
|
106
109
|
json: Any = None,
|
|
110
|
+
files: None | Mapping[string, Any] | Iterable[tuple[string, Any]] = None,
|
|
107
111
|
headers: None | Mapping[string, string] | Iterable[tuple[string, string]] = None,
|
|
108
112
|
follow_redirects: bool = True,
|
|
109
113
|
proxies: None | Mapping[str, str] | Iterable[tuple[str, str]] = None,
|
|
110
114
|
context: None | SSLContext = None,
|
|
111
115
|
cookies: None | CookieJar = None,
|
|
112
116
|
timeout: None | Undefined | float = undefined,
|
|
113
|
-
opener: None | OpenerDirector =
|
|
117
|
+
opener: None | OpenerDirector = _opener,
|
|
114
118
|
**_,
|
|
115
119
|
) -> HTTPResponse:
|
|
116
120
|
if isinstance(url, Request):
|
|
@@ -119,46 +123,69 @@ def urlopen(
|
|
|
119
123
|
if isinstance(data, PathLike):
|
|
120
124
|
data = bio_chunk_iter(open(data, "rb"))
|
|
121
125
|
elif isinstance(data, SupportsRead):
|
|
122
|
-
data =
|
|
126
|
+
data = bio_chunk_iter(data)
|
|
123
127
|
request = Request(**normalize_request_args( # type: ignore
|
|
124
128
|
method=method,
|
|
125
129
|
url=url,
|
|
126
130
|
params=params,
|
|
127
131
|
data=data,
|
|
128
132
|
json=json,
|
|
133
|
+
files=files,
|
|
129
134
|
headers=headers,
|
|
130
135
|
ensure_ascii=True,
|
|
131
136
|
))
|
|
132
137
|
if proxies:
|
|
133
138
|
for host, type in iter_items(proxies):
|
|
134
139
|
request.set_proxy(host, type)
|
|
140
|
+
headers_ = request.headers
|
|
135
141
|
if opener is None:
|
|
136
142
|
handlers: list[BaseHandler] = []
|
|
137
|
-
if context is not None:
|
|
138
|
-
handlers.append(HTTPSHandler(context=context))
|
|
139
|
-
if cookies is not None:
|
|
140
|
-
handlers.append(HTTPCookieProcessor(cookies))
|
|
141
|
-
if not follow_redirects:
|
|
142
|
-
handlers.append(NoRedirectHandler())
|
|
143
|
-
if handlers:
|
|
144
|
-
if not isinstance(handlers[0], HTTPSHandler):
|
|
145
|
-
handlers.insert(0, HTTPSHandler(context=_create_unverified_context()))
|
|
146
|
-
opener = build_opener(*handlers)
|
|
147
|
-
else:
|
|
148
|
-
opener = _opener
|
|
149
|
-
if timeout is undefined:
|
|
150
|
-
return opener.open(request)
|
|
151
143
|
else:
|
|
152
|
-
|
|
144
|
+
handlers = list(map(copy, getattr(opener, "handlers")))
|
|
145
|
+
if cookies is None:
|
|
146
|
+
cookies = getattr(opener, "cookies", None)
|
|
147
|
+
if cookies and "cookie" not in headers_:
|
|
148
|
+
headers_["cookie"] = cookies_dict_to_str(cookies)
|
|
149
|
+
if context is not None:
|
|
150
|
+
handlers.append(HTTPSHandler(context=context))
|
|
151
|
+
elif opener is None:
|
|
152
|
+
handlers.append(HTTPSHandler(context=_create_unverified_context()))
|
|
153
|
+
if cookies is not None and (opener is None or all(
|
|
154
|
+
h.cookiejar is not cookies
|
|
155
|
+
for h in getattr(opener, "handlers") if isinstance(h, HTTPCookieProcessor)
|
|
156
|
+
)):
|
|
157
|
+
handlers.append(HTTPCookieProcessor(cookies))
|
|
158
|
+
response_cookies = CookieJar()
|
|
159
|
+
if cookies is None:
|
|
160
|
+
cookies = response_cookies
|
|
161
|
+
handlers.append(HTTPCookieProcessor(response_cookies))
|
|
162
|
+
if not follow_redirects:
|
|
163
|
+
handlers.append(NoRedirectHandler())
|
|
164
|
+
opener = build_opener(*handlers)
|
|
165
|
+
setattr(opener, "cookies", cookies)
|
|
166
|
+
try:
|
|
167
|
+
if timeout is undefined:
|
|
168
|
+
response = opener.open(request)
|
|
169
|
+
else:
|
|
170
|
+
response = opener.open(request, timeout=cast(None|float, timeout))
|
|
171
|
+
setattr(response, "opener", opener)
|
|
172
|
+
setattr(response, "cookies", response_cookies)
|
|
173
|
+
return response
|
|
174
|
+
except HTTPError as e:
|
|
175
|
+
if response := getattr(e, "file", None):
|
|
176
|
+
setattr(response, "opener", opener)
|
|
177
|
+
setattr(response, "cookies", response_cookies)
|
|
178
|
+
raise
|
|
153
179
|
|
|
154
180
|
|
|
155
181
|
@overload
|
|
156
182
|
def request(
|
|
157
183
|
url: string | SupportsGeturl | URL | Request,
|
|
158
184
|
method: string = "GET",
|
|
159
|
-
params: None | string | Mapping |
|
|
185
|
+
params: None | string | Mapping | Iterable[tuple[Any, Any]] = None,
|
|
160
186
|
data: Any = None,
|
|
161
187
|
json: Any = None,
|
|
188
|
+
files: None | Mapping[string, Any] | Iterable[tuple[string, Any]] = None,
|
|
162
189
|
headers: None | Mapping[string, string] | Iterable[tuple[string, string]] = None,
|
|
163
190
|
follow_redirects: bool = True,
|
|
164
191
|
raise_for_status: bool = True,
|
|
@@ -171,9 +198,10 @@ def request(
|
|
|
171
198
|
def request(
|
|
172
199
|
url: string | SupportsGeturl | URL | Request,
|
|
173
200
|
method: string = "GET",
|
|
174
|
-
params: None | string | Mapping |
|
|
201
|
+
params: None | string | Mapping | Iterable[tuple[Any, Any]] = None,
|
|
175
202
|
data: Any = None,
|
|
176
203
|
json: Any = None,
|
|
204
|
+
files: None | Mapping[string, Any] | Iterable[tuple[string, Any]] = None,
|
|
177
205
|
headers: None | Mapping[string, string] | Iterable[tuple[string, string]] = None,
|
|
178
206
|
follow_redirects: bool = True,
|
|
179
207
|
raise_for_status: bool = True,
|
|
@@ -186,9 +214,10 @@ def request(
|
|
|
186
214
|
def request(
|
|
187
215
|
url: string | SupportsGeturl | URL | Request,
|
|
188
216
|
method: string = "GET",
|
|
189
|
-
params: None | string | Mapping |
|
|
217
|
+
params: None | string | Mapping | Iterable[tuple[Any, Any]] = None,
|
|
190
218
|
data: Any = None,
|
|
191
219
|
json: Any = None,
|
|
220
|
+
files: None | Mapping[string, Any] | Iterable[tuple[string, Any]] = None,
|
|
192
221
|
headers: None | Mapping[string, string] | Iterable[tuple[string, string]] = None,
|
|
193
222
|
follow_redirects: bool = True,
|
|
194
223
|
raise_for_status: bool = True,
|
|
@@ -201,9 +230,10 @@ def request(
|
|
|
201
230
|
def request[T](
|
|
202
231
|
url: string | SupportsGeturl | URL | Request,
|
|
203
232
|
method: string = "GET",
|
|
204
|
-
params: None | string | Mapping |
|
|
233
|
+
params: None | string | Mapping | Iterable[tuple[Any, Any]] = None,
|
|
205
234
|
data: Any = None,
|
|
206
235
|
json: Any = None,
|
|
236
|
+
files: None | Mapping[string, Any] | Iterable[tuple[string, Any]] = None,
|
|
207
237
|
headers: None | Mapping[string, string] | Iterable[tuple[string, string]] = None,
|
|
208
238
|
follow_redirects: bool = True,
|
|
209
239
|
raise_for_status: bool = True,
|
|
@@ -215,9 +245,10 @@ def request[T](
|
|
|
215
245
|
def request[T](
|
|
216
246
|
url: string | SupportsGeturl | URL | Request,
|
|
217
247
|
method: string = "GET",
|
|
218
|
-
params: None | string | Mapping |
|
|
248
|
+
params: None | string | Mapping | Iterable[tuple[Any, Any]] = None,
|
|
219
249
|
data: Any = None,
|
|
220
250
|
json: Any = None,
|
|
251
|
+
files: None | Mapping[string, Any] | Iterable[tuple[string, Any]] = None,
|
|
221
252
|
headers: None | Mapping[string, string] | Iterable[tuple[string, string]] = None,
|
|
222
253
|
follow_redirects: bool = True,
|
|
223
254
|
raise_for_status: bool = True,
|
|
@@ -232,6 +263,7 @@ def request[T](
|
|
|
232
263
|
params=params,
|
|
233
264
|
data=data,
|
|
234
265
|
json=json,
|
|
266
|
+
files=files,
|
|
235
267
|
headers=headers,
|
|
236
268
|
follow_redirects=follow_redirects,
|
|
237
269
|
**request_kwargs,
|
|
@@ -310,12 +342,10 @@ def download(
|
|
|
310
342
|
chunksize = COPY_BUFSIZE
|
|
311
343
|
headers = request_kwargs["headers"] = dict(headers or ())
|
|
312
344
|
headers["accept-encoding"] = "identity"
|
|
313
|
-
|
|
314
345
|
response: HTTPResponse = urlopen(url, **request_kwargs)
|
|
315
346
|
content_length = get_length(response)
|
|
316
347
|
if content_length == 0 and is_chunked(response):
|
|
317
348
|
content_length = None
|
|
318
|
-
|
|
319
349
|
fdst: SupportsWrite[bytes]
|
|
320
350
|
if hasattr(file, "write"):
|
|
321
351
|
file = fdst = cast(SupportsWrite[bytes], file)
|
|
@@ -328,7 +358,6 @@ def download(
|
|
|
328
358
|
except FileNotFoundError:
|
|
329
359
|
makedirs(dirname(file), exist_ok=True)
|
|
330
360
|
fdst = open(file, "ab" if resume else "wb")
|
|
331
|
-
|
|
332
361
|
filesize = 0
|
|
333
362
|
if resume:
|
|
334
363
|
try:
|
|
@@ -347,7 +376,6 @@ def download(
|
|
|
347
376
|
errno.EIO,
|
|
348
377
|
f"file {file!r} is larger than url {url!r}: {filesize} > {content_length} (in bytes)",
|
|
349
378
|
)
|
|
350
|
-
|
|
351
379
|
reporthook_close: None | Callable = None
|
|
352
380
|
if callable(make_reporthook):
|
|
353
381
|
reporthook = make_reporthook(content_length)
|
|
@@ -360,7 +388,6 @@ def download(
|
|
|
360
388
|
reporthook = cast(Callable[[int], Any], reporthook)
|
|
361
389
|
else:
|
|
362
390
|
reporthook = None
|
|
363
|
-
|
|
364
391
|
try:
|
|
365
392
|
if filesize:
|
|
366
393
|
if is_range_request(response):
|
|
@@ -384,6 +411,5 @@ def download(
|
|
|
384
411
|
response.close()
|
|
385
412
|
if callable(reporthook_close):
|
|
386
413
|
reporthook_close()
|
|
387
|
-
|
|
388
414
|
return file
|
|
389
415
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|