bec-ipython-client 3.125.2__tar.gz → 3.127.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 (41) hide show
  1. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/PKG-INFO +1 -1
  2. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/main.py +19 -2
  3. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/pyproject.toml +3 -1
  4. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/tests/client_tests/test_bec_client.py +54 -0
  5. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/.gitignore +0 -0
  6. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/__init__.py +0 -0
  7. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/beamline_mixin.py +0 -0
  8. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/bec_magics.py +0 -0
  9. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/bec_startup.py +0 -0
  10. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/callbacks/__init__.py +0 -0
  11. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/callbacks/device_progress.py +0 -0
  12. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/callbacks/ipython_live_updates.py +0 -0
  13. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/callbacks/live_table.py +0 -0
  14. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/callbacks/move_device.py +0 -0
  15. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/callbacks/utils.py +0 -0
  16. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/high_level_interfaces/__init__.py +0 -0
  17. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/high_level_interfaces/bec_hli.py +0 -0
  18. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/high_level_interfaces/spec_hli.py +0 -0
  19. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/plugins/SLS/__init__.py +0 -0
  20. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/plugins/SLS/sls_info.py +0 -0
  21. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/plugins/XTreme/__init__.py +0 -0
  22. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/plugins/XTreme/x-treme.py +0 -0
  23. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/plugins/__init__.py +0 -0
  24. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/plugins/flomni/flomni_config.yaml +0 -0
  25. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/prettytable.py +0 -0
  26. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/progressbar.py +0 -0
  27. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/bec_ipython_client/signals.py +0 -0
  28. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/demo.py +0 -0
  29. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/tests/client_tests/conftest.py +0 -0
  30. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/tests/client_tests/test_beamline_mixins.py +0 -0
  31. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/tests/client_tests/test_device_progress.py +0 -0
  32. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/tests/client_tests/test_ipython_live_updates.py +0 -0
  33. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/tests/client_tests/test_live_table.py +0 -0
  34. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/tests/client_tests/test_move_callback.py +0 -0
  35. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/tests/client_tests/test_pretty_table.py +0 -0
  36. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/tests/conftest.py +0 -0
  37. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/tests/end-2-end/_ensure_requirements_container.py +0 -0
  38. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/tests/end-2-end/test_procedures_e2e.py +0 -0
  39. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/tests/end-2-end/test_scans_e2e.py +0 -0
  40. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.0}/tests/end-2-end/test_scans_lib_e2e.py +0 -0
  41. {bec_ipython_client-3.125.2 → bec_ipython_client-3.127.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.125.2
3
+ Version: 3.127.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
@@ -5,6 +5,7 @@ import collections
5
5
  import functools
6
6
  import os
7
7
  import sys
8
+ import traceback
8
9
  from typing import Iterable, Literal, Tuple
9
10
 
10
11
  import IPython
@@ -147,7 +148,7 @@ class BECIPythonClient:
147
148
  self._ip.prompts = BECClientPrompt(ip=self._ip, client=self._client, username="unknown")
148
149
  self._refresh_ipython_username()
149
150
  self._load_magics()
150
- self._ip.events.register("post_run_cell", log_console)
151
+ self._ip.events.register("pre_run_cell", log_console)
151
152
  self._ip.set_custom_exc((Exception,), self._create_exception_handler())
152
153
  # represent objects using __str__, if overwritten, otherwise use __repr__
153
154
  self._ip.display_formatter.formatters["text/plain"].for_type(
@@ -252,25 +253,31 @@ def _ip_exception_handler(
252
253
  ):
253
254
  if issubclass(etype, AlarmBase):
254
255
  parent._alarm_history.append((etype, evalue, tb, tb_offset))
256
+ log_console_error(etype, evalue, tb)
255
257
  print("\x1b[31m BEC alarm:\x1b[0m")
256
258
  evalue.pretty_print()
257
259
  print("For more details, use 'bec.show_last_alarm()'")
258
260
  return
259
261
  if issubclass(etype, ValidationError):
262
+ log_console_error(etype, evalue, tb)
260
263
  pretty_print_pydantic_validation_error(evalue)
261
264
  return
262
265
  if issubclass(etype, (ScanInterruption, DeviceConfigError)):
266
+ log_console_error(etype, evalue, tb, f"{evalue.__class__.__name__}: {evalue}")
263
267
  print(f"\x1b[31m {evalue.__class__.__name__}:\x1b[0m {evalue}")
264
268
  return
265
269
  if issubclass(etype, redis.exceptions.NoPermissionError):
266
270
  # pylint: disable=protected-access
267
271
  msg = f"The current user ({bec._client.username}) does not have the required permissions.\n {evalue}"
272
+ log_console_error(etype, evalue, tb, f"Unauthorized: {msg}")
268
273
  logger.info(f"Unauthorized: {msg}")
269
274
  print(f"\x1b[31m Unauthorized:\x1b[0m {msg}")
270
275
  return
271
276
  if issubclass(etype, ExceptionWithErrorInfo):
277
+ log_console_error(etype, evalue, tb)
272
278
  evalue.pretty_print()
273
279
  return
280
+ log_console_error(etype, evalue, tb)
274
281
  self.showtraceback((etype, evalue, tb), tb_offset=None) # standard IPython's printout
275
282
 
276
283
 
@@ -322,7 +329,17 @@ class BECClientPrompt(Prompts):
322
329
 
323
330
  def log_console(execution_info):
324
331
  """log the console input"""
325
- logger.log("CONSOLE_LOG", f"{execution_info.info.raw_cell}")
332
+ logger.log("CONSOLE_LOG", f"{execution_info.raw_cell}")
333
+
334
+
335
+ def log_console_error(etype, evalue, tb=None, message: str | None = None):
336
+ """Log console errors to the shared log stream."""
337
+ if message is None:
338
+ if tb is not None:
339
+ message = "".join(traceback.format_exception(etype, evalue, tb)).rstrip()
340
+ else:
341
+ message = f"{etype.__name__}: {evalue}"
342
+ logger.log("CONSOLE_LOG_ERROR", message)
326
343
 
327
344
 
328
345
  # pylint: disable=wrong-import-position
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "bec_ipython_client"
7
- version = "3.125.2"
7
+ version = "3.127.0"
8
8
  description = "BEC IPython client"
9
9
  requires-python = ">=3.11"
10
10
  classifiers = [
@@ -62,6 +62,8 @@ Homepage = "https://github.com/bec-project/bec"
62
62
 
63
63
 
64
64
 
65
+
66
+
65
67
 
66
68
 
67
69
 
@@ -4,10 +4,12 @@ from unittest import mock
4
4
 
5
5
  import IPython
6
6
  import pytest
7
+ import redis.exceptions
7
8
 
8
9
  from bec_ipython_client import BECIPythonClient, main
9
10
  from bec_lib import messages
10
11
  from bec_lib.alarm_handler import AlarmBase, AlarmHandler, Alarms
12
+ from bec_lib.bec_errors import DeviceConfigError
11
13
  from bec_lib.redis_connector import RedisConnector
12
14
  from bec_lib.service_config import ServiceConfig
13
15
 
@@ -238,3 +240,55 @@ def test_bec_ipython_client_show_last_no_alarm(ipython_client, capsys):
238
240
  client.show_last_alarm()
239
241
  captured = capsys.readouterr()
240
242
  assert "No alarm has been raised in this session." in captured.out
243
+
244
+
245
+ def test_ipython_exception_handler_logs_console_error_for_device_config_error():
246
+ shell = mock.MagicMock()
247
+ with mock.patch.object(main.logger, "log") as mock_log:
248
+ main._ip_exception_handler(
249
+ shell, DeviceConfigError, DeviceConfigError("bad config"), None, parent=mock.MagicMock()
250
+ )
251
+
252
+ mock_log.assert_called_once_with("CONSOLE_LOG_ERROR", "DeviceConfigError: bad config")
253
+ shell.showtraceback.assert_not_called()
254
+
255
+
256
+ def test_ipython_exception_handler_logs_console_error_for_unhandled_exception():
257
+ shell = mock.MagicMock()
258
+ try:
259
+ raise RuntimeError("boom")
260
+ except RuntimeError as exc:
261
+ with mock.patch.object(main.logger, "log") as mock_log:
262
+ main._ip_exception_handler(
263
+ shell, RuntimeError, exc, exc.__traceback__, parent=mock.MagicMock()
264
+ )
265
+
266
+ mock_log.assert_called_once()
267
+ assert mock_log.call_args.args[0] == "CONSOLE_LOG_ERROR"
268
+ assert "RuntimeError: boom" in mock_log.call_args.args[1]
269
+ shell.showtraceback.assert_called_once()
270
+
271
+
272
+ def test_ipython_exception_handler_logs_console_error_for_permission_error():
273
+ shell = mock.MagicMock()
274
+ parent = mock.MagicMock()
275
+ with (
276
+ mock.patch.object(main.logger, "log") as mock_log,
277
+ mock.patch.object(main.logger, "info") as mock_info,
278
+ mock.patch.object(
279
+ main, "bec", mock.MagicMock(_client=mock.MagicMock(username="alice")), create=True
280
+ ),
281
+ ):
282
+ main._ip_exception_handler(
283
+ shell,
284
+ redis.exceptions.NoPermissionError,
285
+ redis.exceptions.NoPermissionError("denied"),
286
+ None,
287
+ parent=parent,
288
+ )
289
+
290
+ mock_log.assert_called_once()
291
+ assert mock_log.call_args.args[0] == "CONSOLE_LOG_ERROR"
292
+ assert "Unauthorized:" in mock_log.call_args.args[1]
293
+ mock_info.assert_called_once()
294
+ shell.showtraceback.assert_not_called()