python-httpfile 0.0.1.2__py3-none-any.whl → 0.0.2__py3-none-any.whl
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.
- httpfile/__init__.py +83 -42
- {python_httpfile-0.0.1.2.dist-info → python_httpfile-0.0.2.dist-info}/METADATA +3 -7
- python_httpfile-0.0.2.dist-info/RECORD +7 -0
- python_httpfile-0.0.1.2.dist-info/RECORD +0 -7
- {python_httpfile-0.0.1.2.dist-info → python_httpfile-0.0.2.dist-info}/LICENSE +0 -0
- {python_httpfile-0.0.1.2.dist-info → python_httpfile-0.0.2.dist-info}/WHEEL +0 -0
httpfile/__init__.py
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
# encoding: utf-8
|
|
3
3
|
|
|
4
4
|
__author__ = "ChenyangGao <https://chenyanggao.github.io>"
|
|
5
|
-
__version__ = (0, 0,
|
|
6
|
-
__all__ = ["HTTPFileReader", "
|
|
5
|
+
__version__ = (0, 0, 2)
|
|
6
|
+
__all__ = ["HTTPFileReader", "AsyncHTTPFileReader"]
|
|
7
7
|
|
|
8
8
|
import errno
|
|
9
9
|
|
|
@@ -19,12 +19,8 @@ from typing import Any, BinaryIO, IO, Optional, Protocol, Self, TypeVar
|
|
|
19
19
|
from types import MappingProxyType
|
|
20
20
|
from warnings import warn
|
|
21
21
|
|
|
22
|
-
from aiohttp import ClientSession
|
|
23
|
-
from filewrap import bio_skip_iter
|
|
24
22
|
from http_response import get_filename, get_length, get_range, get_total_length, is_chunked, is_range_request
|
|
25
|
-
from iterutils import through
|
|
26
23
|
from property import funcproperty
|
|
27
|
-
from requests import Session
|
|
28
24
|
from urlopen import urlopen
|
|
29
25
|
|
|
30
26
|
|
|
@@ -107,6 +103,18 @@ class HTTPFileReader(RawIOBase, BinaryIO):
|
|
|
107
103
|
headers["Range"] = f"bytes={start}-"
|
|
108
104
|
elif start < 0:
|
|
109
105
|
headers["Range"] = f"bytes={start}"
|
|
106
|
+
if callable(url):
|
|
107
|
+
geturl = url
|
|
108
|
+
def url():
|
|
109
|
+
url = geturl()
|
|
110
|
+
headers_extra = getattr(url, "headers")
|
|
111
|
+
if headers_extra:
|
|
112
|
+
headers.update(headers_extra)
|
|
113
|
+
return url
|
|
114
|
+
elif hasattr(url, "headers"):
|
|
115
|
+
headers_extra = getattr(url, "headers")
|
|
116
|
+
if headers_extra:
|
|
117
|
+
headers.update(headers_extra)
|
|
110
118
|
response = urlopen(url() if callable(url) else url, headers=headers)
|
|
111
119
|
if start:
|
|
112
120
|
rng = get_range(response)
|
|
@@ -166,8 +174,9 @@ class HTTPFileReader(RawIOBase, BinaryIO):
|
|
|
166
174
|
self.__dict__["start"] += delta
|
|
167
175
|
|
|
168
176
|
def close(self, /):
|
|
169
|
-
self.
|
|
170
|
-
|
|
177
|
+
if not self.closed:
|
|
178
|
+
self.response.close()
|
|
179
|
+
self.__dict__["closed"] = True
|
|
171
180
|
|
|
172
181
|
@funcproperty
|
|
173
182
|
def closed(self, /):
|
|
@@ -216,7 +225,7 @@ class HTTPFileReader(RawIOBase, BinaryIO):
|
|
|
216
225
|
def readinto(self, buffer, /) -> int:
|
|
217
226
|
if self.closed:
|
|
218
227
|
raise ValueError("I/O operation on closed file.")
|
|
219
|
-
if not self.chunked and self.tell() >= self.length:
|
|
228
|
+
if not buffer or not self.chunked and self.tell() >= self.length:
|
|
220
229
|
return 0
|
|
221
230
|
if self.file.closed:
|
|
222
231
|
self.reconnect()
|
|
@@ -293,8 +302,16 @@ class HTTPFileReader(RawIOBase, BinaryIO):
|
|
|
293
302
|
old_pos = self.tell()
|
|
294
303
|
if old_pos == pos:
|
|
295
304
|
return pos
|
|
296
|
-
if pos > old_pos and pos - old_pos <= self.seek_threshold:
|
|
297
|
-
|
|
305
|
+
if pos > old_pos and (size := pos - old_pos) <= self.seek_threshold:
|
|
306
|
+
if size <= COPY_BUFSIZE:
|
|
307
|
+
self.read(size)
|
|
308
|
+
else:
|
|
309
|
+
buf = bytearray(COPY_BUFSIZE)
|
|
310
|
+
readinto = self.readinto
|
|
311
|
+
while size > COPY_BUFSIZE:
|
|
312
|
+
readinto(buf)
|
|
313
|
+
size -= COPY_BUFSIZE
|
|
314
|
+
self.read(size)
|
|
298
315
|
else:
|
|
299
316
|
self.reconnect(pos)
|
|
300
317
|
return pos
|
|
@@ -369,43 +386,67 @@ class HTTPFileReader(RawIOBase, BinaryIO):
|
|
|
369
386
|
return buffer
|
|
370
387
|
|
|
371
388
|
|
|
372
|
-
|
|
389
|
+
try:
|
|
390
|
+
from requests import Session
|
|
391
|
+
|
|
392
|
+
class RequestsFileReader(HTTPFileReader):
|
|
393
|
+
|
|
394
|
+
def __init__(
|
|
395
|
+
self,
|
|
396
|
+
/,
|
|
397
|
+
url: str | Callable[[], str],
|
|
398
|
+
headers: Optional[Mapping] = None,
|
|
399
|
+
start: int = 0,
|
|
400
|
+
seek_threshold: int = 1 << 20,
|
|
401
|
+
urlopen: Callable = Session().get,
|
|
402
|
+
):
|
|
403
|
+
def urlopen_wrapper(url: str, headers: Optional[Mapping] = headers):
|
|
404
|
+
resp = urlopen(url, headers=headers, stream=True)
|
|
405
|
+
resp.raise_for_status()
|
|
406
|
+
return resp
|
|
407
|
+
super().__init__(
|
|
408
|
+
url,
|
|
409
|
+
headers=headers,
|
|
410
|
+
start=start,
|
|
411
|
+
seek_threshold=seek_threshold,
|
|
412
|
+
urlopen=urlopen_wrapper,
|
|
413
|
+
)
|
|
373
414
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
/,
|
|
377
|
-
url: str | Callable[[], str],
|
|
378
|
-
headers: Optional[Mapping] = None,
|
|
379
|
-
start: int = 0,
|
|
380
|
-
seek_threshold: int = 1 << 20,
|
|
381
|
-
urlopen: Callable = Session().get,
|
|
382
|
-
):
|
|
383
|
-
def urlopen_wrapper(url: str, headers: Optional[Mapping] = headers):
|
|
384
|
-
resp = urlopen(url, headers=headers, stream=True)
|
|
385
|
-
resp.raise_for_status()
|
|
386
|
-
return resp
|
|
387
|
-
super().__init__(
|
|
388
|
-
url,
|
|
389
|
-
headers=headers,
|
|
390
|
-
start=start,
|
|
391
|
-
seek_threshold=seek_threshold,
|
|
392
|
-
urlopen=urlopen_wrapper,
|
|
393
|
-
)
|
|
415
|
+
def _add_start(self, delta: int, /):
|
|
416
|
+
pass
|
|
394
417
|
|
|
395
|
-
|
|
396
|
-
|
|
418
|
+
@funcproperty
|
|
419
|
+
def file(self, /) -> BinaryIO:
|
|
420
|
+
return self.response.raw
|
|
397
421
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
422
|
+
def tell(self, /) -> int:
|
|
423
|
+
start = self.start
|
|
424
|
+
if start >= self.length:
|
|
425
|
+
return start
|
|
426
|
+
return start + self.file.tell()
|
|
401
427
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
428
|
+
__all__.append("RequestsFileReader")
|
|
429
|
+
except ImportError:
|
|
430
|
+
pass
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
try:
|
|
434
|
+
from aiohttp import ClientSession
|
|
435
|
+
|
|
436
|
+
...
|
|
437
|
+
except ImportError:
|
|
438
|
+
pass
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
try:
|
|
442
|
+
from httpx import Client, AsyncClient
|
|
443
|
+
|
|
444
|
+
...
|
|
445
|
+
except ImportError:
|
|
446
|
+
pass
|
|
407
447
|
|
|
408
448
|
|
|
449
|
+
# TODO: 实现 AsyncHTTPFileReader
|
|
409
450
|
# TODO: 支持异步文件,使用 aiohttp,参考 aiofiles 的接口实现
|
|
410
451
|
# TODO: 设计实现一个 HTTPFileWriter,用于实现上传,关闭后视为上传完成
|
|
411
452
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: python-httpfile
|
|
3
|
-
Version: 0.0.
|
|
4
|
-
Summary: Python httpfile
|
|
3
|
+
Version: 0.0.2
|
|
4
|
+
Summary: Python httpfile.
|
|
5
5
|
Home-page: https://github.com/ChenyangGao/web-mount-packs/tree/main/python-module/python-httpfile
|
|
6
6
|
License: MIT
|
|
7
7
|
Keywords: http,file,wrapper
|
|
@@ -21,17 +21,13 @@ 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: aiohttp
|
|
25
24
|
Requires-Dist: http_response
|
|
26
|
-
Requires-Dist: python-filewrap
|
|
27
|
-
Requires-Dist: python-iterutils
|
|
28
25
|
Requires-Dist: python-property
|
|
29
26
|
Requires-Dist: python-urlopen
|
|
30
|
-
Requires-Dist: requests
|
|
31
27
|
Project-URL: Repository, https://github.com/ChenyangGao/web-mount-packs/tree/main/python-module/python-httpfile
|
|
32
28
|
Description-Content-Type: text/markdown
|
|
33
29
|
|
|
34
|
-
# Python httpfile
|
|
30
|
+
# Python httpfile.
|
|
35
31
|
|
|
36
32
|
## Installation
|
|
37
33
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
|
|
2
|
+
httpfile/__init__.py,sha256=cZZjfftX2nWqEZQ0jjDnrTOW2NLbfnQy8ZEnP52y_tY,13427
|
|
3
|
+
httpfile/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
python_httpfile-0.0.2.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
|
|
5
|
+
python_httpfile-0.0.2.dist-info/METADATA,sha256=-hanQvHDGCiX4-fYjubZmioS7o3nAnGPtSkF47eEXr0,1414
|
|
6
|
+
python_httpfile-0.0.2.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
7
|
+
python_httpfile-0.0.2.dist-info/RECORD,,
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
|
|
2
|
-
httpfile/__init__.py,sha256=QMeil_PlioH8MCXWfK4Pty5hJEPFryrkjXTAy_t2anE,12317
|
|
3
|
-
httpfile/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
python_httpfile-0.0.1.2.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
|
|
5
|
-
python_httpfile-0.0.1.2.dist-info/METADATA,sha256=mRCnDifm57cPKclZ4pF9B7g2Xc1naxBCfJzeU0sZF3Q,1542
|
|
6
|
-
python_httpfile-0.0.1.2.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
7
|
-
python_httpfile-0.0.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|