返回提交历史
Modified
g4f/requests/curl_cffi.py
+158
-128
XFEstudio/gpt4free
Fix aarch64 compatibility issue with curl_cffi imports
Co-authored-by: hlohaus <983577+hlohaus@users.noreply.github.com>
31f408ed
代码差异
1 个文件
+158
-128
@@ -1,142 +1,172 @@
1
1
from __future__ import annotations
2
2
3
from curl_cffi.requests import AsyncSession, Response
4
3
try:
5
from curl_cffi import CurlMime
6
has_curl_mime = True
4
from curl_cffi.requests import AsyncSession, Response
5
has_curl_cffi = True
7
6
except ImportError:
7
# Fallback for systems where curl_cffi is not available or causes illegal instruction errors
8
from typing import Any
9
class AsyncSession:
10
def __init__(self, *args, **kwargs):
11
raise ImportError("curl_cffi is not available on this platform")
12
class Response:
13
pass
14
has_curl_cffi = False
15
16
if has_curl_cffi:
17
try:
18
from curl_cffi import CurlMime
19
has_curl_mime = True
20
except ImportError:
21
has_curl_mime = False
22
try:
23
from curl_cffi import CurlWsFlag
24
has_curl_ws = True
25
except ImportError:
26
has_curl_ws = False
27
else:
8
28
has_curl_mime = False
9
try:
10
from curl_cffi import CurlWsFlag
11
has_curl_ws = True
12
except ImportError:
13
29
has_curl_ws = False
14
30
from typing import AsyncGenerator, Any
15
31
from functools import partialmethod
16
32
import json
17
33
18
class StreamResponse:
19
"""
20
A wrapper class for handling asynchronous streaming responses.
21
22
Attributes:
23
inner (Response): The original Response object.
24
"""
25
26
def __init__(self, inner: Response) -> None:
27
"""Initialize the StreamResponse with the provided Response object."""
28
self.inner: Response = inner
29
30
async def text(self) -> str:
31
"""Asynchronously get the response text."""
32
return await self.inner.atext()
33
34
def raise_for_status(self) -> None:
35
"""Raise an HTTPError if one occurred."""
36
self.inner.raise_for_status()
37
38
async def json(self, **kwargs) -> Any:
39
"""Asynchronously parse the JSON response content."""
40
return json.loads(await self.inner.acontent(), **kwargs)
41
42
def iter_lines(self) -> AsyncGenerator[bytes, None]:
43
"""Asynchronously iterate over the lines of the response."""
44
return self.inner.aiter_lines()
45
46
def iter_content(self) -> AsyncGenerator[bytes, None]:
47
"""Asynchronously iterate over the response content."""
48
return self.inner.aiter_content()
49
50
async def sse(self) -> AsyncGenerator[dict, None]:
51
"""Asynchronously iterate over the Server-Sent Events of the response."""
52
async for line in self.iter_lines():
53
if line.startswith(b"data: "):
54
chunk = line[6:]
55
if chunk == b"[DONE]":
56
break
57
try:
58
yield json.loads(chunk)
59
except json.JSONDecodeError:
60
continue
61
62
async def __aenter__(self):
63
"""Asynchronously enter the runtime context for the response object."""
64
inner: Response = await self.inner
65
self.inner = inner
66
self.url = inner.url
67
self.method = inner.request.method
68
self.request = inner.request
69
self.status: int = inner.status_code
70
self.reason: str = inner.reason
71
self.ok: bool = inner.ok
72
self.headers = inner.headers
73
self.cookies = inner.cookies
74
return self
75
76
async def __aexit__(self, *args):
77
"""Asynchronously exit the runtime context for the response object."""
78
await self.inner.aclose()
79
80
class StreamSession(AsyncSession):
81
"""
82
An asynchronous session class for handling HTTP requests with streaming.
83
84
Inherits from AsyncSession.
85
"""
86
87
def request(
88
self, method: str, url: str, ssl = None, **kwargs
89
) -> StreamResponse:
90
if kwargs.get("data") and isinstance(kwargs.get("data"), CurlMime):
91
kwargs["multipart"] = kwargs.pop("data")
92
"""Create and return a StreamResponse object for the given HTTP request."""
93
return StreamResponse(super().request(method, url, stream=True, verify=ssl, **kwargs))
94
95
def ws_connect(self, url, *args, **kwargs):
96
return WebSocket(self, url, **kwargs)
97
98
def _ws_connect(self, url, **kwargs):
99
return super().ws_connect(url, **kwargs)
100
101
# Defining HTTP methods as partial methods of the request method.
102
head = partialmethod(request, "HEAD")
103
get = partialmethod(request, "GET")
104
post = partialmethod(request, "POST")
105
put = partialmethod(request, "PUT")
106
patch = partialmethod(request, "PATCH")
107
delete = partialmethod(request, "DELETE")
108
options = partialmethod(request, "OPTIONS")
109
110
if not has_curl_mime:
111
class FormData():
112
def __init__(self) -> None:
113
raise RuntimeError("CurlMimi in curl_cffi is missing | pip install -U curl_cffi")
34
if has_curl_cffi:
35
class StreamResponse:
36
"""
37
A wrapper class for handling asynchronous streaming responses.
38
39
Attributes:
40
inner (Response): The original Response object.
41
"""
42
43
def __init__(self, inner: Response) -> None:
44
"""Initialize the StreamResponse with the provided Response object."""
45
self.inner: Response = inner
46
47
async def text(self) -> str:
48
"""Asynchronously get the response text."""
49
return await self.inner.atext()
50
51
def raise_for_status(self) -> None:
52
"""Raise an HTTPError if one occurred."""
53
self.inner.raise_for_status()
54
55
async def json(self, **kwargs) -> Any:
56
"""Asynchronously parse the JSON response content."""
57
return json.loads(await self.inner.acontent(), **kwargs)
58
59
def iter_lines(self) -> AsyncGenerator[bytes, None]:
60
"""Asynchronously iterate over the lines of the response."""
61
return self.inner.aiter_lines()
62
63
def iter_content(self) -> AsyncGenerator[bytes, None]:
64
"""Asynchronously iterate over the response content."""
65
return self.inner.aiter_content()
66
67
async def sse(self) -> AsyncGenerator[dict, None]:
68
"""Asynchronously iterate over the Server-Sent Events of the response."""
69
async for line in self.iter_lines():
70
if line.startswith(b"data: "):
71
chunk = line[6:]
72
if chunk == b"[DONE]":
73
break
74
try:
75
yield json.loads(chunk)
76
except json.JSONDecodeError:
77
continue
78
79
async def __aenter__(self):
80
"""Asynchronously enter the runtime context for the response object."""
81
inner: Response = await self.inner
82
self.inner = inner
83
self.url = inner.url
84
self.method = inner.request.method
85
self.request = inner.request
86
self.status: int = inner.status_code
87
self.reason: str = inner.reason
88
self.ok: bool = inner.ok
89
self.headers = inner.headers
90
self.cookies = inner.cookies
91
return self
92
93
async def __aexit__(self, *args):
94
"""Asynchronously exit the runtime context for the response object."""
95
await self.inner.aclose()
96
97
class StreamSession(AsyncSession):
98
"""
99
An asynchronous session class for handling HTTP requests with streaming.
100
101
Inherits from AsyncSession.
102
"""
103
104
def request(
105
self, method: str, url: str, ssl = None, **kwargs
106
) -> StreamResponse:
107
if has_curl_mime and kwargs.get("data") and isinstance(kwargs.get("data"), CurlMime):
108
kwargs["multipart"] = kwargs.pop("data")
109
"""Create and return a StreamResponse object for the given HTTP request."""
110
return StreamResponse(super().request(method, url, stream=True, verify=ssl, **kwargs))
111
112
def ws_connect(self, url, *args, **kwargs):
113
return WebSocket(self, url, **kwargs)
114
115
def _ws_connect(self, url, **kwargs):
116
return super().ws_connect(url, **kwargs)
117
118
# Defining HTTP methods as partial methods of the request method.
119
head = partialmethod(request, "HEAD")
120
get = partialmethod(request, "GET")
121
post = partialmethod(request, "POST")
122
put = partialmethod(request, "PUT")
123
patch = partialmethod(request, "PATCH")
124
delete = partialmethod(request, "DELETE")
125
options = partialmethod(request, "OPTIONS")
126
114
127
else:
128
# Fallback classes when curl_cffi is not available
129
class StreamResponse:
130
def __init__(self, *args, **kwargs):
131
raise ImportError("curl_cffi is not available on this platform")
132
133
class StreamSession:
134
def __init__(self, *args, **kwargs):
135
raise ImportError("curl_cffi is not available on this platform")
136
137
if has_curl_cffi and has_curl_mime:
115
138
class FormData(CurlMime):
116
139
def add_field(self, name, data=None, content_type: str = None, filename: str = None) -> None:
117
140
self.addpart(name, content_type=content_type, filename=filename, data=data)
118
119
class WebSocket():
120
def __init__(self, session, url, **kwargs) -> None:
121
if not has_curl_ws:
122
raise RuntimeError("CurlWsFlag in curl_cffi is missing | pip install -U curl_cffi")
123
self.session: StreamSession = session
124
self.url: str = url
125
del kwargs["autoping"]
126
self.options: dict = kwargs
127
128
async def __aenter__(self):
129
self.inner = await self.session._ws_connect(self.url, **self.options)
130
return self
131
132
async def __aexit__(self, *args):
133
await self.inner.aclose() if hasattr(self.inner, "aclose") else await self.inner.close()
134
135
async def receive_str(self, **kwargs) -> str:
136
method = self.inner.arecv if hasattr(self.inner, "arecv") else self.inner.recv
137
bytes, _ = await method()
138
return bytes.decode(errors="ignore")
139
140
async def send_str(self, data: str):
141
method = self.inner.asend if hasattr(self.inner, "asend") else self.inner.send
142
await method(data.encode(), CurlWsFlag.TEXT)
141
else:
142
class FormData():
143
def __init__(self) -> None:
144
raise RuntimeError("curl_cffi FormData is not available on this platform")
145
146
if has_curl_cffi and has_curl_ws:
147
class WebSocket():
148
def __init__(self, session, url, **kwargs) -> None:
149
self.session: StreamSession = session
150
self.url: str = url
151
del kwargs["autoping"]
152
self.options: dict = kwargs
153
154
async def __aenter__(self):
155
self.inner = await self.session._ws_connect(self.url, **self.options)
156
return self
157
158
async def __aexit__(self, *args):
159
await self.inner.aclose() if hasattr(self.inner, "aclose") else await self.inner.close()
160
161
async def receive_str(self, **kwargs) -> str:
162
method = self.inner.arecv if hasattr(self.inner, "arecv") else self.inner.recv
163
bytes, _ = await method()
164
return bytes.decode(errors="ignore")
165
166
async def send_str(self, data: str):
167
method = self.inner.asend if hasattr(self.inner, "asend") else self.inner.send
168
await method(data.encode(), CurlWsFlag.TEXT)
169
else:
170
class WebSocket():
171
def __init__(self, *args, **kwargs) -> None:
172
raise RuntimeError("curl_cffi WebSocket is not available on this platform")