bec-ipython-client 3.77.0__py3-none-any.whl → 3.78.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.
PKG-INFO CHANGED
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bec_ipython_client
3
- Version: 3.77.0
3
+ Version: 3.78.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
@@ -14,11 +14,16 @@ class LiveUpdatesDeviceProgress(LiveUpdatesTable):
14
14
 
15
15
  REPORT_TYPE = "device_progress"
16
16
 
17
- def _run_update(self, device_names: str):
17
+ def core(self):
18
+ """core function to run the live updates for the table"""
19
+ self._wait_for_report_instructions()
20
+ self._run_update(self.report_instruction[self.REPORT_TYPE])
21
+
22
+ def _run_update(self, device_names: list[str]):
18
23
  """Run the update loop for the progress bar.
19
24
 
20
25
  Args:
21
- device_names (str): The name of the device to monitor.
26
+ device_names (list[str]): The name of the device to monitor.
22
27
  """
23
28
  with ScanProgressBar(
24
29
  scan_number=self.scan_item.scan_number, clear_on_exit=False
@@ -29,7 +34,7 @@ class LiveUpdatesDeviceProgress(LiveUpdatesTable):
29
34
  self._print_client_msgs_asap()
30
35
  self._print_client_msgs_all()
31
36
 
32
- def _update_progressbar(self, progressbar: ScanProgressBar, device_names: str) -> bool:
37
+ def _update_progressbar(self, progressbar: ScanProgressBar, device_names: list[str]) -> bool:
33
38
  """Update the progressbar based on the device status message
34
39
 
35
40
  Args:
@@ -171,6 +171,13 @@ class LiveUpdatesTable(LiveUpdatesBase):
171
171
 
172
172
  def core(self):
173
173
  """core function to run the live updates for the table"""
174
+ self._wait_for_report_instructions()
175
+ show_table = self.report_instruction[self.REPORT_TYPE].get("show_table", True)
176
+ self._print_table_data = show_table
177
+ self._run_update(self.report_instruction[self.REPORT_TYPE]["points"])
178
+
179
+ def _wait_for_report_instructions(self):
180
+ """wait until the report instructions are available"""
174
181
  req_ID = self.scan_queue_request.requestID
175
182
  while True:
176
183
  request_block = [
@@ -182,10 +189,6 @@ class LiveUpdatesTable(LiveUpdatesBase):
182
189
  break
183
190
  self.check_alarms()
184
191
 
185
- show_table = self.report_instruction[self.REPORT_TYPE].get("show_table", True)
186
- self._print_table_data = show_table
187
- self._run_update(self.report_instruction[self.REPORT_TYPE]["points"])
188
-
189
192
  def _run_update(self, target_num_points: int):
190
193
  """run the update loop with the progress bar
191
194
 
@@ -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,17 @@ 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 rich.console import Console
17
+ from rich.panel import Panel
18
+ from rich.text import Text
13
19
 
14
20
  from bec_ipython_client.beamline_mixin import BeamlineMixin
15
21
  from bec_ipython_client.bec_magics import BECMagics
16
22
  from bec_ipython_client.callbacks.ipython_live_updates import IPythonLiveUpdates
17
23
  from bec_ipython_client.signals import ScanInterruption, SigintHandler
18
24
  from bec_lib import plugin_helper
19
- from bec_lib.acl_login import BECAuthenticationError
20
25
  from bec_lib.alarm_handler import AlarmBase
21
26
  from bec_lib.bec_errors import DeviceConfigError
22
27
  from bec_lib.bec_service import parse_cmdline_args
@@ -79,6 +84,7 @@ class BECIPythonClient:
79
84
  self._client.callbacks.register(
80
85
  event_type=EventType.NAMESPACE_UPDATE, callback=self._update_namespace_callback
81
86
  )
87
+ self._alarm_history = collections.deque(maxlen=100)
82
88
 
83
89
  def __getattr__(self, name):
84
90
  return getattr(self._client, name)
@@ -136,7 +142,7 @@ class BECIPythonClient:
136
142
  self._refresh_ipython_username()
137
143
  self._load_magics()
138
144
  self._ip.events.register("post_run_cell", log_console)
139
- self._ip.set_custom_exc((Exception,), _ip_exception_handler) # register your handler
145
+ self._ip.set_custom_exc((Exception,), self._create_exception_handler())
140
146
  # represent objects using __str__, if overwritten, otherwise use __repr__
141
147
  self._ip.display_formatter.formatters["text/plain"].for_type(
142
148
  object,
@@ -187,10 +193,63 @@ class BECIPythonClient:
187
193
  pass
188
194
  self._client.shutdown()
189
195
 
196
+ def _create_exception_handler(self):
197
+ return functools.partial(_ip_exception_handler, parent=self)
190
198
 
191
- def _ip_exception_handler(self, etype, evalue, tb, tb_offset=None):
192
- if issubclass(etype, (AlarmBase, ScanInterruption, DeviceConfigError)):
193
- print(f"\x1b[31m BEC alarm:\x1b[0m {evalue}")
199
+ def show_last_alarm(self, offset: int = 0):
200
+ """
201
+ Show the last alarm raised in this session with rich formatting.
202
+ """
203
+ try:
204
+ alarm: AlarmBase = self._alarm_history[-1 - offset][1]
205
+ except IndexError:
206
+ print("No alarm has been raised in this session.")
207
+ return
208
+
209
+ console = Console()
210
+
211
+ # --- HEADER ---
212
+ header = Text()
213
+ header.append("Alarm Raised\n", style="bold red")
214
+ header.append(f"Severity: {alarm.severity.name}\n", style="bold")
215
+ header.append(f"Type: {alarm.alarm_type}\n", style="bold")
216
+ if alarm.alarm.source.get("device"):
217
+ header.append(f"Device: {alarm.alarm.source['device']}\n", style="bold")
218
+
219
+ console.print(Panel(header, title="Alarm Info", border_style="red", expand=False))
220
+
221
+ # --- SHOW SUMMARY
222
+ if alarm.alarm.compact_msg:
223
+ console.print(
224
+ Panel(
225
+ Text(alarm.alarm.compact_msg, style="yellow"),
226
+ title="Summary",
227
+ border_style="yellow",
228
+ expand=False,
229
+ )
230
+ )
231
+
232
+ # --- SHOW FULL TRACEBACK
233
+ tb_str = alarm.alarm.msg
234
+ if tb_str:
235
+ try:
236
+ console.print(tb_str)
237
+ except Exception:
238
+ # fallback in case msg is not a traceback
239
+ console.print(Panel(tb_str, title="Message", border_style="cyan"))
240
+
241
+
242
+ def _ip_exception_handler(
243
+ self, etype, evalue, tb, tb_offset=None, parent: BECIPythonClient = None, **kwargs
244
+ ):
245
+ if issubclass(etype, AlarmBase):
246
+ parent._alarm_history.append((etype, evalue, tb, tb_offset))
247
+ print("\x1b[31m BEC alarm:\x1b[0m")
248
+ evalue.pretty_print()
249
+ print("For more details, use 'bec.show_last_alarm()'")
250
+ return
251
+ if issubclass(etype, (ScanInterruption, DeviceConfigError)):
252
+ print(f"\x1b[31m {evalue.__class__.__name__}:\x1b[0m {evalue}")
194
253
  return
195
254
  if issubclass(etype, redis.exceptions.NoPermissionError):
196
255
  # pylint: disable=protected-access
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bec_ipython_client
3
- Version: 3.77.0
3
+ Version: 3.78.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
@@ -1,19 +1,19 @@
1
1
  .gitignore,sha256=XxC6jyyftTo2CLtm4K8axuNPYwA9Wgaz2R93WhX8bTQ,3364
2
- PKG-INFO,sha256=kEsaWm0XSawsU8btTo-SyPnXeSFf_jXcDi3vmFZEdds,1052
2
+ PKG-INFO,sha256=RJxXzCYV3zYrykWuJBQir3qMUpj_91SSeW9VqsG-WWQ,1052
3
3
  demo.py,sha256=AquJB-0Tu2bIEFpcJ0Q3RUBt1xldqS6GgU5CjOAg_ww,7083
4
- pyproject.toml,sha256=RVAsp1s-QQRo5cdPW9BS7fozezKy4CG9EuhiBUrSM80,1229
4
+ pyproject.toml,sha256=RXan6rptFvdX9lpzL3DvMM5kDp3D-STl3flmWy4GwQo,1229
5
5
  bec_ipython_client/__init__.py,sha256=ihd_V8I7Qo0MWKMo7bcvPf-ZyUQqkcNf8IAWLJKiFJE,79
6
6
  bec_ipython_client/beamline_mixin.py,sha256=scMWIFbHJajyECzbwEVKyQUGjpqA9C_KiU2M6FuRH_Q,1067
7
7
  bec_ipython_client/bec_magics.py,sha256=Rz2aXkUCeAV_VxdXqLUNHh8T44kSH9ha83OiEtdptzI,2792
8
8
  bec_ipython_client/bec_startup.py,sha256=GGlHyxnSCQfYF5n-pYq2ic0pSyW5zvnT2PAlI5kY77w,1930
9
- bec_ipython_client/main.py,sha256=OQeDX1XxavvKo-0lpM8-Pd8tkMIS09DG7fqeCjsIEHw,11237
9
+ bec_ipython_client/main.py,sha256=PzKLA5BNze_TTDrmMo0tYxvcwez9cFIOVB68nMbOOdU,13275
10
10
  bec_ipython_client/prettytable.py,sha256=TnhGPGuU0XEvnIYJ1UfTEwadcowFW4rqJW8z_Sm0EDw,2534
11
11
  bec_ipython_client/progressbar.py,sha256=aDKYjzXmGSwa82ewm59V8WSuqVQz9GiZPx5G65fEwpk,11090
12
12
  bec_ipython_client/signals.py,sha256=mbThPo6h3mQ6RFRm9viETDMC_unFa7QxiymCdM_ZK7U,4194
13
13
  bec_ipython_client/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- bec_ipython_client/callbacks/device_progress.py,sha256=5sYfk4Xr_4-NCs6lCe7o7nTpMs_sEFYwITAMaELlbOA,2385
14
+ bec_ipython_client/callbacks/device_progress.py,sha256=NzWIO1oowxpN14hPXBne6RfZxOW82EhwDM3jHLnn3xI,2603
15
15
  bec_ipython_client/callbacks/ipython_live_updates.py,sha256=Cx62yJSzIcRvAA_lC86PSs8825hJtk22yBPhuJjgRPc,10520
16
- bec_ipython_client/callbacks/live_table.py,sha256=pUoqg2ib43-XpqRpjtahpUnnsriNYzLtF3IiAiVkGxg,13350
16
+ bec_ipython_client/callbacks/live_table.py,sha256=3qmwCEjqe-xFqo3Ez2dhKchawlr0cOmNgzpg857-gp0,13503
17
17
  bec_ipython_client/callbacks/move_device.py,sha256=TkbtKFBr1nuJ8THTSm70Y8D7_va2YQ57oBGGt0BavW4,8153
18
18
  bec_ipython_client/callbacks/utils.py,sha256=jS92ndZNU2t5n9FsLrSbulmUIeepHhwdQdfO60YjiJk,5702
19
19
  bec_ipython_client/high_level_interfaces/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -28,7 +28,7 @@ bec_ipython_client/plugins/flomni/flomni_config.yaml,sha256=bCN1VKCzF6tw24HYeUE2
28
28
  tests/conftest.py,sha256=4yzGYqs8EVOxxY9Bu-yGbzmOpzG9dxs3cwSCzjmRziQ,233
29
29
  tests/client_tests/conftest.py,sha256=dUvYqvl6wogxN-kRCUuo_Uzna3P2uSYD7qGHyMGBlPY,413
30
30
  tests/client_tests/test_beamline_mixins.py,sha256=8Ws0bmzl2gSW0VuOVu80_JbYNb5Y-htchmrrptwjwDo,4611
31
- tests/client_tests/test_bec_client.py,sha256=pMpjDpgdeynHMJXZkXCMYERm5OEwu4VR50ID28eNFhc,7722
31
+ tests/client_tests/test_bec_client.py,sha256=KJePnvUc1I5218qaCEcIExEGSB_D-v17irarv6Ix994,8832
32
32
  tests/client_tests/test_device_progress.py,sha256=GEw2g8MQZnv5mxABEZlxBmaMpxVS33wogaYohFolDEs,2353
33
33
  tests/client_tests/test_ipython_live_updates.py,sha256=HpA16Mx0WdseqlfP0FojlAd6VfvjnwAUReDkItZVYWs,6081
34
34
  tests/client_tests/test_live_table.py,sha256=0EKgWOgDqpjN8fJEeAzoKNCwFneDEsUY2NfWQkxB6xc,18155
@@ -38,7 +38,7 @@ tests/end-2-end/_ensure_requirements_container.py,sha256=RT2a5cUZnXtMjJRlPImGzgy
38
38
  tests/end-2-end/test_procedures_e2e.py,sha256=3CPzjW3VpsSU8GKVP814SSvLZ0c7bBtPB_VtX725TCY,4737
39
39
  tests/end-2-end/test_scans_e2e.py,sha256=CxlXjQrzDJebaoZObt_oYx41wbnHLuJLSjKlno4u2B4,30981
40
40
  tests/end-2-end/test_scans_lib_e2e.py,sha256=-yYW-bm2jmXg4QYa6jSQFRlaLa81zeUvP7oTodccWg0,19213
41
- bec_ipython_client-3.77.0.dist-info/METADATA,sha256=kEsaWm0XSawsU8btTo-SyPnXeSFf_jXcDi3vmFZEdds,1052
42
- bec_ipython_client-3.77.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
43
- bec_ipython_client-3.77.0.dist-info/entry_points.txt,sha256=oQUXYY0jjD9ZvKPHwaGn2wkUIWpDZM8L4ixDA3RlBWE,53
44
- bec_ipython_client-3.77.0.dist-info/RECORD,,
41
+ bec_ipython_client-3.78.0.dist-info/METADATA,sha256=RJxXzCYV3zYrykWuJBQir3qMUpj_91SSeW9VqsG-WWQ,1052
42
+ bec_ipython_client-3.78.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
43
+ bec_ipython_client-3.78.0.dist-info/entry_points.txt,sha256=oQUXYY0jjD9ZvKPHwaGn2wkUIWpDZM8L4ixDA3RlBWE,53
44
+ bec_ipython_client-3.78.0.dist-info/RECORD,,
pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "bec_ipython_client"
7
- version = "3.77.0"
7
+ version = "3.78.0"
8
8
  description = "BEC IPython client"
9
9
  requires-python = ">=3.11"
10
10
  classifiers = [
@@ -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
+ alarm_msg = messages.AlarmMessage(
220
+ severity=Alarms.MAJOR,
221
+ alarm_type="TestAlarm",
222
+ source={},
223
+ msg="This is a test alarm",
224
+ compact_msg="Test alarm",
225
+ )
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