2727from websockets .asyncio .client import connect
2828
2929from kraken .exceptions import MaxReconnectError
30+ from kraken .utils .utils import WSState
3031
3132if TYPE_CHECKING :
3233 from collections .abc import Callable
@@ -68,6 +69,7 @@ def __init__(
6869 * ,
6970 is_auth : bool = False ,
7071 ) -> None :
72+ self .state : WSState = WSState .INIT
7173 self .__client : SpotWSClientBase = client
7274 self .__ws_endpoint : str = endpoint
7375 self .__callback : Callable = callback
@@ -104,16 +106,19 @@ async def start(self: ConnectSpotWebsocketBase) -> None:
104106 hasattr (self , "task" )
105107 and not self .task .done () # pylint: disable=access-member-before-definition
106108 ):
109+ LOG .warning ("Websocket connection already running!" )
107110 return
108111 self .task : asyncio .Task = asyncio .create_task (
109112 self .__run_forever (),
110113 )
111114
112115 async def stop (self : ConnectSpotWebsocketBase ) -> None :
113116 """Stops the websocket connection"""
117+ self .state = WSState .CANCELLING
114118 self .keep_alive = False
115119 if hasattr (self , "task" ) and not self .task .done ():
116120 await self .task
121+ self .state = WSState .CLOSED
117122
118123 async def __run (self : ConnectSpotWebsocketBase , event : asyncio .Event ) -> None :
119124 """
@@ -123,6 +128,7 @@ async def __run(self: ConnectSpotWebsocketBase, event: asyncio.Event) -> None:
123128 :param event: Event used to control the information flow
124129 :type event: asyncio.Event
125130 """
131+ self .state = WSState .CONNECTING
126132 self ._last_ping = time ()
127133 self .ws_conn_details = (
128134 None if not self .__is_auth else await self .__client .get_ws_token ()
@@ -135,9 +141,10 @@ async def __run(self: ConnectSpotWebsocketBase, event: asyncio.Event) -> None:
135141 ping_interval = 30 ,
136142 max_queue = None , # FIXME: This is not recommended by the docs https://websockets.readthedocs.io/en/stable/reference/asyncio/client.html#module-websockets.asyncio.client
137143 ) as socket :
138- LOG . info ( "Websocket connected!" )
139- self . socket = socket
144+ self . state = WSState . CONNECTED
145+ LOG . info ( "Websocket connection established!" )
140146
147+ self .socket = socket
141148 if not event .is_set ():
142149 await self .send_ping ()
143150 event .set ()
@@ -153,7 +160,6 @@ async def __run(self: ConnectSpotWebsocketBase, event: asyncio.Event) -> None:
153160 except asyncio .CancelledError :
154161 LOG .exception ("asyncio.CancelledError" )
155162 self .keep_alive = False
156- await self .__callback ({"error" : "asyncio.CancelledError" })
157163 else :
158164 try :
159165 message : dict = json .loads (_message )
@@ -172,22 +178,19 @@ async def __run_forever(self: ConnectSpotWebsocketBase) -> None:
172178 while self .keep_alive :
173179 await self .__reconnect ()
174180 except MaxReconnectError :
181+ self .state = WSState .ERROR
175182 await self .__callback (
176- {"error" : "kraken.exceptions.MaxReconnectError" },
183+ {"python-kraken-sdk" : { " error" : "kraken.exceptions.MaxReconnectError" } },
177184 )
178185 self .exception_occur = True
179- except Exception as exc :
180- traceback_ : str = traceback .format_exc ()
181- LOG .exception (
182- "%s: %s" ,
183- exc ,
184- traceback_ ,
185- )
186- await self .__callback ({"error" : traceback_ })
186+ except Exception : # pylint: disable=broad-except
187+ self .state = WSState .ERROR
188+ LOG .exception (traceback .format_exc ())
187189 self .exception_occur = True
188190
189191 async def close_connection (self : ConnectSpotWebsocketBase ) -> None :
190192 """Closes the websocket connection and thus forces a reconnect"""
193+ self .state = WSState .CANCELLING
191194 await self .socket .close ()
192195
193196 async def __reconnect (self : ConnectSpotWebsocketBase ) -> None :
@@ -198,6 +201,7 @@ async def __reconnect(self: ConnectSpotWebsocketBase) -> None:
198201 :raises KrakenException.MaxReconnectError: If there are to many
199202 reconnect retries
200203 """
204+ self .state = WSState .RECONNECTING
201205 LOG .info ("Websocket start connect/reconnect" )
202206
203207 self .__reconnect_num += 1
@@ -225,25 +229,25 @@ async def __reconnect(self: ConnectSpotWebsocketBase) -> None:
225229 tasks ,
226230 return_when = asyncio .FIRST_EXCEPTION ,
227231 )
228- exception_occur : bool = False
232+ exception_occur = False
229233 for task in finished :
230234 if task .exception ():
235+ self .state = WSState .ERRORHANDLING
231236 exception_occur = True
232- traceback .print_stack ()
233- message : str = (
234- f"{ task } got an exception { task .exception ()} \n { task .get_stack ()} "
235- )
237+ message = f"{ task } got an exception { task .exception ()} \n { task .get_stack ()} "
236238 LOG .warning (message )
237239 for process in pending :
238240 LOG .warning ("pending %s" , process )
239241 try :
240242 process .cancel ()
243+ LOG .warning ("Cancelled %s" , process )
241244 except asyncio .CancelledError :
242- LOG .exception ( "asyncio.CancelledError" )
243- await self .__callback ({"error" : message })
245+ LOG .error ( "Failed to cancel %s" , process )
246+ await self .__callback ({"python-kraken-sdk" : { " error" : message } })
244247 if exception_occur :
245248 break
246- LOG .warning ("Connection closed" )
249+ self .state = WSState .CLOSED
250+ LOG .info ("Connection closed!" )
247251
248252 def __get_reconnect_wait (
249253 self : ConnectSpotWebsocketBase ,
@@ -353,10 +357,10 @@ async def _recover_subscriptions(
353357 it is set to ``True`` - which is when the connection is ready)
354358 :type event: asyncio.Event
355359 """
356- log_msg : str = (
357- f'Recover { "authenticated" if self .is_auth else "public" } subscriptions { self ._subscriptions } '
360+ LOG .info (
361+ "%s: waiting" ,
362+ log_msg := f'Recover { "authenticated" if self .is_auth else "public" } subscriptions { self ._subscriptions } ' ,
358363 )
359- LOG .info ("%s: waiting" , log_msg )
360364 await event .wait ()
361365
362366 for subscription in self ._subscriptions :
0 commit comments