bec-ipython-client 3.70.0__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.
- .gitignore +3 -0
- PKG-INFO +1 -1
- bec_ipython_client/callbacks/device_progress.py +11 -6
- bec_ipython_client/callbacks/ipython_live_updates.py +46 -18
- bec_ipython_client/callbacks/live_table.py +36 -11
- bec_ipython_client/callbacks/move_device.py +121 -59
- bec_ipython_client/callbacks/utils.py +5 -23
- bec_ipython_client/main.py +71 -6
- bec_ipython_client/signals.py +9 -3
- {bec_ipython_client-3.70.0.dist-info → bec_ipython_client-3.86.1.dist-info}/METADATA +1 -1
- bec_ipython_client-3.86.1.dist-info/RECORD +44 -0
- {bec_ipython_client-3.70.0.dist-info → bec_ipython_client-3.86.1.dist-info}/WHEEL +1 -1
- demo.py +2 -1
- pyproject.toml +1 -1
- tests/client_tests/conftest.py +19 -0
- tests/client_tests/test_bec_client.py +31 -0
- tests/client_tests/test_ipython_live_updates.py +259 -68
- tests/client_tests/test_live_table.py +0 -1
- 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 +19 -13
- tests/end-2-end/test_scans_lib_e2e.py +18 -12
- bec_ipython_client-3.70.0.dist-info/RECORD +0 -43
- {bec_ipython_client-3.70.0.dist-info → bec_ipython_client-3.86.1.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,16 +11,18 @@ 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
|
|
@@ -25,6 +31,7 @@ from bec_lib.client import BECClient
|
|
|
25
31
|
from bec_lib.logger import bec_logger
|
|
26
32
|
from bec_lib.redis_connector import RedisConnector
|
|
27
33
|
from bec_lib.service_config import ServiceConfig
|
|
34
|
+
from bec_lib.utils.pydantic_pretty_print import pretty_print_pydantic_validation_error
|
|
28
35
|
|
|
29
36
|
logger = bec_logger.logger
|
|
30
37
|
|
|
@@ -51,6 +58,7 @@ class BECIPythonClient:
|
|
|
51
58
|
# the CLIBECClient but directly through the BECIPythonClient. While this is not
|
|
52
59
|
# needed for normal usage, it is required, e.g. for mocks.
|
|
53
60
|
_local_only_types: Tuple = ()
|
|
61
|
+
_client: CLIBECClient | BECClient
|
|
54
62
|
|
|
55
63
|
def __init__(
|
|
56
64
|
self,
|
|
@@ -79,6 +87,7 @@ class BECIPythonClient:
|
|
|
79
87
|
self._client.callbacks.register(
|
|
80
88
|
event_type=EventType.NAMESPACE_UPDATE, callback=self._update_namespace_callback
|
|
81
89
|
)
|
|
90
|
+
self._alarm_history = collections.deque(maxlen=100)
|
|
82
91
|
|
|
83
92
|
def __getattr__(self, name):
|
|
84
93
|
return getattr(self._client, name)
|
|
@@ -136,7 +145,7 @@ class BECIPythonClient:
|
|
|
136
145
|
self._refresh_ipython_username()
|
|
137
146
|
self._load_magics()
|
|
138
147
|
self._ip.events.register("post_run_cell", log_console)
|
|
139
|
-
self._ip.set_custom_exc((Exception,),
|
|
148
|
+
self._ip.set_custom_exc((Exception,), self._create_exception_handler())
|
|
140
149
|
# represent objects using __str__, if overwritten, otherwise use __repr__
|
|
141
150
|
self._ip.display_formatter.formatters["text/plain"].for_type(
|
|
142
151
|
object,
|
|
@@ -187,10 +196,66 @@ class BECIPythonClient:
|
|
|
187
196
|
pass
|
|
188
197
|
self._client.shutdown()
|
|
189
198
|
|
|
199
|
+
def _create_exception_handler(self):
|
|
200
|
+
return functools.partial(_ip_exception_handler, parent=self)
|
|
201
|
+
|
|
202
|
+
def show_last_alarm(self, offset: int = 0):
|
|
203
|
+
"""
|
|
204
|
+
Show the last alarm raised in this session with rich formatting.
|
|
205
|
+
"""
|
|
206
|
+
try:
|
|
207
|
+
alarm: AlarmBase = self._alarm_history[-1 - offset][1]
|
|
208
|
+
except IndexError:
|
|
209
|
+
print("No alarm has been raised in this session.")
|
|
210
|
+
return
|
|
190
211
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
212
|
+
console = Console()
|
|
213
|
+
|
|
214
|
+
# --- HEADER ---
|
|
215
|
+
header = Text()
|
|
216
|
+
header.append("Alarm Raised\n", style="bold red")
|
|
217
|
+
header.append(f"Severity: {alarm.severity.name}\n", style="bold")
|
|
218
|
+
header.append(f"Type: {alarm.alarm_type}\n", style="bold")
|
|
219
|
+
if alarm.alarm.info.device:
|
|
220
|
+
header.append(f"Device: {alarm.alarm.info.device}\n", style="bold")
|
|
221
|
+
|
|
222
|
+
console.print(Panel(header, title="Alarm Info", border_style="red", expand=False))
|
|
223
|
+
|
|
224
|
+
# --- SHOW SUMMARY
|
|
225
|
+
if alarm.alarm.info.compact_error_message:
|
|
226
|
+
console.print(
|
|
227
|
+
Panel(
|
|
228
|
+
Text(alarm.alarm.info.compact_error_message, style="yellow"),
|
|
229
|
+
title="Summary",
|
|
230
|
+
border_style="yellow",
|
|
231
|
+
expand=False,
|
|
232
|
+
)
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# --- SHOW FULL TRACEBACK
|
|
236
|
+
tb_str = alarm.alarm.info.error_message
|
|
237
|
+
if tb_str:
|
|
238
|
+
try:
|
|
239
|
+
console.print(tb_str)
|
|
240
|
+
except Exception:
|
|
241
|
+
# fallback in case msg is not a traceback
|
|
242
|
+
console.print(Panel(tb_str, title="Message", border_style="cyan"))
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _ip_exception_handler(
|
|
246
|
+
self, etype, evalue, tb, tb_offset=None, parent: BECIPythonClient = None, **kwargs
|
|
247
|
+
):
|
|
248
|
+
if issubclass(etype, AlarmBase):
|
|
249
|
+
parent._alarm_history.append((etype, evalue, tb, tb_offset))
|
|
250
|
+
print("\x1b[31m BEC alarm:\x1b[0m")
|
|
251
|
+
evalue.pretty_print()
|
|
252
|
+
print("For more details, use 'bec.show_last_alarm()'")
|
|
253
|
+
return
|
|
254
|
+
if issubclass(etype, ValidationError):
|
|
255
|
+
pretty_print_pydantic_validation_error(evalue)
|
|
256
|
+
return
|
|
257
|
+
if issubclass(etype, (ScanInterruption, DeviceConfigError)):
|
|
258
|
+
print(f"\x1b[31m {evalue.__class__.__name__}:\x1b[0m {evalue}")
|
|
194
259
|
return
|
|
195
260
|
if issubclass(etype, redis.exceptions.NoPermissionError):
|
|
196
261
|
# pylint: disable=protected-access
|
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
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
.gitignore,sha256=XxC6jyyftTo2CLtm4K8axuNPYwA9Wgaz2R93WhX8bTQ,3364
|
|
2
|
+
PKG-INFO,sha256=WyJiPK_VXcT5PwALxqVLaTGweyx5TlaJ6iZbWlZOmAE,1052
|
|
3
|
+
demo.py,sha256=AquJB-0Tu2bIEFpcJ0Q3RUBt1xldqS6GgU5CjOAg_ww,7083
|
|
4
|
+
pyproject.toml,sha256=Xh1I15jBg5rdV9FGNqQP6etbSMC22JEKlj09CHyy7fU,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=_Yz9MVCiWYfIui40Ja1zhAmVTZPGJ8OyG-vAYFGYmPU,13581
|
|
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=qO1x70wAqu03qS73IrB9D2Fgwp7zJDQsBPiBxg1ouLI,11277
|
|
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=RT2a5cUZnXtMjJRlPImGzgyX-KJPLiav5WSp_l3wzv8,725
|
|
38
|
+
tests/end-2-end/test_procedures_e2e.py,sha256=xjczbB33Cf5pDLe1KWcalRRBrSxOQ3cbV3s3w3GsC_A,4922
|
|
39
|
+
tests/end-2-end/test_scans_e2e.py,sha256=1gDAnpfToEgE7ssaLTh5Z6YiRmHzk6UnVPo8exXk-as,31227
|
|
40
|
+
tests/end-2-end/test_scans_lib_e2e.py,sha256=dqs0ojkyQWStIQbqABq9mQrjqQyE43gr37VPhxvQ8b8,19427
|
|
41
|
+
bec_ipython_client-3.86.1.dist-info/METADATA,sha256=WyJiPK_VXcT5PwALxqVLaTGweyx5TlaJ6iZbWlZOmAE,1052
|
|
42
|
+
bec_ipython_client-3.86.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
43
|
+
bec_ipython_client-3.86.1.dist-info/entry_points.txt,sha256=oQUXYY0jjD9ZvKPHwaGn2wkUIWpDZM8L4ixDA3RlBWE,53
|
|
44
|
+
bec_ipython_client-3.86.1.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
|
@@ -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
|
|
|
@@ -211,3 +212,33 @@ def test_bec_ipython_client_property_access(ipython_client):
|
|
|
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
|