bec-ipython-client 3.154.0__tar.gz → 3.156.0__tar.gz

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.
Files changed (44) hide show
  1. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/PKG-INFO +1 -1
  2. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/callbacks/ipython_live_updates.py +4 -3
  3. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/callbacks/live_table.py +4 -2
  4. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/pyproject.toml +1 -1
  5. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/tests/client_tests/test_live_table.py +42 -0
  6. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/tests/end-2-end/test_scans_e2e.py +88 -51
  7. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/tests/end-2-end/test_scans_lib_e2e.py +46 -26
  8. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/.gitignore +0 -0
  9. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/__init__.py +0 -0
  10. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/beamline_mixin.py +0 -0
  11. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/bec_magics.py +0 -0
  12. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/bec_startup.py +0 -0
  13. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/callbacks/__init__.py +0 -0
  14. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/callbacks/device_progress.py +0 -0
  15. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/callbacks/move_device.py +0 -0
  16. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/callbacks/utils.py +0 -0
  17. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/high_level_interfaces/__init__.py +0 -0
  18. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/high_level_interfaces/bec_hli.py +0 -0
  19. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/high_level_interfaces/spec_hli.py +0 -0
  20. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/main.py +0 -0
  21. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/plugins/SLS/__init__.py +0 -0
  22. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/plugins/SLS/sls_info.py +0 -0
  23. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/plugins/XTreme/__init__.py +0 -0
  24. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/plugins/XTreme/x-treme.py +0 -0
  25. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/plugins/__init__.py +0 -0
  26. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/plugins/flomni/flomni_config.yaml +0 -0
  27. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/prettytable.py +0 -0
  28. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/progressbar.py +0 -0
  29. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/bec_ipython_client/signals.py +0 -0
  30. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/demo.py +0 -0
  31. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/tests/client_tests/conftest.py +0 -0
  32. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/tests/client_tests/test_beamline_mixins.py +0 -0
  33. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/tests/client_tests/test_bec_client.py +0 -0
  34. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/tests/client_tests/test_device_progress.py +0 -0
  35. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/tests/client_tests/test_ipython_live_updates.py +0 -0
  36. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/tests/client_tests/test_move_callback.py +0 -0
  37. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/tests/client_tests/test_pretty_table.py +0 -0
  38. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/tests/client_tests/test_signals.py +0 -0
  39. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/tests/conftest.py +0 -0
  40. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/tests/end-2-end/_ensure_requirements_container.py +0 -0
  41. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/tests/end-2-end/test_acl_e2e.py +0 -0
  42. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/tests/end-2-end/test_actors_e2e.py +0 -0
  43. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/tests/end-2-end/test_procedures_e2e.py +0 -0
  44. {bec_ipython_client-3.154.0 → bec_ipython_client-3.156.0}/tests/end-2-end/test_scans_v4_lib_e2e.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bec_ipython_client
3
- Version: 3.154.0
3
+ Version: 3.156.0
4
4
  Summary: BEC IPython client
5
5
  Project-URL: Bug Tracker, https://github.com/bec-project/bec/issues
6
6
  Project-URL: Homepage, https://github.com/bec-project/bec
@@ -322,9 +322,10 @@ class IPythonLiveUpdates:
322
322
  if not available_blocks:
323
323
  return False
324
324
  req_block = available_blocks[self._request_block_index[req_id]]
325
- if (
326
- req_block.msg.scan_type == "mv"
327
- ): # TODO: make this more general for scans without report instructions
325
+ if req_block.msg.scan_type in [
326
+ "mv",
327
+ "_v4_mv",
328
+ ]: # TODO: make this more general for all scan types that don't have report instructions
328
329
  return True
329
330
 
330
331
  report_instructions = req_block.report_instructions or []
@@ -45,6 +45,8 @@ class LiveUpdatesTable(LiveUpdatesBase):
45
45
 
46
46
  MAX_DEVICES = 10
47
47
  REPORT_TYPE = "scan_progress"
48
+ STEP_SCAN_TYPES = {"step", "software_triggered", "hardware_triggered"}
49
+ FLY_SCAN_TYPES = {"fly"}
48
50
 
