crypto-ws-api 2.0.20__tar.gz → 2.1.2__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.
- {crypto_ws_api-2.0.20 → crypto_ws_api-2.1.2}/CHANGELOG.md +17 -0
- {crypto_ws_api-2.0.20 → crypto_ws_api-2.1.2}/PKG-INFO +8 -6
- {crypto_ws_api-2.0.20 → crypto_ws_api-2.1.2}/README.md +3 -2
- {crypto_ws_api-2.0.20 → crypto_ws_api-2.1.2}/crypto_ws_api/__init__.py +1 -1
- {crypto_ws_api-2.0.20 → crypto_ws_api-2.1.2}/crypto_ws_api/demo.py +2 -8
- {crypto_ws_api-2.0.20 → crypto_ws_api-2.1.2}/crypto_ws_api/ws_api.toml.template +2 -2
- {crypto_ws_api-2.0.20 → crypto_ws_api-2.1.2}/crypto_ws_api/ws_session.py +46 -45
- {crypto_ws_api-2.0.20 → crypto_ws_api-2.1.2}/pyproject.toml +2 -2
- {crypto_ws_api-2.0.20 → crypto_ws_api-2.1.2}/requirements.txt +2 -2
- {crypto_ws_api-2.0.20 → crypto_ws_api-2.1.2}/.deepsource.toml +0 -0
- {crypto_ws_api-2.0.20 → crypto_ws_api-2.1.2}/LICENSE.md +0 -0
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
## 2.1.2 - 2025-08-10
|
|
2
|
+
♻️ refactor(demo/demo_loop): Store demo_loop coroutines in variables for reference
|
|
3
|
+
|
|
4
|
+
## 2.1.1 - 2025-08-10
|
|
5
|
+
♻️ refactor(ws_session): introduce `tasks_manage` for centralized task handling
|
|
6
|
+
♻️ chore(deps): update platformdirs and cryptography to latest versions
|
|
7
|
+
|
|
8
|
+
## 2.1.0 - 2025-06-22
|
|
9
|
+
✨ refactor(ws_session.py): simplify `ws_login` and `request` methods by removing redundant `send_api_key` parameter.
|
|
10
|
+
|
|
11
|
+
## 2.0.21 - 2025-06-22
|
|
12
|
+
🔧 fix(README.md): Improve documentation layout for better readability
|
|
13
|
+
🔧 fix(ws_session.py): Handle `asyncio.CancelledError` gracefully in `start_wss` method
|
|
14
|
+
🔧 fix(ws_session.py): Replace `asyncio.CancelledError` with `KeyboardInterrupt` exception in `handle_request` method
|
|
15
|
+
🔧 fix(demo.py): Remove unnecessary `asyncio.CancelledError` handling to clean up error logging
|
|
16
|
+
🔧 fix(ws_api.toml.template): Update testnet URL for Binance websocket API
|
|
17
|
+
|
|
1
18
|
## 2.0.20 - 2025-04-24
|
|
2
19
|
* On `Binance` now API key type [Ed25519](https://www.binance.com/en/support/faq/detail/6b9a63f1e3384cf48a2eedb82767a69a) is used instead of `HMAC`
|
|
3
20
|
✨ feat(ws_session): implement `compose_binance_ws_auth` for Binance WebSocket authentication
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: crypto-ws-api
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.2
|
|
4
4
|
Summary: Crypto WS API connector for ASYNC requests
|
|
5
5
|
Author-email: Jerry Fedorenko <jerry.fedorenko@yahoo.com>
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -11,12 +11,13 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
11
11
|
Classifier: Operating System :: Unix
|
|
12
12
|
Classifier: Operating System :: Microsoft :: Windows
|
|
13
13
|
Classifier: Operating System :: MacOS
|
|
14
|
+
License-File: LICENSE.md
|
|
14
15
|
Requires-Dist: shortuuid~=1.0.13
|
|
15
|
-
Requires-Dist: platformdirs==4.3.
|
|
16
|
+
Requires-Dist: platformdirs==4.3.8
|
|
16
17
|
Requires-Dist: toml~=0.10.2
|
|
17
18
|
Requires-Dist: websockets==15.0.1
|
|
18
19
|
Requires-Dist: ujson~=5.10.0
|
|
19
|
-
Requires-Dist: cryptography~=
|
|
20
|
+
Requires-Dist: cryptography~=45.0.6
|
|
20
21
|
Project-URL: Source, https://github.com/DogsTailFarmer/crypto-ws-api
|
|
21
22
|
|
|
22
23
|
<h1 align="center"><img align="center" src="https://user-images.githubusercontent.com/77513676/250364389-cbedc171-a930-4467-a0cd-21627a6a41ed.svg" width="75">Crypto WS API connector for ASYNC requests</h1>
|
|
@@ -26,12 +27,13 @@ Project-URL: Source, https://github.com/DogsTailFarmer/crypto-ws-api
|
|
|
26
27
|
<h3 align="center">Provides of connection management, keepalive and rate limits control</h3>
|
|
27
28
|
|
|
28
29
|
***
|
|
29
|
-
<a href="https://pypi.org/project/crypto-ws-api/"><img src="https://img.shields.io/pypi/v/crypto-ws-api" alt="PyPI version"></a>
|
|
30
|
-
<a href="https://codeclimate.com/github/DogsTailFarmer/crypto-ws-api/maintainability"><img src="https://api.codeclimate.com/v1/badges/2d2a654ba393eb88d911/maintainability" /></a>
|
|
30
|
+
<h1 align="center"><a href="https://pypi.org/project/crypto-ws-api/"><img src="https://img.shields.io/pypi/v/crypto-ws-api" alt="PyPI version"></a>
|
|
31
31
|
<a href="https://app.deepsource.com/gh/DogsTailFarmer/crypto-ws-api/?ref=repository-badge}" target="_blank"><img alt="DeepSource" title="DeepSource" src="https://app.deepsource.com/gh/DogsTailFarmer/crypto-ws-api.svg/?label=resolved+issues&token=TXghPzbi0YWhkCLU8Q1tmDyQ"/></a>
|
|
32
32
|
<a href="https://app.deepsource.com/gh/DogsTailFarmer/crypto-ws-api/?ref=repository-badge}" target="_blank"><img alt="DeepSource" title="DeepSource" src="https://app.deepsource.com/gh/DogsTailFarmer/crypto-ws-api.svg/?label=active+issues&token=TXghPzbi0YWhkCLU8Q1tmDyQ"/></a>
|
|
33
33
|
<a href="https://sonarcloud.io/summary/new_code?id=DogsTailFarmer_crypto-ws-api" target="_blank"><img alt="sonarcloud" title="sonarcloud" src="https://sonarcloud.io/api/project_badges/measure?project=DogsTailFarmer_crypto-ws-api&metric=alert_status"/></a>
|
|
34
34
|
<a href="https://pepy.tech/project/crypto-ws-api" target="_blank"><img alt="Downloads" title="Downloads" src="https://static.pepy.tech/badge/crypto-ws-api"/></a>
|
|
35
|
+
</h1>
|
|
36
|
+
|
|
35
37
|
***
|
|
36
38
|
For :heavy_check_mark:[Binance](https://accounts.binance.com/en/register?ref=FXQ6HY5O), :heavy_check_mark:[OKX](https://okx.com/join/2607649), :heavy_check_mark:[Bitfinex](https://www.bitfinex.com/sign-up?refcode=v_4az2nCP), :heavy_check_mark:[HTX](https://www.htx.com/invite/en-us/1f?invite_code=9uaw3223)
|
|
37
39
|
***
|
|
@@ -5,12 +5,13 @@
|
|
|
5
5
|
<h3 align="center">Provides of connection management, keepalive and rate limits control</h3>
|
|
6
6
|
|
|
7
7
|
***
|
|
8
|
-
<a href="https://pypi.org/project/crypto-ws-api/"><img src="https://img.shields.io/pypi/v/crypto-ws-api" alt="PyPI version"></a>
|
|
9
|
-
<a href="https://codeclimate.com/github/DogsTailFarmer/crypto-ws-api/maintainability"><img src="https://api.codeclimate.com/v1/badges/2d2a654ba393eb88d911/maintainability" /></a>
|
|
8
|
+
<h1 align="center"><a href="https://pypi.org/project/crypto-ws-api/"><img src="https://img.shields.io/pypi/v/crypto-ws-api" alt="PyPI version"></a>
|
|
10
9
|
<a href="https://app.deepsource.com/gh/DogsTailFarmer/crypto-ws-api/?ref=repository-badge}" target="_blank"><img alt="DeepSource" title="DeepSource" src="https://app.deepsource.com/gh/DogsTailFarmer/crypto-ws-api.svg/?label=resolved+issues&token=TXghPzbi0YWhkCLU8Q1tmDyQ"/></a>
|
|
11
10
|
<a href="https://app.deepsource.com/gh/DogsTailFarmer/crypto-ws-api/?ref=repository-badge}" target="_blank"><img alt="DeepSource" title="DeepSource" src="https://app.deepsource.com/gh/DogsTailFarmer/crypto-ws-api.svg/?label=active+issues&token=TXghPzbi0YWhkCLU8Q1tmDyQ"/></a>
|
|
12
11
|
<a href="https://sonarcloud.io/summary/new_code?id=DogsTailFarmer_crypto-ws-api" target="_blank"><img alt="sonarcloud" title="sonarcloud" src="https://sonarcloud.io/api/project_badges/measure?project=DogsTailFarmer_crypto-ws-api&metric=alert_status"/></a>
|
|
13
12
|
<a href="https://pepy.tech/project/crypto-ws-api" target="_blank"><img alt="Downloads" title="Downloads" src="https://static.pepy.tech/badge/crypto-ws-api"/></a>
|
|
13
|
+
</h1>
|
|
14
|
+
|
|
14
15
|
***
|
|
15
16
|
For :heavy_check_mark:[Binance](https://accounts.binance.com/en/register?ref=FXQ6HY5O), :heavy_check_mark:[OKX](https://okx.com/join/2607649), :heavy_check_mark:[Bitfinex](https://www.bitfinex.com/sign-up?refcode=v_4az2nCP), :heavy_check_mark:[HTX](https://www.htx.com/invite/en-us/1f?invite_code=9uaw3223)
|
|
16
17
|
***
|
|
@@ -13,7 +13,7 @@ __maintainer__ = "Jerry Fedorenko"
|
|
|
13
13
|
__contact__ = "https://github.com/DogsTailFarmer"
|
|
14
14
|
__email__ = "jerry.fedorenko@yahoo.com"
|
|
15
15
|
__credits__ = ["https://github.com/DanyaSWorlD"]
|
|
16
|
-
__version__ = "2.
|
|
16
|
+
__version__ = "2.1.2"
|
|
17
17
|
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
import shutil
|
|
@@ -43,8 +43,8 @@ async def main(account_name):
|
|
|
43
43
|
|
|
44
44
|
# Demo method's calling
|
|
45
45
|
await account_information(user_session, trade_id)
|
|
46
|
-
asyncio.ensure_future(demo_loop(user_session, get_time, trade_id, 2))
|
|
47
|
-
asyncio.ensure_future(demo_loop(user_session, current_average_price, trade_id, 3))
|
|
46
|
+
_t1 = asyncio.ensure_future(demo_loop(user_session, get_time, trade_id, 2))
|
|
47
|
+
_t2 = asyncio.ensure_future(demo_loop(user_session, current_average_price, trade_id, 3))
|
|
48
48
|
|
|
49
49
|
await asyncio.sleep(20)
|
|
50
50
|
|
|
@@ -66,8 +66,6 @@ async def get_time(user_session: UserWSSession, _trade_id):
|
|
|
66
66
|
)
|
|
67
67
|
if res is None:
|
|
68
68
|
logger.warning(CONST_1)
|
|
69
|
-
except asyncio.CancelledError:
|
|
70
|
-
pass # Task cancellation should not be logged as an error
|
|
71
69
|
except Exception as _ex:
|
|
72
70
|
logger.error(CONST_2, _ex)
|
|
73
71
|
else:
|
|
@@ -87,8 +85,6 @@ async def current_average_price(user_session: UserWSSession, _trade_id):
|
|
|
87
85
|
)
|
|
88
86
|
if res is None:
|
|
89
87
|
logger.warning(CONST_1)
|
|
90
|
-
except asyncio.CancelledError:
|
|
91
|
-
pass # Task cancellation should not be logged as an error
|
|
92
88
|
except Exception as _ex:
|
|
93
89
|
logger.error(CONST_2, _ex)
|
|
94
90
|
else:
|
|
@@ -105,8 +101,6 @@ async def account_information(user_session: UserWSSession, _trade_id):
|
|
|
105
101
|
)
|
|
106
102
|
if res is None:
|
|
107
103
|
logger.warning(CONST_1)
|
|
108
|
-
except asyncio.CancelledError:
|
|
109
|
-
pass # Task cancellation should not be logged as an error
|
|
110
104
|
except Exception as _ex:
|
|
111
105
|
logger.error(CONST_2, _ex)
|
|
112
106
|
else:
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Parameters for crypto-ws-api connector
|
|
2
2
|
# Copyright © 2023-2025 Jerry Fedorenko aka VM
|
|
3
|
-
# __version__ = "2.0.
|
|
3
|
+
# __version__ = "2.0.21"
|
|
4
4
|
|
|
5
5
|
# region endpoint
|
|
6
6
|
[endpoint]
|
|
7
7
|
[endpoint.binance]
|
|
8
8
|
ws_api = 'wss://ws-api.binance.com:443/ws-api/v3'
|
|
9
|
-
ws_api_test = 'wss://testnet.binance.vision/ws-api/v3'
|
|
9
|
+
ws_api_test = 'wss://ws-api.testnet.binance.vision:9443/ws-api/v3'
|
|
10
10
|
# endregion
|
|
11
11
|
|
|
12
12
|
# region Binance accounts
|
|
@@ -16,6 +16,7 @@ import random
|
|
|
16
16
|
from datetime import datetime, timezone
|
|
17
17
|
from urllib.parse import urlencode, urlparse
|
|
18
18
|
from cryptography.hazmat.primitives.serialization import load_pem_private_key
|
|
19
|
+
import inspect
|
|
19
20
|
|
|
20
21
|
from websockets.asyncio.client import connect
|
|
21
22
|
from websockets import ConnectionClosed
|
|
@@ -102,6 +103,13 @@ def compose_htx_ws_auth(endpoint, exchange, api_key, api_secret):
|
|
|
102
103
|
"params": _params
|
|
103
104
|
}
|
|
104
105
|
|
|
106
|
+
def tasks_manage(tasks_set: set, coro, name=None, add_done_callback=True):
|
|
107
|
+
_t = asyncio.create_task(coro, name=name or inspect.stack()[1][3])
|
|
108
|
+
tasks_set.add(_t)
|
|
109
|
+
if add_done_callback:
|
|
110
|
+
_t.add_done_callback(tasks_set.discard)
|
|
111
|
+
|
|
112
|
+
|
|
105
113
|
# https://binance-docs.github.io/apidocs/websocket_api/en/#rate-limits
|
|
106
114
|
class RateLimitInterval(Enum):
|
|
107
115
|
SECOND = 1
|
|
@@ -159,13 +167,8 @@ class UserWSS:
|
|
|
159
167
|
else:
|
|
160
168
|
self.logger = logger
|
|
161
169
|
|
|
162
|
-
def
|
|
163
|
-
|
|
164
|
-
self.tasks.add(_t)
|
|
165
|
-
_t.add_done_callback(self.tasks.discard)
|
|
166
|
-
|
|
167
|
-
async def _ws_listener(self):
|
|
168
|
-
self.tasks_manage(self.ws_login())
|
|
170
|
+
async def _ws_listener(self): #NOSONAR
|
|
171
|
+
tasks_manage(self.tasks, self.ws_login(), f"ws_login-{self.ws_id}")
|
|
169
172
|
async for msg in self._ws:
|
|
170
173
|
# logger.info(f"_ws_listener: ws_id: {self.ws_id} msg: {msg}")
|
|
171
174
|
if isinstance(msg, str):
|
|
@@ -191,45 +194,47 @@ class UserWSS:
|
|
|
191
194
|
else:
|
|
192
195
|
logger.warning(f"UserWSS: {self.ws_id}: {msg}")
|
|
193
196
|
await self.stop()
|
|
194
|
-
logger.warning(f"UserWSS: {self.ws_id}: _ws_listener stopped")
|
|
195
|
-
await self.stop()
|
|
196
197
|
|
|
197
198
|
async def start_wss(self):
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
199
|
+
try:
|
|
200
|
+
async for self._ws in connect(
|
|
201
|
+
self.endpoint,
|
|
202
|
+
logger=self.logger,
|
|
203
|
+
ping_interval=None if self.exchange in ('binance', 'huobi') else 20
|
|
204
|
+
):
|
|
205
|
+
try:
|
|
206
|
+
await self._ws_listener()
|
|
207
|
+
except ConnectionClosed as ex:
|
|
208
|
+
if ex.rcvd and ex.rcvd.code == 4000:
|
|
209
|
+
logger.info(f"WSS closed for {self.ws_id}")
|
|
210
|
+
break
|
|
211
|
+
else:
|
|
212
|
+
self.operational_status = False
|
|
213
|
+
[task.cancel() for task in self.tasks if not task.done()]
|
|
214
|
+
self.tasks.clear()
|
|
215
|
+
logger.warning(f"Restart UserWSS for {self.ws_id}")
|
|
216
|
+
continue
|
|
217
|
+
except Exception as ex:
|
|
218
|
+
logger.error(f"UserWSS start other exception: {ex}")
|
|
219
|
+
except asyncio.CancelledError:
|
|
220
|
+
await self.stop()
|
|
221
|
+
raise
|
|
217
222
|
|
|
218
223
|
async def ws_login(self):
|
|
219
|
-
res = await self.request(CONST_WS_START
|
|
224
|
+
res = await self.request(CONST_WS_START)
|
|
220
225
|
if res is None:
|
|
221
226
|
logger.warning(f"UserWSS: Not 'logged in' for {self.ws_id}")
|
|
222
227
|
await self.stop()
|
|
223
228
|
else:
|
|
224
229
|
if self.exchange == 'huobi':
|
|
225
|
-
|
|
230
|
+
tasks_manage(self.tasks, self.htx_keepalive(), f"htx_keepalive-{self.ws_id}")
|
|
226
231
|
|
|
227
232
|
self.operational_status = True
|
|
228
233
|
self.order_handling = True
|
|
229
|
-
|
|
234
|
+
tasks_manage(self.tasks, self._keepalive(), f"keepalive-{self.ws_id}")
|
|
230
235
|
logger.info(f"UserWSS: 'logged in' for {self.ws_id}")
|
|
231
236
|
|
|
232
|
-
async def request(self, method, _params=None,
|
|
237
|
+
async def request(self, method, _params=None, _signed=False):
|
|
233
238
|
"""
|
|
234
239
|
Construct and handling request/response to WS API endpoint, use a description of the methods on
|
|
235
240
|
https://developers.binance.com/docs/binance-spot-api-docs/websocket-api
|
|
@@ -245,13 +250,13 @@ class UserWSS:
|
|
|
245
250
|
logger.warning("UserWSS: exceeded order placement limit, try later")
|
|
246
251
|
return None
|
|
247
252
|
params = _params.copy() if _params else None
|
|
248
|
-
r_id = f"{self.exchange}{method}{''.join(random.choices(ALPHABET, k=8))}"
|
|
253
|
+
r_id = f"{self.exchange}{method}{''.join(random.choices(ALPHABET, k=8))}" #NOSONAR
|
|
249
254
|
if self.exchange in ("okx", "bitfinex", "huobi") and method == CONST_WS_START:
|
|
250
255
|
_id = self.ws_id
|
|
251
256
|
else:
|
|
252
257
|
_id = ''.join(e for e in r_id if e.isalnum())[-ID_LEN_LIMIT.get(self.exchange, 64):]
|
|
253
258
|
await self._ws.send(
|
|
254
|
-
json.dumps(self.compose_request(_id,
|
|
259
|
+
json.dumps(self.compose_request(_id, method, params, _signed))
|
|
255
260
|
)
|
|
256
261
|
await asyncio.sleep(0)
|
|
257
262
|
try:
|
|
@@ -260,8 +265,6 @@ class UserWSS:
|
|
|
260
265
|
logger.warning(f"UserWSS: get response timeout error: {self.ws_id}")
|
|
261
266
|
await self.stop()
|
|
262
267
|
return None
|
|
263
|
-
except asyncio.CancelledError:
|
|
264
|
-
return None
|
|
265
268
|
else:
|
|
266
269
|
return res
|
|
267
270
|
|
|
@@ -275,7 +278,7 @@ class UserWSS:
|
|
|
275
278
|
return self._response_pool.pop(f"NoneResponse{self.ws_id}", None)
|
|
276
279
|
return None
|
|
277
280
|
|
|
278
|
-
def compose_request(self, _id,
|
|
281
|
+
def compose_request(self, _id, method, params, signed):
|
|
279
282
|
if self.exchange == "binance":
|
|
280
283
|
return self._compose_binance_request(_id, method, params, signed)
|
|
281
284
|
elif self.exchange == "okx":
|
|
@@ -400,9 +403,10 @@ class UserWSS:
|
|
|
400
403
|
if msg.get('code') == 200 or msg.get('status') == 'ok':
|
|
401
404
|
return msg
|
|
402
405
|
|
|
403
|
-
|
|
406
|
+
self.htx_error_handle(msg)
|
|
407
|
+
return None
|
|
404
408
|
|
|
405
|
-
|
|
409
|
+
def htx_error_handle(self, msg):
|
|
406
410
|
if msg.get('code') == 500:
|
|
407
411
|
logger.warning(f"An issue occurred on exchange's side: {msg}")
|
|
408
412
|
elif msg.get('code') in {429, 4000}:
|
|
@@ -526,7 +530,6 @@ class UserWSSession:
|
|
|
526
530
|
trade_id: str,
|
|
527
531
|
method: str,
|
|
528
532
|
_params=None,
|
|
529
|
-
send_api_key=False,
|
|
530
533
|
_signed=False,
|
|
531
534
|
):
|
|
532
535
|
|
|
@@ -553,9 +556,7 @@ class UserWSSession:
|
|
|
553
556
|
if user_wss.init:
|
|
554
557
|
user_wss.init = False
|
|
555
558
|
user_wss.operational_status = False
|
|
556
|
-
|
|
557
|
-
self.tasks_wss.add(_t)
|
|
558
|
-
_t.add_done_callback(self.tasks_wss.discard)
|
|
559
|
+
tasks_manage(self.tasks_wss, user_wss.start_wss(), f"start_wss-{ws_id}")
|
|
559
560
|
|
|
560
561
|
duration = 0
|
|
561
562
|
while not (user_wss.operational_status and user_wss.order_handling):
|
|
@@ -566,8 +567,8 @@ class UserWSSession:
|
|
|
566
567
|
duration += DELAY
|
|
567
568
|
|
|
568
569
|
try:
|
|
569
|
-
return await user_wss.request(method=method, _params=_params,
|
|
570
|
-
except
|
|
570
|
+
return await user_wss.request(method=method, _params=_params, _signed=_signed)
|
|
571
|
+
except KeyboardInterrupt:
|
|
571
572
|
pass # Task cancellation should not be logged as an error
|
|
572
573
|
except Exception as ex:
|
|
573
574
|
logger.error(f"crypto_ws_api.ws_session.handle_request(): {ex}")
|
|
@@ -18,11 +18,11 @@ requires-python = ">=3.10"
|
|
|
18
18
|
|
|
19
19
|
dependencies = [
|
|
20
20
|
"shortuuid~=1.0.13",
|
|
21
|
-
"platformdirs==4.3.
|
|
21
|
+
"platformdirs==4.3.8",
|
|
22
22
|
"toml~=0.10.2",
|
|
23
23
|
"websockets==15.0.1",
|
|
24
24
|
"ujson~=5.10.0",
|
|
25
|
-
"cryptography~=
|
|
25
|
+
"cryptography~=45.0.6"
|
|
26
26
|
]
|
|
27
27
|
|
|
28
28
|
[tool.flit.module]
|
|
File without changes
|
|
File without changes
|