python-httpfile 0.0.3__py3-none-any.whl → 0.0.4__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 CHANGED
@@ -4,14 +4,15 @@
4
4
  from __future__ import annotations
5
5
 
6
6
  __author__ = "ChenyangGao <https://chenyanggao.github.io>"
7
- __version__ = (0, 0, 3)
7
+ __version__ = (0, 0, 4)
8
8
  __all__ = ["HTTPFileReader", "AsyncHTTPFileReader"]
9
9
 
10
10
  import errno
11
11
 
12
12
  from collections.abc import Awaitable, Callable, Mapping
13
13
  from functools import cached_property, partial
14
- from inspect import isawaitable, signature
14
+ from importlib.util import find_spec
15
+ from inspect import isawaitable
15
16
  from io import (
16
17
  BufferedReader, RawIOBase, TextIOWrapper, UnsupportedOperation, DEFAULT_BUFFER_SIZE,
17
18
  )
@@ -19,7 +20,7 @@ from os import fstat, stat, PathLike
19
20
  from shutil import COPY_BUFSIZE # type: ignore
20
21
  from sys import exc_info
21
22
  from typing import cast, overload, Any, BinaryIO, Literal, Self
22
- from types import MappingProxyType, MethodType
23
+ from types import MappingProxyType
23
24
  from warnings import warn
24
25
 
25
26
  from asynctools import ensure_async, run_async
@@ -102,7 +103,7 @@ class HTTPFileReader(RawIOBase, BinaryIO):
102
103
  # NOTE: If the offset of the forward seek is not higher than this value,
103
104
  # it will be directly read and discarded, default to 1 MB
104
105
  seek_threshold: int = 1 << 20,
105
- urlopen: Callable = urlopen,
106
+ urlopen = urlopen,
106
107
  ):
107
108
  if headers:
108
109
  headers = {**headers, "Accept-Encoding": "identity"}
@@ -112,6 +113,8 @@ class HTTPFileReader(RawIOBase, BinaryIO):
112
113
  headers["Range"] = f"bytes={start}-"
113
114
  elif start < 0:
114
115
  headers["Range"] = f"bytes={start}"
116
+ if urlopen is None:
117
+ urlopen = globals()["urlopen"]
115
118
  if callable(url):
116
119
  geturl = url
117
120
  def url():
@@ -384,7 +387,7 @@ class HTTPFileReader(RawIOBase, BinaryIO):
384
387
  headers: None | Mapping = None,
385
388
  start: int = 0,
386
389
  seek_threshold: int = 1 << 20,
387
- urlopen: Callable = urlopen,
390
+ urlopen = None,
388
391
  ) -> Self | BufferedReader:
389
392
  ...
390
393
  @overload
@@ -402,7 +405,7 @@ class HTTPFileReader(RawIOBase, BinaryIO):
402
405
  headers: None | Mapping = None,
403
406
  start: int = 0,
404
407
  seek_threshold: int = 1 << 20,
405
- urlopen: Callable = urlopen,
408
+ urlopen = None,
406
409
  ) -> TextIOWrapper:
407
410
  ...
408
411
  @classmethod
@@ -419,7 +422,7 @@ class HTTPFileReader(RawIOBase, BinaryIO):
419
422
  headers: None | Mapping = None,
420
423
  start: int = 0,
421
424
  seek_threshold: int = 1 << 20,
422
- urlopen: Callable = urlopen,
425
+ urlopen = None,
423
426
  ) -> Self | BufferedReader | TextIOWrapper:
424
427
  file = cls(
425
428
  url=url,
@@ -517,9 +520,9 @@ class AsyncHTTPFileReader(HTTPFileReader):
517
520
  headers: None | Mapping = None,
518
521
  start: int = 0,
519
522
  seek_threshold: int = 1 << 20,
520
- urlopen: Callable = urlopen,
523
+ urlopen = urlopen,
521
524
  ):
