mirror of
https://github.com/tiqi-group/pydase.git
synced 2025-04-21 16:50:02 +02:00
commit
f76703340c
@ -0,0 +1,39 @@
|
|||||||
|
::: pydase.data_service
|
||||||
|
handler: python
|
||||||
|
|
||||||
|
::: pydase.server.server
|
||||||
|
handler: python
|
||||||
|
|
||||||
|
::: pydase.server.web_server
|
||||||
|
handler: python
|
||||||
|
|
||||||
|
::: pydase.client
|
||||||
|
handler: python
|
||||||
|
|
||||||
|
::: pydase.components
|
||||||
|
handler: python
|
||||||
|
|
||||||
|
::: pydase.utils.serialization.serializer
|
||||||
|
handler: python
|
||||||
|
|
||||||
|
::: pydase.utils.serialization.deserializer
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
show_root_heading: true
|
||||||
|
show_root_toc_entry: false
|
||||||
|
show_symbol_type_heading: true
|
||||||
|
show_symbol_type_toc: true
|
||||||
|
|
||||||
|
::: pydase.utils.serialization.types
|
||||||
|
handler: python
|
||||||
|
|
||||||
|
::: pydase.utils.decorators
|
||||||
|
handler: python
|
||||||
|
options:
|
||||||
|
filters: ["!render_in_frontend"]
|
||||||
|
|
||||||
|
::: pydase.units
|
||||||
|
handler: python
|
||||||
|
|
||||||
|
::: pydase.config
|
||||||
|
handler: python
|
@ -5,7 +5,7 @@
|
|||||||
end="<!--getting-started-end-->"
|
end="<!--getting-started-end-->"
|
||||||
%}
|
%}
|
||||||
|
|
||||||
[RESTful API]: ./user-guide/interaction/main.md#restful-api
|
[RESTful API]: ./user-guide/interaction/README.md#restful-api
|
||||||
[Python RPC Client]: ./user-guide/interaction/main.md#python-rpc-client
|
[Python RPC Client]: ./user-guide/interaction/README.md#python-rpc-client
|
||||||
[Custom Components]: ./user-guide/Components.md#custom-components-pydasecomponents
|
[Custom Components]: ./user-guide/Components.md#custom-components-pydasecomponents
|
||||||
[Components]: ./user-guide/Components.md
|
[Components]: ./user-guide/Components.md
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
[Defining DataService]: ./getting-started.md#defining-a-dataservice
|
[Defining DataService]: ./getting-started.md#defining-a-dataservice
|
||||||
[Web Interface Access]: ./getting-started.md#accessing-the-web-interface
|
[Web Interface Access]: ./getting-started.md#accessing-the-web-interface
|
||||||
[Short RPC Client]: ./getting-started.md#connecting-to-the-service-via-python-rpc-client
|
[Short RPC Client]: ./getting-started.md#connecting-to-the-service-via-python-rpc-client
|
||||||
[Customizing Web Interface]: ./user-guide/interaction/main.md#customization-options
|
[Customizing Web Interface]: ./user-guide/interaction/README.md#customization-options
|
||||||
[Task Management]: ./user-guide/Tasks.md
|
[Task Management]: ./user-guide/Tasks.md
|
||||||
[Units]: ./user-guide/Understanding-Units.md
|
[Units]: ./user-guide/Understanding-Units.md
|
||||||
[Property Validation]: ./user-guide/Validating-Property-Setters.md
|
[Property Validation]: ./user-guide/Validating-Property-Setters.md
|
||||||
|
@ -5,6 +5,7 @@ charset-normalizer==3.3.2 ; python_version >= "3.10" and python_version < "4.0"
|
|||||||
click==8.1.7 ; python_version >= "3.10" and python_version < "4.0"
|
click==8.1.7 ; python_version >= "3.10" and python_version < "4.0"
|
||||||
colorama==0.4.6 ; python_version >= "3.10" and python_version < "4.0"
|
colorama==0.4.6 ; python_version >= "3.10" and python_version < "4.0"
|
||||||
ghp-import==2.1.0 ; python_version >= "3.10" and python_version < "4.0"
|
ghp-import==2.1.0 ; python_version >= "3.10" and python_version < "4.0"
|
||||||
|
griffe==1.1.0 ; python_version >= "3.10" and python_version < "4.0"
|
||||||
idna==3.7 ; python_version >= "3.10" and python_version < "4.0"
|
idna==3.7 ; python_version >= "3.10" and python_version < "4.0"
|
||||||
jinja2==3.1.4 ; python_version >= "3.10" and python_version < "4.0"
|
jinja2==3.1.4 ; python_version >= "3.10" and python_version < "4.0"
|
||||||
markdown==3.6 ; python_version >= "3.10" and python_version < "4.0"
|
markdown==3.6 ; python_version >= "3.10" and python_version < "4.0"
|
||||||
@ -14,10 +15,12 @@ mkdocs-autorefs==1.0.1 ; python_version >= "3.10" and python_version < "4.0"
|
|||||||
mkdocs-get-deps==0.2.0 ; python_version >= "3.10" and python_version < "4.0"
|
mkdocs-get-deps==0.2.0 ; python_version >= "3.10" and python_version < "4.0"
|
||||||
mkdocs-include-markdown-plugin==3.9.1 ; python_version >= "3.10" and python_version < "4.0"
|
mkdocs-include-markdown-plugin==3.9.1 ; python_version >= "3.10" and python_version < "4.0"
|
||||||
mkdocs-material-extensions==1.3.1 ; python_version >= "3.10" and python_version < "4.0"
|
mkdocs-material-extensions==1.3.1 ; python_version >= "3.10" and python_version < "4.0"
|
||||||
mkdocs-material==9.5.30 ; python_version >= "3.10" and python_version < "4.0"
|
mkdocs-material==9.5.31 ; python_version >= "3.10" and python_version < "4.0"
|
||||||
mkdocs-swagger-ui-tag==0.6.10 ; python_version >= "3.10" and python_version < "4.0"
|
mkdocs-swagger-ui-tag==0.6.10 ; python_version >= "3.10" and python_version < "4.0"
|
||||||
mkdocs==1.6.0 ; python_version >= "3.10" and python_version < "4.0"
|
mkdocs==1.6.0 ; python_version >= "3.10" and python_version < "4.0"
|
||||||
mkdocstrings==0.22.0 ; python_version >= "3.10" and python_version < "4.0"
|
mkdocstrings-python==1.10.8 ; python_version >= "3.10" and python_version < "4.0"
|
||||||
|
mkdocstrings==0.25.2 ; python_version >= "3.10" and python_version < "4.0"
|
||||||
|
mkdocstrings[python]==0.25.2 ; python_version >= "3.10" and python_version < "4.0"
|
||||||
packaging==24.1 ; python_version >= "3.10" and python_version < "4.0"
|
packaging==24.1 ; python_version >= "3.10" and python_version < "4.0"
|
||||||
paginate==0.5.6 ; python_version >= "3.10" and python_version < "4.0"
|
paginate==0.5.6 ; python_version >= "3.10" and python_version < "4.0"
|
||||||
pathspec==0.12.1 ; python_version >= "3.10" and python_version < "4.0"
|
pathspec==0.12.1 ; python_version >= "3.10" and python_version < "4.0"
|
||||||
|
@ -16,7 +16,7 @@ In `pydase`, components are fundamental building blocks that bridge the Python b
|
|||||||
## Method Components
|
## Method Components
|
||||||
Within the `DataService` class of `pydase`, only methods devoid of arguments can be represented in the frontend, classified into two distinct categories
|
Within the `DataService` class of `pydase`, only methods devoid of arguments can be represented in the frontend, classified into two distinct categories
|
||||||
|
|
||||||
1. [**Tasks**](#understanding-tasks-in-pydase): Argument-free asynchronous functions, identified within `pydase` as tasks, are inherently designed for frontend interaction. These tasks are automatically rendered with a start/stop button, allowing users to initiate or halt the task execution directly from the web interface.
|
1. [**Tasks**](./Tasks.md): Argument-free asynchronous functions, identified within `pydase` as tasks, are inherently designed for frontend interaction. These tasks are automatically rendered with a start/stop button, allowing users to initiate or halt the task execution directly from the web interface.
|
||||||
2. **Synchronous Methods with `@frontend` Decorator**: Synchronous methods without arguments can also be presented in the frontend. For this, they have to be decorated with the `@frontend` decorator.
|
2. **Synchronous Methods with `@frontend` Decorator**: Synchronous methods without arguments can also be presented in the frontend. For this, they have to be decorated with the `@frontend` decorator.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@ -348,7 +348,7 @@ In this example, `MySlider` overrides the `min`, `max`, `step_size`, and `value`
|
|||||||
|
|
||||||
- Incorporating units in `NumberSlider`
|
- Incorporating units in `NumberSlider`
|
||||||
|
|
||||||
The `NumberSlider` is capable of [displaying units](#understanding-units-in-pydase) alongside values, enhancing its usability in contexts where unit representation is crucial. When utilizing `pydase.units`, you can specify units for the slider's value, allowing the component to reflect these units in the frontend.
|
The `NumberSlider` is capable of [displaying units](./Understanding-Units.md) alongside values, enhancing its usability in contexts where unit representation is crucial. When utilizing `pydase.units`, you can specify units for the slider's value, allowing the component to reflect these units in the frontend.
|
||||||
|
|
||||||
Here's how to implement a `NumberSlider` with unit display:
|
Here's how to implement a `NumberSlider` with unit display:
|
||||||
|
|
||||||
|
35
mkdocs.yml
35
mkdocs.yml
@ -6,7 +6,7 @@ nav:
|
|||||||
- Getting Started: getting-started.md
|
- Getting Started: getting-started.md
|
||||||
- User Guide:
|
- User Guide:
|
||||||
- Components Guide: user-guide/Components.md
|
- Components Guide: user-guide/Components.md
|
||||||
- Interacting with pydase Services: user-guide/interaction/main.md
|
- Interacting with pydase Services: user-guide/interaction/README.md
|
||||||
- Achieving Service Persistence: user-guide/Service_Persistence.md
|
- Achieving Service Persistence: user-guide/Service_Persistence.md
|
||||||
- Understanding Tasks: user-guide/Tasks.md
|
- Understanding Tasks: user-guide/Tasks.md
|
||||||
- Understanding Units: user-guide/Understanding-Units.md
|
- Understanding Units: user-guide/Understanding-Units.md
|
||||||
@ -44,10 +44,35 @@ markdown_extensions:
|
|||||||
|
|
||||||
|
|
||||||
plugins:
|
plugins:
|
||||||
- include-markdown
|
- include-markdown
|
||||||
- search
|
- search
|
||||||
- mkdocstrings
|
- mkdocstrings:
|
||||||
- swagger-ui-tag
|
handlers:
|
||||||
|
python:
|
||||||
|
paths: [src] # search packages in the src folder
|
||||||
|
import:
|
||||||
|
- https://docs.python.org/3/objects.inv
|
||||||
|
- https://docs.pydantic.dev/latest/objects.inv
|
||||||
|
- https://confz.readthedocs.io/en/latest/objects.inv
|
||||||
|
options:
|
||||||
|
show_source: true
|
||||||
|
inherited_members: true
|
||||||
|
merge_init_into_class: true
|
||||||
|
show_signature_annotations: true
|
||||||
|
signature_crossrefs: true
|
||||||
|
separate_signature: true
|
||||||
|
docstring_options:
|
||||||
|
ignore_init_summary: true
|
||||||
|
# docstring_section_style: list
|
||||||
|
heading_level: 2
|
||||||
|
parameter_headings: true
|
||||||
|
show_root_heading: true
|
||||||
|
show_root_full_path: true
|
||||||
|
show_symbol_type_heading: true
|
||||||
|
show_symbol_type_toc: true
|
||||||
|
# summary: true
|
||||||
|
unwrap_annotated: true
|
||||||
|
- swagger-ui-tag
|
||||||
|
|
||||||
watch:
|
watch:
|
||||||
- src/pydase
|
- src/pydase
|
||||||
|
44
poetry.lock
generated
44
poetry.lock
generated
@ -769,6 +769,20 @@ python-dateutil = ">=2.8.1"
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
dev = ["flake8", "markdown", "twine", "wheel"]
|
dev = ["flake8", "markdown", "twine", "wheel"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "griffe"
|
||||||
|
version = "1.1.0"
|
||||||
|
description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "griffe-1.1.0-py3-none-any.whl", hash = "sha256:38ccc5721571c95ae427123074cf0dc0d36bce7c9701ab2ada9fe0566ff50c10"},
|
||||||
|
{file = "griffe-1.1.0.tar.gz", hash = "sha256:c6328cbdec0d449549c1cc332f59227cd5603f903479d73e4425d828b782ffc3"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
colorama = ">=0.4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h11"
|
name = "h11"
|
||||||
version = "0.14.0"
|
version = "0.14.0"
|
||||||
@ -1212,21 +1226,24 @@ beautifulsoup4 = ">=4.11.1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mkdocstrings"
|
name = "mkdocstrings"
|
||||||
version = "0.22.0"
|
version = "0.25.2"
|
||||||
description = "Automatic documentation from sources, for MkDocs."
|
description = "Automatic documentation from sources, for MkDocs."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.8"
|
||||||
files = [
|
files = [
|
||||||
{file = "mkdocstrings-0.22.0-py3-none-any.whl", hash = "sha256:2d4095d461554ff6a778fdabdca3c00c468c2f1459d469f7a7f622a2b23212ba"},
|
{file = "mkdocstrings-0.25.2-py3-none-any.whl", hash = "sha256:9e2cda5e2e12db8bb98d21e3410f3f27f8faab685a24b03b06ba7daa5b92abfc"},
|
||||||
{file = "mkdocstrings-0.22.0.tar.gz", hash = "sha256:82a33b94150ebb3d4b5c73bab4598c3e21468c79ec072eff6931c8f3bfc38256"},
|
{file = "mkdocstrings-0.25.2.tar.gz", hash = "sha256:5cf57ad7f61e8be3111a2458b4e49c2029c9cb35525393b179f9c916ca8042dc"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
|
click = ">=7.0"
|
||||||
Jinja2 = ">=2.11.1"
|
Jinja2 = ">=2.11.1"
|
||||||
Markdown = ">=3.3"
|
Markdown = ">=3.3"
|
||||||
MarkupSafe = ">=1.1"
|
MarkupSafe = ">=1.1"
|
||||||
mkdocs = ">=1.2"
|
mkdocs = ">=1.4"
|
||||||
mkdocs-autorefs = ">=0.3.1"
|
mkdocs-autorefs = ">=0.3.1"
|
||||||
|
mkdocstrings-python = {version = ">=0.5.2", optional = true, markers = "extra == \"python\""}
|
||||||
|
platformdirs = ">=2.2.0"
|
||||||
pymdown-extensions = ">=6.3"
|
pymdown-extensions = ">=6.3"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
@ -1234,6 +1251,21 @@ crystal = ["mkdocstrings-crystal (>=0.3.4)"]
|
|||||||
python = ["mkdocstrings-python (>=0.5.2)"]
|
python = ["mkdocstrings-python (>=0.5.2)"]
|
||||||
python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"]
|
python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mkdocstrings-python"
|
||||||
|
version = "1.10.8"
|
||||||
|
description = "A Python handler for mkdocstrings."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "mkdocstrings_python-1.10.8-py3-none-any.whl", hash = "sha256:bb12e76c8b071686617f824029cb1dfe0e9afe89f27fb3ad9a27f95f054dcd89"},
|
||||||
|
{file = "mkdocstrings_python-1.10.8.tar.gz", hash = "sha256:5856a59cbebbb8deb133224a540de1ff60bded25e54d8beacc375bb133d39016"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
griffe = ">=0.49"
|
||||||
|
mkdocstrings = ">=0.25"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "multidict"
|
name = "multidict"
|
||||||
version = "6.0.5"
|
version = "6.0.5"
|
||||||
@ -2464,4 +2496,4 @@ multidict = ">=4.0"
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.10"
|
python-versions = "^3.10"
|
||||||
content-hash = "54e25a68577a912301aa9125e3f3de545e03a199a79b2153c106285b92febbba"
|
content-hash = "7131eddc2065147a18c145bb6da09492f03eb7fe050e968109cecb6044d17ed6"
|
||||||
|
@ -38,7 +38,7 @@ optional = true
|
|||||||
[tool.poetry.group.docs.dependencies]
|
[tool.poetry.group.docs.dependencies]
|
||||||
mkdocs-material = "^9.5.30"
|
mkdocs-material = "^9.5.30"
|
||||||
mkdocs-include-markdown-plugin = "^3.9.1"
|
mkdocs-include-markdown-plugin = "^3.9.1"
|
||||||
mkdocstrings = "^0.22.0"
|
mkdocstrings = {extras = ["python"], version = "^0.25.2"}
|
||||||
pymdown-extensions = "^10.1"
|
pymdown-extensions = "^10.1"
|
||||||
mkdocs-swagger-ui-tag = "^0.6.10"
|
mkdocs-swagger-ui-tag = "^0.6.10"
|
||||||
|
|
||||||
|
@ -43,10 +43,10 @@ class ProxyClass(ProxyClassMixin, pydase.components.DeviceConnection):
|
|||||||
via a socket.io client in an asyncio environment.
|
via a socket.io client in an asyncio environment.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
sio_client (socketio.AsyncClient):
|
sio_client:
|
||||||
The socket.io client instance used for asynchronous communication with the
|
The socket.io client instance used for asynchronous communication with the
|
||||||
pydase service server.
|
pydase service server.
|
||||||
loop (asyncio.AbstractEventLoop):
|
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.
|
||||||
|
|
||||||
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
|
||||||
@ -84,19 +84,16 @@ class Client:
|
|||||||
connection, disconnection, and updates, and ensures that the proxy object is
|
connection, disconnection, and updates, and ensures that the proxy object is
|
||||||
up-to-date with the server state.
|
up-to-date with the server state.
|
||||||
|
|
||||||
Attributes:
|
|
||||||
proxy (ProxyClass):
|
|
||||||
A proxy object representing the remote service, facilitating interaction as
|
|
||||||
if it were local.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
url (str):
|
url:
|
||||||
The URL of the pydase Socket.IO server. This should always contain the
|
The URL of the pydase Socket.IO server. This should always contain the
|
||||||
protocol and the hostname.
|
protocol and the hostname.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
- wss://my-service.example.com # for secure connections, use wss
|
|
||||||
- ws://localhost:8001
|
- `wss://my-service.example.com` # for secure connections, use wss
|
||||||
block_until_connected (bool):
|
- `ws://localhost:8001`
|
||||||
|
block_until_connected:
|
||||||
If set to True, the constructor will block until the connection to the
|
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
|
service has been established. This is useful for ensuring the client is
|
||||||
ready to use immediately after instantiation. Default is True.
|
ready to use immediately after instantiation. Default is True.
|
||||||
@ -112,6 +109,8 @@ class Client:
|
|||||||
self._sio = socketio.AsyncClient()
|
self._sio = socketio.AsyncClient()
|
||||||
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)
|
||||||
|
"""A proxy object representing the remote service, facilitating interaction as
|
||||||
|
if it were local."""
|
||||||
self._thread = threading.Thread(
|
self._thread = threading.Thread(
|
||||||
target=asyncio_loop_thread, args=(self._loop,), daemon=True
|
target=asyncio_loop_thread, args=(self._loop,), daemon=True
|
||||||
)
|
)
|
||||||
|
@ -7,6 +7,7 @@ class ColouredEnum(Enum):
|
|||||||
|
|
||||||
This class extends the standard Enum but requires its values to be valid CSS
|
This class extends the standard Enum but requires its values to be valid CSS
|
||||||
colour codes. Supported colour formats include:
|
colour codes. Supported colour formats include:
|
||||||
|
|
||||||
- Hexadecimal colours
|
- Hexadecimal colours
|
||||||
- Hexadecimal colours with transparency
|
- Hexadecimal colours with transparency
|
||||||
- RGB colours
|
- RGB colours
|
||||||
@ -14,19 +15,20 @@ class ColouredEnum(Enum):
|
|||||||
- HSL colours
|
- HSL colours
|
||||||
- HSLA colours
|
- HSLA colours
|
||||||
- Predefined/Cross-browser colour names
|
- Predefined/Cross-browser colour names
|
||||||
|
|
||||||
Refer to the this website for more details on colour formats:
|
Refer to the this website for more details on colour formats:
|
||||||
(https://www.w3schools.com/cssref/css_colours_legal.php)
|
(https://www.w3schools.com/cssref/css_colours_legal.php)
|
||||||
|
|
||||||
The behavior of this component in the UI depends on how it's defined in the data
|
The behavior of this component in the UI depends on how it's defined in the data
|
||||||
service:
|
service:
|
||||||
- As property with a setter or as attribute: Renders as a dropdown menu,
|
|
||||||
allowing users to select and change its value from the frontend.
|
- As property with a setter or as attribute: Renders as a dropdown menu, allowing
|
||||||
|
users to select and change its value from the frontend.
|
||||||
- As property without a setter: Displays as a coloured box with the key of the
|
- As property without a setter: Displays as a coloured box with the key of the
|
||||||
`ColouredEnum` as text inside, serving as a visual indicator without user
|
`ColouredEnum` as text inside, serving as a visual indicator without user
|
||||||
interaction.
|
interaction.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
--------
|
|
||||||
```python
|
```python
|
||||||
import pydase.components as pyc
|
import pydase.components as pyc
|
||||||
import pydase
|
import pydase
|
||||||
@ -57,8 +59,7 @@ class ColouredEnum(Enum):
|
|||||||
my_service.status = MyStatus.FAILED
|
my_service.status = MyStatus.FAILED
|
||||||
```
|
```
|
||||||
|
|
||||||
Note
|
Note:
|
||||||
----
|
|
||||||
Each enumeration name and value must be unique. This means that you should use
|
Each enumeration name and value must be unique. This means that you should use
|
||||||
different colour formats when you want to use a colour multiple times.
|
different colour formats when you want to use a colour multiple times.
|
||||||
"""
|
"""
|
||||||
|
@ -19,22 +19,26 @@ class DeviceConnection(pydase.data_service.DataService):
|
|||||||
to the device. This method should update the `self._connected` attribute to reflect
|
to the device. This method should update the `self._connected` attribute to reflect
|
||||||
the connection status:
|
the connection status:
|
||||||
|
|
||||||
>>> class MyDeviceConnection(DeviceConnection):
|
```python
|
||||||
... def connect(self) -> None:
|
class MyDeviceConnection(DeviceConnection):
|
||||||
... # Implementation to connect to the device
|
def connect(self) -> None:
|
||||||
... # Update self._connected to `True` if connection is successful,
|
# Implementation to connect to the device
|
||||||
... # `False` otherwise
|
# Update self._connected to `True` if connection is successful,
|
||||||
... ...
|
# `False` otherwise
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
Optionally, if additional logic is needed to determine the connection status,
|
Optionally, if additional logic is needed to determine the connection status,
|
||||||
the `connected` property can also be overridden:
|
the `connected` property can also be overridden:
|
||||||
|
|
||||||
>>> class MyDeviceConnection(DeviceConnection):
|
```python
|
||||||
... @property
|
class MyDeviceConnection(DeviceConnection):
|
||||||
... def connected(self) -> bool:
|
@property
|
||||||
... # Custom logic to determine connection status
|
def connected(self) -> bool:
|
||||||
... return some_custom_condition
|
# Custom logic to determine connection status
|
||||||
...
|
return some_custom_condition
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
Frontend Representation
|
Frontend Representation
|
||||||
-----------------------
|
-----------------------
|
||||||
|
@ -11,19 +11,17 @@ class NumberSlider(DataService):
|
|||||||
This class models a UI slider for a data service, allowing for adjustments of a
|
This class models a UI slider for a data service, allowing for adjustments of a
|
||||||
parameter within a specified range and increments.
|
parameter within a specified range and increments.
|
||||||
|
|
||||||
Parameters:
|
Args:
|
||||||
-----------
|
value:
|
||||||
value (float, optional):
|
|
||||||
The initial value of the slider. Defaults to 0.
|
The initial value of the slider. Defaults to 0.
|
||||||
min (float, optional):
|
min_:
|
||||||
The minimum value of the slider. Defaults to 0.
|
The minimum value of the slider. Defaults to 0.
|
||||||
max (float, optional):
|
max_:
|
||||||
The maximum value of the slider. Defaults to 100.
|
The maximum value of the slider. Defaults to 100.
|
||||||
step_size (float, optional):
|
step_size:
|
||||||
The increment/decrement step size of the slider. Defaults to 1.0.
|
The increment/decrement step size of the slider. Defaults to 1.0.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
--------
|
|
||||||
```python
|
```python
|
||||||
class MySlider(pydase.components.NumberSlider):
|
class MySlider(pydase.components.NumberSlider):
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -6,18 +6,30 @@ from confz import BaseConfig, EnvSource
|
|||||||
|
|
||||||
class OperationMode(BaseConfig): # type: ignore[misc]
|
class OperationMode(BaseConfig): # type: ignore[misc]
|
||||||
environment: Literal["testing", "development", "production"] = "development"
|
environment: Literal["testing", "development", "production"] = "development"
|
||||||
|
"""The service's operation mode."""
|
||||||
|
|
||||||
CONFIG_SOURCES = EnvSource(allow=["ENVIRONMENT"])
|
CONFIG_SOURCES = EnvSource(allow=["ENVIRONMENT"])
|
||||||
|
|
||||||
|
|
||||||
class ServiceConfig(BaseConfig): # type: ignore[misc]
|
class ServiceConfig(BaseConfig): # type: ignore[misc]
|
||||||
|
"""Service configuration.
|
||||||
|
|
||||||
|
Variables can be set through environment variables prefixed with `SERVICE_` or an
|
||||||
|
`.env` file containing those variables.
|
||||||
|
"""
|
||||||
|
|
||||||
config_dir: Path = Path("config")
|
config_dir: Path = Path("config")
|
||||||
|
"""Configuration directory"""
|
||||||
web_port: int = 8001
|
web_port: int = 8001
|
||||||
|
"""Web server port"""
|
||||||
|
|
||||||
CONFIG_SOURCES = EnvSource(allow_all=True, prefix="SERVICE_", file=".env")
|
CONFIG_SOURCES = EnvSource(allow_all=True, prefix="SERVICE_", file=".env")
|
||||||
|
|
||||||
|
|
||||||
class WebServerConfig(BaseConfig): # type: ignore[misc]
|
class WebServerConfig(BaseConfig): # type: ignore[misc]
|
||||||
|
"""The service's web server configuration."""
|
||||||
|
|
||||||
generate_web_settings: bool = False
|
generate_web_settings: bool = False
|
||||||
|
"""Should generate web_settings.json file"""
|
||||||
|
|
||||||
CONFIG_SOURCES = EnvSource(allow=["GENERATE_WEB_SETTINGS"])
|
CONFIG_SOURCES = EnvSource(allow=["GENERATE_WEB_SETTINGS"])
|
||||||
|
@ -124,8 +124,10 @@ class DataServiceObserver(PropertyObserver):
|
|||||||
object.
|
object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
callback (Callable[[str, Any, dict[str, Any]]): The callback function to be
|
callback:
|
||||||
registered. The function should have the following signature:
|
The callback function to be registered. The function should have the
|
||||||
|
following signature:
|
||||||
|
|
||||||
- full_access_path (str): The full dot-notation access path of the
|
- full_access_path (str): The full dot-notation access path of the
|
||||||
changed attribute. This path indicates the location of the changed
|
changed attribute. This path indicates the location of the changed
|
||||||
attribute within the observable object's structure.
|
attribute within the observable object's structure.
|
||||||
|
@ -33,17 +33,19 @@ def load_state(func: Callable[..., Any]) -> Callable[..., Any]:
|
|||||||
the value should be loaded from the JSON file.
|
the value should be loaded from the JSON file.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
>>> class Service(pydase.DataService):
|
```python
|
||||||
... _name = "Service"
|
class Service(pydase.DataService):
|
||||||
...
|
_name = "Service"
|
||||||
... @property
|
|
||||||
... def name(self) -> str:
|
@property
|
||||||
... return self._name
|
def name(self) -> str:
|
||||||
...
|
return self._name
|
||||||
... @name.setter
|
|
||||||
... @load_state
|
@name.setter
|
||||||
... def name(self, value: str) -> None:
|
@load_state
|
||||||
... self._name = value
|
def name(self, value: str) -> None:
|
||||||
|
self._name = value
|
||||||
|
```
|
||||||
"""
|
"""
|
||||||
|
|
||||||
func._load_state = True # type: ignore[attr-defined]
|
func._load_state = True # type: ignore[attr-defined]
|
||||||
@ -85,13 +87,11 @@ class StateManager:
|
|||||||
StateManager provides a snapshot of the DataService's state that is sufficiently
|
StateManager provides a snapshot of the DataService's state that is sufficiently
|
||||||
accurate for initial rendering and interaction.
|
accurate for initial rendering and interaction.
|
||||||
|
|
||||||
Attributes:
|
Args:
|
||||||
cache (dict[str, Any]):
|
service:
|
||||||
A dictionary cache of the DataService's state.
|
|
||||||
filename (str):
|
|
||||||
The file name used for storing the DataService's state.
|
|
||||||
service (DataService):
|
|
||||||
The DataService instance whose state is being managed.
|
The DataService instance whose state is being managed.
|
||||||
|
filename:
|
||||||
|
The file name used for storing the DataService's state.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
The StateManager's cache updates are triggered by notifications and do not
|
The StateManager's cache updates are triggered by notifications and do not
|
||||||
@ -200,9 +200,11 @@ class StateManager:
|
|||||||
It also handles type-specific conversions for the new value before setting it.
|
It also handles type-specific conversions for the new value before setting it.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
path: A dot-separated string indicating the hierarchical path to the
|
path:
|
||||||
|
A dot-separated string indicating the hierarchical path to the
|
||||||
attribute.
|
attribute.
|
||||||
value: The new value to set for the attribute.
|
serialized_value:
|
||||||
|
The serialized representation of the new value to set for the attribute.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -17,10 +17,10 @@ def validate_set(
|
|||||||
getter and check against the desired value.
|
getter and check against the desired value.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
timeout (float):
|
timeout:
|
||||||
The maximum time (in seconds) to wait for the value to be within the
|
The maximum time (in seconds) to wait for the value to be within the
|
||||||
precision boundary.
|
precision boundary.
|
||||||
precision (float | None):
|
precision:
|
||||||
The acceptable deviation from the desired value. If None, the value must be
|
The acceptable deviation from the desired value. If None, the value must be
|
||||||
exact.
|
exact.
|
||||||
"""
|
"""
|
||||||
@ -44,13 +44,11 @@ def has_validate_set_decorator(prop: property) -> bool:
|
|||||||
Checks if a property setter has been decorated with the `validate_set` decorator.
|
Checks if a property setter has been decorated with the `validate_set` decorator.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
prop (property):
|
prop:
|
||||||
The property to check.
|
The property to check.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool:
|
True if the property setter has the `validate_set` decorator, False otherwise.
|
||||||
True if the property setter has the `validate_set` decorator, False
|
|
||||||
otherwise.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
property_setter = prop.fset
|
property_setter = prop.fset
|
||||||
@ -68,11 +66,11 @@ def _validate_value_was_correctly_set(
|
|||||||
specified `precision` and time `timeout`.
|
specified `precision` and time `timeout`.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
obj (Observable):
|
obj:
|
||||||
The instance of the class containing the property.
|
The instance of the class containing the property.
|
||||||
name (str):
|
name:
|
||||||
The name of the property to validate.
|
The name of the property to validate.
|
||||||
value (Any):
|
value:
|
||||||
The desired value to check against.
|
The desired value to check against.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
|
@ -64,18 +64,17 @@ class AdditionalServerProtocol(Protocol):
|
|||||||
|
|
||||||
|
|
||||||
class AdditionalServer(TypedDict):
|
class AdditionalServer(TypedDict):
|
||||||
"""
|
"""A TypedDict that represents the configuration for an additional server to be run
|
||||||
A TypedDict that represents the configuration for an additional server to be run
|
|
||||||
alongside the main server.
|
alongside the main server.
|
||||||
|
|
||||||
This class is used to specify the server type, the port on which the server should
|
|
||||||
run, and any additional keyword arguments that should be passed to the server when
|
|
||||||
it's instantiated.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
server: type[AdditionalServerProtocol]
|
server: type[AdditionalServerProtocol]
|
||||||
|
"""Server adhering to the
|
||||||
|
[`AdditionalServerProtocol`][pydase.server.server.AdditionalServerProtocol]."""
|
||||||
port: int
|
port: int
|
||||||
|
"""Port on which the server should run."""
|
||||||
kwargs: dict[str, Any]
|
kwargs: dict[str, Any]
|
||||||
|
"""Additional keyword arguments that will be passed to the server's constructor """
|
||||||
|
|
||||||
|
|
||||||
class Server:
|
class Server:
|
||||||
@ -83,29 +82,20 @@ class Server:
|
|||||||
The `Server` class provides a flexible server implementation for the `DataService`.
|
The `Server` class provides a flexible server implementation for the `DataService`.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
service: DataService
|
service:
|
||||||
The DataService instance that this server will manage.
|
The DataService instance that this server will manage.
|
||||||
host: str
|
host:
|
||||||
The host address for the server. Default is '0.0.0.0', which means all
|
The host address for the server. Defaults to `'0.0.0.0'`, which means all
|
||||||
available network interfaces.
|
available network interfaces.
|
||||||
web_port: int
|
web_port:
|
||||||
The port number for the web server. Default is
|
The port number for the web server. Defaults to
|
||||||
`pydase.config.ServiceConfig().web_port`.
|
[`ServiceConfig().web_port`][pydase.config.ServiceConfig.web_port].
|
||||||
enable_web: bool
|
enable_web:
|
||||||
Whether to enable the web server. Default is True.
|
Whether to enable the web server.
|
||||||
filename: str | Path | None
|
filename:
|
||||||
Filename of the file managing the service state persistence.
|
Filename of the file managing the service state persistence.
|
||||||
Defaults to None.
|
additional_servers:
|
||||||
additional_servers : list[AdditionalServer]
|
A list of additional servers to run alongside the main server.
|
||||||
A list of additional servers to run alongside the main server. Each entry in
|
|
||||||
the list should be a dictionary with the following structure:
|
|
||||||
- server: A class that adheres to the AdditionalServerProtocol. This
|
|
||||||
class should have an `__init__` method that accepts the DataService
|
|
||||||
instance, port, host, and optional keyword arguments, and a `serve`
|
|
||||||
method that is a coroutine responsible for starting the server.
|
|
||||||
- port: The port on which the additional server will be running.
|
|
||||||
- kwargs: A dictionary containing additional keyword arguments that will
|
|
||||||
be passed to the server's `__init__` method.
|
|
||||||
|
|
||||||
Here's an example of how you might define an additional server:
|
Here's an example of how you might define an additional server:
|
||||||
|
|
||||||
@ -145,7 +135,7 @@ class Server:
|
|||||||
)
|
)
|
||||||
server.run()
|
server.run()
|
||||||
```
|
```
|
||||||
**kwargs: Any
|
**kwargs:
|
||||||
Additional keyword arguments.
|
Additional keyword arguments.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -214,7 +204,7 @@ class Server:
|
|||||||
)
|
)
|
||||||
|
|
||||||
server_task = self._loop.create_task(addin_server.serve())
|
server_task = self._loop.create_task(addin_server.serve())
|
||||||
server_task.add_done_callback(self.handle_server_shutdown)
|
server_task.add_done_callback(self._handle_server_shutdown)
|
||||||
self.servers[server_name] = server_task
|
self.servers[server_name] = server_task
|
||||||
if self._enable_web:
|
if self._enable_web:
|
||||||
self._web_server = WebServer(
|
self._web_server = WebServer(
|
||||||
@ -225,10 +215,10 @@ class Server:
|
|||||||
)
|
)
|
||||||
server_task = self._loop.create_task(self._web_server.serve())
|
server_task = self._loop.create_task(self._web_server.serve())
|
||||||
|
|
||||||
server_task.add_done_callback(self.handle_server_shutdown)
|
server_task.add_done_callback(self._handle_server_shutdown)
|
||||||
self.servers["web"] = server_task
|
self.servers["web"] = server_task
|
||||||
|
|
||||||
def handle_server_shutdown(self, task: asyncio.Task[Any]) -> None:
|
def _handle_server_shutdown(self, task: asyncio.Task[Any]) -> None:
|
||||||
"""Handle server shutdown. If the service should exit, do nothing. Else, make
|
"""Handle server shutdown. If the service should exit, do nothing. Else, make
|
||||||
the service exit."""
|
the service exit."""
|
||||||
|
|
||||||
|
@ -54,12 +54,15 @@ class RunMethodDict(TypedDict):
|
|||||||
exposed DataService.
|
exposed DataService.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
name (str): The name of the method to be run.
|
name:
|
||||||
parent_path (str): The access path for the parent object of the method to be
|
The name of the method to be run.
|
||||||
run. This is used to construct the full access path for the method. For
|
parent_path:
|
||||||
example, for an method with access path 'attr1.list_attr[0].method_name',
|
The access path for the parent object of the method to be run. This is used
|
||||||
'attr1.list_attr[0]' would be the parent_path.
|
to construct the full access path for the method. For example, for an method
|
||||||
kwargs (dict[str, Any]): The arguments passed to the method.
|
with access path 'attr1.list_attr[0].method_name', 'attr1.list_attr[0]'
|
||||||
|
would be the parent_path.
|
||||||
|
kwargs:
|
||||||
|
The arguments passed to the method.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name: str
|
name: str
|
||||||
@ -76,15 +79,15 @@ def setup_sio_server(
|
|||||||
Sets up and configures a Socket.IO asynchronous server.
|
Sets up and configures a Socket.IO asynchronous server.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
observer (DataServiceObserver):
|
observer:
|
||||||
The observer managing state updates and communication.
|
The observer managing state updates and communication.
|
||||||
enable_cors (bool):
|
enable_cors:
|
||||||
Flag indicating whether CORS should be enabled for the server.
|
Flag indicating whether CORS should be enabled for the server.
|
||||||
loop (asyncio.AbstractEventLoop):
|
loop:
|
||||||
The event loop in which the server will run.
|
The event loop in which the server will run.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
socketio.AsyncServer: The configured Socket.IO asynchronous server.
|
The configured Socket.IO asynchronous server.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
state_manager = observer.state_manager
|
state_manager = observer.state_manager
|
||||||
|
@ -25,41 +25,50 @@ API_VERSION = "v1"
|
|||||||
|
|
||||||
class WebServer:
|
class WebServer:
|
||||||
"""
|
"""
|
||||||
Represents a web server that adheres to the AdditionalServerProtocol, designed to
|
Represents a web server that adheres to the
|
||||||
work with a DataService instance. This server facilitates client-server
|
[`AdditionalServerProtocol`][pydase.server.server.AdditionalServerProtocol],
|
||||||
communication and state management through web protocols and socket connections.
|
designed to work with a [`DataService`][pydase.DataService] instance. This server
|
||||||
|
facilitates client-server communication and state management through web protocols
|
||||||
|
and socket connections.
|
||||||
|
|
||||||
The WebServer class initializes and manages a web server environment using FastAPI
|
The WebServer class initializes and manages a web server environment aiohttp and
|
||||||
and Socket.IO, allowing for HTTP and WebSocket communications. It incorporates CORS
|
Socket.IO, allowing for HTTP and Socket.IO communications. It incorporates CORS
|
||||||
(Cross-Origin Resource Sharing) support, custom CSS, and serves a frontend static
|
(Cross-Origin Resource Sharing) support, custom CSS, and serves a static files
|
||||||
files directory. It also initializes web server settings based on configuration
|
directory. It also initializes web server settings based on configuration files or
|
||||||
files or generates default settings if necessary.
|
generates default settings if necessary.
|
||||||
|
|
||||||
Configuration for the web server (like service configuration directory and whether
|
Configuration for the web server (like service configuration directory and whether
|
||||||
to generate new web settings) is determined in the following order of precedence:
|
to generate new web settings) is determined in the following order of precedence:
|
||||||
|
|
||||||
1. Values provided directly to the constructor.
|
1. Values provided directly to the constructor.
|
||||||
2. Environment variable settings (via configuration classes like
|
2. Environment variable settings (via configuration classes like
|
||||||
`pydase.config.ServiceConfig` and `pydase.config.WebServerConfig`).
|
[`ServiceConfig`][pydase.config.ServiceConfig] and
|
||||||
|
[`WebServerConfig`][pydase.config.WebServerConfig]).
|
||||||
3. Default values defined in the configuration classes.
|
3. Default values defined in the configuration classes.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data_service_observer (DataServiceObserver): Observer for the DataService,
|
data_service_observer:
|
||||||
handling state updates and communication to connected clients.
|
Observer for the [`DataService`][pydase.DataService], handling state updates and communication to
|
||||||
host (str): Hostname or IP address where the server is accessible. Commonly
|
connected clients.
|
||||||
'0.0.0.0' to bind to all network interfaces.
|
host:
|
||||||
port (int): Port number on which the server listens. Typically in the range
|
Hostname or IP address where the server is accessible. Commonly '0.0.0.0'
|
||||||
1024-65535 (non-standard ports).
|
to bind to all network interfaces.
|
||||||
css (str | Path | None, optional): Path to a custom CSS file for styling the
|
port:
|
||||||
frontend. If None, no custom styles are applied. Defaults to None.
|
Port number on which the server listens. Typically in the range 1024-65535
|
||||||
enable_cors (bool, optional): Flag to enable or disable CORS policy. When True,
|
(non-standard ports).
|
||||||
CORS is enabled, allowing cross-origin requests. Defaults to True.
|
css:
|
||||||
config_dir (Path | None, optional): Path to the configuration
|
Path to a custom CSS file for styling the frontend. If None, no custom
|
||||||
directory where the web settings will be stored. Defaults to
|
styles are applied. Defaults to None.
|
||||||
`pydase.config.ServiceConfig().config_dir`.
|
enable_cors:
|
||||||
generate_new_web_settings (bool | None, optional): Flag to enable or disable
|
Flag to enable or disable CORS policy. When True, CORS is enabled, allowing
|
||||||
generation of new web settings if the configuration file is missing. Defaults
|
cross-origin requests. Defaults to True.
|
||||||
to `pydase.config.WebServerConfig().generate_new_web_settings`.
|
config_dir:
|
||||||
**kwargs (Any): Additional unused keyword arguments.
|
Path to the configuration directory where the web settings will be stored.
|
||||||
|
Defaults to [`ServiceConfig().config_dir`][pydase.config.ServiceConfig.config_dir].
|
||||||
|
generate_web_settings:
|
||||||
|
Flag to enable or disable generation of new web settings if the
|
||||||
|
configuration file is missing. Defaults to
|
||||||
|
[`WebServerConfig().generate_web_settings`][pydase.config.WebServerConfig.generate_web_settings].
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__( # noqa: PLR0913
|
def __init__( # noqa: PLR0913
|
||||||
|
@ -21,18 +21,20 @@ def convert_to_quantity(
|
|||||||
Convert a given value into a pint.Quantity object with the specified unit.
|
Convert a given value into a pint.Quantity object with the specified unit.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value (QuantityDict | float | int | Quantity):
|
value:
|
||||||
The value to be converted into a Quantity object.
|
The value to be converted into a Quantity object.
|
||||||
|
|
||||||
- If value is a float or int, it will be directly converted to the specified
|
- If value is a float or int, it will be directly converted to the specified
|
||||||
unit.
|
unit.
|
||||||
- If value is a dict, it must have keys 'magnitude' and 'unit' to represent
|
- If value is a dict, it must have keys 'magnitude' and 'unit' to represent
|
||||||
the value and unit.
|
the value and unit.
|
||||||
- If value is a Quantity object, it will remain unchanged.\n
|
- If value is a Quantity object, it will remain unchanged.\n
|
||||||
unit (str, optional): The target unit for conversion. If empty and value is not
|
unit:
|
||||||
a Quantity object, it will assume a unitless quantity.
|
The target unit for conversion. If empty and value is not a Quantity object,
|
||||||
|
it will assume a unitless quantity.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Quantity: The converted value as a pint.Quantity object with the specified unit.
|
The converted value as a pint.Quantity object with the specified unit.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
>>> convert_to_quantity(5, 'm')
|
>>> convert_to_quantity(5, 'm')
|
||||||
@ -42,9 +44,9 @@ def convert_to_quantity(
|
|||||||
>>> convert_to_quantity(10.0 * u.units.V)
|
>>> convert_to_quantity(10.0 * u.units.V)
|
||||||
<Quantity(10.0, 'volt')>
|
<Quantity(10.0, 'volt')>
|
||||||
|
|
||||||
Notes:
|
Note:
|
||||||
- If unit is not provided and value is a float or int, the resulting Quantity
|
If unit is not provided and value is a float or int, the resulting Quantity will
|
||||||
will be unitless.
|
be unitless.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isinstance(value, int | float):
|
if isinstance(value, int | float):
|
||||||
|
@ -10,9 +10,9 @@ class FunctionDefinitionError(Exception):
|
|||||||
|
|
||||||
|
|
||||||
def frontend(func: Callable[..., Any]) -> Callable[..., Any]:
|
def frontend(func: Callable[..., Any]) -> Callable[..., Any]:
|
||||||
"""
|
"""Decorator to mark a [`DataService`][pydase.DataService] method for frontend
|
||||||
Decorator to mark a DataService method for frontend rendering. Ensures that the
|
rendering. Ensures that the method does not contain arguments, as they are not
|
||||||
method does not contain arguments, as they are not supported for frontend rendering.
|
supported for frontend rendering.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if function_has_arguments(func):
|
if function_has_arguments(func):
|
||||||
|
@ -19,10 +19,15 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
json.loads
|
||||||
|
|
||||||
|
|
||||||
class Deserializer:
|
class Deserializer:
|
||||||
@classmethod
|
@classmethod
|
||||||
def deserialize(cls, serialized_object: SerializedObject) -> Any:
|
def deserialize(cls, serialized_object: SerializedObject) -> Any:
|
||||||
|
"""Deserialize `serialized_object` (a `dict`) to a Python object."""
|
||||||
type_handler: dict[str | None, None | Callable[..., Any]] = {
|
type_handler: dict[str | None, None | Callable[..., Any]] = {
|
||||||
None: None,
|
None: None,
|
||||||
"int": cls.deserialize_primitive,
|
"int": cls.deserialize_primitive,
|
||||||
@ -159,4 +164,5 @@ class Deserializer:
|
|||||||
|
|
||||||
|
|
||||||
def loads(serialized_object: SerializedObject) -> Any:
|
def loads(serialized_object: SerializedObject) -> Any:
|
||||||
|
"""Deserialize `serialized_object` (a `dict`) to a Python object."""
|
||||||
return Deserializer.deserialize(serialized_object)
|
return Deserializer.deserialize(serialized_object)
|
||||||
|
@ -52,8 +52,27 @@ class SerializationPathError(Exception):
|
|||||||
|
|
||||||
|
|
||||||
class Serializer:
|
class Serializer:
|
||||||
|
"""Serializes objects into
|
||||||
|
[`SerializedObject`][pydase.utils.serialization.types.SerializedObject]
|
||||||
|
representations.
|
||||||
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def serialize_object(cls, obj: Any, access_path: str = "") -> SerializedObject: # noqa: C901
|
def serialize_object(cls, obj: Any, access_path: str = "") -> SerializedObject: # noqa: C901
|
||||||
|
"""Serialize `obj` to a
|
||||||
|
[`SerializedObject`][pydase.utils.serialization.types.SerializedObject].
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obj:
|
||||||
|
Object to be serialized.
|
||||||
|
access_path:
|
||||||
|
String corresponding to the full access path of the object. This will be
|
||||||
|
prepended to the full_access_path in the SerializedObject entries.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary representation of `obj`.
|
||||||
|
"""
|
||||||
|
|
||||||
result: SerializedObject
|
result: SerializedObject
|
||||||
|
|
||||||
if isinstance(obj, Exception):
|
if isinstance(obj, Exception):
|
||||||
@ -313,6 +332,19 @@ class Serializer:
|
|||||||
|
|
||||||
|
|
||||||
def dump(obj: Any) -> SerializedObject:
|
def dump(obj: Any) -> SerializedObject:
|
||||||
|
"""Serialize `obj` to a
|
||||||
|
[`SerializedObject`][pydase.utils.serialization.types.SerializedObject].
|
||||||
|
|
||||||
|
The [`Serializer`][pydase.utils.serialization.serializer.Serializer] is used for
|
||||||
|
encoding.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obj:
|
||||||
|
Object to be serialized.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary representation of `obj`.
|
||||||
|
"""
|
||||||
return Serializer.serialize_object(obj)
|
return Serializer.serialize_object(obj)
|
||||||
|
|
||||||
|
|
||||||
@ -321,12 +353,13 @@ def set_nested_value_by_path(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Set a value in a nested dictionary structure, which conforms to the serialization
|
Set a value in a nested dictionary structure, which conforms to the serialization
|
||||||
format used by `pydase.utils.serializer.Serializer`, using a dot-notation path.
|
format used by [`Serializer`][pydase.utils.serialization.serializer.Serializer],
|
||||||
|
using a dot-notation path.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
serialization_dict:
|
serialization_dict:
|
||||||
The base dictionary representing data serialized with
|
The base dictionary representing data serialized with
|
||||||
`pydase.utils.serializer.Serializer`.
|
[`Serializer`][pydase.utils.serialization.serializer.Serializer].
|
||||||
path:
|
path:
|
||||||
The dot-notation path (e.g., 'attr1.attr2[0].attr3') indicating where to
|
The dot-notation path (e.g., 'attr1.attr2[0].attr3') indicating where to
|
||||||
set the value.
|
set the value.
|
||||||
@ -334,7 +367,7 @@ def set_nested_value_by_path(
|
|||||||
The new value to set at the specified path.
|
The new value to set at the specified path.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
- If the index equals the length of the list, the function will append the
|
If the index equals the length of the list, the function will append the
|
||||||
serialized representation of the 'value' to the list.
|
serialized representation of the 'value' to the list.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -438,26 +471,24 @@ def get_container_item_by_key(
|
|||||||
) -> SerializedObject:
|
) -> SerializedObject:
|
||||||
"""
|
"""
|
||||||
Retrieve an item from a container specified by the passed key. Add an item to the
|
Retrieve an item from a container specified by the passed key. Add an item to the
|
||||||
container if allow_append is set to True.
|
container if `allow_append` is set to `True`.
|
||||||
|
|
||||||
If specified keys or indexes do not exist, the function can append new elements to
|
If specified keys or indexes do not exist, the function can append new elements to
|
||||||
dictionaries and to lists if `allow_append` is True and the missing element is
|
dictionaries and to lists if `allow_append` is True and the missing element is
|
||||||
exactly the next sequential index (for lists).
|
exactly the next sequential index (for lists).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
container: dict[str, SerializedObject] | list[SerializedObject]
|
container:
|
||||||
The container representing serialized data.
|
The container representing serialized data.
|
||||||
key: str
|
key:
|
||||||
The key name representing the attribute in the dictionary, which may include
|
The key name representing the attribute in the dictionary, which may include
|
||||||
direct keys or indexes (e.g., 'attr_name', '["key"]' or '[0]').
|
direct keys or indexes (e.g., 'attr_name', '["key"]' or '[0]').
|
||||||
allow_append: bool
|
allow_append:
|
||||||
Flag to allow appending a new entry if the specified index is out of range
|
Flag to allow appending a new entry if the specified index is out of range
|
||||||
by exactly one position.
|
by exactly one position.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
SerializedObject
|
The dictionary or list item corresponding to the specified attribute and index.
|
||||||
The dictionary or list item corresponding to the specified attribute and
|
|
||||||
index.
|
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
SerializationPathError:
|
SerializationPathError:
|
||||||
@ -485,12 +516,11 @@ def get_data_paths_from_serialized_object( # noqa: C901
|
|||||||
Recursively extracts full access paths from a serialized object.
|
Recursively extracts full access paths from a serialized object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
serialized_obj (SerializedObject):
|
serialized_obj:
|
||||||
The dictionary representing the serialization of an object. Produced by
|
The dictionary representing the serialization of an object. Produced by
|
||||||
`pydase.utils.serializer.Serializer`.
|
`pydase.utils.serializer.Serializer`.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[str]:
|
|
||||||
A list of strings, each representing a full access path in the serialized
|
A list of strings, each representing a full access path in the serialized
|
||||||
object.
|
object.
|
||||||
"""
|
"""
|
||||||
@ -532,11 +562,10 @@ def generate_serialized_data_paths(
|
|||||||
Recursively extracts full access paths from a serialized DataService class instance.
|
Recursively extracts full access paths from a serialized DataService class instance.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data (dict[str, SerializedObject]):
|
data:
|
||||||
The value of the "value" key of a serialized DataService class instance.
|
The value of the "value" key of a serialized DataService class instance.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
list[str]:
|
|
||||||
A list of strings, each representing a full access path in the serialized
|
A list of strings, each representing a full access path in the serialized
|
||||||
object.
|
object.
|
||||||
"""
|
"""
|
||||||
@ -556,3 +585,6 @@ def serialized_dict_is_nested_object(serialized_dict: SerializedObject) -> bool:
|
|||||||
# We are excluding Quantity here as the value corresponding to the "value" key is
|
# We are excluding Quantity here as the value corresponding to the "value" key is
|
||||||
# a dictionary of the form {"magnitude": ..., "unit": ...}
|
# a dictionary of the form {"magnitude": ..., "unit": ...}
|
||||||
return serialized_dict["type"] != "Quantity" and (isinstance(value, dict | list))
|
return serialized_dict["type"] != "Quantity" and (isinstance(value, dict | list))
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["Serializer", "dump"]
|
||||||
|
@ -123,3 +123,21 @@ SerializedObject = (
|
|||||||
| SerializedQuantity
|
| SerializedQuantity
|
||||||
| SerializedNoValue
|
| SerializedNoValue
|
||||||
)
|
)
|
||||||
|
"""
|
||||||
|
This type can be any of the following:
|
||||||
|
|
||||||
|
- SerializedBool
|
||||||
|
- SerializedFloat
|
||||||
|
- SerializedInteger
|
||||||
|
- SerializedString
|
||||||
|
- SerializedDatetime
|
||||||
|
- SerializedList
|
||||||
|
- SerializedDict
|
||||||
|
- SerializedNoneType
|
||||||
|
- SerializedMethod
|
||||||
|
- SerializedException
|
||||||
|
- SerializedDataService
|
||||||
|
- SerializedEnum
|
||||||
|
- SerializedQuantity
|
||||||
|
- SerializedNoValue
|
||||||
|
"""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user