bec-ipython-client 3.64.1__py3-none-any.whl → 3.86.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of bec-ipython-client might be problematic. Click here for more details.

@@ -1,20 +1,23 @@
1
1
  import collections
2
+ import time
2
3
  from unittest import mock
3
4
 
4
5
  import pytest
5
6
 
6
7
  from bec_ipython_client.callbacks.move_device import (
7
8
  LiveUpdatesReadbackProgressbar,
8
- ReadbackDataMixin,
9
+ ReadbackDataHandler,
9
10
  )
10
11
  from bec_lib import messages
11
12
  from bec_lib.endpoints import MessageEndpoints
12
13
 
13
14
 
14
15
  @pytest.fixture
15
- def readback_data_mixin(bec_client_mock):
16
- with mock.patch.object(bec_client_mock.device_manager, "connector"):
17
- yield ReadbackDataMixin(bec_client_mock.device_manager, ["samx", "samy"])
16
+ def readback_data_handler(bec_client_mock, connected_connector):
17
+ with mock.patch.object(bec_client_mock.device_manager, "connector", connected_connector):
18
+ yield ReadbackDataHandler(
19
+ bec_client_mock.device_manager, ["samx", "samy"], request_id="something"
20
+ )
18
21
 
19
22
 
20
23
  def test_move_callback(bec_client_mock):
@@ -33,30 +36,25 @@ def test_move_callback(bec_client_mock):
33
36
  return readback[0]
34
37
 
35
38
  req_done = collections.deque()
36
- msg_acc = messages.DeviceReqStatusMessage(
37
- device="samx", success=True, metadata={"RID": "something"}
38
- )
39
- req_done.extend([[None], [None], [None], [msg_acc]])
39
+ req_done.extend([{"samx": (False, False)}, {"samx": (False, False)}, {"samx": (True, True)}])
40
40
 
41
41
  def mock_req_msg(*args):
42
42
  if len(req_done) > 1:
43
43
  return req_done.popleft()
44
44
  return req_done[0]
45
45
 
46
- with mock.patch("bec_ipython_client.callbacks.move_device.check_alarms") as check_alarms_mock:
47
- with mock.patch.object(ReadbackDataMixin, "wait_for_RID"):
48
- with mock.patch.object(LiveUpdatesReadbackProgressbar, "wait_for_request_acceptance"):
46
+ with mock.patch("bec_ipython_client.callbacks.move_device.check_alarms"):
47
+ with mock.patch.object(LiveUpdatesReadbackProgressbar, "wait_for_request_acceptance"):
48
+ with mock.patch.object(
49
+ LiveUpdatesReadbackProgressbar, "_print_client_msgs_asap"
50
+ ) as mock_client_msgs:
49
51
  with mock.patch.object(
50
- LiveUpdatesReadbackProgressbar, "_print_client_msgs_asap"
51
- ) as mock_client_msgs:
52
- with mock.patch.object(
53
- LiveUpdatesReadbackProgressbar, "_print_client_msgs_all"
54
- ) as mock_client_msgs_all:
55
- with mock.patch.object(
56
- ReadbackDataMixin, "get_device_values", mock_readback
57
- ):
52
+ LiveUpdatesReadbackProgressbar, "_print_client_msgs_all"
53
+ ) as mock_client_msgs_all:
54
+ with mock.patch.object(ReadbackDataHandler, "get_device_values", mock_readback):
55
+ with mock.patch.object(ReadbackDataHandler, "device_states", mock_req_msg):
58
56
  with mock.patch.object(
59
- ReadbackDataMixin, "get_request_done_msgs", mock_req_msg
57
+ ReadbackDataHandler, "done", side_effect=[False, False, True]
60
58
  ):
61
59
  LiveUpdatesReadbackProgressbar(bec=client, request=request).run()
62
60
  assert mock_client_msgs.called is True
@@ -82,28 +80,21 @@ def test_move_callback_with_report_instruction(bec_client_mock):
82
80
  return readback[0]
83
81
 
84
82
  req_done = collections.deque()
85
- msg_acc = messages.DeviceReqStatusMessage(
86
- device="samx", success=True, metadata={"RID": "something"}
87
- )
88
- req_done.extend([[None], [None], [None], [msg_acc]])
83
+ req_done.extend([{"samx": (False, False)}, {"samx": (False, False)}, {"samx": (True, True)}])
89
84
 
