mirror of
https://github.com/tiqi-group/pydase.git
synced 2025-04-20 00:10:03 +02:00
adds reconnection method to proxy class which is called when the sio client does not reconnect
This commit is contained in:
parent
5ec7a8b530
commit
9eedf03c01
@ -86,7 +86,9 @@ class Client:
|
|||||||
self._url = url
|
self._url = url
|
||||||
self._sio = socketio.AsyncClient(**sio_client_kwargs)
|
self._sio = socketio.AsyncClient(**sio_client_kwargs)
|
||||||
self._loop = asyncio.new_event_loop()
|
self._loop = asyncio.new_event_loop()
|
||||||
self.proxy = ProxyClass(sio_client=self._sio, loop=self._loop)
|
self.proxy = ProxyClass(
|
||||||
|
sio_client=self._sio, loop=self._loop, reconnect=self.connect
|
||||||
|
)
|
||||||
"""A proxy object representing the remote service, facilitating interaction as
|
"""A proxy object representing the remote service, facilitating interaction as
|
||||||
if it were local."""
|
if it were local."""
|
||||||
self._thread = threading.Thread(
|
self._thread = threading.Thread(
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
from collections.abc import Callable
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from typing import TYPE_CHECKING, cast
|
from typing import TYPE_CHECKING, cast
|
||||||
|
|
||||||
@ -24,6 +25,8 @@ class ProxyClass(ProxyClassMixin, pydase.components.DeviceConnection):
|
|||||||
pydase service server.
|
pydase service server.
|
||||||
loop:
|
loop:
|
||||||
The event loop in which the client operations are managed and executed.
|
The event loop in which the client operations are managed and executed.
|
||||||
|
reconnect:
|
||||||
|
The method that is called periodically when the client is not connected.
|
||||||
|
|
||||||
This class is used to create a proxy object that behaves like a local representation
|
This class is used to create a proxy object that behaves like a local representation
|
||||||
of a remote pydase service, facilitating direct interaction as if it were local
|
of a remote pydase service, facilitating direct interaction as if it were local
|
||||||
@ -47,7 +50,10 @@ class ProxyClass(ProxyClassMixin, pydase.components.DeviceConnection):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, sio_client: socketio.AsyncClient, loop: asyncio.AbstractEventLoop
|
self,
|
||||||
|
sio_client: socketio.AsyncClient,
|
||||||
|
loop: asyncio.AbstractEventLoop,
|
||||||
|
reconnect: Callable[..., None],
|
||||||
) -> None:
|
) -> None:
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
self._service_representation: None | SerializedObject = None
|
self._service_representation: None | SerializedObject = None
|
||||||
@ -56,6 +62,7 @@ class ProxyClass(ProxyClassMixin, pydase.components.DeviceConnection):
|
|||||||
pydase.components.DeviceConnection.__init__(self)
|
pydase.components.DeviceConnection.__init__(self)
|
||||||
self._initialise(sio_client=sio_client, loop=loop)
|
self._initialise(sio_client=sio_client, loop=loop)
|
||||||
object.__setattr__(self, "_service_representation", None)
|
object.__setattr__(self, "_service_representation", None)
|
||||||
|
self.reconnect = reconnect
|
||||||
|
|
||||||
def serialize(self) -> SerializedObject:
|
def serialize(self) -> SerializedObject:
|
||||||
if self._service_representation is None:
|
if self._service_representation is None:
|
||||||
@ -99,3 +106,7 @@ class ProxyClass(ProxyClassMixin, pydase.components.DeviceConnection):
|
|||||||
"readonly": readonly,
|
"readonly": readonly,
|
||||||
"doc": doc,
|
"doc": doc,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def connect(self) -> None:
|
||||||
|
if not self._sio.reconnection or self._sio.reconnection_attempts > 0:
|
||||||
|
self.reconnect(block_until_connected=False)
|
||||||
|
107
tests/client/test_reconnection.py
Normal file
107
tests/client/test_reconnection.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import threading
|
||||||
|
from collections.abc import Callable, Generator
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
import pydase
|
||||||
|
import pytest
|
||||||
|
import socketio.exceptions
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="function")
|
||||||
|
def pydase_restartable_server() -> (
|
||||||
|
Generator[
|
||||||
|
tuple[
|
||||||
|
pydase.Server,
|
||||||
|
threading.Thread,
|
||||||
|
pydase.DataService,
|
||||||
|
Callable[
|
||||||
|
[pydase.Server, threading.Thread, pydase.DataService],
|
||||||
|
tuple[pydase.Server, threading.Thread],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
None,
|
||||||
|
Any,
|
||||||
|
]
|
||||||
|
):
|
||||||
|
class MyService(pydase.DataService):
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self._name = "MyService"
|
||||||
|
self._my_property = 12.1
|
||||||
|
|
||||||
|
@property
|
||||||
|
def my_property(self) -> float:
|
||||||
|
return self._my_property
|
||||||
|
|
||||||
|
@my_property.setter
|
||||||
|
def my_property(self, value: float) -> None:
|
||||||
|
self._my_property = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> str:
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
service_instance = MyService()
|
||||||
|
server = pydase.Server(service_instance, web_port=9999)
|
||||||
|
thread = threading.Thread(target=server.run, daemon=True)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
def restart(
|
||||||
|
server: pydase.Server,
|
||||||
|
thread: threading.Thread,
|
||||||
|
service_instance: pydase.DataService,
|
||||||
|
) -> tuple[pydase.Server, threading.Thread]:
|
||||||
|
server.handle_exit()
|
||||||
|
thread.join()
|
||||||
|
|
||||||
|
server = pydase.Server(service_instance, web_port=9999)
|
||||||
|
new_thread = threading.Thread(target=server.run, daemon=True)
|
||||||
|
new_thread.start()
|
||||||
|
|
||||||
|
return server, new_thread
|
||||||
|
|
||||||
|
yield server, thread, service_instance, restart
|
||||||
|
|
||||||
|
server.handle_exit()
|
||||||
|
thread.join()
|
||||||
|
|
||||||
|
|
||||||
|
def test_reconnection(
|
||||||
|
pydase_restartable_server: tuple[
|
||||||
|
pydase.Server,
|
||||||
|
threading.Thread,
|
||||||
|
pydase.DataService,
|
||||||
|
Callable[
|
||||||
|
[pydase.Server, threading.Thread, pydase.DataService],
|
||||||
|
tuple[pydase.Server, threading.Thread],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
) -> None:
|
||||||
|
client = pydase.Client(
|
||||||
|
url="ws://localhost:9999",
|
||||||
|
sio_client_kwargs={
|
||||||
|
"reconnection": False,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
client_2 = pydase.Client(
|
||||||
|
url="ws://localhost:9999",
|
||||||
|
sio_client_kwargs={
|
||||||
|
"reconnection_attempts": 1,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
server, thread, service_instance, restart = pydase_restartable_server
|
||||||
|
service_instance._name = "New service name"
|
||||||
|
|
||||||
|
server, thread = restart(server, thread, service_instance)
|
||||||
|
|
||||||
|
with pytest.raises(socketio.exceptions.BadNamespaceError):
|
||||||
|
client.proxy.name
|
||||||
|
client_2.proxy.name
|
||||||
|
|
||||||
|
client.proxy.reconnect()
|
||||||
|
client_2.proxy.reconnect()
|
||||||
|
|
||||||
|
# the service proxies successfully reconnect and get the new service name
|
||||||
|
assert client.proxy.name == "New service name"
|
||||||
|
assert client_2.proxy.name == "New service name"
|
Loading…
x
Reference in New Issue
Block a user