diff options
author | morpheus65535 <[email protected]> | 2024-03-03 12:15:23 -0500 |
---|---|---|
committer | GitHub <[email protected]> | 2024-03-03 12:15:23 -0500 |
commit | 03afeb347075381bcb7fd6036295c9fa4a90d2dc (patch) | |
tree | 7c5d72c973d2c8e4ade57391a1c9ad5e94903a46 /libs/socketio/async_simple_client.py | |
parent | 9ae684240b5bdd40a870d8122f0e380f8d03a187 (diff) | |
download | bazarr-03afeb347075381bcb7fd6036295c9fa4a90d2dc.tar.gz bazarr-03afeb347075381bcb7fd6036295c9fa4a90d2dc.zip |
Updated multiple Python modules (now in libs and custom_libs directories) and React libraries
Diffstat (limited to 'libs/socketio/async_simple_client.py')
-rw-r--r-- | libs/socketio/async_simple_client.py | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/libs/socketio/async_simple_client.py b/libs/socketio/async_simple_client.py new file mode 100644 index 000000000..c6cd4fc11 --- /dev/null +++ b/libs/socketio/async_simple_client.py @@ -0,0 +1,209 @@ +import asyncio +from socketio import AsyncClient +from socketio.exceptions import SocketIOError, TimeoutError, DisconnectedError + + +class AsyncSimpleClient: + """A Socket.IO client. + + This class implements a simple, yet fully compliant Socket.IO web client + with support for websocket and long-polling transports. + + The positional and keyword arguments given in the constructor are passed + to the underlying :func:`socketio.AsyncClient` object. + """ + def __init__(self, *args, **kwargs): + self.client_args = args + self.client_kwargs = kwargs + self.client = None + self.namespace = '/' + self.connected_event = asyncio.Event() + self.connected = False + self.input_event = asyncio.Event() + self.input_buffer = [] + + async def connect(self, url, headers={}, auth=None, transports=None, + namespace='/', socketio_path='socket.io', + wait_timeout=5): + """Connect to a Socket.IO server. + + :param url: The URL of the Socket.IO server. It can include custom + query string parameters if required by the server. If a + function is provided, the client will invoke it to obtain + the URL each time a connection or reconnection is + attempted. + :param headers: A dictionary with custom headers to send with the + connection request. If a function is provided, the + client will invoke it to obtain the headers dictionary + each time a connection or reconnection is attempted. + :param auth: Authentication data passed to the server with the + connection request, normally a dictionary with one or + more string key/value pairs. If a function is provided, + the client will invoke it to obtain the authentication + data each time a connection or reconnection is attempted. + :param transports: The list of allowed transports. Valid transports + are ``'polling'`` and ``'websocket'``. If not + given, the polling transport is connected first, + then an upgrade to websocket is attempted. + :param namespace: The namespace to connect to as a string. If not + given, the default namespace ``/`` is used. + :param socketio_path: The endpoint where the Socket.IO server is + installed. The default value is appropriate for + most cases. + :param wait_timeout: How long the client should wait for the + connection. The default is 5 seconds. + + Note: this method is a coroutine. + """ + if self.connected: + raise RuntimeError('Already connected') + self.namespace = namespace + self.input_buffer = [] + self.input_event.clear() + self.client = AsyncClient(*self.client_args, **self.client_kwargs) + + @self.client.event(namespace=self.namespace) + def connect(): # pragma: no cover + self.connected = True + self.connected_event.set() + + @self.client.event(namespace=self.namespace) + def disconnect(): # pragma: no cover + self.connected_event.clear() + + @self.client.event(namespace=self.namespace) + def __disconnect_final(): # pragma: no cover + self.connected = False + self.connected_event.set() + + @self.client.on('*', namespace=self.namespace) + def on_event(event, *args): # pragma: no cover + self.input_buffer.append([event, *args]) + self.input_event.set() + + await self.client.connect( + url, headers=headers, auth=auth, transports=transports, + namespaces=[namespace], socketio_path=socketio_path, + wait_timeout=wait_timeout) + + @property + def sid(self): + """The session ID received from the server. + + The session ID is not guaranteed to remain constant throughout the life + of the connection, as reconnections can cause it to change. + """ + return self.client.get_sid(self.namespace) if self.client else None + + @property + def transport(self): + """The name of the transport currently in use. + + The transport is returned as a string and can be one of ``polling`` + and ``websocket``. + """ + return self.client.transport if self.client else '' + + async def emit(self, event, data=None): + """Emit an event to the server. + + :param event: The event name. It can be any string. The event names + ``'connect'``, ``'message'`` and ``'disconnect'`` are + reserved and should not be used. + :param data: The data to send to the server. Data can be of + type ``str``, ``bytes``, ``list`` or ``dict``. To send + multiple arguments, use a tuple where each element is of + one of the types indicated above. + + Note: this method is a coroutine. + + This method schedules the event to be sent out and returns, without + actually waiting for its delivery. In cases where the client needs to + ensure that the event was received, :func:`socketio.SimpleClient.call` + should be used instead. + """ + while True: + await self.connected_event.wait() + if not self.connected: + raise DisconnectedError() + try: + return await self.client.emit(event, data, + namespace=self.namespace) + except SocketIOError: + pass + + async def call(self, event, data=None, timeout=60): + """Emit an event to the server and wait for a response. + + This method issues an emit and waits for the server to provide a + response or acknowledgement. If the response does not arrive before the + timeout, then a ``TimeoutError`` exception is raised. + + :param event: The event name. It can be any string. The event names + ``'connect'``, ``'message'`` and ``'disconnect'`` are + reserved and should not be used. + :param data: The data to send to the server. Data can be of + type ``str``, ``bytes``, ``list`` or ``dict``. To send + multiple arguments, use a tuple where each element is of + one of the types indicated above. + :param timeout: The waiting timeout. If the timeout is reached before + the server acknowledges the event, then a + ``TimeoutError`` exception is raised. + + Note: this method is a coroutine. + """ + while True: + await self.connected_event.wait() + if not self.connected: + raise DisconnectedError() + try: + return await self.client.call(event, data, + namespace=self.namespace, + timeout=timeout) + except SocketIOError: + pass + + async def receive(self, timeout=None): + """Wait for an event from the server. + + :param timeout: The waiting timeout. If the timeout is reached before + the server acknowledges the event, then a + ``TimeoutError`` exception is raised. + + Note: this method is a coroutine. + + The return value is a list with the event name as the first element. If + the server included arguments with the event, they are returned as + additional list elements. + """ + while not self.input_buffer: + try: + await asyncio.wait_for(self.connected_event.wait(), + timeout=timeout) + except asyncio.TimeoutError: # pragma: no cover + raise TimeoutError() + if not self.connected: + raise DisconnectedError() + try: + await asyncio.wait_for(self.input_event.wait(), + timeout=timeout) + except asyncio.TimeoutError: + raise TimeoutError() + self.input_event.clear() + return self.input_buffer.pop(0) + + async def disconnect(self): + """Disconnect from the server. + + Note: this method is a coroutine. + """ + if self.connected: + await self.client.disconnect() + self.client = None + self.connected = False + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await self.disconnect() |