bec-ipython-client 3.64.5__py3-none-any.whl → 3.84.0__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.
- .gitignore +3 -0
- PKG-INFO +2 -2
- bec_ipython_client/callbacks/device_progress.py +11 -6
- bec_ipython_client/callbacks/ipython_live_updates.py +8 -3
- bec_ipython_client/callbacks/live_table.py +9 -4
- bec_ipython_client/callbacks/move_device.py +121 -59
- bec_ipython_client/callbacks/utils.py +4 -4
- bec_ipython_client/main.py +76 -7
- {bec_ipython_client-3.64.5.dist-info → bec_ipython_client-3.84.0.dist-info}/METADATA +2 -2
- {bec_ipython_client-3.64.5.dist-info → bec_ipython_client-3.84.0.dist-info}/RECORD +22 -21
- {bec_ipython_client-3.64.5.dist-info → bec_ipython_client-3.84.0.dist-info}/WHEEL +1 -1
- demo.py +2 -1
- pyproject.toml +2 -2
- tests/client_tests/conftest.py +19 -0
- tests/client_tests/test_bec_client.py +32 -1
- tests/client_tests/test_live_table.py +33 -12
- tests/client_tests/test_move_callback.py +112 -70
- tests/end-2-end/_ensure_requirements_container.py +3 -3
- tests/end-2-end/test_procedures_e2e.py +26 -17
- tests/end-2-end/test_scans_e2e.py +4 -4
- tests/end-2-end/test_scans_lib_e2e.py +23 -19
- {bec_ipython_client-3.64.5.dist-info → bec_ipython_client-3.84.0.dist-info}/entry_points.txt +0 -0
|
@@ -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
|
|
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.
|
|
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.
|
|
81
|
-
_wait_while(lambda: manager.
|
|
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.
|
|
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.
|
|
103
|
-
_wait_while(lambda: manager.
|
|
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(
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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}"
|
|
@@ -737,12 +737,12 @@ def test_update_config(bec_ipython_client_fixture):
|
|
|
737
737
|
bec = bec_ipython_client_fixture
|
|
738
738
|
bec.metadata.update({"unit_test": "test_update_config"})
|
|
739
739
|
demo_config_path = os.path.join(os.path.dirname(configs.__file__), "demo_config.yaml")
|
|
740
|
-
config = bec.
|
|
740
|
+
config = bec.device_manager.config_helper._load_config_from_file(demo_config_path)
|
|
741
741
|
config.pop("samx")
|
|
742
|
-
bec.
|
|
742
|
+
bec.device_manager.config_helper.send_config_request(action="set", config=config)
|
|
743
743
|
assert "samx" not in bec.device_manager.devices
|
|
744
|
-
config = bec.
|
|
745
|
-
bec.
|
|
744
|
+
config = bec.device_manager.config_helper._load_config_from_file(demo_config_path)
|
|
745
|
+
bec.device_manager.config_helper.send_config_request(action="set", config=config)
|
|
746
746
|
|
|
747
747
|
|
|
748
748
|
@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(
|
|
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.
|
|
417
|
+
bec.device_manager.config_helper.send_config_request(action="add", config=config)
|
|
418
418
|
with pytest.raises(DeviceConfigError) as config_error:
|
|
419
|
-
bec.
|
|
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.
|
|
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.
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
{bec_ipython_client-3.64.5.dist-info → bec_ipython_client-3.84.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|