bec-ipython-client 3.64.5__py3-none-any.whl → 3.89.3__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 +49 -19
- bec_ipython_client/callbacks/live_table.py +36 -9
- bec_ipython_client/callbacks/move_device.py +121 -59
- bec_ipython_client/callbacks/utils.py +5 -23
- bec_ipython_client/main.py +83 -14
- bec_ipython_client/signals.py +9 -3
- {bec_ipython_client-3.64.5.dist-info → bec_ipython_client-3.89.3.dist-info}/METADATA +2 -2
- bec_ipython_client-3.89.3.dist-info/RECORD +44 -0
- {bec_ipython_client-3.64.5.dist-info → bec_ipython_client-3.89.3.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_ipython_live_updates.py +259 -68
- 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 +5 -5
- tests/end-2-end/test_procedures_e2e.py +36 -26
- tests/end-2-end/test_scans_e2e.py +62 -13
- tests/end-2-end/test_scans_lib_e2e.py +23 -19
- bec_ipython_client-3.64.5.dist-info/RECORD +0 -43
- {bec_ipython_client-3.64.5.dist-info → bec_ipython_client-3.89.3.dist-info}/entry_points.txt +0 -0
bec_ipython_client/main.py
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import argparse
|
|
4
|
+
import collections
|
|
5
|
+
import functools
|
|
2
6
|
import os
|
|
3
7
|
import sys
|
|
4
8
|
from importlib.metadata import version
|
|
@@ -7,24 +11,28 @@ from typing import Iterable, Literal, Tuple
|
|
|
7
11
|
import IPython
|
|
8
12
|
import redis
|
|
9
13
|
import redis.exceptions
|
|
10
|
-
import requests
|
|
11
14
|
from IPython.terminal.ipapp import TerminalIPythonApp
|
|
12
15
|
from IPython.terminal.prompts import Prompts, Token
|
|
16
|
+
from pydantic import ValidationError
|
|
17
|
+
from rich.console import Console
|
|
18
|
+
from rich.panel import Panel
|
|
19
|
+
from rich.text import Text
|
|
13
20
|
|
|
14
21
|
from bec_ipython_client.beamline_mixin import BeamlineMixin
|
|
15
22
|
from bec_ipython_client.bec_magics import BECMagics
|
|
16
23
|
from bec_ipython_client.callbacks.ipython_live_updates import IPythonLiveUpdates
|
|
17
24
|
from bec_ipython_client.signals import ScanInterruption, SigintHandler
|
|
18
25
|
from bec_lib import plugin_helper
|
|
19
|
-
from bec_lib.acl_login import BECAuthenticationError
|
|
20
26
|
from bec_lib.alarm_handler import AlarmBase
|
|
21
27
|
from bec_lib.bec_errors import DeviceConfigError
|
|
22
28
|
from bec_lib.bec_service import parse_cmdline_args
|
|
23
29
|
from bec_lib.callback_handler import EventType
|
|
24
30
|
from bec_lib.client import BECClient
|
|
25
31
|
from bec_lib.logger import bec_logger
|
|
32
|
+
from bec_lib.procedures.hli import ProcedureHli
|
|
26
33
|
from bec_lib.redis_connector import RedisConnector
|
|
27
34
|
from bec_lib.service_config import ServiceConfig
|
|
35
|
+
from bec_lib.utils.pydantic_pretty_print import pretty_print_pydantic_validation_error
|
|
28
36
|
|
|
29
37
|
logger = bec_logger.logger
|
|
30
38
|
|
|
@@ -51,6 +59,7 @@ class BECIPythonClient:
|
|
|
51
59
|
# the CLIBECClient but directly through the BECIPythonClient. While this is not
|
|
52
60
|
# needed for normal usage, it is required, e.g. for mocks.
|
|
53
61
|
_local_only_types: Tuple = ()
|
|
62
|
+
_client: CLIBECClient | BECClient
|
|
54
63
|
|
|
55
64
|
def __init__(
|
|
56
65
|
self,
|
|
@@ -68,6 +77,7 @@ class BECIPythonClient:
|
|
|
68
77
|
name="BECIPythonClient",
|
|
69
78
|
prompt_for_acl=True,
|
|
70
79
|
)
|
|
80
|
+
|
|
71
81
|
self._ip = IPython.get_ipython()
|
|
72
82
|
self.started = False
|
|
73
83
|
self._sighandler = None
|
|
@@ -79,6 +89,7 @@ class BECIPythonClient:
|
|
|
79
89
|
self._client.callbacks.register(
|
|
80
90
|
event_type=EventType.NAMESPACE_UPDATE, callback=self._update_namespace_callback
|
|
81
91
|
)
|
|
92
|
+
self._alarm_history = collections.deque(maxlen=100)
|
|
82
93
|
|
|
83
94
|
def __getattr__(self, name):
|
|
84
95
|
return getattr(self._client, name)
|
|
@@ -136,12 +147,13 @@ class BECIPythonClient:
|
|
|
136
147
|
self._refresh_ipython_username()
|
|
137
148
|
self._load_magics()
|
|
138
149
|
self._ip.events.register("post_run_cell", log_console)
|
|
139
|
-
self._ip.set_custom_exc((Exception,),
|
|
150
|
+
self._ip.set_custom_exc((Exception,), self._create_exception_handler())
|
|
140
151
|
# represent objects using __str__, if overwritten, otherwise use __repr__
|
|
141
152
|
self._ip.display_formatter.formatters["text/plain"].for_type(
|
|
142
153
|
object,
|
|
143
154
|
lambda o, p, cycle: o.__str__ is object.__str__ and p.text(repr(o)) or p.text(str(o)),
|
|
144
155
|
)
|
|
156
|
+
self._set_idle()
|
|
145
157
|
|
|
146
158
|
def _update_namespace_callback(self, action: Literal["add", "remove"], ns_objects: dict):
|
|
147
159
|
"""Callback to update the global namespace of ipython.
|
|
@@ -179,18 +191,75 @@ class BECIPythonClient:
|
|
|
179
191
|
magics = BECMagics(self._ip, self)
|
|
180
192
|
self._ip.register_magics(magics)
|
|
181
193
|
|
|
182
|
-
def shutdown(self):
|
|
194
|
+
def shutdown(self, per_thread_timeout_s: float | None = None):
|
|
183
195
|
"""shutdown the client and all its components"""
|
|
184
196
|
try:
|
|
185
197
|
self.gui.close()
|
|
186
198
|
except AttributeError:
|
|
187
199
|
pass
|
|
188
|
-
self._client.shutdown()
|
|
200
|
+
self._client.shutdown(per_thread_timeout_s)
|
|
201
|
+
logger.success("done")
|
|
202
|
+
|
|
203
|
+
def _create_exception_handler(self):
|
|
204
|
+
return functools.partial(_ip_exception_handler, parent=self)
|
|
189
205
|
|
|
206
|
+
def show_last_alarm(self, offset: int = 0):
|
|
207
|
+
"""
|
|
208
|
+
Show the last alarm raised in this session with rich formatting.
|
|
209
|
+
"""
|
|
210
|
+
try:
|
|
211
|
+
alarm: AlarmBase = self._alarm_history[-1 - offset][1]
|
|
212
|
+
except IndexError:
|
|
213
|
+
print("No alarm has been raised in this session.")
|
|
214
|
+
return
|
|
190
215
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
216
|
+
console = Console()
|
|
217
|
+
|
|
218
|
+
# --- HEADER ---
|
|
219
|
+
header = Text()
|
|
220
|
+
header.append("Alarm Raised\n", style="bold red")
|
|
221
|
+
header.append(f"Severity: {alarm.severity.name}\n", style="bold")
|
|
222
|
+
header.append(f"Type: {alarm.alarm_type}\n", style="bold")
|
|
223
|
+
if alarm.alarm.info.device:
|
|
224
|
+
header.append(f"Device: {alarm.alarm.info.device}\n", style="bold")
|
|
225
|
+
|
|
226
|
+
console.print(Panel(header, title="Alarm Info", border_style="red", expand=False))
|
|
227
|
+
|
|
228
|
+
# --- SHOW SUMMARY
|
|
229
|
+
if alarm.alarm.info.compact_error_message:
|
|
230
|
+
console.print(
|
|
231
|
+
Panel(
|
|
232
|
+
Text(alarm.alarm.info.compact_error_message, style="yellow"),
|
|
233
|
+
title="Summary",
|
|
234
|
+
border_style="yellow",
|
|
235
|
+
expand=False,
|
|
236
|
+
)
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
# --- SHOW FULL TRACEBACK
|
|
240
|
+
tb_str = alarm.alarm.info.error_message
|
|
241
|
+
if tb_str:
|
|
242
|
+
try:
|
|
243
|
+
console.print(tb_str)
|
|
244
|
+
except Exception:
|
|
245
|
+
# fallback in case msg is not a traceback
|
|
246
|
+
console.print(Panel(tb_str, title="Message", border_style="cyan"))
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def _ip_exception_handler(
|
|
250
|
+
self, etype, evalue, tb, tb_offset=None, parent: BECIPythonClient = None, **kwargs
|
|
251
|
+
):
|
|
252
|
+
if issubclass(etype, AlarmBase):
|
|
253
|
+
parent._alarm_history.append((etype, evalue, tb, tb_offset))
|
|
254
|
+
print("\x1b[31m BEC alarm:\x1b[0m")
|
|
255
|
+
evalue.pretty_print()
|
|
256
|
+
print("For more details, use 'bec.show_last_alarm()'")
|
|
257
|
+
return
|
|
258
|
+
if issubclass(etype, ValidationError):
|
|
259
|
+
pretty_print_pydantic_validation_error(evalue)
|
|
260
|
+
return
|
|
261
|
+
if issubclass(etype, (ScanInterruption, DeviceConfigError)):
|
|
262
|
+
print(f"\x1b[31m {evalue.__class__.__name__}:\x1b[0m {evalue}")
|
|
194
263
|
return
|
|
195
264
|
if issubclass(etype, redis.exceptions.NoPermissionError):
|
|
196
265
|
# pylint: disable=protected-access
|
|
@@ -220,9 +289,14 @@ class BECClientPrompt(Prompts):
|
|
|
220
289
|
next_scan_number = str(self.client.queue.next_scan_number)
|
|
221
290
|
except Exception:
|
|
222
291
|
next_scan_number = "?"
|
|
292
|
+
|
|
293
|
+
if self.client.active_account:
|
|
294
|
+
username = f"{self.client.active_account} | {self.username}"
|
|
295
|
+
else:
|
|
296
|
+
username = self.username
|
|
223
297
|
return [
|
|
224
298
|
(status_led, "\u2022"),
|
|
225
|
-
(Token.Prompt, " " +
|
|
299
|
+
(Token.Prompt, " " + username), # BEC ACL username and pgroup
|
|
226
300
|
(Token.Prompt, "@" + self.session_name),
|
|
227
301
|
(Token.Prompt, " ["),
|
|
228
302
|
(Token.PromptNum, str(self.shell.execution_count)),
|
|
@@ -269,7 +343,6 @@ def main():
|
|
|
269
343
|
parser = argparse.ArgumentParser(
|
|
270
344
|
prog="BEC IPython client", description="BEC command line interface"
|
|
271
345
|
)
|
|
272
|
-
parser.add_argument("--version", action="store_true", default=False)
|
|
273
346
|
parser.add_argument("--nogui", action="store_true", default=False)
|
|
274
347
|
parser.add_argument(
|
|
275
348
|
"--gui-id",
|
|
@@ -289,10 +362,6 @@ def main():
|
|
|
289
362
|
# remove already parsed args from command line args
|
|
290
363
|
sys.argv = sys.argv[:1] + left_args
|
|
291
364
|
|
|
292
|
-
if args.version:
|
|
293
|
-
print(f"BEC IPython client: {version('bec_ipython_client')}")
|
|
294
|
-
sys.exit(0)
|
|
295
|
-
|
|
296
365
|
if available_plugins and config.is_default():
|
|
297
366
|
# check if config is defined in a plugin;
|
|
298
367
|
# in this case the plugin config takes precedence over
|
bec_ipython_client/signals.py
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import signal
|
|
2
4
|
import threading
|
|
3
5
|
import time
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
4
7
|
|
|
5
8
|
from bec_lib.bec_errors import ScanInterruption
|
|
6
9
|
|
|
10
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
11
|
+
from bec_lib.client import BECClient
|
|
12
|
+
|
|
7
13
|
PAUSE_MSG = """
|
|
8
14
|
The Scan Queue is entering a paused state. These are your options for changing
|
|
9
15
|
the state of the queue:
|
|
@@ -73,7 +79,7 @@ class SignalHandler:
|
|
|
73
79
|
|
|
74
80
|
|
|
75
81
|
class SigintHandler(SignalHandler):
|
|
76
|
-
def __init__(self, bec):
|
|
82
|
+
def __init__(self, bec: BECClient):
|
|
77
83
|
super().__init__(signal.SIGINT)
|
|
78
84
|
self.bec = bec
|
|
79
85
|
self.last_sigint_time = None # time most recent SIGINT was processed
|
|
@@ -84,11 +90,11 @@ class SigintHandler(SignalHandler):
|
|
|
84
90
|
if not current_scan:
|
|
85
91
|
raise KeyboardInterrupt
|
|
86
92
|
|
|
87
|
-
status = current_scan.
|
|
93
|
+
status = current_scan.status.lower()
|
|
88
94
|
if status not in ["running", "deferred_pause"]:
|
|
89
95
|
raise KeyboardInterrupt
|
|
90
96
|
|
|
91
|
-
if any(current_scan.
|
|
97
|
+
if any(current_scan.is_scan) and (
|
|
92
98
|
self.last_sigint_time is None or time.time() - self.last_sigint_time > 10
|
|
93
99
|
):
|
|
94
100
|
# reset the counter to 1
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bec_ipython_client
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.89.3
|
|
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
|
|
7
7
|
Classifier: Development Status :: 3 - Alpha
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
9
|
Classifier: Topic :: Scientific/Engineering
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
11
|
Requires-Dist: bec-lib~=3.0
|
|
12
12
|
Requires-Dist: ipython~=8.22
|
|
13
13
|
Requires-Dist: numpy<3.0,>=1.24
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
.gitignore,sha256=XxC6jyyftTo2CLtm4K8axuNPYwA9Wgaz2R93WhX8bTQ,3364
|
|
2
|
+
PKG-INFO,sha256=trZwsE2OpNig8TZ6MjQ6HN5lX5tRxLrygSFHiz4FATs,1052
|
|
3
|
+
demo.py,sha256=AquJB-0Tu2bIEFpcJ0Q3RUBt1xldqS6GgU5CjOAg_ww,7083
|
|
4
|
+
pyproject.toml,sha256=Gv8V_TLxvNXUR9W-ROwlP7dGq9cbC8BfRC9anNsoddQ,1229
|
|
5
|
+
bec_ipython_client/__init__.py,sha256=ihd_V8I7Qo0MWKMo7bcvPf-ZyUQqkcNf8IAWLJKiFJE,79
|
|
6
|
+
bec_ipython_client/beamline_mixin.py,sha256=scMWIFbHJajyECzbwEVKyQUGjpqA9C_KiU2M6FuRH_Q,1067
|
|
7
|
+
bec_ipython_client/bec_magics.py,sha256=Rz2aXkUCeAV_VxdXqLUNHh8T44kSH9ha83OiEtdptzI,2792
|
|
8
|
+
bec_ipython_client/bec_startup.py,sha256=GGlHyxnSCQfYF5n-pYq2ic0pSyW5zvnT2PAlI5kY77w,1930
|
|
9
|
+
bec_ipython_client/main.py,sha256=h7417qQj0rwj5gjbKg6pn8Y_yYJf4b8Fs5EP29sXXOg,13564
|
|
10
|
+
bec_ipython_client/prettytable.py,sha256=TnhGPGuU0XEvnIYJ1UfTEwadcowFW4rqJW8z_Sm0EDw,2534
|
|
11
|
+
bec_ipython_client/progressbar.py,sha256=aDKYjzXmGSwa82ewm59V8WSuqVQz9GiZPx5G65fEwpk,11090
|
|
12
|
+
bec_ipython_client/signals.py,sha256=P7FrIB66dGdPESUngBnIVPGrAhcd2Tek8ufq9ALBoYU,4340
|
|
13
|
+
bec_ipython_client/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
bec_ipython_client/callbacks/device_progress.py,sha256=NzWIO1oowxpN14hPXBne6RfZxOW82EhwDM3jHLnn3xI,2603
|
|
15
|
+
bec_ipython_client/callbacks/ipython_live_updates.py,sha256=zaXR6UX7fA01-N0B0C2t67lOqjBgq8nal8eZpgf_nDc,11416
|
|
16
|
+
bec_ipython_client/callbacks/live_table.py,sha256=ewdter1z1arV1EvEsReOihluyTmAHMrHtOu_S29vixo,14432
|
|
17
|
+
bec_ipython_client/callbacks/move_device.py,sha256=TkbtKFBr1nuJ8THTSm70Y8D7_va2YQ57oBGGt0BavW4,8153
|
|
18
|
+
bec_ipython_client/callbacks/utils.py,sha256=e8USLnufNTpI2Bkp-sjRsSFvIXxB2CirF6WRPVsOets,5338
|
|
19
|
+
bec_ipython_client/high_level_interfaces/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
+
bec_ipython_client/high_level_interfaces/bec_hli.py,sha256=G1eF-lbLEDuN2mr_AYSxhhGb4LT6P72g0a4sU9M9zgk,1221
|
|
21
|
+
bec_ipython_client/high_level_interfaces/spec_hli.py,sha256=z7WtjiC4LtMfKJn12SbguHPCLqbAsZNfUDyiUW0LOiU,5818
|
|
22
|
+
bec_ipython_client/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
+
bec_ipython_client/plugins/SLS/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
+
bec_ipython_client/plugins/SLS/sls_info.py,sha256=93GU_XNRDsCO0ZvUrCf_SWnUMJ1FlRTJSuXXh_bYJ30,4934
|
|
25
|
+
bec_ipython_client/plugins/XTreme/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
|
+
bec_ipython_client/plugins/XTreme/x-treme.py,sha256=ZKI_EEPYyWW-sdrK9DIllb81aeObc1O3fU1eWqipjTQ,3631
|
|
27
|
+
bec_ipython_client/plugins/flomni/flomni_config.yaml,sha256=bCN1VKCzF6tw24HYeUE2b81kXMXzhEd69Ea8_q8Ywfk,6184
|
|
28
|
+
tests/conftest.py,sha256=4yzGYqs8EVOxxY9Bu-yGbzmOpzG9dxs3cwSCzjmRziQ,233
|
|
29
|
+
tests/client_tests/conftest.py,sha256=dUvYqvl6wogxN-kRCUuo_Uzna3P2uSYD7qGHyMGBlPY,413
|
|
30
|
+
tests/client_tests/test_beamline_mixins.py,sha256=8Ws0bmzl2gSW0VuOVu80_JbYNb5Y-htchmrrptwjwDo,4611
|
|
31
|
+
tests/client_tests/test_bec_client.py,sha256=gks7IYoDDXpbkC_EGWQfKtH08Yig71PRZtleXFhkU8A,8903
|
|
32
|
+
tests/client_tests/test_device_progress.py,sha256=GEw2g8MQZnv5mxABEZlxBmaMpxVS33wogaYohFolDEs,2353
|
|
33
|
+
tests/client_tests/test_ipython_live_updates.py,sha256=s4HLqF2hVQaaJhRKv0dxj0DiZK5M9Z_WwMAbGk19EFg,11918
|
|
34
|
+
tests/client_tests/test_live_table.py,sha256=0EKgWOgDqpjN8fJEeAzoKNCwFneDEsUY2NfWQkxB6xc,18155
|
|
35
|
+
tests/client_tests/test_move_callback.py,sha256=bUCcWoz_tc-yRAtwmEUMrKE_qKwatop_wReSugGGEIQ,8670
|
|
36
|
+
tests/client_tests/test_pretty_table.py,sha256=uQ-KPb3RXoCFE_t1IrpkT6kZAoqW7pFXxbFc445sX0Y,469
|
|
37
|
+
tests/end-2-end/_ensure_requirements_container.py,sha256=dv_ACfo9nHyiiEL3AMccaEg38VxGqB0W7u_iPyCVeiE,701
|
|
38
|
+
tests/end-2-end/test_procedures_e2e.py,sha256=ai8pUrHaL-5sYuaKYucuYEwyvhelS1dsW9KGoBb1Riw,4872
|
|
39
|
+
tests/end-2-end/test_scans_e2e.py,sha256=JhyWjDyUlBZAM_gL6QtfKXfI_fXdUnykYCIWCSorRiA,32527
|
|
40
|
+
tests/end-2-end/test_scans_lib_e2e.py,sha256=dqs0ojkyQWStIQbqABq9mQrjqQyE43gr37VPhxvQ8b8,19427
|
|
41
|
+
bec_ipython_client-3.89.3.dist-info/METADATA,sha256=trZwsE2OpNig8TZ6MjQ6HN5lX5tRxLrygSFHiz4FATs,1052
|
|
42
|
+
bec_ipython_client-3.89.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
43
|
+
bec_ipython_client-3.89.3.dist-info/entry_points.txt,sha256=oQUXYY0jjD9ZvKPHwaGn2wkUIWpDZM8L4ixDA3RlBWE,53
|
|
44
|
+
bec_ipython_client-3.89.3.dist-info/RECORD,,
|
demo.py
CHANGED
|
@@ -23,7 +23,7 @@ scans = bec.scans
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
logger.success("Started BECClient")
|
|
26
|
-
scans.umv(dev.samx, 5, relative=
|
|
26
|
+
scans.umv(dev.samx, 5, dev.samy, 20, relative=False)
|
|
27
27
|
|
|
28
28
|
# with scans.interactive_scan() as scan:
|
|
29
29
|
# for ii in range(10):
|
|
@@ -226,4 +226,5 @@ scans.umv(dev.samx, 5, relative=True)
|
|
|
226
226
|
# event = threading.Event()
|
|
227
227
|
# event.wait()
|
|
228
228
|
print("eos")
|
|
229
|
+
bec.shutdown()
|
|
229
230
|
# p.join()
|
pyproject.toml
CHANGED
|
@@ -4,9 +4,9 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "bec_ipython_client"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.89.3"
|
|
8
8
|
description = "BEC IPython client"
|
|
9
|
-
requires-python = ">=3.
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
10
|
classifiers = [
|
|
11
11
|
"Development Status :: 3 - Alpha",
|
|
12
12
|
"Programming Language :: Python :: 3",
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import fakeredis
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from bec_lib.redis_connector import RedisConnector
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def fake_redis_server(host, port, **kwargs):
|
|
8
|
+
redis = fakeredis.FakeRedis()
|
|
9
|
+
return redis
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.fixture
|
|
13
|
+
def connected_connector():
|
|
14
|
+
connector = RedisConnector("localhost:1", redis_cls=fake_redis_server)
|
|
15
|
+
connector._redis_conn.flushall()
|
|
16
|
+
try:
|
|
17
|
+
yield connector
|
|
18
|
+
finally:
|
|
19
|
+
connector.shutdown()
|
|
@@ -7,6 +7,7 @@ import pytest
|
|
|
7
7
|
|
|
8
8
|
from bec_ipython_client import BECIPythonClient, main
|
|
9
9
|
from bec_lib import messages
|
|
10
|
+
from bec_lib.alarm_handler import AlarmBase, AlarmHandler, Alarms
|
|
10
11
|
from bec_lib.redis_connector import RedisConnector
|
|
11
12
|
from bec_lib.service_config import ServiceConfig
|
|
12
13
|
|
|
@@ -204,10 +205,40 @@ def test_bec_ipython_client_property_access(ipython_client):
|
|
|
204
205
|
client.start()
|
|
205
206
|
|
|
206
207
|
with mock.patch.object(client._client, "connector") as mock_connector:
|
|
207
|
-
mock_connector.
|
|
208
|
+
mock_connector.get_last.return_value = messages.VariableMessage(value="account")
|
|
208
209
|
assert client._client.active_account == "account"
|
|
209
210
|
|
|
210
211
|
with pytest.raises(AttributeError):
|
|
211
212
|
client.active_account = "account"
|
|
212
213
|
with pytest.raises(AttributeError):
|
|
213
214
|
client._client.active_account = "account"
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def test_bec_ipython_client_show_last_alarm(ipython_client, capsys):
|
|
218
|
+
client = ipython_client
|
|
219
|
+
error_info = messages.ErrorInfo(
|
|
220
|
+
error_message="This is a test alarm",
|
|
221
|
+
compact_error_message="Test alarm",
|
|
222
|
+
exception_type="TestAlarm",
|
|
223
|
+
device=None,
|
|
224
|
+
)
|
|
225
|
+
alarm_msg = messages.AlarmMessage(severity=Alarms.MAJOR, info=error_info)
|
|
226
|
+
client.alarm_handler = AlarmHandler(connector=mock.MagicMock())
|
|
227
|
+
client.alarm_handler.add_alarm(alarm_msg)
|
|
228
|
+
client._alarm_history.append(
|
|
229
|
+
(AlarmBase, client.alarm_handler.get_unhandled_alarms()[0], None, None)
|
|
230
|
+
)
|
|
231
|
+
client.show_last_alarm()
|
|
232
|
+
captured = capsys.readouterr()
|
|
233
|
+
assert "Alarm Raised" in captured.out
|
|
234
|
+
assert "Severity: MAJOR" in captured.out
|
|
235
|
+
assert "Type: TestAlarm" in captured.out
|
|
236
|
+
assert "This is a test alarm" in captured.out
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def test_bec_ipython_client_show_last_no_alarm(ipython_client, capsys):
|
|
240
|
+
client = ipython_client
|
|
241
|
+
client._alarm_history = []
|
|
242
|
+
client.show_last_alarm()
|
|
243
|
+
captured = capsys.readouterr()
|
|
244
|
+
assert "No alarm has been raised in this session." in captured.out
|