crypto-ws-api 2.0.7__tar.gz → 2.0.9__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.
@@ -1,3 +1,11 @@
1
+ ## 2.0.9 - 2024-04-14
2
+ ### Fix
3
+ * Creating asynchronous tasks done right
4
+
5
+ ## 2.0.8 - 2024-03-31
6
+ ### Fix
7
+ * tons log records: `websockets socket.send() raised exception.`
8
+
1
9
  ## 2.0.7 - 2024-03-25
2
10
  ### Update
3
11
  * Refactoring and some minor fixes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: crypto-ws-api
3
- Version: 2.0.7
3
+ Version: 2.0.9
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.8
@@ -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.0.7"
16
+ __version__ = "2.0.9"
17
17
 
18
18
  from pathlib import Path
19
19
  import shutil
@@ -21,7 +21,7 @@ from platformdirs import user_config_path
21
21
 
22
22
 
23
23
  TIMEOUT = 5 # sec timeout for WSS initialization and get response
24
- DELAY = 0.1 # sec delay in while loop
24
+ DELAY = 0.1 # sec delay in keepalive loop
25
25
  # Maximum str size for unique query ID
26
26
  ID_LEN_LIMIT = {
27
27
  "binance": 36,
@@ -66,7 +66,7 @@ class UserWSS:
66
66
  "request_limit_reached",
67
67
  "in_event",
68
68
  "_response_pool",
69
- "tasks_list",
69
+ "tasks",
70
70
  )
71
71
 
72
72
  def __init__(self, method, ws_id, exchange, endpoint, api_key, api_secret, passphrase=None):
@@ -87,10 +87,17 @@ class UserWSS:
87
87
  self.order_handling = False
88
88
  self.request_limit_reached = False
89
89
  self.in_event = asyncio.Event()
90
- self.tasks_list = []
90
+ self.tasks = set()
91
+
92
+ def tasks_manage(self, coro, name=None):
93
+ _t = asyncio.create_task(coro)
94
+ if name:
95
+ _t.set_name(name)
96
+ self.tasks.add(_t)
97
+ _t.add_done_callback(self.tasks.discard)
91
98
 
92
99
  async def _ws_listener(self):
93
- asyncio.ensure_future(self.ws_login())
100
+ self.tasks_manage(self.ws_login())
94
101
  async for msg in self._ws:
95
102
  # logger.info(f"_ws_listener: msg: {self.ws_id}: {msg}")
96
103
  if isinstance(msg, str):
@@ -104,7 +111,7 @@ class UserWSS:
104
111
  elif self.exchange in ['okx', 'bitfinex']:
105
112
  self._response_pool[res.get('id') or self.ws_id] = res.get('data') or res
106
113
  self.in_event.set()
107
- await asyncio.sleep(0)
114
+ await asyncio.sleep(0)
108
115
  else:
109
116
  logger.warning(f"UserWSS: {self.ws_id}: {msg}")
110
117
  await self.stop()
@@ -119,8 +126,8 @@ class UserWSS:
119
126
  break
120
127
  else:
121
128
  self.operational_status = False
122
- [task.cancel() for task in self.tasks_list if not task.done()]
123
- self.tasks_list.clear()
129
+ [task.cancel() for task in self.tasks if not task.done()]
130
+ self.tasks.clear()
124
131
  logger.warning(f"Restart WSS for {self.ws_id}")
125
132
  continue
126
133
  except Exception as ex:
@@ -134,16 +141,12 @@ class UserWSS:
134
141
  else:
135
142
  if self.exchange == 'binance':
136
143
  self._listen_key = res.get('listenKey')
137
- _t = asyncio.ensure_future(self.heartbeat())
138
- _t.set_name(f"heartbeat-{self.ws_id}")
139
- self.tasks_list.append(_t)
144
+ self.tasks_manage(self.heartbeat(), f"heartbeat-{self.ws_id}")
140
145
  else:
141
146
  self._listen_key = f"{int(time.time() * 1000)}{self.ws_id}"