90
85
  def mock_req_msg(*args):
91
86
  if len(req_done) > 1:
92
87
  return req_done.popleft()
93
88
  return req_done[0]
94
89
 
95
- with mock.patch("bec_ipython_client.callbacks.move_device.check_alarms") as check_alarms_mock:
96
- with mock.patch.object(ReadbackDataMixin, "wait_for_RID"):
97
- with mock.patch.object(LiveUpdatesReadbackProgressbar, "wait_for_request_acceptance"):
98
- with mock.patch.object(LiveUpdatesReadbackProgressbar, "_print_client_msgs_asap"):
99
- with mock.patch.object(
100
- LiveUpdatesReadbackProgressbar, "_print_client_msgs_all"
101
- ):
102
- with mock.patch.object(
103
- ReadbackDataMixin, "get_device_values", mock_readback
104
- ):
90
+ with mock.patch("bec_ipython_client.callbacks.move_device.check_alarms"):
91
+ with mock.patch.object(LiveUpdatesReadbackProgressbar, "wait_for_request_acceptance"):
92
+ with mock.patch.object(LiveUpdatesReadbackProgressbar, "_print_client_msgs_asap"):
93
+ with mock.patch.object(LiveUpdatesReadbackProgressbar, "_print_client_msgs_all"):
94
+ with mock.patch.object(ReadbackDataHandler, "get_device_values", mock_readback):
95
+ with mock.patch.object(ReadbackDataHandler, "device_states", mock_req_msg):
105
96
  with mock.patch.object(
106
- ReadbackDataMixin, "get_request_done_msgs", mock_req_msg
97
+ ReadbackDataHandler, "done", side_effect=[False, False, False, True]
107
98
  ):
108
99
  LiveUpdatesReadbackProgressbar(
109
100
  bec=client,
@@ -112,70 +103,121 @@ def test_move_callback_with_report_instruction(bec_client_mock):
112
103
  ).run()
113
104
 
114
105
 
