mirror of
https://github.com/tiqi-group/pydase.git
synced 2026-01-05 20:14:09 +01:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a46945ed29 | ||
|
|
21809b49e8 | ||
|
|
c45f1bd489 | ||
|
|
5784818e5a | ||
|
|
64a7097568 | ||
|
|
5ef382728c | ||
|
|
51d6189002 |
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2023-2024 Mose Müller <mosemueller@gmail.com>
|
Copyright (c) 2023-2025 ETH Zurich, Mose Müller, Carmelo Mordini
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
[](https://pypi.org/project/pydase/)
|
[](https://pypi.org/project/pydase/)
|
||||||
[](https://pydase.readthedocs.io/en/stable/)
|
[](https://pydase.readthedocs.io/en/stable/)
|
||||||
[][License]
|
[][License]
|
||||||
[](https://doi.org/10.5281/zenodo.15703190)
|
[](http://doi.org/10.5905/ethz-1007-907)
|
||||||
|
|
||||||
`pydase` is a Python library that simplifies the creation of remote control interfaces for Python objects. It exposes the public attributes of a user-defined class via a [Socket.IO](https://python-socketio.readthedocs.io/en/stable/) web server, ensuring they are always in sync with the service state. You can interact with these attributes using an RPC client, a RESTful API, or a web browser. The web browser frontend is auto-generated, displaying components that correspond to each public attribute of the class for direct interaction.
|
`pydase` is a Python library that simplifies the creation of remote control interfaces for Python objects. It exposes the public attributes of a user-defined class via a [Socket.IO](https://python-socketio.readthedocs.io/en/stable/) web server, ensuring they are always in sync with the service state. You can interact with these attributes using an RPC client, a RESTful API, or a web browser. The web browser frontend is auto-generated, displaying components that correspond to each public attribute of the class for direct interaction.
|
||||||
`pydase` implements an [observer pattern][Observer Pattern] to provide the real-time updates, ensuring that changes to the class attributes are reflected across all clients.
|
`pydase` implements an [observer pattern][Observer Pattern] to provide the real-time updates, ensuring that changes to the class attributes are reflected across all clients.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "pydase"
|
name = "pydase"
|
||||||
version = "0.10.19"
|
version = "0.10.21"
|
||||||
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 = [
|
authors = [
|
||||||
{name = "Mose Müller",email = "mosemueller@gmail.com"}
|
{name = "Mose Müller",email = "mosemueller@gmail.com"}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ from pydase.observer_pattern.observable.observable import (
|
|||||||
from pydase.utils.helpers import (
|
from pydase.utils.helpers import (
|
||||||
get_class_and_instance_attributes,
|
get_class_and_instance_attributes,
|
||||||
is_descriptor,
|
is_descriptor,
|
||||||
is_property_attribute,
|
|
||||||
)
|
)
|
||||||
from pydase.utils.serialization.serializer import (
|
from pydase.utils.serialization.serializer import (
|
||||||
Serializer,
|
Serializer,
|
||||||
@@ -28,9 +27,6 @@ class DataService(AbstractDataService):
|
|||||||
self.__check_instance_classes()
|
self.__check_instance_classes()
|
||||||
|
|
||||||
def __setattr__(self, name: str, value: Any, /) -> None:
|
def __setattr__(self, name: str, value: Any, /) -> None:
|
||||||
# Check and warn for unexpected type changes in attributes
|
|
||||||
self._warn_on_type_change(name, value)
|
|
||||||
|
|
||||||
# every class defined by the user should inherit from DataService if it is
|
# every class defined by the user should inherit from DataService if it is
|
||||||
# assigned to a public attribute
|
# assigned to a public attribute
|
||||||
if not name.startswith("_") and not inspect.isfunction(value):
|
if not name.startswith("_") and not inspect.isfunction(value):
|
||||||
@@ -39,21 +35,6 @@ class DataService(AbstractDataService):
|
|||||||
# Set the attribute
|
# Set the attribute
|
||||||
super().__setattr__(name, value)
|
super().__setattr__(name, value)
|
||||||
|
|
||||||
def _warn_on_type_change(self, attr_name: str, new_value: Any) -> None:
|
|
||||||
if is_property_attribute(self, attr_name):
|
|
||||||
return
|
|
||||||
|
|
||||||
current_value = getattr(self, attr_name, None)
|
|
||||||
if self._is_unexpected_type_change(current_value, new_value):
|
|
||||||
logger.warning(
|
|
||||||
"Type of '%s' changed from '%s' to '%s'. This may have unwanted "
|
|
||||||
"side effects! Consider setting it to '%s' directly.",
|
|
||||||
attr_name,
|
|
||||||
type(current_value).__name__,
|
|
||||||
type(new_value).__name__,
|
|
||||||
type(current_value).__name__,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _is_unexpected_type_change(self, current_value: Any, new_value: Any) -> bool:
|
def _is_unexpected_type_change(self, current_value: Any, new_value: Any) -> bool:
|
||||||
return (
|
return (
|
||||||
isinstance(current_value, float) and not isinstance(new_value, float)
|
isinstance(current_value, float) and not isinstance(new_value, float)
|
||||||
|
|||||||
@@ -1,38 +1,13 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import pydase
|
|
||||||
import pydase.units as u
|
|
||||||
import pytest
|
import pytest
|
||||||
from pydase import DataService
|
|
||||||
from pydase.data_service.data_service_observer import DataServiceObserver
|
|
||||||
from pydase.data_service.state_manager import StateManager
|
|
||||||
from pydase.utils.decorators import FunctionDefinitionError, frontend
|
|
||||||
from pytest import LogCaptureFixture
|
from pytest import LogCaptureFixture
|
||||||
|
|
||||||
|
import pydase
|
||||||
def test_unexpected_type_change_warning(caplog: LogCaptureFixture) -> None:
|
import pydase.units as u
|
||||||
class ServiceClass(DataService):
|
from pydase import DataService
|
||||||
attr_1 = 1.0
|
from pydase.utils.decorators import FunctionDefinitionError, frontend
|
||||||
current = 1.0 * u.units.A
|
|
||||||
|
|
||||||
service_instance = ServiceClass()
|
|
||||||
state_manager = StateManager(service_instance)
|
|
||||||
DataServiceObserver(state_manager)
|
|
||||||
service_instance.attr_1 = 2
|
|
||||||
|
|
||||||
assert "'attr_1' changed to '2'" in caplog.text
|
|
||||||
assert (
|
|
||||||
"Type of 'attr_1' changed from 'float' to 'int'. This may have unwanted "
|
|
||||||
"side effects! Consider setting it to 'float' directly." in caplog.text
|
|
||||||
)
|
|
||||||
|
|
||||||
service_instance.current = 2
|
|
||||||
assert "'current' changed to '2'" in caplog.text
|
|
||||||
assert (
|
|
||||||
"Type of 'current' changed from 'Quantity' to 'int'. This may have unwanted "
|
|
||||||
"side effects! Consider setting it to 'Quantity' directly." in caplog.text
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_basic_inheritance_warning(caplog: LogCaptureFixture) -> None:
|
def test_basic_inheritance_warning(caplog: LogCaptureFixture) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user