12 Commits

Author SHA1 Message Date
Mose Müller
71e29c890e Merge pull request #256 from tiqi-group/release-v0.10.19
updates to version v0.10.19
2025-07-08 15:39:59 +02:00
Mose Müller
6e407ba1d6 updates to version v0.10.19 2025-07-08 15:39:40 +02:00
Mose Müller
4fb5e56aa8 Merge pull request #255 from tiqi-group/fix/property_observer_race_condition
fix: race-condition in PropertyObserver
2025-07-08 15:37:45 +02:00
Mose Müller
d55ba3a85f fix: race-condition in PropertyObserver
When a proxy of a pydase client initialised with
block_until_connected=False is set as an attribute of a data service, a
race condition can happen: when the client connects while the
DataServiceObserver is being initialised, the property_deps_dict
attribute might not be set yet while the DataServiceObserver was already
added as an observer to the client proxy. The proxy will then emit a
notification, which in turn tries to get the dependent properties from
the property_deps_dict attribute, which has not been initialised yet.
The resulting exception will not tell the proxy that the client has
connected.
2025-07-08 15:29:27 +02:00
Mose Müller
265d9a7ef5 Merge pull request #254 from tiqi-group/fix/serialize_exception
fix: serialize exception
2025-07-03 15:59:35 +02:00
Mose Müller
4cd36b4a2b tests: adds test for exception serialization 2025-07-03 15:55:41 +02:00
Mose Müller
1b2ff38aff fix: serializing exception that didn't take an argument
An exception that was instantiated without any argument could not be
serilaized before. Now, I check if any args were supplied and set the
value to an empty string if no args were passed.
2025-07-03 15:55:25 +02:00
Mose Müller
4b243985e8 Merge pull request #253 from tiqi-group/feat/reset_frontend_value_on_exception
feat: reset frontend value to last value on exception
2025-07-03 15:53:36 +02:00
Mose Müller
8615bdeadc npm run build 2025-07-03 15:52:32 +02:00
Mose Müller
d24893a989 feat: reset frontend value to last value on exception
When changing a value in the frontend and this operation triggers an
exception in the backend, reset the frontend value to the last known
value.
2025-07-03 15:52:22 +02:00
Mose Müller
661603ef71 Merge pull request #252 from tiqi-group/alert-autofix-4
Potential fix for code scanning alert no. 4: Workflow does not contain permissions
2025-06-27 08:56:48 +02:00
Mose Müller
d6947b0f43 Potential fix for code scanning alert no. 4: Workflow does not contain permissions
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-06-27 08:54:57 +02:00
11 changed files with 58 additions and 21 deletions

View File

@@ -2,6 +2,8 @@
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
name: Python package
permissions:
contents: read
on:
push:

View File

@@ -50,7 +50,7 @@ const createDisplayNameFromAccessPath = (fullAccessPath: string): string => {
function changeCallback(
value: SerializedObject,
callback: (ack: unknown) => void = () => {},
callback: (ack: undefined | SerializedObject) => void = () => {},
) {
updateValue(value, callback);
}

View File

@@ -38,7 +38,10 @@ interface NumberComponentProps {
isInstantUpdate: boolean;
unit?: string;
addNotification: (message: string, levelname?: LevelName) => void;
changeCallback?: (value: SerializedObject, callback?: (ack: unknown) => void) => void;
changeCallback?: (
value: SerializedObject,
callback?: (ack: undefined | SerializedObject) => void,
) => void;
displayName?: string;
id: string;
}
@@ -217,6 +220,15 @@ export const NumberComponent = React.memo((props: NumberComponentProps) => {
id,
} = props;
const handleChange = (newValue: SerializedObject) => {
changeCallback(newValue, (result: undefined | SerializedObject) => {
if (result === undefined) return;
if (result.type == "Exception") {
setInputString(value.toString());
}
});
};
// Create a state for the cursor position
const cursorPositionRef = useRef<number | null>(null);
@@ -319,7 +331,7 @@ export const NumberComponent = React.memo((props: NumberComponentProps) => {
};
}
changeCallback(serializedObject);
handleChange(serializedObject);
return;
} else {
console.debug(key);
@@ -350,7 +362,7 @@ export const NumberComponent = React.memo((props: NumberComponentProps) => {
};
}
changeCallback(serializedObject);
handleChange(serializedObject);
}
setInputString(newValue);
@@ -384,7 +396,7 @@ export const NumberComponent = React.memo((props: NumberComponentProps) => {
};
}
changeCallback(serializedObject);
handleChange(serializedObject);
}
};
useEffect(() => {

View File

@@ -19,7 +19,10 @@ interface SliderComponentProps {
stepSize: NumberObject;
isInstantUpdate: boolean;
addNotification: (message: string, levelname?: LevelName) => void;
changeCallback?: (value: SerializedObject, callback?: (ack: unknown) => void) => void;
changeCallback?: (
value: SerializedObject,
callback?: (ack: undefined | SerializedObject) => void,
) => void;
displayName: string;
id: string;
}

View File

@@ -28,7 +28,7 @@ export const socket = io(URL, {
export const updateValue = (
serializedObject: SerializedObject,
callback?: (ack: unknown) => void,
callback?: (ack: undefined | SerializedObject) => void,
) => {
if (callback) {
socket.emit(

View File

@@ -1,6 +1,6 @@
[project]
name = "pydase"
version = "0.10.18"
version = "0.10.19"
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 = [
{name = "Mose Müller",email = "mosemueller@gmail.com"}

View File

@@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site displaying a pydase UI." />
<script type="module" crossorigin src="/assets/index-XZbNXHJp.js"></script>
<script type="module" crossorigin src="/assets/index-CKS_bS2p.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Cs09d5Pk.css">
</head>

View File

@@ -29,6 +29,7 @@ def get_property_dependencies(prop: property, prefix: str = "") -> list[str]:
class PropertyObserver(Observer):
def __init__(self, observable: Observable) -> None:
self.property_deps_dict: dict[str, list[str]] = {}
super().__init__(observable)
self._update_property_deps_dict()

View File

@@ -158,7 +158,7 @@ class Serializer:
"doc": None,
"readonly": True,
"type": "Exception",
"value": obj.args[0],
"value": obj.args[0] if len(obj.args) > 0 else "",
"name": obj.__class__.__name__,
}

View File

@@ -1225,3 +1225,22 @@ def test_add_prefix_to_full_access_path(
serialized_obj: SerializedObject, prefix: str, expected: SerializedObject
) -> None:
assert add_prefix_to_full_access_path(serialized_obj, prefix) == expected
def test_serialize_exception() -> None:
assert dump(Exception()) == {
"doc": None,
"full_access_path": "",
"name": "Exception",
"readonly": True,
"type": "Exception",
"value": "",
}
assert dump(Exception("Exception message")) == {
"doc": None,
"full_access_path": "",
"name": "Exception",
"readonly": True,
"type": "Exception",
"value": "Exception message",
}