diff --git a/simvue/run.py b/simvue/run.py index d916347c..695ae8d8 100644 --- a/simvue/run.py +++ b/simvue/run.py @@ -60,6 +60,22 @@ logger = logging.getLogger(__name__) +def check_run_initialised( + function: typing.Callable[..., typing.Any], +) -> typing.Callable[..., typing.Any]: + def _wrapper(self: "Run", *args: typing.Any, **kwargs: typing.Any) -> typing.Any: + if not self._simvue: + raise RuntimeError( + "Simvue Run must be initialised before calling " + f"'{function.__name__}'" + ) + return function(self, *args, **kwargs) + + _wrapper.__name__ = f"{function.__name__}__init_locked" + + return _wrapper + + class Run: """Track simulation details based on token and URL @@ -822,6 +838,7 @@ def config( return True + @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) @pydantic.validate_call def update_metadata(self, metadata: dict[str, typing.Any]) -> bool: @@ -847,6 +864,7 @@ def update_metadata(self, metadata: dict[str, typing.Any]) -> bool: return False + @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) @pydantic.validate_call def update_tags(self, tags: list[str]) -> bool: @@ -867,6 +885,7 @@ def update_tags(self, tags: list[str]) -> bool: return False + @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) @pydantic.validate_call def log_event(self, message, timestamp: typing.Optional[str] = None) -> bool: @@ -941,6 +960,7 @@ def _add_metrics_to_dispatch( return True + @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) @pydantic.validate_call def log_metrics( @@ -959,6 +979,7 @@ def log_metrics( self._step += 1 return add_dispatch + @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) @pydantic.validate_call def save( @@ -1075,6 +1096,7 @@ def save( return True + @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) @pydantic.validate_call def save_directory( @@ -1111,6 +1133,7 @@ def save_directory( return True + @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) @pydantic.validate_call def save_all( @@ -1141,6 +1164,7 @@ def save_all( return True + @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) @pydantic.validate_call def set_status( @@ -1210,6 +1234,7 @@ def close(self) -> bool: return True + @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) @pydantic.validate_call def set_folder_details( @@ -1264,6 +1289,7 @@ def set_folder_details( return False + @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) @pydantic.validate_call def add_alerts( @@ -1311,6 +1337,7 @@ def add_alerts( return False + @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", None) @pydantic.validate_call def create_alert( @@ -1472,6 +1499,7 @@ def create_alert( return alert_id + @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) @pydantic.validate_call def log_alert( diff --git a/tests/refactor/test_run_class.py b/tests/refactor/test_run_class.py index ff2a884a..9a3a4c3e 100644 --- a/tests/refactor/test_run_class.py +++ b/tests/refactor/test_run_class.py @@ -6,6 +6,7 @@ import uuid import concurrent.futures import random +import inspect import simvue.run as sv_run import simvue.client as sv_cl @@ -14,6 +15,16 @@ from .conftest import CountingLogHandler +@pytest.mark.run +def test_check_run_initialised_decorator() -> None: + with sv_run.Run(mode="offline") as run: + for method_name, method in inspect.getmembers(run, inspect.ismethod): + if not method.__name__.endswith("init_locked"): + continue + with pytest.raises(RuntimeError) as e: + getattr(run, method_name)() + assert "Simvue Run must be initialised" in str(e.value) + @pytest.mark.run @pytest.mark.parametrize("overload_buffer", (True, False), ids=("overload", "normal")) @pytest.mark.parametrize("visibility", ("bad_option", "tenant", "public", ["ciuser01"], None))