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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-urlopen
3
- Version: 0.1.0
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.5)
24
+ Requires-Dist: http_response (>=0.0.6)
25
25
  Requires-Dist: python-argtools (>=0.0.2)
26
- Requires-Dist: python-dicttools (>=0.0.1)
27
- Requires-Dist: python-ensure (>=0.0.1)
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.9)
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.0"
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.5"
31
+ http_response = ">=0.0.6"
32
32
  python-argtools = ">=0.0.2"
33
- python-dicttools = ">=0.0.1"
34
- python-ensure = ">=0.0.1"
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.9"
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, 0)
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, Sequence
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
- _opener: OpenerDirector = build_opener(HTTPSHandler(context=_create_unverified_context()))
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 | Sequence[tuple[Any, Any]] = None,
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 = None,
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 = map(ensure_buffer, bio_chunk_iter(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
- return opener.open(request, timeout=cast(None|float, timeout))
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 | Sequence[tuple[Any, Any]] = None,
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 | Sequence[tuple[Any, Any]] = None,
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 | Sequence[tuple[Any, Any]] = None,
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 | Sequence[tuple[Any, Any]] = None,
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 | Sequence[tuple[Any, Any]] = None,
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