522
- run_async(self._init(
525
+ run_async(self.__ainit__(
523
526
  url=url,
524
527
  headers=headers,
525
528
  start=start,
@@ -542,7 +545,7 @@ class AsyncHTTPFileReader(HTTPFileReader):
542
545
  else:
543
546
  raise StopAsyncIteration
544
547
 
545
- async def _init(
548
+ async def __ainit__(
546
549
  self,
547
550
  /,
548
551
  url: str | Callable[[], str] | Callable[[], Awaitable[str]],
@@ -551,8 +554,6 @@ class AsyncHTTPFileReader(HTTPFileReader):
551
554
  seek_threshold: int = 1 << 20,
552
555
  urlopen = None,
553
556
  ):
554
- if urlopen is None:
555
- urlopen = signature(type(self).__init__).parameters["urlopen"].default
556
557
  if headers:
557
558
  headers = {**headers, "Accept-Encoding": "identity"}
558
559
  else:
@@ -561,6 +562,8 @@ class AsyncHTTPFileReader(HTTPFileReader):
561
562
  headers["Range"] = f"bytes={start}-"
562
563
  elif start < 0:
563
564
  headers["Range"] = f"bytes={start}"
565
+ if urlopen is None:
566
+ urlopen = globals()["urlopen"]
564
567
  if callable(url):
565
568
  geturl = url
566
569
  async def url() -> str:
@@ -749,7 +752,7 @@ class AsyncHTTPFileReader(HTTPFileReader):
749
752
  urlopen = None,
750
753
  ) -> Self: # type: ignore
751
754
  self = cls.__new__(cls)
752
- await self._init(
755
+ await self.__ainit__(
753
756
  url=url,
754
757
  headers=headers,
755
758
  start=start,
@@ -773,7 +776,7 @@ class AsyncHTTPFileReader(HTTPFileReader):
773
776
  headers: None | Mapping = None,
774
777
  start: int = 0,
775
778
  seek_threshold: int = 1 << 20,
776
- urlopen: Callable = urlopen,
779
+ urlopen = None,
777
780
  ) -> Self | AsyncBufferedReader:
778
781
  ...
779
782
  @overload
@@ -791,7 +794,7 @@ class AsyncHTTPFileReader(HTTPFileReader):
791
794
  headers: None | Mapping = None,
792
795
  start: int = 0,
793
796
  seek_threshold: int = 1 << 20,
794
- urlopen: Callable = urlopen,
797
+ urlopen = None,
795
798
  ) -> AsyncTextIOWrapper:
796
799
  ...
797
800
  @classmethod
@@ -807,7 +810,7 @@ class AsyncHTTPFileReader(HTTPFileReader):
807
810
  headers: None | Mapping = None,
808
811
  start: int = 0,
809
812
  seek_threshold: int = 1 << 20,
810
- urlopen: Callable = urlopen,
813
+ urlopen = None,
811
814
  ) -> Self | AsyncBufferedReader | AsyncTextIOWrapper:
812
815
  file = await cls.new(
813
816
  url=url,
@@ -895,10 +898,10 @@ class AsyncHTTPFileReader(HTTPFileReader):
895
898
  return buffer
896
899
 
897
900
 
898
- try:
901
+ if find_spec("urllib3"):
899
902
  from urllib.error import HTTPError
900
- from urllib3 import request as urllib3_request
901
- from urllib3.poolmanager import PoolManager
903
+
904
+ _urllib3_urlopen = None
902
905
 
903
906
  class Urllib3FileReader(HTTPFileReader):
904
907
 
@@ -909,17 +912,28 @@ try:
909
912
  headers: None | Mapping = None,
910
913
  start: int = 0,
911
914
  seek_threshold: int = 1 << 20,
912
- urlopen = partial(urllib3_request, "GET"), # type: ignore
915
+ urlopen = None,
913
916
  ):
917
+ global _urllib3_urlopen
918
+ from urllib3 import request
919
+ from urllib3.poolmanager import PoolManager
920
+
914
921
  if isinstance(urlopen, PoolManager):
915
922
  urlopen = partial(urlopen.request, "GET", preload_content=False)
923
+ elif urlopen is None:
924
+ if _urllib3_urlopen is None:
925
+ urlopen = _urllib3_urlopen = partial(cast(Callable, request), "GET", preload_content=False)
926
+ else:
927
+ urlopen = _urllib3_urlopen
916
928
  else:
917
929
  urlopen = partial(urlopen, preload_content=False)
930
+
918
931
  def urlopen_wrapper(url: str, headers: None | Mapping = headers):
919
932
  resp = urlopen(url, headers=headers)
920
933
  if resp.status >= 400:
921
934
  raise HTTPError(resp.url, resp.status, resp.reason, resp.headers, resp)
922
935
  return resp
936
+
923
937
  super().__init__(
924
938
  url,
925
939
  headers=headers,
@@ -929,15 +943,10 @@ try:
929
943
  )
930
944
 
931
945
  __all__.append("Urllib3FileReader")
932
- except ImportError:
933
- pass
934
946
 
935
947
 
936
- try:
937
- from requests import request as requests_request, Session
938
-
939
- if "__del__" not in Session.__dict__:
940
- setattr(Session, "__del__", lambda self, /: self.close())
948
+ if find_spec("requests"):
949
+ _requests_urlopen = None
941
950
 
942
951
  class RequestsFileReader(HTTPFileReader):
943
952
 
@@ -948,10 +957,19 @@ try:
948
957
  headers: None | Mapping = None,
949
958
  start: int = 0,
950
959
  seek_threshold: int = 1 << 20,
951
- urlopen = partial(requests_request, "GET"),
960
+ urlopen = None,
952
961
  ):
962
+ global _requests_urlopen
963
+ from requests import request, Session
953
964
  if isinstance(urlopen, Session):
954
965
  urlopen = partial(urlopen.request, "GET", stream=True)
966
+ elif urlopen is None:
967
+ if _requests_urlopen is None:
968
+ if "__del__" not in Session.__dict__:
969
+ setattr(Session, "__del__", lambda self, /: self.close())
970
+ urlopen = _requests_urlopen = partial(request, "GET", stream=True)
971
+ else:
972
+ urlopen = _requests_urlopen
955
973
  else:
956
974
  urlopen = partial(urlopen, stream=True)
957
975
  def urlopen_wrapper(url: str, headers: None | Mapping = headers):
@@ -971,33 +989,16 @@ try:
971
989
  return self.response.raw
972
990
 
973
991
  __all__.append("RequestsFileReader")
974
- except ImportError:
975
- pass
976
992
 
977
993
 
978
- try:
979
- from aiohttp import request as aiohttp_request, ClientSession
994
+ if find_spec("aiohttp"):
995
+ from types import MethodType
980
996
 
981
- class AiohttpFileReader(AsyncHTTPFileReader):
997
+ _aiohttp_urlopen = None
982
998
 
983
- def __init__(
984
- self,
985
- /,
986
- url: str | Callable[[], str] | Callable[[], Awaitable[str]],
987
- headers: None | Mapping = None,
988
- start: int = 0,
989
- seek_threshold: int = 1 << 20,
990
- urlopen = partial(aiohttp_request, "GET"),
991
- ):
992
- super().__init__(
993
- url=url,
994
- headers=headers,
995
- start=start,
996
- seek_threshold=seek_threshold,
997
- urlopen=urlopen,
998
- )
999
+ class AiohttpFileReader(AsyncHTTPFileReader):
999
1000
 
1000
- async def _init(
1001
+ async def __ainit__(
1001
1002
  self,
1002
1003
  /,
1003
1004
  url: str | Callable[[], str] | Callable[[], Awaitable[str]],
@@ -1006,17 +1007,26 @@ try:
1006
1007
  seek_threshold: int = 1 << 20,
1007
1008
  urlopen = None,
1008
1009
  ):
1009
- if urlopen is None:
1010
- urlopen = signature(type(self).__init__).parameters["urlopen"].default
1010
+ global _aiohttp_urlopen
1011
+ from aiohttp import request, ClientSession
1012
+
1011
1013
  close_session = True
1012
1014
  if isinstance(urlopen, ClientSession):
1013
1015
  urlopen = urlopen.get
1014
- do_not_close_session = False
1015
- if close_session:
1016
+ close_session = False
1017
+ if urlopen is None:
1018
+ if _aiohttp_urlopen is None:
1019
+ urlopen = _aiohttp_urlopen = partial(request, "GET")
1020
+ else:
1021
+ urlopen = _aiohttp_urlopen
1022
+ else:
1016
1023
  func = urlopen
1017
1024
  if isinstance(func, partial):
1018
1025
  func = func.func
1019
- close_session = not (isinstance(func, MethodType) and isinstance(func.__self__, ClientSession))
1026
+ close_session = not (
1027
+ isinstance(func, MethodType) and
1028
+ isinstance(func.__self__, ClientSession)
1029
+ )
1020
1030
  async def urlopen_wrapper(url: str, headers: None | Mapping = headers):
1021
1031
  resp = await urlopen(url, headers=headers).__aenter__()
1022
1032
  async def aclose():
@@ -1029,7 +1039,7 @@ try:
1029
1039
  resp.aclose = aclose
1030
1040
  resp.raise_for_status()
1031
1041
  return resp
1032
- await super()._init(
1042
+ await super().__ainit__(
1033
1043
  url=url,
1034
1044
  headers=headers,
1035
1045
  start=start,
@@ -1046,60 +1056,13 @@ try:
1046
1056
  return self.response.closed
1047
1057
 
1048
1058
  __all__.append("AiohttpFileReader")
1049
- except ImportError:
1050
- pass
1051
1059
 
1052
1060
 
1053
- try:
1054
- from contextlib import asynccontextmanager
1061
+ if find_spec("httpx"):
1055
1062
  from filewrap import bytes_iter_to_reader, bytes_iter_to_async_reader
1056
- from httpx import stream, Client, AsyncClient
1057
-
1058
- if "__del__" not in Client.__dict__:
1059
- setattr(Client, "__del__", lambda self: self.close())
1060
- if "__del__" not in AsyncClient.__dict__:
1061
- setattr(AsyncClient, "__del__", lambda self: run_async(self.aclose()))
1062
-
1063
- def async_stream(
1064
- method,
1065
- url,
1066
- params = None,
1067
- content = None,
1068
- data = None,
1069
- files = None,
1070
- json = None,
1071
- headers = None,
1072
- cookies = None,
1073
- auth = None,
1074
- proxy = None,
1075
- proxies = None,
1076
- timeout = 5.0,
1077
- follow_redirects = False,
1078
- verify = True,
1079
- cert = None,
1080
- trust_env = True,
1081
- ):
1082
- client = AsyncClient(
1083
- cookies=cookies,
1084
- proxy=proxy,
1085
- proxies=proxies,
1086
- cert=cert,
1087
- verify=verify,
1088
- timeout=timeout,
1089
- trust_env=trust_env,
1090
- )
1091
- return client.stream(
1092
- method=method,
1093
- url=url,
1094
- content=content,
1095
- data=data,
1096
- files=files,
1097
- json=json,
1098
- params=params,
1099
- headers=headers,
1100
- auth=auth,
1101
- follow_redirects=follow_redirects,
1102
- )
1063
+
1064
+ _httpx_urlopen = None
1065
+ _httpx_urlopen_async = None
1103
1066
 
1104
1067
  class HttpxFileReader(HTTPFileReader):
1105
1068
 
@@ -1110,10 +1073,21 @@ try:
1110
1073
  headers: None | Mapping = None,
1111
1074
  start: int = 0,
1112
1075
  seek_threshold: int = 1 << 20,
1113
- urlopen = partial(stream, "GET"),
1076
+ urlopen = None,
1114
1077
  ):
1078
+ global _httpx_urlopen
1079
+ from httpx import stream, Client
1080
+
1115
1081
  if isinstance(urlopen, Client):
1116
1082
  urlopen = partial(urlopen.stream, "GET")
1083
+ elif urlopen is None:
1084
+ if _httpx_urlopen is None:
1085
+ if "__del__" not in Client.__dict__:
1086
+ setattr(Client, "__del__", lambda self: self.close())
1087
+ urlopen = _httpx_urlopen = partial(stream, "GET")
1088
+ else:
1089
+ urlopen = _httpx_urlopen
1090
+
1117
1091
  def urlopen_wrapper(url: str, headers: None | Mapping = headers):
1118
1092
  context = urlopen(url, headers=headers)
1119
1093
  resp = context.__enter__()
@@ -1122,6 +1096,7 @@ try:
1122
1096
  file = bytes_iter_to_reader(resp.iter_raw())
1123
1097
  self.__dict__["file"] = file
1124
1098
  return resp
1099
+
1125
1100
  super().__init__(
1126
1101
  url,
1127
1102
  headers=headers,
@@ -1136,24 +1111,7 @@ try:
1136
1111
 
1137
1112
  class AsyncHttpxFileReader(AsyncHTTPFileReader):
1138
1113
 
1139
- def __init__(
1140
- self,
1141
- /,
1142
- url: str | Callable[[], str] | Callable[[], Awaitable[str]],
1143
- headers: None | Mapping = None,
1144
- start: int = 0,
1145
- seek_threshold: int = 1 << 20,
1146
- urlopen = partial(async_stream, "GET"),
1147
- ):
1148
- super().__init__(
1149
- url=url,
1150
- headers=headers,
1151
- start=start,
1152
- seek_threshold=seek_threshold,
1153
- urlopen=urlopen,
1154
- )
1155
-
1156
- async def _init(
1114
+ async def __ainit__(
1157
1115
  self,
1158
1116
  /,
1159
1117
  url: str | Callable[[], str] | Callable[[], Awaitable[str]],
@@ -1162,10 +1120,59 @@ try:
1162
1120
  seek_threshold: int = 1 << 20,
1163
1121
  urlopen = None,
1164
1122
  ):
1165
- if urlopen is None:
1166
- urlopen = signature(type(self).__init__).parameters["urlopen"].default
1123
+ global _httpx_urlopen_async
1124
+ from httpx import AsyncClient
1125
+
1167
1126
  if isinstance(urlopen, AsyncClient):
1168
1127
  urlopen = partial(urlopen.stream, "GET")
1128
+ elif urlopen is None:
1129
+ if _httpx_urlopen_async is None:
1130
+ if "__del__" not in AsyncClient.__dict__:
1131
+ setattr(AsyncClient, "__del__", lambda self: run_async(self.aclose()))
1132
+ def async_stream(
1133
+ method,
1134
+ url,
1135
+ params = None,
1136
+ content = None,
1137
+ data = None,
1138
+ files = None,
1139
+ json = None,
1140
+ headers = None,
1141
+ cookies = None,
1142
+ auth = None,
1143
+ proxy = None,
1144
+ proxies = None,
1145
+ timeout = 5.0,
1146
+ follow_redirects = False,
1147
+ verify = True,
1148
+ cert = None,
1149
+ trust_env = True,
1150
+ ):
1151
+ client = AsyncClient(
1152
+ cookies=cookies,
1153
+ proxy=proxy,
1154
+ proxies=proxies,
1155
+ cert=cert,
1156
+ verify=verify,
1157
+ timeout=timeout,
1158
+ trust_env=trust_env,
1159
+ )
1160
+ return client.stream(
1161
+ method=method,
1162
+ url=url,
1163
+ content=content,
1164
+ data=data,
1165
+ files=files,
1166
+ json=json,
1167
+ params=params,
1168
+ headers=headers,
1169
+ auth=auth,
1170
+ follow_redirects=follow_redirects,
1171
+ )
1172
+ urlopen = _httpx_urlopen_async = partial(async_stream, "GET")
1173
+ else:
1174
+ urlopen = _httpx_urlopen_async
1175
+
1169
1176
  async def urlopen_wrapper(url: str, headers: None | Mapping = headers):
1170
1177
  context = urlopen(url, headers=headers)
1171
1178
  resp = await context.__aenter__()
@@ -1174,7 +1181,8 @@ try:
1174
1181
  file = bytes_iter_to_async_reader(resp.aiter_raw())
1175
1182
  self.__dict__["file"] = file
1176
1183
  return resp
1177
- await super()._init(
1184
+
1185
+ await super().__ainit__(
1178
1186
  url=url,
1179
1187
  headers=headers,
1180
1188
  start=start,
@@ -1188,7 +1196,5 @@ try:
1188
1196
 
1189
1197
  __all__.append("HttpxFileReader")
1190
1198
  __all__.append("AsyncHttpxFileReader")
1191
- except ImportError:
1192
- pass
1193
1199
 
1194
1200
  # TODO: 设计实现一个 HTTPFileWriter,用于实现上传,关闭后视为上传完成
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-httpfile
3
- Version: 0.0.3
3
+ Version: 0.0.4
4
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
@@ -25,7 +25,7 @@ Requires-Dist: http_response
25
25
  Requires-Dist: python-asynctools (>=0.0.5)
26
26
  Requires-Dist: python-filewrap (>=0.2.1)
27
27
  Requires-Dist: python-property (>=0.0.2)
28
- Requires-Dist: python-urlopen (>=0.0.7)
28
+ Requires-Dist: python-urlopen (>=0.0.7.1)
29
29
  Project-URL: Repository, https://github.com/ChenyangGao/web-mount-packs/tree/main/python-module/python-httpfile
30
30
  Description-Content-Type: text/markdown
31
31
 
@@ -0,0 +1,7 @@
1
+ LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
2
+ httpfile/__init__.py,sha256=PAqvhrEAfTEzVSZ7wbLrPplYX-8G1epkW7MZt2sWWaw,38657
3
+ httpfile/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ python_httpfile-0.0.4.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
5
+ python_httpfile-0.0.4.dist-info/METADATA,sha256=1CtTQ_EoeIP9eVYlevzlEBdA9D26S9pHfZmdTOnXB0Q,1520
6
+ python_httpfile-0.0.4.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
7
+ python_httpfile-0.0.4.dist-info/RECORD,,
@@ -1,7 +0,0 @@
1
- LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
2
- httpfile/__init__.py,sha256=Cp1mdmn7hLNIUsvScclByZZC0hNZh-ggljiYFH1PLVU,37815
3
- httpfile/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- python_httpfile-0.0.3.dist-info/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
5
- python_httpfile-0.0.3.dist-info/METADATA,sha256=5a1WYcujAY1_K8zdnseHhqzSjrUSPaw_a8sc78kR3lM,1518
6
- python_httpfile-0.0.3.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
7
- python_httpfile-0.0.3.dist-info/RECORD,,