unicex 0.1.14__tar.gz → 0.1.16__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.
- {unicex-0.1.14/unicex.egg-info → unicex-0.1.16}/PKG-INFO +1 -1
- {unicex-0.1.14 → unicex-0.1.16}/pyproject.toml +1 -1
- {unicex-0.1.14 → unicex-0.1.16}/unicex/_base/asyncio/client.py +38 -6
- {unicex-0.1.14 → unicex-0.1.16}/unicex/_base/asyncio/websocket.py +6 -1
- {unicex-0.1.14 → unicex-0.1.16}/unicex/_base/sync/client.py +38 -21
- {unicex-0.1.14 → unicex-0.1.16}/unicex/bitget/_mixins/client.py +1 -0
- unicex-0.1.16/unicex/exceptions.py +64 -0
- {unicex-0.1.14 → unicex-0.1.16/unicex.egg-info}/PKG-INFO +1 -1
- unicex-0.1.14/unicex/exceptions.py +0 -39
- {unicex-0.1.14 → unicex-0.1.16}/LICENSE +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/README.md +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/setup.cfg +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/__init__.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/_abc/__init__.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/_abc/adapter.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/_abc/asyncio/__init__.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/_abc/asyncio/uni_client.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/_abc/asyncio/uni_websocket_manager.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/_abc/sync/__init__.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/_abc/sync/uni_client.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/_abc/sync/uni_websocket_manager.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/_base/__init__.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/_base/asyncio/__init__.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/_base/sync/__init__.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/_base/sync/websocket.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/binance/__init__.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/binance/_mixins/__init__.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/binance/_mixins/client.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/binance/_mixins/user_websocket.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/binance/_mixins/websocket_manager.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/binance/adapter.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/binance/asyncio/__init__.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/binance/asyncio/client.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/binance/asyncio/uni_client.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/binance/asyncio/uni_websocket_manager.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/binance/asyncio/user_websocket.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/binance/asyncio/websocket_manager.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/binance/sync/__init__.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/binance/sync/client.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/binance/sync/uni_client.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/binance/sync/uni_websocket_manager.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/binance/sync/user_websocket.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/binance/sync/websocket_manager.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/binance/types.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/bitget/__init__.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/bitget/_mixins/__init__.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/bitget/_mixins/user_websocket.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/bitget/_mixins/websocket_manager.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/bitget/adapter.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/bitget/asyncio/__init__.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/bitget/asyncio/client.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/bitget/asyncio/uni_client.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/bitget/asyncio/uni_websocket_manager.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/bitget/asyncio/websocket_manager.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/bitget/sync/__init__.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/bitget/types.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/bybit/__init__.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/bybit/adapter.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/bybit/client.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/bybit/types.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/bybit/uni_client.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/bybit/websocket.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/bybit/websocket_manager.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/enums.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/extra.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/mapper.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/types.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex/utils.py +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex.egg-info/SOURCES.txt +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex.egg-info/dependency_links.txt +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex.egg-info/requires.txt +0 -0
- {unicex-0.1.14 → unicex-0.1.16}/unicex.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ name = "unicex"
|
|
|
4
4
|
# • PATCH (x.y.Z) → увеличивается при багфиксе, который не ломает совместимость.
|
|
5
5
|
# • MINOR (x.Y.z) → увеличивается при добавлении новой функциональности, но без ломающих изменений (backward-compatible).
|
|
6
6
|
# • MAJOR (X.y.z) → увеличивается при изменениях, которые ломают обратную совместимость.
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.16"
|
|
8
8
|
|
|
9
9
|
description = "Unified Crypto Exchange API "
|
|
10
10
|
readme = "README.md"
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
__all__ = ["BaseClient"]
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import json
|
|
4
5
|
from itertools import cycle
|
|
5
6
|
from typing import Any, Self
|
|
6
7
|
|
|
7
8
|
import aiohttp
|
|
8
9
|
from loguru import logger as _logger
|
|
9
10
|
|
|
11
|
+
from unicex.exceptions import ResponseError
|
|
10
12
|
from unicex.types import LoggerLike, RequestMethod
|
|
11
13
|
|
|
12
14
|
|
|
@@ -168,15 +170,45 @@ class BaseClient:
|
|
|
168
170
|
Возвращает:
|
|
169
171
|
`dict | list`: Ответ API в формате JSON.
|
|
170
172
|
"""
|
|
171
|
-
response.
|
|
172
|
-
|
|
173
|
+
response_text = await response.text()
|
|
174
|
+
status_code = response.status
|
|
173
175
|
|
|
176
|
+
# Парсинг JSON
|
|
177
|
+
try:
|
|
178
|
+
response_json = json.loads(response_text)
|
|
179
|
+
except json.JSONDecodeError as e:
|
|
180
|
+
raise ResponseError(
|
|
181
|
+
f"JSONDecodeError: {e}. Response: {response_text}. Status code: {response.status}",
|
|
182
|
+
status_code=status_code,
|
|
183
|
+
response_text=response_text,
|
|
184
|
+
) from None
|
|
185
|
+
|
|
186
|
+
# Проверка HTTP-статуса
|
|
187
|
+
try:
|
|
188
|
+
response.raise_for_status()
|
|
189
|
+
except Exception as e:
|
|
190
|
+
error_code = next(
|
|
191
|
+
(
|
|
192
|
+
response_json[k]
|
|
193
|
+
for k in ("code", "err_code", "errCode", "status")
|
|
194
|
+
if k in response_json
|
|
195
|
+
),
|
|
196
|
+
"",
|
|
197
|
+
)
|
|
198
|
+
raise ResponseError(
|
|
199
|
+
f"HTTP error: {e}. Response: {response_json}. Status code: {response.status}",
|
|
200
|
+
status_code=status_code,
|
|
201
|
+
code=error_code,
|
|
202
|
+
response_text=response_text,
|
|
203
|
+
response_json=response_json,
|
|
204
|
+
) from None
|
|
205
|
+
|
|
206
|
+
# Логирование ответа
|
|
174
207
|
try:
|
|
175
|
-
result_str: str = str(result)
|
|
176
208
|
self._logger.debug(
|
|
177
|
-
f"Response: {
|
|
209
|
+
f"Response: {response_text[:300]}{'...' if len(response_text) > 300 else ''}"
|
|
178
210
|
)
|
|
179
211
|
except Exception as e:
|
|
180
|
-
self._logger.error(f"Error while
|
|
212
|
+
self._logger.error(f"Error while logging response: {e}")
|
|
181
213
|
|
|
182
|
-
return
|
|
214
|
+
return response_json
|
|
@@ -71,7 +71,12 @@ class Websocket:
|
|
|
71
71
|
self._running = True
|
|
72
72
|
|
|
73
73
|
# Запускаем вебсокет
|
|
74
|
-
|
|
74
|
+
try:
|
|
75
|
+
await self._connect()
|
|
76
|
+
except Exception as e:
|
|
77
|
+
self._logger.error(f"Failed to connect to websocket: {e}")
|
|
78
|
+
self._running = False
|
|
79
|
+
raise
|
|
75
80
|
|
|
76
81
|
async def stop(self) -> None:
|
|
77
82
|
"""Останавливает вебсокет и рабочие задачи."""
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
__all__ = ["BaseClient"]
|
|
2
2
|
|
|
3
|
+
import json
|
|
3
4
|
import time
|
|
4
5
|
from itertools import cycle
|
|
5
6
|
from typing import Any, Self
|
|
@@ -7,7 +8,7 @@ from typing import Any, Self
|
|
|
7
8
|
import requests
|
|
8
9
|
from loguru import logger as _logger
|
|
9
10
|
|
|
10
|
-
from unicex.exceptions import
|
|
11
|
+
from unicex.exceptions import ResponseError
|
|
11
12
|
from unicex.types import LoggerLike, RequestMethod
|
|
12
13
|
|
|
13
14
|
|
|
@@ -133,37 +134,53 @@ class BaseClient:
|
|
|
133
134
|
) from errors[-1]
|
|
134
135
|
|
|
135
136
|
def _handle_response(self, response: requests.Response) -> Any:
|
|
136
|
-
"""Обрабатывает HTTP
|
|
137
|
+
"""Обрабатывает HTTP-ответ.
|
|
137
138
|
|
|
138
139
|
Параметры:
|
|
139
|
-
response (`requests.Response`): Ответ HTTP
|
|
140
|
+
response (`requests.Response`): Ответ HTTP-запроса.
|
|
140
141
|
|
|
141
142
|
Возвращает:
|
|
142
143
|
`dict | list`: Ответ API в формате JSON.
|
|
143
144
|
"""
|
|
145
|
+
response_text = response.text
|
|
146
|
+
status_code = response.status_code
|
|
147
|
+
|
|
148
|
+
# Парсинг JSON
|
|
149
|
+
try:
|
|
150
|
+
response_json = json.loads(response_text)
|
|
151
|
+
except json.JSONDecodeError as e:
|
|
152
|
+
raise ResponseError(
|
|
153
|
+
f"JSONDecodeError: {e}. Response: {response_text}. Status code: {status_code}",
|
|
154
|
+
status_code=status_code,
|
|
155
|
+
response_text=response_text,
|
|
156
|
+
) from None
|
|
157
|
+
|
|
158
|
+
# Проверка HTTP-статуса
|
|
144
159
|
try:
|
|
145
160
|
response.raise_for_status()
|
|
146
161
|
except Exception as e:
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
162
|
+
error_code = next(
|
|
163
|
+
(
|
|
164
|
+
response_json[k]
|
|
165
|
+
for k in ("code", "err_code", "errCode", "status")
|
|
166
|
+
if k in response_json
|
|
167
|
+
),
|
|
168
|
+
"",
|
|
169
|
+
)
|
|
170
|
+
raise ResponseError(
|
|
171
|
+
f"HTTP error: {e}. Response: {response_json}. Status code: {status_code}",
|
|
172
|
+
status_code=status_code,
|
|
173
|
+
code=error_code,
|
|
174
|
+
response_text=response_text,
|
|
175
|
+
response_json=response_json,
|
|
176
|
+
) from None
|
|
177
|
+
|
|
178
|
+
# Логирование ответа
|
|
161
179
|
try:
|
|
162
|
-
result_str: str = str(result)
|
|
163
180
|
self._logger.debug(
|
|
164
|
-
f"Response: {
|
|
181
|
+
f"Response: {response_text[:300]}{'...' if len(response_text) > 300 else ''}"
|
|
165
182
|
)
|
|
166
183
|
except Exception as e:
|
|
167
|
-
self._logger.error(f"Error while
|
|
184
|
+
self._logger.error(f"Error while logging response: {e}")
|
|
168
185
|
|
|
169
|
-
return
|
|
186
|
+
return response_json
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Модуль,который описывает исключения и ошибки, которые могут возникнуть при работе с библиотекой."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class UniCexException(Exception):
|
|
8
|
+
"""Базовое исключение библиотеки."""
|
|
9
|
+
|
|
10
|
+
message: str
|
|
11
|
+
"""Сообщение об ошибке."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class NotAuthorized(UniCexException):
|
|
16
|
+
"""Исключение, возникающее при отсутствии авторизации."""
|
|
17
|
+
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class NotSupported(UniCexException):
|
|
23
|
+
"""Исключение, возникающее при попытке использования не поддерживаемой функции."""
|
|
24
|
+
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class AdapterError(UniCexException):
|
|
30
|
+
"""Исключение, возникающее при ошибке адаптации данных."""
|
|
31
|
+
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class QueueOverflowError(UniCexException):
|
|
37
|
+
"""Исключение, возникающее при переполнении очереди сообщений."""
|
|
38
|
+
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class ResponseError(UniCexException):
|
|
44
|
+
"""Исключение, возникающее при ошибке ответа."""
|
|
45
|
+
|
|
46
|
+
status_code: int
|
|
47
|
+
code: str = "" # "" - means undefined
|
|
48
|
+
response_json: dict = field(default_factory=dict)
|
|
49
|
+
response_text: str = ""
|
|
50
|
+
|
|
51
|
+
def __str__(self) -> str:
|
|
52
|
+
"""Возвращает строковое представление исключения."""
|
|
53
|
+
if self.response_json:
|
|
54
|
+
preview = str(self.response_json)
|
|
55
|
+
if len(preview) > 500:
|
|
56
|
+
preview = preview[:500] + "..."
|
|
57
|
+
return f"ResponseError: status_code={self.status_code}, code={self.code}, response_json: {preview}"
|
|
58
|
+
elif self.response_text:
|
|
59
|
+
preview = str(self.response_text)
|
|
60
|
+
if len(preview) > 500:
|
|
61
|
+
preview = preview[:500] + "..."
|
|
62
|
+
return f"ResponseError: status_code={self.status_code}, code={self.code}, response_text: {preview}"
|
|
63
|
+
else:
|
|
64
|
+
return f"ResponseError: status_code={self.status_code}, code={self.code}"
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
"""Модуль,который описывает исключения и ошибки, которые могут возникнуть при работе с библиотекой."""
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
@dataclass
|
|
7
|
-
class UniCexException(Exception):
|
|
8
|
-
"""Базовое исключение библиотеки."""
|
|
9
|
-
|
|
10
|
-
message: str
|
|
11
|
-
"""Сообщение об ошибке."""
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@dataclass
|
|
15
|
-
class NotAuthorized(UniCexException):
|
|
16
|
-
"""Исключение, возникающее при отсутствии авторизации."""
|
|
17
|
-
|
|
18
|
-
pass
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@dataclass
|
|
22
|
-
class NotSupported(UniCexException):
|
|
23
|
-
"""Исключение, возникающее при попытке использования не поддерживаемой функции."""
|
|
24
|
-
|
|
25
|
-
pass
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
@dataclass
|
|
29
|
-
class AdapterError(UniCexException):
|
|
30
|
-
"""Исключение, возникающее при ошибке адаптации данных."""
|
|
31
|
-
|
|
32
|
-
pass
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@dataclass
|
|
36
|
-
class QueueOverflowError(UniCexException):
|
|
37
|
-
"""Исключение, возникающее при переполнении очереди сообщений."""
|
|
38
|
-
|
|
39
|
-
pass
|
|
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
|