diff --git a/poetry.lock b/poetry.lock index 0ad77a84..eb11fe2f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -557,7 +557,7 @@ files = [ name = "jinja2" version = "3.1.4" description = "A very fast and expressive template engine." -optional = true +optional = false python-versions = ">=3.7" files = [ {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, @@ -687,7 +687,7 @@ files = [ name = "markupsafe" version = "2.1.5" description = "Safely add untrusted strings to HTML/XML markup." -optional = true +optional = false python-versions = ">=3.7" files = [ {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, @@ -902,6 +902,7 @@ files = [ {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, + {file = "msgpack-1.0.8-py3-none-any.whl", hash = "sha256:24f727df1e20b9876fa6e95f840a2a2651e34c0ad147676356f4bf5fbb0206ca"}, {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"}, ] @@ -1129,7 +1130,6 @@ optional = false python-versions = ">=3.9" files = [ {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"}, - {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"}, {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"}, {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"}, {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"}, @@ -1143,14 +1143,12 @@ files = [ {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, - {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"}, - {file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"}, {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"}, {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"}, {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"}, @@ -1976,4 +1974,4 @@ torch = ["torch", "torch", "torch"] [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "dfb75f9b40165a4788ab1ecd2b49257806bedfac3c0e28bb0db7ae8e30c2bf7a" +content-hash = "f21186ce8185581e0ab2b2106376f29464cb9e3e843e6747e78d7ef40c267265" diff --git a/pyproject.toml b/pyproject.toml index 66f4ee1f..38f7f9b3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,6 +70,7 @@ pytest-cov = ">=4.1,<6.0" pytest-mock = "^3.14.0" pytest-sugar = "^1.0.0" pytest-xdist = "^3.6.1" +jinja2 = "^3.1.4" [build-system] requires = ["poetry-core"] diff --git a/simvue/client.py b/simvue/client.py index 1d41d30c..e34678cb 100644 --- a/simvue/client.py +++ b/simvue/client.py @@ -279,7 +279,7 @@ def get_runs( alerts : bool, optional whether to include alert information in the response. Default False. - format : str ('dict' | 'dataframe'), optional + format : Literal['dict', 'dataframe'], optional the structure of the response, either a dictionary or a dataframe. Default is 'dict'. Pandas must be installed for 'dataframe'. count : int, optional @@ -932,9 +932,9 @@ def get_metric_values( ---------- metric_names : list[str] the names of metrics to return values for - xaxis : str ('step' | 'time' | 'timestamp') + xaxis : Literal['step', 'time', 'timestamp'] the xaxis type - output_format : str ('dataframe' | 'list') + output_format : Literal['dataframe', 'list'] the format of the output, either a list or a Pandas dataframe run_ids : list[str], optional list of runs by id to include within metric retrieval diff --git a/simvue/run.py b/simvue/run.py index 77e6dcd9..60bc35ca 100644 --- a/simvue/run.py +++ b/simvue/run.py @@ -20,6 +20,7 @@ import re import sys import time +import functools import platform import typing import uuid @@ -63,6 +64,7 @@ def check_run_initialised( function: typing.Callable[..., typing.Any], ) -> typing.Callable[..., typing.Any]: + @functools.wraps(function) def _wrapper(self: "Run", *args: typing.Any, **kwargs: typing.Any) -> typing.Any: if not self._simvue: raise RuntimeError( @@ -71,8 +73,6 @@ def _wrapper(self: "Run", *args: typing.Any, **kwargs: typing.Any) -> typing.Any ) return function(self, *args, **kwargs) - _wrapper.__name__ = f"{function.__name__}__init_locked" - return _wrapper @@ -838,8 +838,8 @@ def config( return True - @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) + @check_run_initialised @pydantic.validate_call def update_metadata(self, metadata: dict[str, typing.Any]) -> bool: """ @@ -864,8 +864,8 @@ def update_metadata(self, metadata: dict[str, typing.Any]) -> bool: return False - @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) + @check_run_initialised @pydantic.validate_call def update_tags(self, tags: list[str]) -> bool: """ @@ -885,8 +885,8 @@ def update_tags(self, tags: list[str]) -> bool: return False - @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) + @check_run_initialised @pydantic.validate_call def log_event(self, message, timestamp: typing.Optional[str] = None) -> bool: """ @@ -960,8 +960,8 @@ def _add_metrics_to_dispatch( return True - @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) + @check_run_initialised @pydantic.validate_call def log_metrics( self, @@ -979,8 +979,8 @@ def log_metrics( self._step += 1 return add_dispatch - @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) + @check_run_initialised @pydantic.validate_call def save_object( self, @@ -1035,6 +1035,7 @@ def save_object( return self._simvue is not None and self._simvue.save_file(data) is not None @skip_if_failed("_aborted", "_suppress_errors", False) + @check_run_initialised @pydantic.validate_call def save_file( self, @@ -1118,8 +1119,8 @@ def save_file( # Register file return self._simvue.save_file(data) is not None - @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) + @check_run_initialised @pydantic.validate_call def save_directory( self, @@ -1155,8 +1156,8 @@ def save_directory( return True - @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) + @check_run_initialised @pydantic.validate_call def save_all( self, @@ -1186,8 +1187,8 @@ def save_all( return True - @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) + @check_run_initialised @pydantic.validate_call def set_status( self, status: typing.Literal["completed", "failed", "terminated"] @@ -1256,8 +1257,8 @@ def close(self) -> bool: return True - @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) + @check_run_initialised @pydantic.validate_call def set_folder_details( self, @@ -1311,8 +1312,8 @@ def set_folder_details( return False - @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) + @check_run_initialised @pydantic.validate_call def add_alerts( self, @@ -1359,8 +1360,8 @@ def add_alerts( return False - @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", None) + @check_run_initialised @pydantic.validate_call def create_alert( self, @@ -1521,8 +1522,8 @@ def create_alert( return alert_id - @check_run_initialised @skip_if_failed("_aborted", "_suppress_errors", False) + @check_run_initialised @pydantic.validate_call def log_alert( self, identifier: str, state: typing.Literal["ok", "critical"] diff --git a/simvue/utilities.py b/simvue/utilities.py index 2ad9ae27..b3c3fe57 100644 --- a/simvue/utilities.py +++ b/simvue/utilities.py @@ -6,6 +6,7 @@ import tabulate import pydantic import importlib.util +import functools import contextlib import os import typing @@ -77,6 +78,7 @@ def check_extra(extra_name: str) -> typing.Callable: def decorator( class_func: typing.Optional[typing.Callable] = None, ) -> typing.Optional[typing.Callable]: + @functools.wraps(class_func) def wrapper(self, *args, **kwargs) -> typing.Any: if extra_name == "plot" and not all( [ @@ -129,6 +131,7 @@ def skip_if_failed( """ def decorator(class_func: typing.Callable) -> typing.Callable: + @functools.wraps(class_func) def wrapper(self, *args, **kwargs) -> typing.Any: if getattr(self, failure_attr, None) and getattr( self, ignore_exc_attr, None @@ -158,7 +161,7 @@ def wrapper(self, *args, **kwargs) -> typing.Any: return on_failure_return raise RuntimeError(err_str) - wrapper.__name__ = f"{class_func.__name__}__fail_safe" + setattr(wrapper, "__fail_safe", True) return wrapper return decorator diff --git a/tests/refactor/test_executor.py b/tests/refactor/test_executor.py index c6a9f1f9..45b8bbbf 100644 --- a/tests/refactor/test_executor.py +++ b/tests/refactor/test_executor.py @@ -15,7 +15,7 @@ def test_executor_add_process( completion_trigger = multiprocessing.Event() run.init( f"test_executor_{'success' if successful else 'fail'}", - tags=["simvue_client_unit_tests", request.node.name], + tags=["simvue_client_unit_tests", request.node.name.replace("[", "_").replace("]", "_")], folder="/simvue_unit_testing" ) diff --git a/tests/refactor/test_run_class.py b/tests/refactor/test_run_class.py index d1da1c8a..ddf35572 100644 --- a/tests/refactor/test_run_class.py +++ b/tests/refactor/test_run_class.py @@ -50,7 +50,7 @@ def test_log_metrics( with pytest.raises(RuntimeError): run.init( name=f"test_run_{str(uuid.uuid4()).split('-', 1)[0]}", - tags=["simvue_client_unit_tests", request.node.name], + tags=["simvue_client_unit_tests", request.node.name.replace("[", "_").replace("]", "_")], folder="/simvue_unit_testing", retention_period="1 hour", visibility=visibility, @@ -60,7 +60,7 @@ def test_log_metrics( run.init( name=f"test_run_{str(uuid.uuid4()).split('-', 1)[0]}", - tags=["simvue_client_unit_tests", request.node.name], + tags=["simvue_client_unit_tests", request.node.name.replace("[", "_").replace("]", "_")], folder="/simvue_unit_testing", visibility=visibility, resources_metrics_interval=1, @@ -155,7 +155,7 @@ def thread_func(index: int) -> tuple[int, list[dict[str, typing.Any]], str]: run.config(suppress_errors=False) run.init( name=f"test_runs_multiple_{index + 1}", - tags=["simvue_client_unit_tests", request.node.name], + tags=["simvue_client_unit_tests", request.node.name.replace("[", "_").replace("]", "_")], folder="/simvue_unit_testing", retention_period="1 hour", ) @@ -192,7 +192,7 @@ def thread_func(index: int) -> tuple[int, list[dict[str, typing.Any]], str]: run_1.config(suppress_errors=False) run_1.init( name="test_runs_multiple_unthreaded_1", - tags=["simvue_client_unit_tests", request.node.name], + tags=["simvue_client_unit_tests", request.node.name.replace("[", "_").replace("]", "_")], folder="/simvue_unit_testing", retention_period="1 hour", ) @@ -246,7 +246,7 @@ def test_runs_multiple_series(request: pytest.FixtureRequest) -> None: run.config(suppress_errors=False) run.init( name=f"test_runs_multiple_series_{index}", - tags=["simvue_client_unit_tests", request.node.name], + tags=["simvue_client_unit_tests", request.node.name.replace("[", "_").replace("]", "_")], folder="/simvue_unit_testing", retention_period="1 hour", ) @@ -288,7 +288,7 @@ def test_suppressed_errors( decorated_funcs = [ name for name, method in inspect.getmembers(run, inspect.ismethod) - if method.__name__.endswith("__fail_safe") + if hasattr(method, "__fail_safe") ] if post_init: @@ -296,7 +296,7 @@ def test_suppressed_errors( run.init( name="test_suppressed_errors", folder="/simvue_unit_testing", - tags=["simvue_client_unit_tests", request.node.name], + tags=["simvue_client_unit_tests", request.node.name.replace("[", "_").replace("]", "_")], retention_period="1 hour" ) @@ -323,7 +323,7 @@ def test_set_folder_details(request: pytest.FixtureRequest) -> None: with sv_run.Run() as run: folder_name: str ="/simvue_unit_tests" description: str = "test description" - tags: list[str] = ["simvue_client_unit_tests", request.node.name] + tags: list[str] = ["simvue_client_unit_tests", request.node.name.replace("[", "_").replace("]", "_")] run.init(folder=folder_name) run.set_folder_details(path=folder_name, tags=tags, description=description)