49
51
  def __init__(
50
52
  self,
@@ -118,9 +120,9 @@ class LiveUpdatesTable(LiveUpdatesBase):
118
120
  @property
119
121
  def devices(self):
120
122
  """get the devices for the callback"""
121
- if self.point_data.metadata["scan_type"] == "step":
123
+ if self.point_data.metadata["scan_type"] in self.STEP_SCAN_TYPES:
122
124
  return self.get_devices_from_scan_data(self.scan_item.live_data[0])
123
- if self.point_data.metadata["scan_type"] == "fly":
125
+ if self.point_data.metadata["scan_type"] in self.FLY_SCAN_TYPES:
124
126
  devices = list(self.point_data.content["data"].keys())
125
127
  if len(devices) > self.MAX_DEVICES:
126
128
  return devices[0 : self.MAX_DEVICES]
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "bec_ipython_client"
7
- version = "3.154.0"
7
+ version = "3.156.0"
8
8
  description = "BEC IPython client"
9
9
  requires-python = ">=3.11"
10
10
  classifiers = [
@@ -251,6 +251,48 @@ class TestLiveTable:
251
251
  live_update.print_table_data()
252
252
  assert mock_client_msgs.called
253
253
 
254
+ def test_print_table_data_with_v4_software_triggered_scan(self, client_with_grid_scan):
255
+ client, request_msg = client_with_grid_scan
256
+ response_msg = messages.RequestResponseMessage(
257
+ accepted=True, message={"msg": ""}, metadata={"RID": "something"}
258
+ )
259
+ client.queue.request_storage.update_with_request(request_msg)
260
+ client.queue.request_storage.update_with_response(response_msg)
261
+ live_update = LiveUpdatesTable(
262
+ client, {"scan_progress": {"points": 10, "show_table": True}}, request_msg
263
+ )
264
+ live_update.point_data = messages.ScanMessage(
265
+ point_id=0,
266
+ scan_id="",
267
+ data={"samx": {"samx": {"value": 0}}},
268
+ metadata={"scan_report_devices": ["samx"], "scan_type": "software_triggered"},
269
+ )
270
+ live_update.scan_item = ScanItemMock(live_data={0: live_update.point_data})
271
+ with mock.patch.object(live_update, "_print_client_msgs_asap") as mock_client_msgs:
272
+ live_update.print_table_data()
273
+ assert mock_client_msgs.called
274
+
275
+ def test_print_table_data_with_v4_hardware_triggered_scan(self, client_with_grid_scan):
276
+ client, request_msg = client_with_grid_scan
277
+ response_msg = messages.RequestResponseMessage(
278
+ accepted=True, message={"msg": ""}, metadata={"RID": "something"}
279
+ )
280
+ client.queue.request_storage.update_with_request(request_msg)
281
+ client.queue.request_storage.update_with_response(response_msg)
282
+ live_update = LiveUpdatesTable(
283
+ client, {"scan_progress": {"points": 10, "show_table": True}}, request_msg
284
+ )
285
+ live_update.point_data = messages.ScanMessage(
286
+ point_id=0,
287
+ scan_id="",
288
+ data={"samx": {"samx": {"value": 0}}},
289
+ metadata={"scan_report_devices": ["samx"], "scan_type": "hardware_triggered"},
290
+ )
291
+ live_update.scan_item = ScanItemMock(live_data={0: live_update.point_data})
292
+ with mock.patch.object(live_update, "_print_client_msgs_asap") as mock_client_msgs:
293
+ live_update.print_table_data()
294
+ assert mock_client_msgs.called
295
+
254
296
  def test_print_table_data_lamni_flyer(self, client_with_grid_scan):
255
297
  client, request_msg = client_with_grid_scan
256
298
  response_msg = messages.RequestResponseMessage(
@@ -31,13 +31,16 @@ if TYPE_CHECKING: # pragma: no cover
31
31
 
32
32
 
33
33
  @pytest.mark.timeout(100)
34
- def test_grid_scan(capsys, bec_ipython_client_fixture):
34
+ @pytest.mark.parametrize("scan_name", ["grid_scan", "_v4_grid_scan"])
35
+ def test_grid_scan(capsys, bec_ipython_client_fixture, scan_name):
35
36
  bec = bec_ipython_client_fixture
36
37
  scans = bec.scans
37
38
  bec.metadata.update({"unit_test": "test_grid_scan"})
38
39
  dev = bec.device_manager.devices
39
40
  scans.umv(dev.samx, 0, dev.samy, 0, relative=False)
40
- status = scans.grid_scan(dev.samx, -5, 5, 10, dev.samy, -5, 5, 10, exp_time=0.01, relative=True)
41
+ status = getattr(scans, scan_name)(
42
+ dev.samx, -5, 5, 10, dev.samy, -5, 5, 10, exp_time=0.01, relative=True
43
+ )
41
44
  assert len(status.scan.live_data) == 100
42
45
  assert status.scan.num_points == 100
43
46
  captured = capsys.readouterr()
@@ -45,12 +48,13 @@ def test_grid_scan(capsys, bec_ipython_client_fixture):
45
48
 
46
49
 
47
50
  @pytest.mark.timeout(100)
48
- def test_fermat_scan(capsys, bec_ipython_client_fixture):
51
+ @pytest.mark.parametrize("scan_name", ["fermat_scan", "_v4_fermat_scan"])
52
+ def test_fermat_scan(capsys, bec_ipython_client_fixture, scan_name):
49
53
  bec = bec_ipython_client_fixture
50
54
  scans = bec.scans
51
55
  bec.metadata.update({"unit_test": "test_fermat_scan"})
52
56
  dev = bec.device_manager.devices
53
- status = scans.fermat_scan(
57
+ status = getattr(scans, scan_name)(
54
58
  dev.samx,
55
59
  -5,
56
60
  5,
@@ -62,19 +66,27 @@ def test_fermat_scan(capsys, bec_ipython_client_fixture):
62
66
  relative=True,
63
67
  optim_trajectory="corridor",
64
68
  )
65
- assert len(status.scan.live_data) == 393
66
- assert status.scan.num_points == 393
69
+ if scan_name == "_v4_fermat_scan":
70
+ # NOTE: v4 scan calculates the angle more accurately, which results in a slightly different number of points
71
+ assert len(status.scan.live_data) == 400
72
+ assert status.scan.num_points == 400
73
+ else:
74
+ assert len(status.scan.live_data) == 393
75
+ assert status.scan.num_points == 393
67
76
  captured = capsys.readouterr()
68
77
  assert "finished. Scan ID" in captured.out
69
78
 
70
79
 
71
80
  @pytest.mark.timeout(100)
72
- def test_line_scan(capsys, bec_ipython_client_fixture):
81
+ @pytest.mark.parametrize("scan_name", ["line_scan", "_v4_line_scan"])
82
+ def test_line_scan(capsys, bec_ipython_client_fixture, scan_name):
73
83
  bec = bec_ipython_client_fixture
74
84
  scans = bec.scans
75
85
  bec.metadata.update({"unit_test": "test_line_scan"})
76
86
  dev = bec.device_manager.devices
77
- status = scans.line_scan(dev.samx, -5, 5, steps=10, exp_time=0.01, relative=True)
87
+ status = getattr(scans, scan_name)(
88
+ dev.samx, -5, 5, dev.samy, -5, 5, steps=10, exp_time=0.01, relative=True
89
+ )
78
90
  assert len(status.scan.live_data) == 10
79
91
  assert status.scan.num_points == 10
80
92
  captured = capsys.readouterr()
@@ -83,12 +95,13 @@ def test_line_scan(capsys, bec_ipython_client_fixture):
83
95
 
84
96
  @pytest.mark.flaky # marked as flaky as the simulation might return a new readback value within the tolerance
85
97
  @pytest.mark.timeout(100)
86
- def test_mv_scan(capsys, bec_ipython_client_fixture):
98
+ @pytest.mark.parametrize("scan_name", [("mv", "umv"), ("_v4_mv", "_v4_umv")])
99
+ def test_mv_scan(capsys, bec_ipython_client_fixture, scan_name):
87
100
  bec = bec_ipython_client_fixture
88
101
  scans = bec.scans
89
102
  bec.metadata.update({"unit_test": "test_mv_scan"})
90
103
  dev = bec.device_manager.devices
91
- scans.mv(dev.samx, 10, dev.samy, 20, relative=False).wait()
104
+ getattr(scans, scan_name[0])(dev.samx, 10, dev.samy, 20, relative=False).wait()
92
105
  current_pos_samx = dev.samx.read(cached=True)["samx"]["value"]
93
106
  current_pos_samy = dev.samy.read(cached=True)["samy"]["value"]
94
107
  assert np.isclose(
@@ -97,7 +110,7 @@ def test_mv_scan(capsys, bec_ipython_client_fixture):
97
110
  assert np.isclose(
98
111
  current_pos_samy, 20, atol=dev.samy._config["deviceConfig"].get("tolerance", 0.05)
99
112
  )
100
- scans.umv(dev.samx, 10, dev.samy, 20, relative=False)
113
+ getattr(scans, scan_name[1])(dev.samx, 10, dev.samy, 20, relative=False)
101
114
  current_pos_samx = dev.samx.read(cached=True)["samx"]["value"]
102
115
  current_pos_samy = dev.samy.read(cached=True)["samy"]["value"]
103
116
  captured = capsys.readouterr()
@@ -109,12 +122,13 @@ def test_mv_scan(capsys, bec_ipython_client_fixture):
109
122
 
110
123
  @pytest.mark.flaky # marked as flaky as the simulation might return a new readback value within the tolerance
111
124
  @pytest.mark.timeout(100)
112
- def test_mv_scan_nested_device(capsys, bec_ipython_client_fixture):
125
+ @pytest.mark.parametrize("scan_name", [("mv", "umv"), ("_v4_mv", "_v4_umv")])
126
+ def test_mv_scan_nested_device(capsys, bec_ipython_client_fixture, scan_name):
113
127
  bec = bec_ipython_client_fixture
114
128
  scans = bec.scans
115
129
  bec.metadata.update({"unit_test": "test_mv_scan_nested_device"})
116
130
  dev = bec.device_manager.devices
117
- scans.mv(dev.hexapod.x, 10, dev.hexapod.y, 20, relative=False).wait()
131
+ getattr(scans, scan_name[0])(dev.hexapod.x, 10, dev.hexapod.y, 20, relative=False).wait()
118
132
  if not bec.connector._managed_connection._message_callbacks_queue.empty():
119
133
  print("Waiting for messages to be processed")
120
134
  time.sleep(0.5)
@@ -126,7 +140,7 @@ def test_mv_scan_nested_device(capsys, bec_ipython_client_fixture):
126
140
  assert np.isclose(
127
141
  current_pos_hexapod_y, 20, atol=dev.hexapod._config["deviceConfig"].get("tolerance", 0.5)
128
142
  )
129
- scans.umv(dev.hexapod.x, 10, dev.hexapod.y, 20, relative=False)
143
+ getattr(scans, scan_name[1])(dev.hexapod.x, 10, dev.hexapod.y, 20, relative=False)
130
144
  if not bec.connector._managed_connection._message_callbacks_queue.empty():
131
145
  print("Waiting for messages to be processed")
132
146
  time.sleep(0.5)
@@ -145,7 +159,8 @@ def test_mv_scan_nested_device(capsys, bec_ipython_client_fixture):
145
159
 
146
160
 
147
161
  @pytest.mark.timeout(100)
148
- def test_mv_scan_mv(bec_ipython_client_fixture):
162
+ @pytest.mark.parametrize("scan_name", [("umv", "grid_scan"), ("_v4_umv", "_v4_grid_scan")])
163
+ def test_mv_scan_mv(bec_ipython_client_fixture, scan_name):
149
164
  bec = bec_ipython_client_fixture
150
165
  scans = bec.scans
151
166
  bec.metadata.update({"unit_test": "test_mv_scan_mv"})
@@ -154,7 +169,7 @@ def test_mv_scan_mv(bec_ipython_client_fixture):
154
169
 
155
170
  dev.samx.limits = [-50, 50]
156
171
  dev.samy.limits = [-50, 50]
157
- scans.umv(dev.samx, 10, dev.samy, 20, relative=False)
172
+ getattr(scans, scan_name[0])(dev.samx, 10, dev.samy, 20, relative=False)
158
173
  tolerance_samx = dev.samx._config["deviceConfig"].get("tolerance", 0.05)
159
174
  tolerance_samy = dev.samy._config["deviceConfig"].get("tolerance", 0.05)
160
175
  current_pos_samx = dev.samx.read()["samx"]["value"]
@@ -164,7 +179,9 @@ def test_mv_scan_mv(bec_ipython_client_fixture):
164
179
  assert np.isclose(current_pos_samx, 10, atol=tolerance_samx)
165
180
  assert np.isclose(current_pos_samy, 20, atol=tolerance_samy)
166
181
 
167
- status = scans.grid_scan(dev.samx, -5, 5, 10, dev.samy, -5, 5, 10, exp_time=0.01, relative=True)
182
+ status = getattr(scans, scan_name[1])(
183
+ dev.samx, -5, 5, 10, dev.samy, -5, 5, 10, exp_time=0.01, relative=True
184
+ )
168
185
 
169
186
  # make sure the scan completed the expected number of positions
170
187
  assert len(status.scan.live_data) == 100
@@ -184,7 +201,7 @@ def test_mv_scan_mv(bec_ipython_client_fixture):
184
201
  assert np.isclose(current_pos_samx, 10, atol=tolerance_samx * 2)
185
202
  assert np.isclose(current_pos_samy, 20, atol=tolerance_samy * 2)
186
203
 
187
- scans.umv(dev.samx, 20, dev.samy, -20, relative=False)
204
+ getattr(scans, scan_name[0])(dev.samx, 20, dev.samy, -20, relative=False)
188
205
  current_pos_samx = dev.samx.read()["samx"]["value"]
189
206
  current_pos_samy = dev.samy.read()["samy"]["value"]
190
207
 
@@ -192,7 +209,7 @@ def test_mv_scan_mv(bec_ipython_client_fixture):
192
209
  assert np.isclose(current_pos_samx, 20, atol=tolerance_samx)
193
210
  assert np.isclose(current_pos_samy, -20, atol=tolerance_samy)
194
211
 
195
- status = scans.grid_scan(
212
+ status = getattr(scans, scan_name[1])(
196
213
  dev.samx, -5, 5, 10, dev.samy, -5, 5, 10, exp_time=0.01, relative=False
197
214
  )
198
215
 
@@ -209,7 +226,8 @@ def test_mv_scan_mv(bec_ipython_client_fixture):
209
226
 
210
227
 
211
228
  @pytest.mark.timeout(100)
212
- def test_scan_abort(bec_ipython_client_fixture: BECIPythonClient):
229
+ @pytest.mark.parametrize("scan_name", ["line_scan", "_v4_line_scan"])
230
+ def test_scan_abort(bec_ipython_client_fixture: BECIPythonClient, scan_name: str):
213
231
  def send_abort(bec):
214
232
  while True:
215
233
  current_scan_info = bec.queue.scan_storage.current_scan_info
@@ -238,7 +256,7 @@ def test_scan_abort(bec_ipython_client_fixture: BECIPythonClient):
238
256
  aborted_scan = False
239
257
  try:
240
258
  threading.Thread(target=send_abort, args=(bec,), daemon=True).start()
241
- scans.line_scan(dev.samx, -5, 5, steps=200, exp_time=0.1, relative=True)
259
+ getattr(scans, scan_name)(dev.samx, -5, 5, steps=200, exp_time=0.1, relative=True)
242
260
  except ScanInterruption:
243
261
  logger.info("Raised ScanInterruption")
244
262
  time.sleep(2)
@@ -255,7 +273,7 @@ def test_scan_abort(bec_ipython_client_fixture: BECIPythonClient):
255
273
 
256
274
  assert len(bec.queue.scan_storage.storage[-1].live_data) < 200
257
275
 
258
- scans.line_scan(dev.samx, -5, 5, steps=10, exp_time=0.1, relative=True)
276
+ getattr(scans, scan_name)(dev.samx, -5, 5, steps=10, exp_time=0.1, relative=True)
259
277
  scan_number_end = bec.queue.next_scan_number
260
278
  assert scan_number_end == scan_number_start + 2
261
279
 
@@ -301,8 +319,8 @@ def test_umv_ctrl_c_stops_motion(bec_ipython_client_fixture: BECIPythonClient):
301
319
  dev.samx.velocity.set(original_velocity).wait()
302
320
 
303
321
 
304
- @pytest.mark.timeout(100)
305
- def test_limit_error(bec_ipython_client_fixture):
322
+ @pytest.mark.parametrize("scan_name", [("line_scan", "umv"), ("_v4_line_scan", "_v4_umv")])
323
+ def test_limit_error(bec_ipython_client_fixture, scan_name):
306
324
  bec = bec_ipython_client_fixture
307
325
  bec.metadata.update({"unit_test": "test_limit_error"})
308
326
  scan_number_start = bec.queue.next_scan_number
@@ -311,7 +329,7 @@ def test_limit_error(bec_ipython_client_fixture):
311
329
  aborted_scan = False
312
330
  dev.samx.limits = [-50, 50]
313
331
  try:
314
- scans.line_scan(dev.samx, -520, 5, steps=200, exp_time=0.1, relative=False)
332
+ getattr(scans, scan_name[0])(dev.samx, -520, 5, steps=200, exp_time=0.1, relative=False)
315
333
  except AlarmBase as alarm:
316
334
  assert alarm.alarm_type == "LimitError"
317
335
  aborted_scan = True
@@ -321,7 +339,7 @@ def test_limit_error(bec_ipython_client_fixture):
321
339
  aborted_scan = False
322
340
  dev.samx.limits = [-50, 50]
323
341
  try:
324
- scans.umv(dev.samx, 500, relative=False)
342
+ getattr(scans, scan_name[1])(dev.samx, 500, relative=False)
325
343
  except AlarmBase as alarm:
326
344
  assert alarm.alarm_type == "LimitError"
327
345
  aborted_scan = True
@@ -332,16 +350,17 @@ def test_limit_error(bec_ipython_client_fixture):
332
350
 
333
351
 
334
352
  @pytest.mark.timeout(100)
335
- def test_queued_scan(bec_ipython_client_fixture):
353
+ @pytest.mark.parametrize("scan_name", ["line_scan", "_v4_line_scan"])
354
+ def test_queued_scan(bec_ipython_client_fixture, scan_name):
336
355
  bec = bec_ipython_client_fixture
337
356
  bec.metadata.update({"unit_test": "test_queued_scan"})
338
357
  scan_number_start = bec.queue.next_scan_number
339
358
  scans = bec.scans
340
359
  dev = bec.device_manager.devices
341
- scan1 = scans.line_scan(
360
+ scan1 = getattr(scans, scan_name)(
342
361
  dev.samx, -5, 5, steps=100, exp_time=0.1, hide_report=True, relative=True
343
362
  )
344
- scan2 = scans.line_scan(
363
+ scan2 = getattr(scans, scan_name)(
345
364
  dev.samx, -5, 5, steps=50, exp_time=0.1, hide_report=True, relative=True
346
365
  )
347
366
 
@@ -375,7 +394,8 @@ def test_fly_scan(bec_ipython_client_fixture):
375
394
 
376
395
 
377
396
  @pytest.mark.timeout(100)
378
- def test_scan_restart(bec_ipython_client_fixture):
397
+ @pytest.mark.parametrize("scan_name", ["line_scan", "_v4_line_scan"])
398
+ def test_scan_restart(bec_ipython_client_fixture, scan_name):
379
399
  bec = bec_ipython_client_fixture
380
400
  bec.metadata.update({"unit_test": "test_scan_restart"})
381
401
  scans = bec.scans
@@ -397,10 +417,12 @@ def test_scan_restart(bec_ipython_client_fixture):
397
417
 
398
418
  # We start two scans to ensure that the scan restart logic works correctly in a queued scenario
399
419
  # The first scan is started without printout to allow us to submit the second scan immediately after
400
- scans.line_scan(dev.samx, -5, 5, steps=50, exp_time=0.1, hide_report=True, relative=True)
420
+ getattr(scans, scan_name)(
421
+ dev.samx, -5, 5, steps=50, exp_time=0.1, hide_report=True, relative=True
422
+ )
401
423
 
402
424
  # The second scan is using the live table printout. It should properly continue after the restart
403
- scan2 = scans.line_scan(dev.samx, -5, 5, steps=50, exp_time=0.1, relative=True)
425
+ scan2 = getattr(scans, scan_name)(dev.samx, -5, 5, steps=50, exp_time=0.1, relative=True)
404
426
 
405
427
  scan2.wait()
406
428
 
@@ -413,7 +435,8 @@ def test_scan_restart(bec_ipython_client_fixture):
413
435
 
414
436
 
415
437
  @pytest.mark.timeout(100)
416
- def test_scan_observer_repeat_queued(bec_ipython_client_fixture: BECIPythonClient):
438
+ @pytest.mark.parametrize("scan_name", ["line_scan", "_v4_line_scan"])
439
+ def test_scan_observer_repeat_queued(bec_ipython_client_fixture: BECIPythonClient, scan_name):
417
440
  bec = bec_ipython_client_fixture
418
441
  bec.metadata.update({"unit_test": "test_scan_observer_repeat_queued"})
419
442
  scans = bec.scans
@@ -435,10 +458,10 @@ def test_scan_observer_repeat_queued(bec_ipython_client_fixture: BECIPythonClien
435
458
  # start repeat thread
436
459
  threading.Thread(target=send_repeat, args=(bec,), daemon=True).start()
437
460
  # start scan
438
- scan1 = scans.line_scan(
461
+ scan1 = getattr(scans, scan_name)(
439
462
  dev.samx, -5, 5, steps=100, exp_time=0.1, hide_report=True, relative=True
440
463
  )
441
- scan2 = scans.line_scan(
464
+ scan2 = getattr(scans, scan_name)(
442
465
  dev.samx, -5, 5, steps=100, exp_time=0.1, hide_report=True, relative=True
443
466
  )
444
467
 
@@ -453,7 +476,8 @@ def test_scan_observer_repeat_queued(bec_ipython_client_fixture: BECIPythonClien
453
476
 
454
477
 
455
478
  @pytest.mark.timeout(100)
456
- def test_scan_observer_repeat(bec_ipython_client_fixture):
479
+ @pytest.mark.parametrize("scan_name", ["line_scan", "_v4_line_scan"])
480
+ def test_scan_observer_repeat(bec_ipython_client_fixture, scan_name):
457
481
  bec = bec_ipython_client_fixture
458
482
  bec.metadata.update({"unit_test": "test_scan_observer_repeat"})
459
483
  scans = bec.scans
@@ -473,7 +497,7 @@ def test_scan_observer_repeat(bec_ipython_client_fixture):
473
497
  threading.Thread(target=send_repeat, args=(bec,), daemon=True).start()
474
498
  # start scan
475
499
  with pytest.raises(ScanAbortion):
476
- scan1 = scans.line_scan(
500
+ scan1 = getattr(scans, scan_name)(
477
501
  dev.samx, -5, 5, steps=50, exp_time=0.1, hide_report=True, relative=True
478
502
  )
479
503
  scan1.wait()
@@ -488,7 +512,8 @@ def test_scan_observer_repeat(bec_ipython_client_fixture):
488
512
 
489
513
 
490
514
  @pytest.mark.timeout(100)
491
- def test_file_writer(bec_ipython_client_fixture):
515
+ @pytest.mark.parametrize("scan_name", ["grid_scan", "_v4_grid_scan"])
516
+ def test_file_writer(bec_ipython_client_fixture, scan_name):
492
517
  bec = bec_ipython_client_fixture
493
518
  bec.metadata.update({"unit_test": "test_file_writer"})
494
519
  scans = bec.scans
@@ -498,7 +523,7 @@ def test_file_writer(bec_ipython_client_fixture):
498
523
  dev.samx.velocity.set(98).wait()
499
524
  dev.samy.velocity.set(101).wait()
500
525
 
501
- scan = scans.grid_scan(
526
+ scan = getattr(scans, scan_name)(
502
527
  dev.samx,
503
528
  -5,
504
529
  5,
@@ -566,21 +591,22 @@ def test_group_def(bec_ipython_client_fixture):
566
591
 
567
592
 
568
593
  @pytest.mark.timeout(100)
569
- def test_list_scan(bec_ipython_client_fixture):
594
+ @pytest.mark.parametrize("scan_name", ["list_scan", "_v4_list_scan"])
595
+ def test_list_scan(bec_ipython_client_fixture, scan_name):
570
596
  bec = bec_ipython_client_fixture
571
597
  bec.metadata.update({"unit_test": "test_list_scan"})
572
598
  scans = bec.scans
573
599
  dev = bec.device_manager.devices
574
600
 
575
- status = scans.list_scan(
601
+ status = getattr(scans, scan_name)(
576
602
  dev.samx, [0, 1, 2, 3, 4], dev.samy, [0, 1, 2, 3, 4], exp_time=0.1, relative=False
577
603
  )
578
604
  assert len(status.scan.live_data) == 5
579
605
 
580
- status = scans.list_scan(dev.samx, [0, 1, 2, 3, 4, 5], exp_time=0.1, relative=False)
606
+ status = getattr(scans, scan_name)(dev.samx, [0, 1, 2, 3, 4, 5], exp_time=0.1, relative=False)
581
607
  assert len(status.scan.live_data) == 6
582
608
 
583
- status = scans.list_scan(
609
+ status = getattr(scans, scan_name)(
584
610
  dev.samx,
585
611
  [0, 1, 2, 3],
586
612
  dev.samy,
@@ -855,7 +881,8 @@ def test_grid_scan_secondary_queue(capsys, bec_ipython_client_fixture):
855
881
 
856
882
 
857
883
  @pytest.mark.timeout(100)
858
- def test_scan_after_scan_lock(capsys, bec_ipython_client_fixture):
884
+ @pytest.mark.parametrize("scan_name", ["line_scan", "_v4_line_scan"])
885
+ def test_scan_after_scan_lock(capsys, bec_ipython_client_fixture, scan_name):
859
886
  """
860
887
  Test that pending scans are properly executed after a scan lock is released.
861
888
  """
@@ -864,8 +891,10 @@ def test_scan_after_scan_lock(capsys, bec_ipython_client_fixture):
864
891
  bec.metadata.update({"unit_test": "test_scan_after_scan_lock"})
865
892
  dev = bec.device_manager.devices
866
893
  bec.queue.add_queue_lock(queue="primary", reason="unit_test_scan_lock", lock_id="test_lock")
867
- scans.line_scan(dev.samx, -5, 5, steps=10, exp_time=0.01, relative=True, hide_report=True)
868
- scan2 = scans.line_scan(
894
+ getattr(scans, scan_name)(
895
+ dev.samx, -5, 5, steps=10, exp_time=0.01, relative=True, hide_report=True
896
+ )
897
+ scan2 = getattr(scans, scan_name)(
869
898
  dev.samx, -5, 5, steps=10, exp_time=0.01, relative=True, hide_report=True
870
899
  )
871
900
 
@@ -879,7 +908,8 @@ def test_scan_after_scan_lock(capsys, bec_ipython_client_fixture):
879
908
 
880
909
 
881
910
  @pytest.mark.timeout(100)
882
- def test_scan_repeat_decorator(bec_ipython_client_fixture):
911
+ @pytest.mark.parametrize("scan_name", ["line_scan", "_v4_line_scan"])
912
+ def test_scan_repeat_decorator(bec_ipython_client_fixture, scan_name):
883
913
  """
884
914
  Test the scan_repeat decorator by simulating a communication failure during a scan.
885
915
  The scan should be retried according to the specified max_repeats and exc_handler.
@@ -923,7 +953,9 @@ def test_scan_repeat_decorator(bec_ipython_client_fixture):
923
953
 
924
954
  @scan_repeat(max_repeats=2, exc_handler=exc_handler)
925
955
  def my_scan():
926
- scans.line_scan(dev.positioner_with_failure, -5, 5, steps=10, exp_time=0.01, relative=True)
956
+ getattr(scans, scan_name)(
957
+ dev.positioner_with_failure, -5, 5, steps=10, exp_time=0.01, relative=True
958
+ )
927
959
 
928
960
  my_scan()
929
961
 
@@ -933,7 +965,10 @@ def test_scan_repeat_decorator(bec_ipython_client_fixture):
933
965
 
934
966
 
935
967
  @pytest.mark.timeout(100)
936
- def test_scan_set_completed(bec_ipython_client_fixture):
968
+ @pytest.mark.parametrize(
969
+ "scan_name", [("grid_scan", "line_scan"), ("_v4_grid_scan", "_v4_line_scan")]
970
+ )
971
+ def test_scan_set_completed(bec_ipython_client_fixture, scan_name):
937
972
  """
938
973
  Test that a scan can be manually set to completed.
939
974
  """
@@ -953,7 +988,9 @@ def test_scan_set_completed(bec_ipython_client_fixture):
953
988
 
954
989
  threading.Thread(target=_set_scan_completed, args=(bec,), daemon=True).start()
955
990
 
956
- scans.grid_scan(dev.samx, -5, 5, 10, dev.samy, -5, 5, 10, exp_time=0.01, relative=True)
991
+ getattr(scans, scan_name[0])(
992
+ dev.samx, -5, 5, 10, dev.samy, -5, 5, 10, exp_time=0.01, relative=True
993
+ )
957
994
 
958
995
  # Now add a second scan to ensure scans can continue after setting completed
959
- scans.line_scan(dev.samx, -5, 5, steps=10, exp_time=0.01, relative=True)
996
+ getattr(scans, scan_name[1])(dev.samx, -5, 5, steps=10, exp_time=0.01, relative=True)
@@ -16,25 +16,31 @@ logger = bec_logger.logger
16
16
 
17
17
 
18
18
  @pytest.mark.timeout(100)
19
- def test_grid_scan_lib(bec_client_lib):
19
+ @pytest.mark.parametrize("scan_name", [("umv", "grid_scan"), ("_v4_umv", "_v4_grid_scan")])
20
+ def test_grid_scan_lib(bec_client_lib, scan_name):
20
21
  bec = bec_client_lib
21
22
  scans = bec.scans
22
23
  bec.metadata.update({"unit_test": "test_grid_scan_bec_client_lib"})
23
24
  dev = bec.device_manager.devices
24
- scans.umv(dev.samx, 0, dev.samy, 0, relative=False)
25
- status = scans.grid_scan(dev.samx, -5, 5, 10, dev.samy, -5, 5, 10, exp_time=0.01, relative=True)
25
+ getattr(scans, scan_name[0])(dev.samx, 0, dev.samy, 0, relative=False)
26
+ status = getattr(scans, scan_name[1])(
27
+ dev.samx, -5, 5, 10, dev.samy, -5, 5, 10, exp_time=0.01, relative=True
28
+ )
26
29
  status.wait(num_points=True, file_written=True)
27
30
  assert len(status.scan.live_data) == 100
28
31
  assert status.scan.num_points == 100
29
32
 
30
33
 
31
34
  @pytest.mark.timeout(100)
32
- def test_grid_scan_lib_cancel(bec_client_lib):
35
+ @pytest.mark.parametrize("scan_name", ["grid_scan", "_v4_grid_scan"])
36
+ def test_grid_scan_lib_cancel(bec_client_lib, scan_name):
33
37
  bec = bec_client_lib
34
38
  scans = bec.scans
35
39
  bec.metadata.update({"unit_test": "test_grid_scan_bec_client_lib"})
36
40
  dev = bec.device_manager.devices
37
- status = scans.grid_scan(dev.samx, -5, 5, 10, dev.samy, -5, 5, 10, exp_time=1, relative=False)
41
+ status = getattr(scans, scan_name)(
42
+ dev.samx, -5, 5, 10, dev.samy, -5, 5, 10, exp_time=1, relative=False
43
+ )
38
44
  time.sleep(0.5)
39
45
  status.cancel()
40
46
 
@@ -375,6 +381,7 @@ def test_config_reload(
375
381
  assert bec.device_manager.devices[dev].enabled is False
376
382
 
377
383
 
384
+ @pytest.mark.timeout(100)
378
385
  def test_config_reload_with_describe_failure(bec_test_config_file_path, bec_client_lib):
379
386
  bec = bec_client_lib
380
387
  bec.metadata.update({"unit_test": "test_config_reload"})
@@ -404,34 +411,43 @@ def test_config_reload_with_describe_failure(bec_test_config_file_path, bec_clie
404
411
  },
405
412
  }
406
413
 
407
- # set hexapod to fail
408
- bec.connector.set(
409
- f"e2e_test_hexapod_fail", messages.DeviceStatusMessage(device="hexapod", status=1)
410
- )
414
+ try:
415
+ # set hexapod to fail
416
+ bec.connector.set(
417
+ "e2e_test_hexapod_fail", messages.DeviceStatusMessage(device="hexapod", status=1)
418
+ )
411
419
 
412
- # write new config to disk
413
- with open(runtime_config_file_path, "w") as f:
414
- f.write(yaml.dump(config))
420
+ # write new config to disk
421
+ with open(runtime_config_file_path, "w") as f:
422
+ f.write(yaml.dump(config))
415
423
 
416
- with pytest.raises(DeviceConfigError):
417
- bec.config.update_session_with_file(runtime_config_file_path, force=True, validate=False)
424
+ with pytest.raises(DeviceConfigError):
425
+ bec.config.update_session_with_file(
426
+ runtime_config_file_path, force=True, validate=False
427
+ )
418
428
 
419
- assert len(bec.device_manager.devices) == 2
420
- assert bec.device_manager.devices["eyefoc"].enabled is True
421
- assert bec.device_manager.devices["hexapod"].enabled is False
429
+ assert len(bec.device_manager.devices) == 2
430
+ assert bec.device_manager.devices["eyefoc"].enabled is True
431
+ assert bec.device_manager.devices["hexapod"].enabled is False
422
432
 
423
- # set hexapod to pass
424
- bec.connector.set(
425
- f"e2e_test_hexapod_fail", messages.DeviceStatusMessage(device="hexapod", status=0)
426
- )
433
+ # set hexapod to pass
434
+ bec.connector.set(
435
+ "e2e_test_hexapod_fail", messages.DeviceStatusMessage(device="hexapod", status=0)
436
+ )
427
437
 
428
- bec.config.update_session_with_file(runtime_config_file_path, force=True)
429
- assert len(bec.device_manager.devices) == 2
430
- assert bec.device_manager.devices["eyefoc"].enabled is True
431
- assert bec.device_manager.devices["hexapod"].enabled is True
432
- assert bec.device_manager.devices["hexapod"].precision == 3
438
+ bec.config.update_session_with_file(runtime_config_file_path, force=True)
439
+ assert len(bec.device_manager.devices) == 2
440
+ assert bec.device_manager.devices["eyefoc"].enabled is True
441
+ assert bec.device_manager.devices["hexapod"].enabled is True
442
+ assert bec.device_manager.devices["hexapod"].precision == 3
443
+ finally:
444
+ bec.connector.set(
445
+ "e2e_test_hexapod_fail", messages.DeviceStatusMessage(device="hexapod", status=0)
446
+ )
447
+ bec.config.update_session_with_file(bec_test_config_file_path, force=True)
433
448
 
434
449
 
450
+ @pytest.mark.timeout(100)
435
451
  def test_config_add_remove_device(bec_client_lib):
436
452
  bec = bec_client_lib
437
453
  bec.metadata.update({"unit_test": "test_config_add_device"})
@@ -474,6 +490,7 @@ def test_config_add_remove_device(bec_client_lib):
474
490
  assert "samx" in dev
475
491
 
476
492
 
493
+ @pytest.mark.timeout(100)
477
494
  def test_computed_signal(bec_client_lib):
478
495
  bec = bec_client_lib
479
496
  bec.metadata.update({"unit_test": "test_computed_signal"})
@@ -494,6 +511,7 @@ def test_computed_signal(bec_client_lib):
494
511
  assert dev.pseudo_signal1.read()["pseudo_signal1"]["value"] == 5
495
512
 
496
513
 
514
+ @pytest.mark.timeout(100)
497
515
  def test_cached_device_readout(bec_client_lib):
498
516
  bec = bec_client_lib
499
517
  bec.metadata.update({"unit_test": "test_cached_device_readout"})
@@ -532,6 +550,8 @@ def test_cached_device_readout(bec_client_lib):
532
550
  assert timestamp_3 == timestamp_2
533
551
 
534
552
 
553
+ @pytest.mark.timeout(100)
554
+ @pytest.mark.skip(reason="Interactive scans are currently not supported in v4 scans.")
535
555
  def test_interactive_scan(bec_client_lib):
536
556
  bec = bec_client_lib
537
557
  bec.metadata.update({"unit_test": "test_interactive_scan"})