mirror of
https://github.com/tiqi-group/pydase.git
synced 2025-12-19 12:41:19 +01:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e85e93a1d9 | ||
|
|
ea5fd42919 | ||
|
|
247113f1db | ||
|
|
c76b0b0b6e | ||
|
|
2d39c56e3d | ||
|
|
60287fef95 | ||
|
|
c5e1a08c54 | ||
|
|
9424d4c412 | ||
|
|
0a4c13c617 | ||
|
|
5d72604199 | ||
|
|
3479c511fe | ||
|
|
9bf3b28390 | ||
|
|
0195f9d6f6 |
@@ -50,12 +50,14 @@ import pydase
|
|||||||
class MyService(pydase.DataService):
|
class MyService(pydase.DataService):
|
||||||
proxy = pydase.Client(
|
proxy = pydase.Client(
|
||||||
url="ws://<ip_addr>:<service_port>",
|
url="ws://<ip_addr>:<service_port>",
|
||||||
block_until_connected=False
|
block_until_connected=False,
|
||||||
|
client_id="my_pydase_client_id",
|
||||||
).proxy
|
).proxy
|
||||||
# For SSL-encrypted services, use the wss protocol
|
# For SSL-encrypted services, use the wss protocol
|
||||||
# proxy = pydase.Client(
|
# proxy = pydase.Client(
|
||||||
# url="wss://your-domain.ch",
|
# url="wss://your-domain.ch",
|
||||||
# block_until_connected=False
|
# block_until_connected=False,
|
||||||
|
# client_id="my_pydase_client_id",
|
||||||
# ).proxy
|
# ).proxy
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -67,6 +69,7 @@ if __name__ == "__main__":
|
|||||||
In this example:
|
In this example:
|
||||||
- The `MyService` class has a `proxy` attribute that connects to a `pydase` service at `<ip_addr>:<service_port>`.
|
- The `MyService` class has a `proxy` attribute that connects to a `pydase` service at `<ip_addr>:<service_port>`.
|
||||||
- By setting `block_until_connected=False`, the service can start without waiting for the connection to succeed, which is particularly useful in distributed systems where services may initialize in any order.
|
- By setting `block_until_connected=False`, the service can start without waiting for the connection to succeed, which is particularly useful in distributed systems where services may initialize in any order.
|
||||||
|
- By setting `client_id`, the server will provide more accurate logs of the connecting client. If set, this ID is sent as `X-Client-Id` header in the HTTP(s) request.
|
||||||
|
|
||||||
## Custom `socketio.AsyncClient` Connection Parameters
|
## Custom `socketio.AsyncClient` Connection Parameters
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
// this will be set by the python backend if the service is behind a proxy which strips a prefix. The frontend can use this to build the paths to the resources.
|
// this will be set by the python backend if the service is behind a proxy which strips a prefix. The frontend can use this to build the paths to the resources.
|
||||||
window.__FORWARDED_PREFIX__ = "";
|
window.__FORWARDED_PREFIX__ = "";
|
||||||
window.__FORWARDED_PROTO__ = "";
|
window.__FORWARDED_PROTO__ = "";
|
||||||
</script>`
|
</script>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
|||||||
3070
frontend/package-lock.json
generated
3070
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -10,31 +10,31 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.14.0",
|
||||||
"@mui/material": "^5.14.1",
|
"@mui/material": "^5.16.14",
|
||||||
"bootstrap": "^5.3.3",
|
"bootstrap": "^5.3.3",
|
||||||
"deep-equal": "^2.2.3",
|
"deep-equal": "^2.2.3",
|
||||||
"react": "^18.3.1",
|
"react": "^19.0.0",
|
||||||
"react-bootstrap": "^2.10.0",
|
"react-bootstrap": "^2.10.7",
|
||||||
"react-bootstrap-icons": "^1.11.4",
|
"react-bootstrap-icons": "^1.11.5",
|
||||||
"socket.io-client": "^4.7.1"
|
"socket.io-client": "^4.8.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.6.0",
|
"@eslint/js": "^9.18.0",
|
||||||
"@types/deep-equal": "^1.0.4",
|
"@types/deep-equal": "^1.0.4",
|
||||||
"@types/eslint__js": "^8.42.3",
|
"@types/eslint__js": "^8.42.3",
|
||||||
"@types/node": "^20.14.10",
|
"@types/node": "^20.17.14",
|
||||||
"@types/react": "^18.3.3",
|
"@types/react": "^19.0.7",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^19.0.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.15.0",
|
"@typescript-eslint/eslint-plugin": "^7.15.0",
|
||||||
"@vitejs/plugin-react-swc": "^3.5.0",
|
"@vitejs/plugin-react-swc": "^3.7.2",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.1",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.2.3",
|
||||||
"eslint-plugin-react": "^7.34.3",
|
"eslint-plugin-react": "^7.37.4",
|
||||||
"prettier": "3.3.2",
|
"prettier": "3.3.2",
|
||||||
"typescript": "^5.5.3",
|
"typescript": "^5.7.3",
|
||||||
"typescript-eslint": "^7.15.0",
|
"typescript-eslint": "^7.18.0",
|
||||||
"vite": "^5.3.1"
|
"vite": "^5.4.12"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "pydase"
|
name = "pydase"
|
||||||
version = "0.10.8"
|
version = "0.10.9"
|
||||||
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."
|
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>"]
|
authors = ["Mose Mueller <mosmuell@ethz.ch>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ class Client:
|
|||||||
[`AsyncClient`][socketio.AsyncClient]. This allows fine-tuning of the
|
[`AsyncClient`][socketio.AsyncClient]. This allows fine-tuning of the
|
||||||
client's behaviour (e.g., reconnection attempts or reconnection delay).
|
client's behaviour (e.g., reconnection attempts or reconnection delay).
|
||||||
Default is an empty dictionary.
|
Default is an empty dictionary.
|
||||||
|
client_id: Client identification that will be shown in the server logs this
|
||||||
|
client is connecting to. This ID is passed as a `X-Client-Id` header in the
|
||||||
|
HTTP(s) request. Defaults to None.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
The following example demonstrates a `Client` instance that connects to another
|
The following example demonstrates a `Client` instance that connects to another
|
||||||
@@ -84,6 +87,7 @@ class Client:
|
|||||||
url: str,
|
url: str,
|
||||||
block_until_connected: bool = True,
|
block_until_connected: bool = True,
|
||||||
sio_client_kwargs: dict[str, Any] = {},
|
sio_client_kwargs: dict[str, Any] = {},
|
||||||
|
client_id: str | None = None,
|
||||||
):
|
):
|
||||||
# Parse the URL to separate base URL and path prefix
|
# Parse the URL to separate base URL and path prefix
|
||||||
parsed_url = urllib.parse.urlparse(url)
|
parsed_url = urllib.parse.urlparse(url)
|
||||||
@@ -98,6 +102,7 @@ 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._client_id = client_id
|
||||||
self.proxy = ProxyClass(
|
self.proxy = ProxyClass(
|
||||||
sio_client=self._sio, loop=self._loop, reconnect=self.connect
|
sio_client=self._sio, loop=self._loop, reconnect=self.connect
|
||||||
)
|
)
|
||||||
@@ -136,8 +141,14 @@ class Client:
|
|||||||
async def _connect(self) -> None:
|
async def _connect(self) -> None:
|
||||||
logger.debug("Connecting to server '%s' ...", self._url)
|
logger.debug("Connecting to server '%s' ...", self._url)
|
||||||
await self._setup_events()
|
await self._setup_events()
|
||||||
|
|
||||||
|
headers = {}
|
||||||
|
if self._client_id is not None:
|
||||||
|
headers["X-Client-Id"] = self._client_id
|
||||||
|
|
||||||
await self._sio.connect(
|
await self._sio.connect(
|
||||||
self._base_url,
|
url=self._base_url,
|
||||||
|
headers=headers,
|
||||||
socketio_path=f"{self._path_prefix}/ws/socket.io",
|
socketio_path=f"{self._path_prefix}/ws/socket.io",
|
||||||
transports=["websocket"],
|
transports=["websocket"],
|
||||||
retry=True,
|
retry=True,
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
71
src/pydase/frontend/assets/index-DpoEqi_N.js
Normal file
71
src/pydase/frontend/assets/index-DpoEqi_N.js
Normal file
File diff suppressed because one or more lines are too long
@@ -7,15 +7,15 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta name="description" content="Web site displaying a pydase UI." />
|
<meta name="description" content="Web site displaying a pydase UI." />
|
||||||
<script type="module" crossorigin src="/assets/index-B4RiTBEo.js"></script>
|
<script type="module" crossorigin src="/assets/index-DpoEqi_N.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-D2aktF3W.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-DJzFvk4W.css">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// this will be set by the python backend if the service is behind a proxy which strips a prefix. The frontend can use this to build the paths to the resources.
|
// this will be set by the python backend if the service is behind a proxy which strips a prefix. The frontend can use this to build the paths to the resources.
|
||||||
window.__FORWARDED_PREFIX__ = "";
|
window.__FORWARDED_PREFIX__ = "";
|
||||||
window.__FORWARDED_PROTO__ = "";
|
window.__FORWARDED_PROTO__ = "";
|
||||||
</script>`
|
</script>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
|||||||
@@ -150,10 +150,7 @@ class WebServer:
|
|||||||
f"{escaped_prefix}/favicon.ico",
|
f"{escaped_prefix}/favicon.ico",
|
||||||
)
|
)
|
||||||
|
|
||||||
return aiohttp.web.Response(
|
return aiohttp.web.Response(text=modified_html, content_type="text/html")
|
||||||
text=modified_html, content_type="text/html"
|
|
||||||
)
|
|
||||||
return aiohttp.web.FileResponse(self.frontend_src / "index.html")
|
|
||||||
|
|
||||||
app = aiohttp.web.Application()
|
app = aiohttp.web.Application()
|
||||||
|
|
||||||
|
|||||||
@@ -161,3 +161,15 @@ def test_context_manager(pydase_client: pydase.Client) -> None:
|
|||||||
assert client.proxy.my_property == 1337.01
|
assert client.proxy.my_property == 1337.01
|
||||||
|
|
||||||
assert not client.proxy.connected
|
assert not client.proxy.connected
|
||||||
|
|
||||||
|
|
||||||
|
def test_client_id(
|
||||||
|
pydase_client: pydase.Client, caplog: pytest.LogCaptureFixture
|
||||||
|
) -> None:
|
||||||
|
pydase.Client(url="ws://localhost:9999")
|
||||||
|
|
||||||
|
assert "Client [sid=" in caplog.text
|
||||||
|
caplog.clear()
|
||||||
|
|
||||||
|
pydase.Client(url="ws://localhost:9999", client_id="my_service")
|
||||||
|
assert "Client [id=my_service] connected" in caplog.text
|
||||||
|
|||||||
Reference in New Issue
Block a user