diff --git a/README.md b/README.md index a4ce2c4..7faaa9d 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,8 @@ import pydase # Replace the hostname and port with the IP address and the port of the machine where # the service is running, respectively -client_proxy = pydase.Client(hostname="<ip_addr>", port=8001).proxy +client_proxy = pydase.Client(url="ws://<ip_addr>:<service_port>").proxy +# client_proxy = pydase.Client(url="wss://your-domain.ch").proxy # if your service uses ssl-encryption # After the connection, interact with the service attributes as if they were local client_proxy.voltage = 5.0 @@ -195,7 +196,8 @@ import pydase class MyService(pydase.DataService): # Initialize the client without blocking the constructor - proxy = pydase.Client(hostname="<ip_addr>", port=8001, block_until_connected=False).proxy + proxy = pydase.Client(url="ws://<ip_addr>:<service_port>", block_until_connected=False).proxy + # proxy = pydase.Client(url="wss://your-domain.ch", block_until_connected=False).proxy # communicating with ssl-encrypted service if __name__ == "__main__": service = MyService() diff --git a/docs/user-guide/interaction/Python Client.md b/docs/user-guide/interaction/Python Client.md index ea844f2..e41c43c 100644 --- a/docs/user-guide/interaction/Python Client.md +++ b/docs/user-guide/interaction/Python Client.md @@ -5,9 +5,10 @@ You can connect to the service using the `pydase.Client`. Below is an example of ```python import pydase -# Replace the hostname and port with the IP address and the port of the machine -# where the service is running, respectively -client_proxy = pydase.Client(hostname="<ip_addr>", port=8001).proxy +# Replace the hostname and port with the IP address and the port of the machine where +# the service is running, respectively +client_proxy = pydase.Client(url="ws://<ip_addr>:<service_port>").proxy +# client_proxy = pydase.Client(url="wss://your-domain.ch").proxy # if your service uses ssl-encryption # Interact with the service attributes as if they were local client_proxy.voltage = 5.0 @@ -32,7 +33,8 @@ import pydase class MyService(pydase.DataService): # Initialize the client without blocking the constructor - proxy = pydase.Client(hostname="<ip_addr>", port=8001, block_until_connected=False).proxy + proxy = pydase.Client(url="ws://<ip_addr>:<service_port>", block_until_connected=False).proxy + # proxy = pydase.Client(url="wss://your-domain.ch", block_until_connected=False).proxy # communicating with ssl-encrypted service if __name__ == "__main__": service = MyService() diff --git a/pyproject.toml b/pyproject.toml index e621311..a881a33 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pydase" -version = "0.8.5" +version = "0.9.0" description = "A flexible and robust Python library for creating, managing, and interacting with data services, with built-in support for web and RPC servers, and customizable features for diverse use cases." authors = ["Mose Mueller <mosmuell@ethz.ch>"] readme = "README.md" diff --git a/src/pydase/client/client.py b/src/pydase/client/client.py index 537a628..17b1c6b 100644 --- a/src/pydase/client/client.py +++ b/src/pydase/client/client.py @@ -80,12 +80,12 @@ class Client: if it were local. Args: - hostname (str): - Hostname of the exposed service this client attempts to connect to. - Default is "localhost". - port (int): - Port of the exposed service this client attempts to connect on. - Default is 8001. + url (str): + The URL of the pydase Socket.IO server. This should always contain the + protocol and the hostname. + Examples: + - wss://my-service.example.com # for secure connections, use wss + - ws://localhost:8001 block_until_connected (bool): If set to True, the constructor will block until the connection to the service has been established. This is useful for ensuring the client is @@ -94,12 +94,11 @@ class Client: def __init__( self, - hostname: str, - port: int, + *, + url: str, block_until_connected: bool = True, ): - self._hostname = hostname - self._port = port + self._url = url self._sio = socketio.AsyncClient() self._loop = asyncio.new_event_loop() self.proxy = ProxyClass(sio_client=self._sio, loop=self._loop) @@ -107,29 +106,41 @@ class Client: target=asyncio_loop_thread, args=(self._loop,), daemon=True ) self._thread.start() + self.connect(block_until_connected=block_until_connected) + + def connect(self, block_until_connected: bool = True) -> None: connection_future = asyncio.run_coroutine_threadsafe( self._connect(), self._loop ) if block_until_connected: connection_future.result() + def disconnect(self) -> None: + connection_future = asyncio.run_coroutine_threadsafe( + self._disconnect(), self._loop + ) + connection_future.result() + async def _connect(self) -> None: - logger.debug("Connecting to server '%s:%s' ...", self._hostname, self._port) + logger.debug("Connecting to server '%s' ...", self._url) await self._setup_events() await self._sio.connect( - f"ws://{self._hostname}:{self._port}", + self._url, socketio_path="/ws/socket.io", transports=["websocket"], retry=True, ) + async def _disconnect(self) -> None: + await self._sio.disconnect() + async def _setup_events(self) -> None: self._sio.on("connect", self._handle_connect) self._sio.on("disconnect", self._handle_disconnect) self._sio.on("notify", self._handle_update) async def _handle_connect(self) -> None: - logger.debug("Connected to '%s:%s' ...", self._hostname, self._port) + logger.debug("Connected to '%s' ...", self._url) serialized_object = cast( SerializedDataService, await self._sio.call("service_serialization") ) @@ -141,7 +152,7 @@ class Client: self.proxy._connected = True async def _handle_disconnect(self) -> None: - logger.debug("Disconnected from '%s:%s' ...", self._hostname, self._port) + logger.debug("Disconnected from '%s' ...", self._url) self.proxy._connected = False async def _handle_update(self, data: NotifyDict) -> None: diff --git a/tests/client/test_client.py b/tests/client/test_client.py index 7a66bb2..27c2cd0 100644 --- a/tests/client/test_client.py +++ b/tests/client/test_client.py @@ -45,7 +45,7 @@ def pydase_client() -> Generator[pydase.Client, None, Any]: thread = threading.Thread(target=server.run, daemon=True) thread.start() - client = pydase.Client(hostname="localhost", port=9999) + client = pydase.Client(url="ws://localhost:9999") yield client