From 832c7ed15d5f8dd72f93ffc3c264159b21f0e47b Mon Sep 17 00:00:00 2001 From: LilithCole Date: Wed, 13 Aug 2025 10:06:14 +0100 Subject: [PATCH 1/4] change default location to data, put RB before number and bluesky_scans after --- src/ibex_bluesky_core/callbacks/_file_logger.py | 6 ++++-- src/ibex_bluesky_core/callbacks/_fitting.py | 3 ++- src/ibex_bluesky_core/callbacks/_plotting.py | 5 ++++- src/ibex_bluesky_core/callbacks/_utils.py | 2 +- tests/callbacks/fitting/test_fit_logging_callback.py | 6 +++--- tests/callbacks/test_utils.py | 2 +- tests/callbacks/test_write_log_callback.py | 7 ++++--- 7 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/ibex_bluesky_core/callbacks/_file_logger.py b/src/ibex_bluesky_core/callbacks/_file_logger.py index 22159010..982affdd 100644 --- a/src/ibex_bluesky_core/callbacks/_file_logger.py +++ b/src/ibex_bluesky_core/callbacks/_file_logger.py @@ -38,7 +38,7 @@ class HumanReadableFileCallback(CallbackBase): """Outputs bluesky runs to human-readable output files in the specified directory path.""" - def __init__(self, fields: list[str], *, output_dir: Path | None, postfix: str = "") -> None: + def __init__(self, fields: list[str], *, output_dir: Path | None = None, postfix: str = "") -> None: """Output human-readable output files of bluesky runs. If fields are given, just output those, otherwise output all hinted signals. @@ -67,6 +67,7 @@ def start(self, doc: RunStart) -> None: self.current_start_document = doc[UID] rb_num = _get_rb_num(doc) + rb_num_str = rb_num if rb_num == "Unknown RB" else f"RB{rb_num}" # motors is a tuple, we need to convert to a list to join the two below motors = list(doc.get(MOTORS, [])) @@ -75,7 +76,8 @@ def start(self, doc: RunStart) -> None: self.filename = ( self.output_dir - / f"{rb_num}" + / rb_num_str + / "bluesky_scans" / f"{get_instrument()}{'_' + '_'.join(motors) if motors else ''}_" f"{formatted_time}Z{self.postfix}.txt" ) diff --git a/src/ibex_bluesky_core/callbacks/_fitting.py b/src/ibex_bluesky_core/callbacks/_fitting.py index b4f69d30..f0971206 100644 --- a/src/ibex_bluesky_core/callbacks/_fitting.py +++ b/src/ibex_bluesky_core/callbacks/_fitting.py @@ -172,8 +172,9 @@ def start(self, doc: RunStart) -> None: file = f"{get_instrument()}_{self.x}_{self.y}_{title_format_datetime}Z{self.postfix}.txt" rb_num = _get_rb_num(doc) + rb_num_str = rb_num if rb_num == "Unknown RB" else f"RB{rb_num}" - self.filename = self.output_dir / f"{rb_num}" / file + self.filename = self.output_dir / f"{rb_num_str}" / "bluesky_scans" / file def event(self, doc: Event) -> Event: """Start collecting, y, x, and yerr data. diff --git a/src/ibex_bluesky_core/callbacks/_plotting.py b/src/ibex_bluesky_core/callbacks/_plotting.py index ddc1f3d4..19337fa9 100644 --- a/src/ibex_bluesky_core/callbacks/_plotting.py +++ b/src/ibex_bluesky_core/callbacks/_plotting.py @@ -227,9 +227,12 @@ def __init__( self.filename = None def start(self, doc: RunStart) -> None: + rb_num = _get_rb_num(doc) + rb_num_str = rb_num if rb_num == "Unknown RB" else f"RB{rb_num}" self.filename = ( self.output_dir - / f"{_get_rb_num(doc)}" + / f"{rb_num_str}" + / "bluesky_scans" / f"{get_instrument()}_{self.x}_{self.y}_{format_time(doc)}Z{self.postfix}.png" ) diff --git a/src/ibex_bluesky_core/callbacks/_utils.py b/src/ibex_bluesky_core/callbacks/_utils.py index a2ebbceb..e6666e54 100644 --- a/src/ibex_bluesky_core/callbacks/_utils.py +++ b/src/ibex_bluesky_core/callbacks/_utils.py @@ -34,7 +34,7 @@ def get_instrument() -> str: def get_default_output_path() -> Path: output_dir_env = os.environ.get(OUTPUT_DIR_ENV_VAR) return ( - Path("//isis.cclrc.ac.uk/inst$") / node() / "user" / "bluesky_scans" + Path("C:/Data") if output_dir_env is None else Path(output_dir_env) ) diff --git a/tests/callbacks/fitting/test_fit_logging_callback.py b/tests/callbacks/fitting/test_fit_logging_callback.py index bfe6c7b6..6823f2ba 100644 --- a/tests/callbacks/fitting/test_fit_logging_callback.py +++ b/tests/callbacks/fitting/test_fit_logging_callback.py @@ -35,7 +35,7 @@ def test_after_fitting_callback_writes_to_file_successfully_no_y_uncertainty( RE(scan([invariant], mot, -1, 1, 3), [lf, lfl], rb_number="0") assert m.call_args_list[0].args == ( - filepath / "0" / f"{node()}_motor_invariant_2024-10-04_13-43-43Z{postfix}.txt", + filepath / "RB0" / "bluesky_scans" / f"{node()}_motor_invariant_2024-10-04_13-43-43Z{postfix}.txt", "w", ) # type: ignore @@ -69,7 +69,7 @@ def test_fitting_callback_handles_no_rb_number_save( RE(scan([invariant], mot, -1, 1, 3), [lf, lfl]) assert m.call_args_list[0].args == ( - filepath / "Unknown RB" / f"{node()}_motor_invariant_2024-10-04_13-43-43Z{postfix}.txt", + filepath / "Unknown RB" / "bluesky_scans" / f"{node()}_motor_invariant_2024-10-04_13-43-43Z{postfix}.txt", "w", ) # type: ignore @@ -98,7 +98,7 @@ def test_after_fitting_callback_writes_to_file_successfully_with_y_uncertainty( RE(scan([invariant, uncertainty], mot, -1, 1, 3), [lf, lfl], rb_number="0") assert m.call_args_list[0].args == ( - filepath / "0" / f"{node()}_motor_invariant_2024-10-04_13-43-43Z{postfix}.txt", + filepath / "RB0" / "bluesky_scans" / f"{node()}_motor_invariant_2024-10-04_13-43-43Z{postfix}.txt", "w", ) # type: ignore diff --git a/tests/callbacks/test_utils.py b/tests/callbacks/test_utils.py index 07a20fd9..36453700 100644 --- a/tests/callbacks/test_utils.py +++ b/tests/callbacks/test_utils.py @@ -14,4 +14,4 @@ def test_default_output_location_with_env_var(): @pytest.mark.skipif(os.name != "nt", reason="Windows only") def test_default_output_location_without_env_var(): with patch("ibex_bluesky_core.callbacks._utils.os.environ.get", return_value=None): - assert str(get_default_output_path()).startswith(r"\\isis.cclrc.ac.uk") + assert str(get_default_output_path()) == r"C:\Data" diff --git a/tests/callbacks/test_write_log_callback.py b/tests/callbacks/test_write_log_callback.py index 935e290b..0a3b78fe 100644 --- a/tests/callbacks/test_write_log_callback.py +++ b/tests/callbacks/test_write_log_callback.py @@ -32,7 +32,8 @@ def test_header_data_all_available_on_start(cb): cb.start(run_start) result = ( save_path - / f"{run_start.get('rb_number', None)}" + / f"RB{run_start.get('rb_number', None)}" + / "bluesky_scans" / f"{node()}_block_2024-10-04_13-43-43Z.txt" ) @@ -54,7 +55,7 @@ def test_no_rb_number_folder(cb): patch("ibex_bluesky_core.callbacks._file_logger.os.makedirs") as mock_mkdir, ): cb.start(run_start) - result = save_path / "Unknown RB" / f"{node()}_block_2024-10-04_13-43-43Z.txt" + result = save_path / "Unknown RB" / "bluesky_scans" / f"{node()}_block_2024-10-04_13-43-43Z.txt" assert mock_mkdir.called mock_file.assert_called_with(result, "a", newline="\n", encoding="utf-8") @@ -75,7 +76,7 @@ def test_no_motors_doesnt_append_to_filename(cb): patch("ibex_bluesky_core.callbacks._file_logger.os.makedirs") as mock_mkdir, ): cb.start(run_start) - result = save_path / "Unknown RB" / f"{node()}_2024-10-04_13-43-43Z.txt" + result = save_path / "Unknown RB" / "bluesky_scans" / f"{node()}_2024-10-04_13-43-43Z.txt" assert mock_mkdir.called mock_file.assert_called_with(result, "a", newline="\n", encoding="utf-8") From 8aa1e5b47a2805456f9a7d0b7ba15e509eafdfad Mon Sep 17 00:00:00 2001 From: LilithCole Date: Wed, 13 Aug 2025 14:37:29 +0100 Subject: [PATCH 2/4] add readonly flag after done with a file --- .../callbacks/_file_logger.py | 2 + src/ibex_bluesky_core/callbacks/_fitting.py | 2 + src/ibex_bluesky_core/callbacks/_plotting.py | 2 + .../fitting/test_fit_logging_callback.py | 28 ++++++++ tests/callbacks/test_plotting_callback.py | 68 +++++++++++++------ tests/callbacks/test_write_log_callback.py | 30 +++++++- 6 files changed, 110 insertions(+), 22 deletions(-) diff --git a/src/ibex_bluesky_core/callbacks/_file_logger.py b/src/ibex_bluesky_core/callbacks/_file_logger.py index 982affdd..d0911269 100644 --- a/src/ibex_bluesky_core/callbacks/_file_logger.py +++ b/src/ibex_bluesky_core/callbacks/_file_logger.py @@ -4,6 +4,7 @@ import logging import os from pathlib import Path +from stat import S_IRUSR, S_IRGRP, S_IROTH from bluesky.callbacks import CallbackBase from event_model.documents.event import Event @@ -152,4 +153,5 @@ def stop(self, doc: RunStop) -> RunStop | None: """Clear descriptors.""" logger.info("Stopping run, clearing descriptors, filename=%s", self.filename) self.descriptors.clear() + os.chmod(self.filename, S_IRUSR | S_IRGRP | S_IROTH) return super().stop(doc) diff --git a/src/ibex_bluesky_core/callbacks/_fitting.py b/src/ibex_bluesky_core/callbacks/_fitting.py index f0971206..2eb64c14 100644 --- a/src/ibex_bluesky_core/callbacks/_fitting.py +++ b/src/ibex_bluesky_core/callbacks/_fitting.py @@ -6,6 +6,7 @@ import warnings from itertools import zip_longest from pathlib import Path +from stat import S_IRUSR, S_IRGRP, S_IROTH import lmfit import numpy as np @@ -238,6 +239,7 @@ def stop(self, doc: RunStop) -> None: self.write_fields_table_uncertainty() logger.info("Fitting information successfully written to: %s", self.filename.resolve()) + os.chmod(self.filename, S_IRUSR | S_IRGRP | S_IROTH) def write_fields_table(self) -> None: """Write collected run info to the fitting file.""" diff --git a/src/ibex_bluesky_core/callbacks/_plotting.py b/src/ibex_bluesky_core/callbacks/_plotting.py index 19337fa9..fe8ef8e2 100644 --- a/src/ibex_bluesky_core/callbacks/_plotting.py +++ b/src/ibex_bluesky_core/callbacks/_plotting.py @@ -5,6 +5,7 @@ import threading from pathlib import Path from typing import Any +from stat import S_IRUSR, S_IRGRP, S_IROTH import matplotlib import matplotlib.pyplot as plt @@ -248,3 +249,4 @@ def stop(self, doc: RunStop) -> None: self.filename.parent.mkdir(parents=True, exist_ok=True) self.ax.figure.savefig(self.filename, format="png") # pyright: ignore [reportAttributeAccessIssue] + os.chmod(self.filename, S_IRUSR | S_IRGRP | S_IROTH) diff --git a/tests/callbacks/fitting/test_fit_logging_callback.py b/tests/callbacks/fitting/test_fit_logging_callback.py index 6823f2ba..7255b6b8 100644 --- a/tests/callbacks/fitting/test_fit_logging_callback.py +++ b/tests/callbacks/fitting/test_fit_logging_callback.py @@ -3,6 +3,7 @@ from pathlib import Path from platform import node from unittest.mock import MagicMock, mock_open, patch +from stat import S_IRUSR, S_IRGRP, S_IROTH import pytest from bluesky.plans import scan @@ -30,6 +31,7 @@ def test_after_fitting_callback_writes_to_file_successfully_no_y_uncertainty( with ( patch("ibex_bluesky_core.callbacks._fitting.open", m), patch("ibex_bluesky_core.callbacks._fitting.os.makedirs"), + patch("os.chmod") ): with patch("time.time", MagicMock(return_value=time)): RE(scan([invariant], mot, -1, 1, 3), [lf, lfl], rb_number="0") @@ -64,6 +66,7 @@ def test_fitting_callback_handles_no_rb_number_save( with ( patch("ibex_bluesky_core.callbacks._fitting.open", m), patch("ibex_bluesky_core.callbacks._fitting.os.makedirs"), + patch("os.chmod") ): with patch("time.time", MagicMock(return_value=time)): RE(scan([invariant], mot, -1, 1, 3), [lf, lfl]) @@ -93,6 +96,7 @@ def test_after_fitting_callback_writes_to_file_successfully_with_y_uncertainty( with ( patch("ibex_bluesky_core.callbacks._fitting.open", m), patch("ibex_bluesky_core.callbacks._fitting.os.makedirs"), + patch("os.chmod") ): with patch("time.time", MagicMock(return_value=time)): RE(scan([invariant, uncertainty], mot, -1, 1, 3), [lf, lfl], rb_number="0") @@ -188,3 +192,27 @@ def test_error_thrown_if_no_y_err_data_in_event(RE: run_engine.RunEngine): } } ) + + +def test_file_set_readonly_after_written( + RE: run_engine.RunEngine, +): + invariant = soft_signal_rw(float, 0.5, name="invariant") + mot = soft_signal_rw(float, name="motor") + + filepath = Path("C:\\") / "instrument" / "var" / "logs" + postfix = "fit1" + m = mock_open() + + lf = LiveFit(Linear.fit(), y="invariant", x="motor", update_every=50) + lfl = LiveFitLogger(lf, y="invariant", x="motor", postfix=postfix, output_dir=filepath) + with ( + patch("ibex_bluesky_core.callbacks._fitting.open"), + patch("ibex_bluesky_core.callbacks._fitting.os.makedirs"), + patch("os.chmod") as mock_chmod, + ): + with patch("time.time", MagicMock(return_value=time)): + RE(scan([invariant], mot, -1, 1, 3), [lf, lfl]) + + fit_filepath = filepath / "Unknown RB" / "bluesky_scans" / f"{node()}_motor_invariant_2024-10-04_13-43-43Z{postfix}.txt" + mock_chmod.assert_called_with(fit_filepath, S_IRUSR | S_IRGRP | S_IROTH) diff --git a/tests/callbacks/test_plotting_callback.py b/tests/callbacks/test_plotting_callback.py index ae579871..92a4960c 100644 --- a/tests/callbacks/test_plotting_callback.py +++ b/tests/callbacks/test_plotting_callback.py @@ -1,5 +1,7 @@ from typing import Any from unittest.mock import MagicMock, patch +import os +from stat import S_IRUSR, S_IRGRP, S_IROTH import pytest from matplotlib import pyplot as plt @@ -82,32 +84,58 @@ def test_errorbars_created_if_yerr_is_given(): def test_png_saved_on_run_stop(): - ax = MagicMock(spec=Axes) - ax.figure = MagicMock(spec=Figure) - s = PlotPNGSaver(x="x", y="y", ax=ax, postfix="123", output_dir="") - - s.start( - { - "uid": "0", - RB: 1234, # pyright: ignore reportArgumentType - "time": 123456789, - } - ) - - s.stop( - { - "time": 234567891, - "uid": "2", - "exit_status": "success", - "run_start": "", - } - ) + with patch("os.chmod"): + ax = MagicMock(spec=Axes) + ax.figure = MagicMock(spec=Figure) + s = PlotPNGSaver(x="x", y="y", ax=ax, postfix="123", output_dir="") + + s.start( + { + "uid": "0", + RB: 1234, # pyright: ignore reportArgumentType + "time": 123456789, + } + ) + + s.stop( + { + "time": 234567891, + "uid": "2", + "exit_status": "success", + "run_start": "", + } + ) assert ax.figure.savefig.call_count == 1 assert ax.figure.savefig.call_args.kwargs["format"] == "png" assert "x_y_1973-11-29_21-33-09Z123.png" in ax.figure.savefig.call_args.args[0].name +def test_file_readonly_on_stop(): + with patch("os.chmod") as mock_chmod: + ax = MagicMock(spec=Axes) + ax.figure = MagicMock(spec=Figure) + s = PlotPNGSaver(x="x", y="y", ax=ax, postfix="123", output_dir="") + + s.start( + { + "uid": "0", + RB: 1234, # pyright: ignore reportArgumentType + "time": 123456789, + } + ) + directory = s.filename + s.stop( + { + "time": 234567891, + "uid": "2", + "exit_status": "success", + "run_start": "", + } + ) + mock_chmod.assert_called_with(directory, S_IRUSR | S_IRGRP | S_IROTH) + + def test_errorbars_not_created_if_no_yerr(): _, ax = plt.subplots() ax.errorbar = MagicMock() diff --git a/tests/callbacks/test_write_log_callback.py b/tests/callbacks/test_write_log_callback.py index 0a3b78fe..eea9bd0a 100644 --- a/tests/callbacks/test_write_log_callback.py +++ b/tests/callbacks/test_write_log_callback.py @@ -4,6 +4,7 @@ from pathlib import Path from platform import node from unittest.mock import call, mock_open, patch +from stat import S_IRUSR, S_IRGRP, S_IROTH import pytest from event_model import DataKey, Event, EventDescriptor, RunStart, RunStop @@ -203,7 +204,32 @@ def test_event_called_before_filename_specified_does_nothing(): def test_stop_clears_descriptors(cb): cb.descriptors["test"] = EventDescriptor(uid="test", run_start="", time=0.1, data_keys={}) - - cb.stop(RunStop(uid="test", run_start="", time=0.1, exit_status="success")) + with patch("os.chmod"): + cb.stop(RunStop(uid="test", run_start="", time=0.1, exit_status="success")) assert not cb.descriptors + +def test_file_set_readonly_when_finished(cb): + time = 1728049423.5860472 + start_uid = "test123start" + stop_uid = "test123stop" + scan_id = 1234 + run_start = RunStart( + time=time, uid=start_uid, scan_id=scan_id, rb_number="0", detectors=["dae"], motors=("block",) + ) + run_stop = RunStop( + time=time, run_start=start_uid, uid=stop_uid, exit_status="success") + with ( + patch("ibex_bluesky_core.callbacks._file_logger.open", mock_open()), + patch("ibex_bluesky_core.callbacks._file_logger.os.makedirs"), + patch("ibex_bluesky_core.callbacks._file_logger.os.chmod") as mock_chmod, + ): + cb.start(run_start) + result = ( + save_path + / f"RB{run_start.get('rb_number', None)}" + / "bluesky_scans" + / f"{node()}_block_2024-10-04_13-43-43Z.txt" + ) + cb.stop(run_stop) + mock_chmod.assert_called_with(result, S_IRUSR | S_IRGRP | S_IROTH) \ No newline at end of file From 47593991216ecfe73302d08b7ce6580ce4f670f7 Mon Sep 17 00:00:00 2001 From: LilithCole Date: Wed, 15 Oct 2025 15:23:24 +0100 Subject: [PATCH 3/4] ruff/pyright --- .../callbacks/_file_logger.py | 9 +++-- src/ibex_bluesky_core/callbacks/_fitting.py | 2 +- src/ibex_bluesky_core/callbacks/_plotting.py | 2 +- src/ibex_bluesky_core/callbacks/_utils.py | 6 +--- .../fitting/test_fit_logging_callback.py | 36 +++++++++++++------ tests/callbacks/test_plotting_callback.py | 3 +- tests/callbacks/test_write_log_callback.py | 29 +++++++++------ 7 files changed, 54 insertions(+), 33 deletions(-) diff --git a/src/ibex_bluesky_core/callbacks/_file_logger.py b/src/ibex_bluesky_core/callbacks/_file_logger.py index d0911269..6f028e7e 100644 --- a/src/ibex_bluesky_core/callbacks/_file_logger.py +++ b/src/ibex_bluesky_core/callbacks/_file_logger.py @@ -4,7 +4,7 @@ import logging import os from pathlib import Path -from stat import S_IRUSR, S_IRGRP, S_IROTH +from stat import S_IRGRP, S_IROTH, S_IRUSR from bluesky.callbacks import CallbackBase from event_model.documents.event import Event @@ -39,7 +39,9 @@ class HumanReadableFileCallback(CallbackBase): """Outputs bluesky runs to human-readable output files in the specified directory path.""" - def __init__(self, fields: list[str], *, output_dir: Path | None = None, postfix: str = "") -> None: + def __init__( + self, fields: list[str], *, output_dir: Path | None = None, postfix: str = "" + ) -> None: """Output human-readable output files of bluesky runs. If fields are given, just output those, otherwise output all hinted signals. @@ -153,5 +155,6 @@ def stop(self, doc: RunStop) -> RunStop | None: """Clear descriptors.""" logger.info("Stopping run, clearing descriptors, filename=%s", self.filename) self.descriptors.clear() - os.chmod(self.filename, S_IRUSR | S_IRGRP | S_IROTH) + if self.filename is not None: + os.chmod(self.filename, S_IRUSR | S_IRGRP | S_IROTH) return super().stop(doc) diff --git a/src/ibex_bluesky_core/callbacks/_fitting.py b/src/ibex_bluesky_core/callbacks/_fitting.py index 2eb64c14..5846f66e 100644 --- a/src/ibex_bluesky_core/callbacks/_fitting.py +++ b/src/ibex_bluesky_core/callbacks/_fitting.py @@ -6,7 +6,7 @@ import warnings from itertools import zip_longest from pathlib import Path -from stat import S_IRUSR, S_IRGRP, S_IROTH +from stat import S_IRGRP, S_IROTH, S_IRUSR import lmfit import numpy as np diff --git a/src/ibex_bluesky_core/callbacks/_plotting.py b/src/ibex_bluesky_core/callbacks/_plotting.py index fe8ef8e2..e816e7df 100644 --- a/src/ibex_bluesky_core/callbacks/_plotting.py +++ b/src/ibex_bluesky_core/callbacks/_plotting.py @@ -4,8 +4,8 @@ import os import threading from pathlib import Path +from stat import S_IRGRP, S_IROTH, S_IRUSR from typing import Any -from stat import S_IRUSR, S_IRGRP, S_IROTH import matplotlib import matplotlib.pyplot as plt diff --git a/src/ibex_bluesky_core/callbacks/_utils.py b/src/ibex_bluesky_core/callbacks/_utils.py index e6666e54..822953f3 100644 --- a/src/ibex_bluesky_core/callbacks/_utils.py +++ b/src/ibex_bluesky_core/callbacks/_utils.py @@ -33,11 +33,7 @@ def get_instrument() -> str: def get_default_output_path() -> Path: output_dir_env = os.environ.get(OUTPUT_DIR_ENV_VAR) - return ( - Path("C:/Data") - if output_dir_env is None - else Path(output_dir_env) - ) + return Path("C:/Data") if output_dir_env is None else Path(output_dir_env) def format_time(doc: Event | RunStart | RunStop) -> str: diff --git a/tests/callbacks/fitting/test_fit_logging_callback.py b/tests/callbacks/fitting/test_fit_logging_callback.py index 7255b6b8..18190c3d 100644 --- a/tests/callbacks/fitting/test_fit_logging_callback.py +++ b/tests/callbacks/fitting/test_fit_logging_callback.py @@ -2,8 +2,8 @@ import os from pathlib import Path from platform import node +from stat import S_IRGRP, S_IROTH, S_IRUSR from unittest.mock import MagicMock, mock_open, patch -from stat import S_IRUSR, S_IRGRP, S_IROTH import pytest from bluesky.plans import scan @@ -31,13 +31,16 @@ def test_after_fitting_callback_writes_to_file_successfully_no_y_uncertainty( with ( patch("ibex_bluesky_core.callbacks._fitting.open", m), patch("ibex_bluesky_core.callbacks._fitting.os.makedirs"), - patch("os.chmod") + patch("os.chmod"), ): with patch("time.time", MagicMock(return_value=time)): RE(scan([invariant], mot, -1, 1, 3), [lf, lfl], rb_number="0") assert m.call_args_list[0].args == ( - filepath / "RB0" / "bluesky_scans" / f"{node()}_motor_invariant_2024-10-04_13-43-43Z{postfix}.txt", + ( + filepath / "RB0" / "bluesky_scans" / f"{node()}_motor_invariant_2024-10-04_13-43-43" + f"Z{postfix}.txt" + ), "w", ) # type: ignore @@ -66,13 +69,18 @@ def test_fitting_callback_handles_no_rb_number_save( with ( patch("ibex_bluesky_core.callbacks._fitting.open", m), patch("ibex_bluesky_core.callbacks._fitting.os.makedirs"), - patch("os.chmod") + patch("os.chmod"), ): with patch("time.time", MagicMock(return_value=time)): RE(scan([invariant], mot, -1, 1, 3), [lf, lfl]) assert m.call_args_list[0].args == ( - filepath / "Unknown RB" / "bluesky_scans" / f"{node()}_motor_invariant_2024-10-04_13-43-43Z{postfix}.txt", + ( + filepath + / "Unknown RB" + / "bluesky_scans" + / f"{node()}_motor_invariant_2024-10-04_13-43-43Z{postfix}.txt" + ), "w", ) # type: ignore @@ -96,13 +104,16 @@ def test_after_fitting_callback_writes_to_file_successfully_with_y_uncertainty( with ( patch("ibex_bluesky_core.callbacks._fitting.open", m), patch("ibex_bluesky_core.callbacks._fitting.os.makedirs"), - patch("os.chmod") + patch("os.chmod"), ): with patch("time.time", MagicMock(return_value=time)): RE(scan([invariant, uncertainty], mot, -1, 1, 3), [lf, lfl], rb_number="0") assert m.call_args_list[0].args == ( - filepath / "RB0" / "bluesky_scans" / f"{node()}_motor_invariant_2024-10-04_13-43-43Z{postfix}.txt", + ( + filepath / "RB0" / "bluesky_scans" / f"{node()}_motor_invariant_2024-10-04_13-43-43" + f"Z{postfix}.txt" + ), "w", ) # type: ignore @@ -195,7 +206,7 @@ def test_error_thrown_if_no_y_err_data_in_event(RE: run_engine.RunEngine): def test_file_set_readonly_after_written( - RE: run_engine.RunEngine, + RE: run_engine.RunEngine, ): invariant = soft_signal_rw(float, 0.5, name="invariant") mot = soft_signal_rw(float, name="motor") @@ -207,12 +218,17 @@ def test_file_set_readonly_after_written( lf = LiveFit(Linear.fit(), y="invariant", x="motor", update_every=50) lfl = LiveFitLogger(lf, y="invariant", x="motor", postfix=postfix, output_dir=filepath) with ( - patch("ibex_bluesky_core.callbacks._fitting.open"), + patch("ibex_bluesky_core.callbacks._fitting.open", m), patch("ibex_bluesky_core.callbacks._fitting.os.makedirs"), patch("os.chmod") as mock_chmod, ): with patch("time.time", MagicMock(return_value=time)): RE(scan([invariant], mot, -1, 1, 3), [lf, lfl]) - fit_filepath = filepath / "Unknown RB" / "bluesky_scans" / f"{node()}_motor_invariant_2024-10-04_13-43-43Z{postfix}.txt" + fit_filepath = ( + filepath + / "Unknown RB" + / "bluesky_scans" + / f"{node()}_motor_invariant_2024-10-04_13-43-43Z{postfix}.txt" + ) mock_chmod.assert_called_with(fit_filepath, S_IRUSR | S_IRGRP | S_IROTH) diff --git a/tests/callbacks/test_plotting_callback.py b/tests/callbacks/test_plotting_callback.py index 92a4960c..24d277c1 100644 --- a/tests/callbacks/test_plotting_callback.py +++ b/tests/callbacks/test_plotting_callback.py @@ -1,7 +1,6 @@ +from stat import S_IRGRP, S_IROTH, S_IRUSR from typing import Any from unittest.mock import MagicMock, patch -import os -from stat import S_IRUSR, S_IRGRP, S_IROTH import pytest from matplotlib import pyplot as plt diff --git a/tests/callbacks/test_write_log_callback.py b/tests/callbacks/test_write_log_callback.py index eea9bd0a..35a8c23e 100644 --- a/tests/callbacks/test_write_log_callback.py +++ b/tests/callbacks/test_write_log_callback.py @@ -3,8 +3,8 @@ from pathlib import Path from platform import node +from stat import S_IRGRP, S_IROTH, S_IRUSR from unittest.mock import call, mock_open, patch -from stat import S_IRUSR, S_IRGRP, S_IROTH import pytest from event_model import DataKey, Event, EventDescriptor, RunStart, RunStop @@ -56,11 +56,13 @@ def test_no_rb_number_folder(cb): patch("ibex_bluesky_core.callbacks._file_logger.os.makedirs") as mock_mkdir, ): cb.start(run_start) - result = save_path / "Unknown RB" / "bluesky_scans" / f"{node()}_block_2024-10-04_13-43-43Z.txt" + result = ( + save_path / "Unknown RB" / "bluesky_scans" / f"{node()}_block_2024-10-04_13-43-43Z.txt" + ) assert mock_mkdir.called mock_file.assert_called_with(result, "a", newline="\n", encoding="utf-8") - # time should have been renamed to start_time and converted to human readable + # time should have been renamed to start_time and converted to human-readable writelines_call_args = mock_file().writelines.call_args[0][0] assert "start_time: 2024-10-04_13-43-43\n" in writelines_call_args assert f"uid: {uid}\n" in writelines_call_args @@ -209,16 +211,21 @@ def test_stop_clears_descriptors(cb): assert not cb.descriptors + def test_file_set_readonly_when_finished(cb): time = 1728049423.5860472 start_uid = "test123start" stop_uid = "test123stop" scan_id = 1234 run_start = RunStart( - time=time, uid=start_uid, scan_id=scan_id, rb_number="0", detectors=["dae"], motors=("block",) + time=time, + uid=start_uid, + scan_id=scan_id, + rb_number="0", + detectors=["dae"], + motors=("block",), ) - run_stop = RunStop( - time=time, run_start=start_uid, uid=stop_uid, exit_status="success") + run_stop = RunStop(time=time, run_start=start_uid, uid=stop_uid, exit_status="success") with ( patch("ibex_bluesky_core.callbacks._file_logger.open", mock_open()), patch("ibex_bluesky_core.callbacks._file_logger.os.makedirs"), @@ -226,10 +233,10 @@ def test_file_set_readonly_when_finished(cb): ): cb.start(run_start) result = ( - save_path - / f"RB{run_start.get('rb_number', None)}" - / "bluesky_scans" - / f"{node()}_block_2024-10-04_13-43-43Z.txt" + save_path + / f"RB{run_start.get('rb_number', None)}" + / "bluesky_scans" + / f"{node()}_block_2024-10-04_13-43-43Z.txt" ) cb.stop(run_stop) - mock_chmod.assert_called_with(result, S_IRUSR | S_IRGRP | S_IROTH) \ No newline at end of file + mock_chmod.assert_called_with(result, S_IRUSR | S_IRGRP | S_IROTH) From 3c24c83fc3adeac48aae029dc8500ac601de287d Mon Sep 17 00:00:00 2001 From: LilithCole Date: Wed, 15 Oct 2025 16:31:29 +0100 Subject: [PATCH 4/4] 'fix' pyright --- tests/callbacks/fitting/test_fit_logging_callback.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/callbacks/fitting/test_fit_logging_callback.py b/tests/callbacks/fitting/test_fit_logging_callback.py index 2597d45c..dc05a9e8 100644 --- a/tests/callbacks/fitting/test_fit_logging_callback.py +++ b/tests/callbacks/fitting/test_fit_logging_callback.py @@ -238,7 +238,10 @@ def test_file_set_readonly_after_written( patch("os.chmod") as mock_chmod, ): with patch("time.time", MagicMock(return_value=time)): - RE(scan([invariant], mot, -1, 1, 3), [lf, lfl]) + RE( + scan([invariant], mot, -1, 1, 3), + [lf, lfl], # pyright: ignore until https://github.com/bluesky/bluesky/issues/1938 + ) fit_filepath = ( filepath