142
147
  self.operational_status = True
143
148
  self.order_handling = True
144
- _t = asyncio.ensure_future(self._keepalive())
145
- _t.set_name(f"keepalive-{self.ws_id}")
146
- self.tasks_list.append(_t)
149
+ self.tasks_manage(self._keepalive(), f"keepalive-{self.ws_id}")
147
150
  logger.info(f"UserWSS: 'logged in' for {self.ws_id}")
148
151
 
149
152
  async def request(self, _method=None, _params=None, _api_key=False, _signed=False):
@@ -171,9 +174,10 @@ class UserWSS:
171
174
  await self._ws.send(
172
175
  json.dumps(self.compose_request(_id, _api_key, method, params, _signed))
173
176
  )
177
+ await asyncio.sleep(0)
174
178
  try:
175
179
  res = await asyncio.wait_for(self._response_distributor(_id), timeout=TIMEOUT)
176
- except asyncio.TimeoutError:
180
+ except asyncio.exceptions.TimeoutError:
177
181
  logger.warning(f"UserWSS: get response timeout error: {self.ws_id}")
178
182
  await self.stop()
179
183
  except asyncio.CancelledError:
@@ -274,8 +278,8 @@ class UserWSS:
274
278
  self.operational_status = None # Not restart and break all loops
275
279
  self.order_handling = False
276
280
  self.init = True
277
- [task.cancel() for task in self.tasks_list if not task.done()]
278
- self.tasks_list.clear()
281
+ [task.cancel() for task in self.tasks if not task.done()]
282
+ self.tasks.clear()
279
283
  if self._ws and not self._ws.closed:
280
284
  await self._ws.close(code=4000)
281
285
  gc.collect()
@@ -296,7 +300,7 @@ class UserWSS:
296
300
  elif self.exchange == 'bitfinex':
297
301
  return await self.bitfinex_error_handle(msg)
298
302
 
299
- #region BitfinexErrorHandle
303
+ # region BitfinexErrorHandle
300
304
  async def bitfinex_error_handle(self, msg):
301
305
  if isinstance(msg, dict):
302
306
  return await self._handle_dict_message(msg)
@@ -348,7 +352,7 @@ class UserWSS:
348
352
  msg[2][7]
349
353
  ]
350
354
  }
351
- #endregion
355
+ # endregion
352
356
 
353
357
  async def okx_error_handle(self, msg):
354
358
  if msg.get('code') == '1':
@@ -390,6 +394,7 @@ class UserWSSession:
390
394
  "_api_secret",
391
395
  "_passphrase",
392
396
  "user_wss",
397
+ "tasks_wss",
393
398
  )
394
399
 
395
400
  def __init__(self, exchange, endpoint, api_key, api_secret, passphrase=None):
@@ -402,6 +407,7 @@ class UserWSSession:
402
407
  self._api_secret = api_secret
403
408
  self._passphrase = passphrase
404
409
  self.user_wss = {}
410
+ self.tasks_wss = set()
405
411
 
406
412
  async def handle_request(
407
413
  self,
@@ -428,7 +434,9 @@ class UserWSSession:
428
434
  if user_wss.init:
429
435
  user_wss.init = False
430
436
  user_wss.operational_status = False
431
- asyncio.ensure_future(user_wss.start_wss())
437
+ _t = asyncio.create_task(user_wss.start_wss())
438
+ self.tasks_wss.add(_t)
439
+ _t.add_done_callback(self.tasks_wss.discard)
432
440
 
433
441
  duration = 0
434
442
  while not (user_wss.operational_status and user_wss.order_handling):
@@ -450,3 +458,5 @@ class UserWSSession:
450
458
  for ws in user_wss_copy.values():
451
459
  await ws.stop()
452
460
  self.user_wss.clear()
461
+ [task.cancel() for task in self.tasks_wss if not task.done()]
462
+ self.tasks_wss.clear()
File without changes
File without changes