115
- def test_readback_data_mixin(readback_data_mixin):
116
- readback_data_mixin.device_manager.connector.get.side_effect = [
117
- messages.DeviceMessage(
106
+ def test_readback_data_handler(readback_data_handler):
107
+ readback_data_handler.data = {
108
+ "samx": messages.DeviceMessage(
118
109
  signals={"samx": {"value": 10}, "samx_setpoint": {"value": 20}},
119
110
  metadata={"device": "samx"},
120
111
  ),
121
- messages.DeviceMessage(
112
+ "samy": messages.DeviceMessage(
122
113
  signals={"samy": {"value": 10}, "samy_setpoint": {"value": 20}},
123
114
  metadata={"device": "samy"},
124
115
  ),
125
- ]
126
- res = readback_data_mixin.get_device_values()
116
+ }
117
+
118
+ res = readback_data_handler.get_device_values()
127
119
  assert res == [10, 10]
128
120
 
129
121
 
130
- def test_readback_data_mixin_multiple_hints(readback_data_mixin):
131
- readback_data_mixin.device_manager.devices.samx._info["hints"]["fields"] = [
122
+ def test_readback_data_handler_multiple_hints(readback_data_handler):
123
+ readback_data_handler.device_manager.devices.samx._info["hints"]["fields"] = [
132
124
  "samx_setpoint",
133
125
  "samx",
134
126
  ]
135
- readback_data_mixin.device_manager.connector.get.side_effect = [
136
- messages.DeviceMessage(
127
+ readback_data_handler.data = {
128
+ "samx": messages.DeviceMessage(
137
129
  signals={"samx": {"value": 10}, "samx_setpoint": {"value": 20}},
138
130
  metadata={"device": "samx"},
139
131
  ),
140
- messages.DeviceMessage(
132
+ "samy": messages.DeviceMessage(
141
133
  signals={"samy": {"value": 10}, "samy_setpoint": {"value": 20}},
142
134
  metadata={"device": "samy"},
143
135
  ),
144
- ]
145
- res = readback_data_mixin.get_device_values()
136
+ }
137
+ res = readback_data_handler.get_device_values()
146
138
  assert res == [20, 10]
147
139
 
148
140
 
149
- def test_readback_data_mixin_multiple_no_hints(readback_data_mixin):
150
- readback_data_mixin.device_manager.devices.samx._info["hints"]["fields"] = []
151
- readback_data_mixin.device_manager.connector.get.side_effect = [
152
- messages.DeviceMessage(
141
+ def test_readback_data_handler_multiple_no_hints(readback_data_handler):
142
+ readback_data_handler.device_manager.devices.samx._info["hints"]["fields"] = []
143
+ readback_data_handler.data = {
144
+ "samx": messages.DeviceMessage(
153
145
  signals={"samx": {"value": 10}, "samx_setpoint": {"value": 20}},
154
146
  metadata={"device": "samx"},
155
147
  ),
156
- messages.DeviceMessage(
148
+ "samy": messages.DeviceMessage(
157
149
  signals={"samy": {"value": 10}, "samy_setpoint": {"value": 20}},
158
150
  metadata={"device": "samy"},
159
151
  ),
160
- ]
161
- res = readback_data_mixin.get_device_values()
152
+ }
153
+ res = readback_data_handler.get_device_values()
162
154
  assert res == [10, 10]
163
155
 
164
156
 
165
- def test_get_request_done_msgs(readback_data_mixin):
166
- res = readback_data_mixin.get_request_done_msgs()
167
- readback_data_mixin.device_manager.connector.pipeline.assert_called_once()
168
- assert (
169
- mock.call(
170
- MessageEndpoints.device_req_status("samx"),
171
- readback_data_mixin.device_manager.connector.pipeline.return_value,
172
- )
173
- in readback_data_mixin.device_manager.connector.get.call_args_list
157
+ def test_readback_data_handler_init(readback_data_handler):
158
+ """
159
+ Test that the ReadbackDataHandler is initialized correctly.
160
+ """
161
+
162
+ # Initial state
163
+ assert readback_data_handler._devices_done_state == {
164
+ "samx": (False, False),
165
+ "samy": (False, False),
166
+ }
167
+ assert readback_data_handler._devices_received == {"samx": False, "samy": False}
168
+ assert readback_data_handler.data == {}
169
+
170
+
171
+ def test_readback_data_handler_readback_callbacks(readback_data_handler):
172
+ """
173
+ Test that the readback callback properly updates the readback data.
174
+ """
175
+
176
+ # Submit readback for samx
177
+ msg = messages.DeviceMessage(
178
+ signals={"samx": {"value": 15}}, metadata={"device": "samx", "RID": "something"}
174
179
  )
175
- assert (
176
- mock.call(
177
- MessageEndpoints.device_req_status("samy"),
178
- readback_data_mixin.device_manager.connector.pipeline.return_value,
179
- )
180
- in readback_data_mixin.device_manager.connector.get.call_args_list
180
+ readback_data_handler.connector.set_and_publish(MessageEndpoints.device_readback("samx"), msg)
181
+
182
+ msg_old = messages.DeviceMessage(
183
+ signals={"samy": {"value": 10}}, metadata={"device": "samx", "RID": "something_else"}
184
+ )
185
+ readback_data_handler.connector.set_and_publish(
186
+ MessageEndpoints.device_readback("samy"), msg_old
187
+ )
188
+ while (
189
+ not readback_data_handler._devices_received["samx"]
190
+ or "samx" not in readback_data_handler.data
191
+ ):
192
+ time.sleep(0.01)
193
+ assert readback_data_handler.data["samx"].signals["samx"]["value"] == 15
194
+ dev_data = readback_data_handler.get_device_values()
195
+ assert dev_data[0] == 15
196
+ assert dev_data[1] == 10 # samy remains unchanged
197
+
198
+
199
+ def test_readback_data_handler_request_done_callbacks(readback_data_handler):
200
+ """
201
+ Test that the request done callback properly updates the device done state.
202
+ """
203
+
204
+ # Submit request done for samx
205
+ msg = messages.DeviceReqStatusMessage(device="samx", success=True, request_id="something")
206
+ readback_data_handler.connector.xadd(
207
+ MessageEndpoints.device_req_status("something"), {"data": msg}
208
+ )
209
+ while not readback_data_handler._devices_done_state["samx"][0]:
210
+ time.sleep(0.01)
211
+ assert readback_data_handler._devices_done_state["samx"] == (True, True)
212
+
213
+ assert readback_data_handler.done() is False
214
+
215
+ # Submit request done for samy
216
+ msg = messages.DeviceReqStatusMessage(device="samy", success=False, request_id="something")
217
+ readback_data_handler.connector.xadd(
218
+ MessageEndpoints.device_req_status("something"), {"data": msg}
181
219
  )
220
+ while not readback_data_handler._devices_done_state["samy"][0]:
221
+ time.sleep(0.01)
222
+ assert readback_data_handler._devices_done_state["samy"] == (True, False)
223
+ assert readback_data_handler.done() is True
@@ -0,0 +1,21 @@
1
+ from time import sleep
2
+
3
+ from bec_server.scan_server.procedures.constants import PROCEDURE, ProcedureWorkerError
4
+ from bec_server.scan_server.procedures.container_utils import PodmanCliUtils
5
+
6
+ image_name = (
7
+ f"ghcr.io/bec-project/{PROCEDURE.CONTAINER.REQUIREMENTS_IMAGE_NAME}:v{PROCEDURE.BEC_VERSION}"
8
+ )
9
+ podman = PodmanCliUtils()
10
+
11
+ for i in range(1, 6):
12
+ try:
13
+ output = podman._run_and_capture_error("podman", "pull", image_name)
14
+ print("successfully pulled requirements image for current version")
15
+ exit(0)
16
+ except ProcedureWorkerError as e:
17
+ print(e)
18
+ print("retrying in 5 minutes...")
19
+ sleep(5 * 60)
20
+ print(f"No more retries. Check if {image_name} actually exists!")
21
+ exit(1)
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import time
4
+ from dataclasses import dataclass
4
5
  from importlib.metadata import version
5
6
  from typing import TYPE_CHECKING, Callable, Generator
6
7
  from unittest.mock import MagicMock, patch
@@ -11,7 +12,7 @@ from bec_ipython_client.main import BECIPythonClient
11
12
  from bec_lib import messages
12
13
  from bec_lib.endpoints import MessageEndpoints
13
14
  from bec_lib.logger import bec_logger
14
- from bec_server.scan_server.procedures.constants import PROCEDURE
15
+ from bec_server.scan_server.procedures.constants import _CONTAINER, _WORKER
15
16
  from bec_server.scan_server.procedures.container_utils import get_backend
16
17
  from bec_server.scan_server.procedures.container_worker import ContainerProcedureWorker
17
18
  from bec_server.scan_server.procedures.manager import ProcedureManager
@@ -28,6 +29,15 @@ logger = bec_logger.logger
28
29
  pytestmark = pytest.mark.random_order(disabled=True)
29
30
 
30
31
 
32
+ @dataclass(frozen=True)
33
+ class PATCHED_CONSTANTS:
34
+ WORKER = _WORKER()
35
+ CONTAINER = _CONTAINER()
36
+ MANAGER_SHUTDOWN_TIMEOUT_S = 2
37
+ BEC_VERSION = version("bec_lib")
38
+ REDIS_HOST = "localhost"
39
+
40
+
31
41
  @pytest.fixture
32
42
  def client_logtool_and_manager(
33
43
  bec_ipython_client_fixture_with_logtool: tuple[BECIPythonClient, "LogTestTool"],
@@ -52,7 +62,7 @@ def _wait_while(cond: Callable[[], bool], timeout_s):
52
62
  def test_building_worker_image():
53
63
  podman_utils = get_backend()
54
64
  build = podman_utils.build_worker_image()
55
- assert len(build._command_output.splitlines()[-1]) == 64
65
+ assert len(build._command_output.splitlines()[-1]) == 64 # type: ignore
56
66
  assert podman_utils.image_exists(f"bec_procedure_worker:v{version('bec_lib')}")
57
67
 
58
68
 
@@ -62,7 +72,7 @@ def test_procedure_runner_spawns_worker(
62
72
  client_logtool_and_manager: tuple[BECIPythonClient, "LogTestTool", ProcedureManager],
63
73
  ):
64
74
  client, _, manager = client_logtool_and_manager
65
- assert manager.active_workers == {}
75
+ assert manager._active_workers == {}
66
76
  endpoint = MessageEndpoints.procedure_request()
67
77
  msg = messages.ProcedureRequestMessage(
68
78
  identifier="sleep", args_kwargs=((), {"time_s": 2}), queue="test"
@@ -77,21 +87,22 @@ def test_procedure_runner_spawns_worker(
77
87
  manager.add_callback("test", cb)
78
88
  client.connector.xadd(topic=endpoint, msg_dict=msg.model_dump())
79
89
 
80
- _wait_while(lambda: manager.active_workers == {}, 5)
81
- _wait_while(lambda: manager.active_workers != {}, 20)
90
+ _wait_while(lambda: manager._active_workers == {}, 5)
91
+ _wait_while(lambda: manager._active_workers != {}, 20)
82
92
 
83
93
  assert logs != []
84
94
 
85
95
 
86
96
  @pytest.mark.timeout(100)
87
97
  @patch("bec_server.scan_server.procedures.manager.procedure_registry.is_registered", lambda _: True)
98
+ @patch("bec_server.scan_server.procedures.container_worker.PROCEDURE", PATCHED_CONSTANTS())
88
99
  def test_happy_path_container_procedure_runner(
89
100
  client_logtool_and_manager: tuple[BECIPythonClient, "LogTestTool", ProcedureManager],
90
101
  ):
91
102
  test_args = (1, 2, 3)
92
103
  test_kwargs = {"a": "b", "c": "d"}
93
104
  client, logtool, manager = client_logtool_and_manager
94
- assert manager.active_workers == {}
105
+ assert manager._active_workers == {}
95
106
  conn = client.connector
96
107
  endpoint = MessageEndpoints.procedure_request()
97
108
  msg = messages.ProcedureRequestMessage(
@@ -99,12 +110,15 @@ def test_happy_path_container_procedure_runner(
99
110
  )
100
111
  conn.xadd(topic=endpoint, msg_dict=msg.model_dump())
101
112
 
102
- _wait_while(lambda: manager.active_workers == {}, 5)
103
- _wait_while(lambda: manager.active_workers != {}, 20)
113
+ _wait_while(lambda: manager._active_workers == {}, 5)
114
+ _wait_while(lambda: manager._active_workers != {}, 20)
104
115
 
105
116
  logtool.fetch()
106
117
  assert logtool.is_present_in_any_message("procedure accepted: True, message:")
107
- assert logtool.is_present_in_any_message("ContainerWorker started container for queue primary")
118
+ assert logtool.is_present_in_any_message(
119
+ "ContainerWorker started container for queue primary"
120
+ ), f"Log content relating to procedures: {manager._logs}"
121
+
108
122
  res, msg = logtool.are_present_in_order(
109
123
  [
110
124
  "Container worker 'primary' status update: IDLE",
@@ -114,12 +128,7 @@ def test_happy_path_container_procedure_runner(
114
128
  ]
115
129
  )
116
130
  assert res, f"failed on {msg}"
117
- res, msg = logtool.are_present_in_order(
118
- [
119
- "Container worker 'primary' status update: IDLE",
120
- f"Builtin procedure log_message_args_kwargs called with args: {test_args} and kwargs: {test_kwargs}",
121
- "Container worker 'primary' status update: IDLE",
122
- "Container worker 'primary' status update: FINISHED",
123
- ]
131
+
132
+ assert logtool.is_present_in_any_message(
133
+ f"Builtin procedure log_message_args_kwargs called with args: {test_args} and kwargs: {test_kwargs}"
124
134
  )
125
- assert res, f"failed on {msg}"
@@ -1,9 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  import _thread
2
4
  import io
3
5
  import os
4
6
  import threading
5
7
  import time
6
8
  from contextlib import redirect_stdout
9
+ from typing import TYPE_CHECKING
7
10
  from unittest.mock import PropertyMock
8
11
 
9
12
  import h5py
@@ -19,6 +22,9 @@ from bec_lib.logger import bec_logger
19
22
 
20
23
  logger = bec_logger.logger
21
24
 
25
+ if TYPE_CHECKING: # pragma: no cover
26
+ from bec_ipython_client.main import BECIPythonClient
27
+
22
28
  # pylint: disable=protected-access
23
29
 
24
30
 
@@ -201,13 +207,13 @@ def test_mv_scan_mv(bec_ipython_client_fixture):
201
207
 
202
208
 
203
209
  @pytest.mark.timeout(100)
204
- def test_scan_abort(bec_ipython_client_fixture):
210
+ def test_scan_abort(bec_ipython_client_fixture: BECIPythonClient):
205
211
  def send_abort(bec):
206
212
  while True:
207
213
  current_scan_info = bec.queue.scan_storage.current_scan_info
208
214
  if not current_scan_info:
209
215
  continue
210
- status = current_scan_info.get("status").lower()
216
+ status = current_scan_info.status.lower()
211
217
  if status not in ["running", "deferred_pause"]:
212
218
  continue
213
219
  if bec.queue.scan_storage.current_scan is None:
@@ -217,7 +223,7 @@ def test_scan_abort(bec_ipython_client_fixture):
217
223
  break
218
224
  while True:
219
225
  queue = bec.queue.queue_storage.current_scan_queue
220
- if queue["primary"]["info"][0]["status"] == "DEFERRED_PAUSE":
226
+ if queue["primary"].info[0].status == "DEFERRED_PAUSE":
221
227
  break
222
228
  time.sleep(0.5)
223
229
  _thread.interrupt_main()
@@ -241,7 +247,7 @@ def test_scan_abort(bec_ipython_client_fixture):
241
247
  time.sleep(0.5)
242
248
 
243
249
  current_queue = bec.queue.queue_storage.current_scan_queue["primary"]
244
- while current_queue["info"] or current_queue["status"] != "RUNNING":
250
+ while current_queue.info or current_queue.status != "RUNNING":
245
251
  time.sleep(0.5)
246
252
  current_queue = bec.queue.queue_storage.current_scan_queue["primary"]
247
253
 
@@ -307,7 +313,7 @@ def test_queued_scan(bec_ipython_client_fixture):
307
313
  while len(scan2.scan.live_data) != 50:
308
314
  time.sleep(0.5)
309
315
  current_queue = bec.queue.queue_storage.current_scan_queue["primary"]
310
- while current_queue["info"] or current_queue["status"] != "RUNNING":
316
+ while current_queue.info or current_queue.status != "RUNNING":
311
317
  time.sleep(0.5)
312
318
  current_queue = bec.queue.queue_storage.current_scan_queue["primary"]
313
319
  scan_number_end = bec.queue.next_scan_number
@@ -356,7 +362,7 @@ def test_scan_restart(bec_ipython_client_fixture):
356
362
  scan2.wait()
357
363
 
358
364
  current_queue = bec.queue.queue_storage.current_scan_queue["primary"]
359
- while current_queue["info"] or current_queue["status"] != "RUNNING":
365
+ while current_queue.info or current_queue.status != "RUNNING":
360
366
  time.sleep(0.5)
361
367
  current_queue = bec.queue.queue_storage.current_scan_queue["primary"]
362
368
  scan_number_end = bec.queue.next_scan_number
@@ -364,7 +370,7 @@ def test_scan_restart(bec_ipython_client_fixture):
364
370
 
365
371
 
366
372
  @pytest.mark.timeout(100)
367
- def test_scan_observer_repeat_queued(bec_ipython_client_fixture):
373
+ def test_scan_observer_repeat_queued(bec_ipython_client_fixture: BECIPythonClient):
368
374
  bec = bec_ipython_client_fixture
369
375
  bec.metadata.update({"unit_test": "test_scan_observer_repeat_queued"})
370
376
  scans = bec.scans
@@ -396,7 +402,7 @@ def test_scan_observer_repeat_queued(bec_ipython_client_fixture):
396
402
  scan2.wait()
397
403
 
398
404
  current_queue = bec.queue.queue_storage.current_scan_queue["primary"]
399
- while current_queue["info"] or current_queue["status"] != "RUNNING":
405
+ while current_queue.info or current_queue.status != "RUNNING":
400
406
  time.sleep(0.5)
401
407
  current_queue = bec.queue.queue_storage.current_scan_queue["primary"]
402
408
  scan_number_end = bec.queue.next_scan_number
@@ -433,7 +439,7 @@ def test_scan_observer_repeat(bec_ipython_client_fixture):
433
439
  scan1.wait()
434
440
 
435
441
  current_queue = bec.queue.queue_storage.current_scan_queue["primary"]
436
- while current_queue["info"] or current_queue["status"] != "RUNNING":
442
+ while current_queue.info or current_queue.status != "RUNNING":
437
443
  time.sleep(0.5)
438
444
  current_queue = bec.queue.queue_storage.current_scan_queue["primary"]
439
445
  while True:
@@ -737,12 +743,12 @@ def test_update_config(bec_ipython_client_fixture):
737
743
  bec = bec_ipython_client_fixture
738
744
  bec.metadata.update({"unit_test": "test_update_config"})
739
745
  demo_config_path = os.path.join(os.path.dirname(configs.__file__), "demo_config.yaml")
740
- config = bec.config._load_config_from_file(demo_config_path)
746
+ config = bec.device_manager.config_helper._load_config_from_file(demo_config_path)
741
747
  config.pop("samx")
742
- bec.config.send_config_request(action="set", config=config)
748
+ bec.device_manager.config_helper.send_config_request(action="set", config=config)
743
749
  assert "samx" not in bec.device_manager.devices
744
- config = bec.config._load_config_from_file(demo_config_path)
745
- bec.config.send_config_request(action="set", config=config)
750
+ config = bec.device_manager.config_helper._load_config_from_file(demo_config_path)
751
+ bec.device_manager.config_helper.send_config_request(action="set", config=config)
746
752
 
747
753
 
748
754
  @pytest.mark.timeout(100)
@@ -108,12 +108,10 @@ def test_config_updates(bec_client_lib):
108
108
  assert dev.rt_controller.limits == [-50, 50]
109
109
 
110
110
  dev.rt_controller.velocity.set(10).wait()
111
+ assert dev.rt_controller.velocity.read(cached=True)["rt_controller_velocity"]["value"] == 10
111
112
  assert dev.rt_controller.velocity.read()["rt_controller_velocity"]["value"] == 10
112
- assert dev.rt_controller.velocity.read(cached=False)["rt_controller_velocity"]["value"] == 10
113
113
  assert dev.rt_controller.read_configuration()["rt_controller_velocity"]["value"] == 10
114
- assert (
115
- dev.rt_controller.read_configuration(cached=False)["rt_controller_velocity"]["value"] == 10
116
- )
114
+ assert dev.rt_controller.read_configuration()["rt_controller_velocity"]["value"] == 10
117
115
 
118
116
  dev.rt_controller.velocity.put(5)
119
117
  assert dev.rt_controller.velocity.get() == 5
@@ -324,7 +322,9 @@ def test_config_reload(
324
322
  num_devices = len(bec.device_manager.devices)
325
323
  if raises_error:
326
324
  with pytest.raises(DeviceConfigError):
327
- bec.config.update_session_with_file(runtime_config_file_path)
325
+ bec.config.update_session_with_file(
326
+ runtime_config_file_path, force=True, validate=False
327
+ )
328
328
  if deletes_config:
329
329
  assert len(bec.device_manager.devices) == 0
330
330
  elif disabled_device:
@@ -332,7 +332,7 @@ def test_config_reload(
332
332
  else:
333
333
  assert len(bec.device_manager.devices) == num_devices
334
334
  else:
335
- bec.config.update_session_with_file(runtime_config_file_path)
335
+ bec.config.update_session_with_file(runtime_config_file_path, force=True, validate=False)
336
336
  assert len(bec.device_manager.devices) == 2
337
337
  for dev in disabled_device:
338
338
  assert bec.device_manager.devices[dev].enabled is False
@@ -377,7 +377,7 @@ def test_config_reload_with_describe_failure(bec_test_config_file_path, bec_clie
377
377
  f.write(yaml.dump(config))
378
378
 
379
379
  with pytest.raises(DeviceConfigError):
380
- bec.config.update_session_with_file(runtime_config_file_path)
380
+ bec.config.update_session_with_file(runtime_config_file_path, force=True, validate=False)
381
381
 
382
382
  assert len(bec.device_manager.devices) == 2
383
383
  assert bec.device_manager.devices["eyefoc"].enabled is True
@@ -388,7 +388,7 @@ def test_config_reload_with_describe_failure(bec_test_config_file_path, bec_clie
388
388
  f"e2e_test_hexapod_fail", messages.DeviceStatusMessage(device="hexapod", status=0)
389
389
  )
390
390
 
391
- bec.config.update_session_with_file(runtime_config_file_path)
391
+ bec.config.update_session_with_file(runtime_config_file_path, force=True)
392
392
  assert len(bec.device_manager.devices) == 2
393
393
  assert bec.device_manager.devices["eyefoc"].enabled is True
394
394
  assert bec.device_manager.devices["hexapod"].enabled is True
@@ -414,13 +414,13 @@ def test_config_add_remove_device(bec_client_lib):
414
414
  "readOnly": False,
415
415
  }
416
416
  }
417
- bec.config.send_config_request(action="add", config=config)
417
+ bec.device_manager.config_helper.send_config_request(action="add", config=config)
418
418
  with pytest.raises(DeviceConfigError) as config_error:
419
- bec.config.send_config_request(action="add", config=config)
419
+ bec.device_manager.config_helper.send_config_request(action="add", config=config)
420
420
  assert config_error.match("Device new_device already exists")
421
421
  assert "new_device" in dev
422
422
 
423
- bec.config.send_config_request(action="remove", config={"new_device": {}})
423
+ bec.device_manager.config_helper.send_config_request(action="remove", config={"new_device": {}})
424
424
  assert "new_device" not in dev
425
425
 
426
426
  device_config_msg = bec.connector.get(MessageEndpoints.device_config())
@@ -431,7 +431,7 @@ def test_config_add_remove_device(bec_client_lib):
431
431
 
432
432
  config["new_device"]["deviceClass"] = "ophyd_devices.doesnt_exist"
433
433
  with pytest.raises(DeviceConfigError) as config_error:
434
- bec.config.send_config_request(action="add", config=config)
434
+ bec.device_manager.config_helper.send_config_request(action="add", config=config)
435
435
  assert config_error.match("module 'ophyd_devices' has no attribute 'doesnt_exist'")
436
436
  assert "new_device" not in dev
437
437
  assert "samx" in dev
@@ -454,7 +454,7 @@ def test_computed_signal(bec_client_lib):
454
454
  dev.pseudo_signal1.set_compute_method(compute_signal1)
455
455
  dev.pseudo_signal1.set_input_signals()
456
456
 
457
- assert dev.pseudo_signal1.read(cached=False)["pseudo_signal1"]["value"] == 5
457
+ assert dev.pseudo_signal1.read()["pseudo_signal1"]["value"] == 5
458
458
 
459
459
 
460
460
  def test_cached_device_readout(bec_client_lib):
@@ -463,26 +463,30 @@ def test_cached_device_readout(bec_client_lib):
463
463
  dev = bec.device_manager.devices
464
464
 
465
465
  dev.samx.setpoint.put(5)
466
- data = dev.samx.setpoint.get()
466
+ data = dev.samx.setpoint.get(cached=True)
467
467
  assert data == 5
468
468
 
469
- orig_velocity = dev.samx.velocity.get()
469
+ orig_velocity = dev.samx.velocity.get(cached=True)
470
470
  dev.samx.velocity.put(10)
471
- data = dev.samx.velocity.get()
471
+ data = dev.samx.velocity.get(cached=True)
472
472
  assert data == 10
473
473
 
474
- config = dev.samx.read_configuration()
474
+ config = dev.samx.read_configuration(cached=True)
475
475
  assert config["samx_velocity"]["value"] == 10
476
476
 
477
+ dev.samx.velocity.set(20).wait()
478
+ data = dev.samx.velocity.get(cached=True)
479
+ assert data == 20
480
+
477
481
  dev.samx.velocity.put(orig_velocity)
478
482
 
479
- data = dev.hexapod.x.readback.read(cached=False)
483
+ data = dev.hexapod.x.readback.read()
480
484
  timestamp = data["hexapod_x"]["timestamp"]
481
485
  data = dev.hexapod.x.readback.read(cached=True)
482
486
  assert data["hexapod_x"]["timestamp"] == timestamp
483
487
 
484
488
  # check that .get also updates the cache
485
- dev.hexapod.x.readback.get(cached=False)
489
+ dev.hexapod.x.readback.get()
486
490
  timestamp_2 = dev.hexapod.x.readback.read(cached=True)["hexapod_x"]["timestamp"]
487
491
  assert timestamp_2 != timestamp
488
492