ErisPulse 2.4.6.dev6__tar.gz → 2.4.8__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.
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/PKG-INFO +1 -1
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/pyproject.toml +1 -1
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/client.py +95 -44
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/client.pyi +1 -1
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/.gitignore +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/LICENSE +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/README.md +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/__init__.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/__init__.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/base.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/base.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/cli.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/cli.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/commands/__init__.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/commands/__init__.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/commands/create.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/commands/create.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/commands/init.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/commands/init.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/commands/install.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/commands/install.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/commands/list.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/commands/list.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/commands/list_remote.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/commands/list_remote.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/commands/run.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/commands/run.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/commands/self_update.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/commands/self_update.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/commands/uninstall.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/commands/uninstall.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/commands/upgrade.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/commands/upgrade.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/console.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/console.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/registry.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/registry.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/utils/__init__.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/utils/__init__.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/utils/display.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/utils/display.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/utils/package_manager.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/CLI/utils/package_manager.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Bases/__init__.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Bases/__init__.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Bases/adapter.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Bases/adapter.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Bases/client.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Bases/client.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Bases/errors.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Bases/errors.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Bases/manager.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Bases/manager.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Bases/module.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Bases/module.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Bases/router.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Bases/router.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Bases/storage.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Bases/storage.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Bases/websocket.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Bases/websocket.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Event/__init__.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Event/__init__.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Event/base.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Event/base.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Event/command.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Event/command.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Event/message.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Event/message.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Event/message_builder.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Event/message_builder.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Event/meta.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Event/meta.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Event/notice.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Event/notice.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Event/request.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Event/request.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Event/session_type.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Event/session_type.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Event/wrapper.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/Event/wrapper.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/__init__.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/__init__.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/adapter.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/adapter.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/config.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/config.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/constants.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/constants.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/lifecycle.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/lifecycle.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/logger.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/logger.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/module.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/module.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/router.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/router.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/storage.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/Core/storage.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/__init__.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/__init__.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/__main__.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/__main__.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/finders/__init__.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/finders/__init__.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/finders/adapter.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/finders/adapter.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/finders/bases/__init__.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/finders/bases/__init__.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/finders/bases/finder.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/finders/bases/finder.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/finders/module.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/finders/module.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/loaders/__init__.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/loaders/__init__.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/loaders/adapter.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/loaders/adapter.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/loaders/bases/__init__.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/loaders/bases/__init__.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/loaders/bases/loader.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/loaders/bases/loader.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/loaders/module.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/loaders/module.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/loaders/strategy.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/loaders/strategy.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/runtime/__init__.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/runtime/__init__.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/runtime/config_schema.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/runtime/config_schema.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/runtime/context.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/runtime/context.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/runtime/exceptions.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/runtime/exceptions.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/runtime/frame_config.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/runtime/frame_config.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/sdk.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/sdk.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/web_status/4xx.png +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/web_status/5xx.png +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/web_status/__init__.py +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/web_status/__init__.pyi +0 -0
- {erispulse-2.4.6.dev6 → erispulse-2.4.8}/src/ErisPulse/web_status/unknow.png +0 -0
|
@@ -73,7 +73,7 @@ class HttpResponse(BaseHttpResponse):
|
|
|
73
73
|
>>> data = await resp.json()
|
|
74
74
|
"""
|
|
75
75
|
|
|
76
|
-
__slots__ = ("_response", "_body", "_body_read")
|
|
76
|
+
__slots__ = ("_response", "_body", "_body_read", "_released")
|
|
77
77
|
|
|
78
78
|
def __init__(self, response):
|
|
79
79
|
"""
|
|
@@ -82,6 +82,7 @@ class HttpResponse(BaseHttpResponse):
|
|
|
82
82
|
self._response = response
|
|
83
83
|
self._body: bytes | None = None
|
|
84
84
|
self._body_read = False
|
|
85
|
+
self._released = False
|
|
85
86
|
|
|
86
87
|
@property
|
|
87
88
|
def status(self) -> int:
|
|
@@ -179,7 +180,9 @@ class HttpResponse(BaseHttpResponse):
|
|
|
179
180
|
return self
|
|
180
181
|
|
|
181
182
|
async def __aexit__(self, *args):
|
|
182
|
-
self.
|
|
183
|
+
if not self._released:
|
|
184
|
+
self._response.release()
|
|
185
|
+
self._released = True
|
|
183
186
|
|
|
184
187
|
|
|
185
188
|
class ClientWebSocket(BaseClientWebSocket):
|
|
@@ -201,13 +204,14 @@ class ClientWebSocket(BaseClientWebSocket):
|
|
|
201
204
|
... await ws.send_text(f"Echo: {text}")
|
|
202
205
|
"""
|
|
203
206
|
|
|
204
|
-
__slots__ = ()
|
|
207
|
+
__slots__ = ("_recv_lock",)
|
|
205
208
|
|
|
206
209
|
def __init__(self, ws):
|
|
207
210
|
"""
|
|
208
211
|
:param ws: aiohttp.ClientWebSocketResponse 底层 aiohttp WS 对象
|
|
209
212
|
"""
|
|
210
213
|
super().__init__(ws)
|
|
214
|
+
self._recv_lock = asyncio.Lock()
|
|
211
215
|
|
|
212
216
|
@property
|
|
213
217
|
def closed(self) -> bool:
|
|
@@ -226,6 +230,8 @@ class ClientWebSocket(BaseClientWebSocket):
|
|
|
226
230
|
|
|
227
231
|
:param data: str 文本内容
|
|
228
232
|
"""
|
|
233
|
+
if self._ws.closed:
|
|
234
|
+
raise WebSocketError("WebSocket is closed")
|
|
229
235
|
await self._ws.send_str(data)
|
|
230
236
|
|
|
231
237
|
async def send_bytes(self, data: bytes) -> None:
|
|
@@ -234,6 +240,8 @@ class ClientWebSocket(BaseClientWebSocket):
|
|
|
234
240
|
|
|
235
241
|
:param data: bytes 二进制内容
|
|
236
242
|
"""
|
|
243
|
+
if self._ws.closed:
|
|
244
|
+
raise WebSocketError("WebSocket is closed")
|
|
237
245
|
await self._ws.send_bytes(data)
|
|
238
246
|
|
|
239
247
|
async def send_json(self, data: Any, mode: str = "text") -> None:
|
|
@@ -243,7 +251,13 @@ class ClientWebSocket(BaseClientWebSocket):
|
|
|
243
251
|
:param data: Any 要序列化的数据
|
|
244
252
|
:param mode: str 发送模式 ("text" 或 "binary") (默认: "text")
|
|
245
253
|
"""
|
|
246
|
-
|
|
254
|
+
if self._ws.closed:
|
|
255
|
+
raise WebSocketError("WebSocket is closed")
|
|
256
|
+
if mode == "binary":
|
|
257
|
+
payload = json.dumps(data).encode("utf-8")
|
|
258
|
+
await self._ws.send_bytes(payload)
|
|
259
|
+
else:
|
|
260
|
+
await self._ws.send_json(data)
|
|
247
261
|
|
|
248
262
|
# ---- Receive ----
|
|
249
263
|
|
|
@@ -277,7 +291,8 @@ class ClientWebSocket(BaseClientWebSocket):
|
|
|
277
291
|
|
|
278
292
|
:return: WSMessage 消息对象
|
|
279
293
|
"""
|
|
280
|
-
|
|
294
|
+
async with self._recv_lock:
|
|
295
|
+
msg = await self._ws.receive()
|
|
281
296
|
return self._convert_ws_msg(msg)
|
|
282
297
|
|
|
283
298
|
async def receive_text(self) -> str:
|
|
@@ -290,7 +305,8 @@ class ClientWebSocket(BaseClientWebSocket):
|
|
|
290
305
|
"""
|
|
291
306
|
import aiohttp
|
|
292
307
|
|
|
293
|
-
|
|
308
|
+
async with self._recv_lock:
|
|
309
|
+
msg = await self._ws.receive()
|
|
294
310
|
if msg.type == aiohttp.WSMsgType.TEXT:
|
|
295
311
|
return msg.data
|
|
296
312
|
elif msg.type in (
|
|
@@ -315,7 +331,8 @@ class ClientWebSocket(BaseClientWebSocket):
|
|
|
315
331
|
"""
|
|
316
332
|
import aiohttp
|
|
317
333
|
|
|
318
|
-
|
|
334
|
+
async with self._recv_lock:
|
|
335
|
+
msg = await self._ws.receive()
|
|
319
336
|
if msg.type == aiohttp.WSMsgType.BINARY:
|
|
320
337
|
return msg.data
|
|
321
338
|
elif msg.type in (
|
|
@@ -421,6 +438,7 @@ class HttpClient(BaseHttpClient):
|
|
|
421
438
|
)
|
|
422
439
|
self._session = None
|
|
423
440
|
self._ws_session = None
|
|
441
|
+
self._session_lock = asyncio.Lock()
|
|
424
442
|
self._stats = {
|
|
425
443
|
"total_requests": 0,
|
|
426
444
|
"total_errors": 0,
|
|
@@ -431,39 +449,58 @@ class HttpClient(BaseHttpClient):
|
|
|
431
449
|
# ---- Session 管理 ----
|
|
432
450
|
|
|
433
451
|
async def _get_ws_session(self):
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
452
|
+
async with self._session_lock:
|
|
453
|
+
if self._ws_session is None or self._ws_session.closed:
|
|
454
|
+
import aiohttp
|
|
455
|
+
|
|
456
|
+
self._ws_session = aiohttp.ClientSession(
|
|
457
|
+
timeout=aiohttp.ClientTimeout(),
|
|
458
|
+
headers=self._default_headers or None,
|
|
459
|
+
)
|
|
460
|
+
return self._ws_session
|
|
461
|
+
|
|
462
|
+
async def _drain_sessions(self):
|
|
463
|
+
async with self._session_lock:
|
|
464
|
+
old_session = self._session
|
|
465
|
+
old_ws = self._ws_session
|
|
466
|
+
self._session = None
|
|
467
|
+
self._ws_session = None
|
|
468
|
+
if old_session and not old_session.closed:
|
|
469
|
+
try:
|
|
470
|
+
await old_session.close()
|
|
471
|
+
except Exception:
|
|
472
|
+
pass
|
|
473
|
+
if old_ws and not old_ws.closed:
|
|
474
|
+
try:
|
|
475
|
+
await old_ws.close()
|
|
476
|
+
except Exception:
|
|
477
|
+
pass
|
|
445
478
|
|
|
446
479
|
async def _get_http_session(self):
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
480
|
+
async with self._session_lock:
|
|
481
|
+
if self._session is None or self._session.closed:
|
|
482
|
+
import aiohttp
|
|
483
|
+
|
|
484
|
+
timeout = aiohttp.ClientTimeout(
|
|
485
|
+
total=self._timeout,
|
|
486
|
+
connect=self._connect_timeout,
|
|
487
|
+
)
|
|
488
|
+
self._session = aiohttp.ClientSession(
|
|
489
|
+
timeout=timeout,
|
|
490
|
+
headers=self._default_headers,
|
|
491
|
+
)
|
|
492
|
+
return self._session
|
|
459
493
|
|
|
460
494
|
async def close(self) -> None:
|
|
461
|
-
|
|
462
|
-
|
|
495
|
+
async with self._session_lock:
|
|
496
|
+
old_session = self._session
|
|
497
|
+
old_ws = self._ws_session
|
|
463
498
|
self._session = None
|
|
464
|
-
if self._ws_session and not self._ws_session.closed:
|
|
465
|
-
await self._ws_session.close()
|
|
466
499
|
self._ws_session = None
|
|
500
|
+
if old_session and not old_session.closed:
|
|
501
|
+
await old_session.close()
|
|
502
|
+
if old_ws and not old_ws.closed:
|
|
503
|
+
await old_ws.close()
|
|
467
504
|
|
|
468
505
|
# ---- 核心请求方法 ----
|
|
469
506
|
|
|
@@ -529,6 +566,8 @@ class HttpClient(BaseHttpClient):
|
|
|
529
566
|
**kwargs,
|
|
530
567
|
) as resp:
|
|
531
568
|
response = HttpResponse(resp)
|
|
569
|
+
await response._eager_read()
|
|
570
|
+
response._released = True
|
|
532
571
|
elapsed = time.monotonic() - start
|
|
533
572
|
|
|
534
573
|
self._stats["total_requests"] += 1
|
|
@@ -544,38 +583,50 @@ class HttpClient(BaseHttpClient):
|
|
|
544
583
|
|
|
545
584
|
return response
|
|
546
585
|
|
|
547
|
-
except
|
|
548
|
-
last_exc = e
|
|
586
|
+
except asyncio.TimeoutError as e:
|
|
587
|
+
last_exc = _convert_aiohttp_exception(e)
|
|
588
|
+
self._stats["total_errors"] += 1
|
|
589
|
+
elapsed = time.monotonic() - start
|
|
590
|
+
if attempt < retries:
|
|
591
|
+
logger.debug(
|
|
592
|
+
f"[HttpClient] {method} {url} 超时 (尝试 {attempt + 1}/{retries + 1})"
|
|
593
|
+
)
|
|
594
|
+
await asyncio.sleep(self._retry_delay)
|
|
595
|
+
else:
|
|
596
|
+
logger.error(
|
|
597
|
+
f"[HttpClient] {method} {url} 最终超时 ({elapsed:.3f}s)"
|
|
598
|
+
)
|
|
599
|
+
except aiohttp.ClientConnectionError as e:
|
|
600
|
+
last_exc = _convert_aiohttp_exception(e)
|
|
549
601
|
self._stats["total_errors"] += 1
|
|
550
602
|
elapsed = time.monotonic() - start
|
|
551
603
|
if attempt < retries:
|
|
552
604
|
logger.debug(
|
|
553
605
|
f"[HttpClient] {method} {url} 连接中断,重建 session 后重试 (尝试 {attempt + 1}/{retries + 1})"
|
|
554
606
|
)
|
|
555
|
-
self._drain_sessions()
|
|
607
|
+
await self._drain_sessions()
|
|
556
608
|
await asyncio.sleep(self._retry_delay)
|
|
557
609
|
else:
|
|
558
610
|
logger.error(
|
|
559
611
|
f"[HttpClient] {method} {url} 最终连接失败 ({elapsed:.3f}s)"
|
|
560
612
|
)
|
|
561
|
-
except
|
|
613
|
+
except aiohttp.ClientError as e:
|
|
562
614
|
last_exc = _convert_aiohttp_exception(e)
|
|
563
615
|
self._stats["total_errors"] += 1
|
|
564
616
|
elapsed = time.monotonic() - start
|
|
565
617
|
if attempt < retries:
|
|
566
618
|
logger.debug(
|
|
567
|
-
f"[HttpClient] {method} {url}
|
|
619
|
+
f"[HttpClient] {method} {url} 失败 (尝试 {attempt + 1}/{retries + 1}): {e}"
|
|
568
620
|
)
|
|
569
621
|
await asyncio.sleep(self._retry_delay)
|
|
570
622
|
else:
|
|
571
623
|
logger.error(
|
|
572
|
-
f"[HttpClient] {method} {url}
|
|
624
|
+
f"[HttpClient] {method} {url} 最终失败 ({elapsed:.3f}s): {e}"
|
|
573
625
|
)
|
|
626
|
+
except ClientError:
|
|
627
|
+
raise
|
|
574
628
|
except Exception as e:
|
|
575
|
-
|
|
576
|
-
last_exc = _convert_aiohttp_exception(e)
|
|
577
|
-
else:
|
|
578
|
-
last_exc = ClientError(str(e))
|
|
629
|
+
last_exc = ClientError(str(e))
|
|
579
630
|
self._stats["total_errors"] += 1
|
|
580
631
|
elapsed = time.monotonic() - start
|
|
581
632
|
if attempt < retries:
|
|
@@ -253,7 +253,7 @@ class HttpClient(BaseHttpClient):
|
|
|
253
253
|
...
|
|
254
254
|
async def _get_ws_session(self: object) -> ...:
|
|
255
255
|
...
|
|
256
|
-
def _drain_sessions(self: object) -> ...:
|
|
256
|
+
async def _drain_sessions(self: object) -> ...:
|
|
257
257
|
...
|
|
258
258
|
async def _get_http_session(self: object) -> ...:
|
|
259
259
|
...
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|