1
0
mirror of https://github.com/bec-project/bec_widgets.git synced 2026-04-16 05:30:54 +02:00

Compare commits

...

6 Commits

Author SHA1 Message Date
semantic-release
bb4c30ad80 2.30.2
Automatically generated by python-semantic-release
2025-07-23 06:57:35 +00:00
3fd09fceef test(test_plotting_framework_e2e): added test for waveform with passing device from dev container 2025-07-23 08:56:52 +02:00
8eb8225a7f fix: factor out device name function and add test 2025-07-23 08:56:52 +02:00
491d04467c fix(rpc_base): rpc_call wrapper passes full_name for Devices indeed of name 2025-07-23 08:56:52 +02:00
semantic-release
3bcff75107 2.30.1
Automatically generated by python-semantic-release
2025-07-22 18:19:10 +00:00
608590c542 fix: ignore KeyError in SignalLabel 2025-07-22 20:18:28 +02:00
7 changed files with 108 additions and 22 deletions

View File

@@ -1,6 +1,30 @@
# CHANGELOG
## v2.30.2 (2025-07-23)
### Bug Fixes
- Factor out device name function and add test
([`8eb8225`](https://github.com/bec-project/bec_widgets/commit/8eb8225a7f56014d6093aa142b3a5d071837982e))
- **rpc_base**: Rpc_call wrapper passes full_name for Devices indeed of name
([`491d044`](https://github.com/bec-project/bec_widgets/commit/491d04467c8ce4e116d61e614895d1dcc6b4b201))
### Testing
- **test_plotting_framework_e2e**: Added test for waveform with passing device from dev container
([`3fd09fc`](https://github.com/bec-project/bec_widgets/commit/3fd09fceef2ffa7e7c3eee20176304bafb00d0db))
## v2.30.1 (2025-07-22)
### Bug Fixes
- Ignore KeyError in SignalLabel
([`608590c`](https://github.com/bec-project/bec_widgets/commit/608590c5421368d5bba0e4b0f5187d90cac323be))
## v2.30.0 (2025-07-22)
### Bug Fixes

View File

@@ -7,6 +7,7 @@ from functools import wraps
from typing import TYPE_CHECKING, Any, cast
from bec_lib.client import BECClient
from bec_lib.device import DeviceBaseWithConfig
from bec_lib.endpoints import MessageEndpoints
from bec_lib.utils.import_utils import lazy_import, lazy_import_from
@@ -24,6 +25,20 @@ else:
# pylint: disable=protected-access
def _name_arg(arg):
if isinstance(arg, DeviceBaseWithConfig):
# if dev.<device> is passed to GUI, it passes full_name
if hasattr(arg, "full_name"):
return arg.full_name
elif hasattr(arg, "name"):
return arg.name
return arg
def _transform_args_kwargs(args, kwargs) -> tuple[tuple, dict]:
return tuple(_name_arg(arg) for arg in args), {k: _name_arg(v) for k, v in kwargs.items()}
def rpc_call(func):
"""
A decorator for calling a function on the server.
@@ -47,15 +62,7 @@ def rpc_call(func):
return None # func(*args, **kwargs)
caller_frame = caller_frame.f_back
out = []
for arg in args:
if hasattr(arg, "name"):
arg = arg.name
out.append(arg)
args = tuple(out)
for key, val in kwargs.items():
if hasattr(val, "name"):
kwargs[key] = val.name
args, kwargs = _transform_args_kwargs(args, kwargs)
if not self._root._gui_is_alive():
raise RuntimeError("GUI is not alive")
return self._run_rpc(func.__name__, *args, **kwargs)

View File

@@ -28,6 +28,10 @@ class EntryValidator:
if not available_entries:
available_entries = [name]
# edge case for if name is passed instead of full_name, should not happen
if entry in signals_dict:
entry = signals_dict[entry].get("obj_name", entry)
if entry is None or entry == "":
entry = next(iter(device._hints), name) if hasattr(device, "_hints") else name
if entry not in available_entries:

View File

@@ -300,7 +300,10 @@ class SignalLabel(BECWidget, QWidget):
def _signal_key_and_info(self) -> tuple[str, dict]:
if isinstance(self._device_obj, Device):
signal_info = self._device_obj._info["signals"][self._signal]
try:
signal_info = self._device_obj._info["signals"][self._signal]
except KeyError:
return "", {}
if signal_info["kind_str"] == Kind.hinted.name:
return signal_info["obj_name"], signal_info
else:
@@ -309,15 +312,6 @@ class SignalLabel(BECWidget, QWidget):
return self._device, self._device_obj._info["describe_configuration"]
return "", {}
# if self.dev[self._device]._info["signals"] == {}:
# return self._signal or self._device
# signal_info = self.dev[self._device]._info["signals"][self._signal]
# return (
# signal_info["obj_name"]
# if signal_info["kind_str"] == Kind.hinted.name
# else (self._signal or self._device)
# )
@SafeProperty(str)
def device(self) -> str:
"""The device from which to select a signal"""

View File

@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "bec_widgets"
version = "2.30.0"
version = "2.30.2"
description = "BEC Widgets"
requires-python = ">=3.10"
classifiers = [

View File

@@ -254,3 +254,35 @@ def test_dap_rpc(qtbot, bec_client_lib, connected_client_gui_obj):
res.wait()
qtbot.waitUntil(wait_for_fit, timeout=10000)
def test_waveform_passing_device(qtbot, bec_client_lib, connected_client_gui_obj):
gui = connected_client_gui_obj
client = bec_client_lib
dev = client.device_manager.devices
scans = client.scans
dock = gui.bec
wf = dock.new("wf_dock").new("Waveform")
c1 = wf.plot(
y_name=dev.samx, y_entry=dev.samx.setpoint
) # using setpoint to not use readback signal
assert c1.object_name == "samx_samx_setpoint"
status = scans.line_scan(dev.samx, -5, 5, steps=5, exp_time=0.05, relative=False)
status.wait()
# Wait for the scan to finish and the data to be available in history
# Wait until scan_id is in history
def _wait_for_scan_in_history():
if len(client.history) == 0:
return False
# Once items appear in storage, the last one hast to be the one we just scanned
return client.history[-1].metadata.bec["scan_id"] == status.scan.scan_id
qtbot.waitUntil(_wait_for_scan_in_history, timeout=10000)
last_scan_data = client.history[-1]
# check plotted data
x_data, y_data = c1.get_data()
assert np.array_equal(y_data, last_scan_data.devices.samx.samx_setpoint.read().get("value"))

View File

@@ -1,6 +1,14 @@
import pytest
from unittest.mock import MagicMock
from bec_widgets.cli.rpc.rpc_base import DeletedWidgetError, RPCBase, RPCReference
import pytest
from bec_lib.device import DeviceBaseWithConfig, Signal
from bec_widgets.cli.rpc.rpc_base import (
DeletedWidgetError,
RPCBase,
RPCReference,
_transform_args_kwargs,
)
@pytest.fixture
@@ -26,3 +34,20 @@ def test_rpc_base(rpc_base):
with pytest.raises(DeletedWidgetError):
ref._root # Object no longer referenced in registry
def test_transform_args_kwargs():
device_mock = MagicMock(spec=DeviceBaseWithConfig)
device_mock.full_name = "full name"
fallthrough_device_mock = MagicMock()
fallthrough_device_mock.name = "short name"
string_arg = "string_arg"
signal_mock = MagicMock(spec=Signal)
signal_mock.full_name = "full name"
args, kwargs = _transform_args_kwargs(
(device_mock, fallthrough_device_mock, string_arg, signal_mock),
{"a": device_mock, "b": fallthrough_device_mock, "c": string_arg, "d": signal_mock},
)
assert args == ("full name", "short name", "string_arg", "full name")
assert kwargs == {"a": "full name", "b": "short name", "c": "string_arg", "d": "full name"}