From da11bf978a0d165a57167e6ba01621a77eedd001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Zar=C4=99bski?= Date: Mon, 22 Jun 2026 11:52:46 +0100 Subject: [PATCH 1/3] Added noSim version retrieval to config --- simvue/config/user.py | 45 ++++++++++++++++++++++++------ tests/functional/test_run_class.py | 5 +++- uv.lock | 2 +- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/simvue/config/user.py b/simvue/config/user.py index 8e80bb0b..be09baa4 100644 --- a/simvue/config/user.py +++ b/simvue/config/user.py @@ -14,6 +14,7 @@ import pydantic import semver import toml +from pandas.core.common import contextlib try: from typing import Self @@ -70,6 +71,7 @@ class SimvueConfiguration(pydantic.BaseModel): eco: EcoConfig = EcoConfig() current_profile: str | None = None _server_version: semver.Version | None = None + _nosim_version: semver.Version | None = None @property def server_verify(self) -> str | bool: @@ -84,6 +86,11 @@ def server_version(self) -> semver.Version: raise RuntimeError("Expected server version to be defined") return self._server_version + @property + def nosim_version(self) -> semver.Version | None: + """Retrieve current noSim version if available.""" + return self._nosim_version + @classmethod def _load_pyproject_configs(cls) -> dict | None: """Recover any Simvue non-authentication configurations from pyproject.toml.""" @@ -127,14 +134,16 @@ def _check_server( url: str, mode: typing.Literal["offline", "online", "disabled"], verify: str | bool, - ) -> semver.Version | None: + ) -> tuple[semver.Version | None, semver.Version | None]: if mode in {"offline", "disabled"}: - return None + return None, None headers: dict[str, str] = { "Authorization": f"Bearer {token}", "User-Agent": f"Simvue Python client {__version__}", } + + # Retrieve Server version try: _url = URL(url) / "version" _response = sv_get(f"{_url}", headers=headers, verify=verify) @@ -152,6 +161,22 @@ def _check_server( f"Exception retrieving server version:\n {err!s}", ) from err + _no_sim_version_str: str = "" + _no_sim_version: semver.Version | None = None + + # Retrieve noSim version if applicable + with contextlib.suppress(Exception): + _url = URL(url) / "nosim" / "version" + _response = sv_get(f"{_url}", headers=headers, verify=verify) + + if _response.status_code == http.HTTPStatus.UNAUTHORIZED: + raise AssertionError("Unauthorised token") + + if _response.status_code != http.HTTPStatus.NOT_FOUND and ( + _no_sim_version_str := _response.json().get("version") + ): + _no_sim_version = semver.Version.parse(_no_sim_version_str) + _version = semver.Version.parse(_version_str) if ( @@ -159,16 +184,18 @@ def _check_server( and _version >= SIMVUE_SERVER_UPPER_CONSTRAINT ): raise AssertionError( - f"Python API v{_version_str} is not compatible " - "with Simvue server versions " - f">= {SIMVUE_SERVER_UPPER_CONSTRAINT}", + f"Python API v{__version__} is not compatible " + "with the current Simvue server version: " + f"{_version_str} >= {SIMVUE_SERVER_UPPER_CONSTRAINT}", ) if SIMVUE_SERVER_LOWER_CONSTRAINT and _version < SIMVUE_SERVER_LOWER_CONSTRAINT: raise AssertionError( - f"Python API v{_version_str} is not compatible with Simvue " - f"server versions < {SIMVUE_SERVER_LOWER_CONSTRAINT}", + f"Python API v{__version__} is not compatible " + "with the current Simvue server version: " + f"{_version_str} < {SIMVUE_SERVER_UPPER_CONSTRAINT}", ) - return _version + + return _version, _no_sim_version @pydantic.validate_call def write(self, out_directory: pydantic.DirectoryPath) -> None: @@ -183,7 +210,7 @@ def check_valid_server(self) -> Self: if not self.server.token: raise ValueError("No token provided.") - self._server_version = self._check_server( + self._server_version, self._nosim_version = self._check_server( token=self.server.token.get_secret_value(), url=self.server.url, verify=self.server_verify, diff --git a/tests/functional/test_run_class.py b/tests/functional/test_run_class.py index a6d6f04b..0d7c7842 100644 --- a/tests/functional/test_run_class.py +++ b/tests/functional/test_run_class.py @@ -814,6 +814,9 @@ def test_set_folder_details(request: pytest.FixtureRequest) -> None: with sv_run.Run() as run: folder_name: str = f"/simvue_unit_testing/{_uuid}" description: str = "test description" + metadata: dict[str, str] = { + "test_name": "test_set_folder_details" + } tags: list[str] = [ "simvue_client_unit_tests", "test_set_folder_details" @@ -824,7 +827,7 @@ def test_set_folder_details(request: pytest.FixtureRequest) -> None: visibility="tenant" if os.environ.get("CI") else None, retention_period=os.environ.get("SIMVUE_TESTING_RETENTION_PERIOD", "2 mins"), ) - run.set_folder_details(tags=tags, description=description) + run.set_folder_details(tags=tags, description=description, metadata=metadata) client = sv_cl.Client() _folder = client.get_folder(folder_path=folder_name) diff --git a/uv.lock b/uv.lock index 9a8c5877..1004b286 100644 --- a/uv.lock +++ b/uv.lock @@ -1847,7 +1847,7 @@ wheels = [ [[package]] name = "simvue" -version = "2.5.5" +version = "2.5.7" source = { editable = "." } dependencies = [ { name = "click" }, From 37cfd578535d7b1d6d6bf147a96b66fa8759add1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Zar=C4=99bski?= Date: Thu, 25 Jun 2026 15:28:00 +0100 Subject: [PATCH 2/3] Use builtin contextlib --- simvue/config/user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simvue/config/user.py b/simvue/config/user.py index be09baa4..912b343c 100644 --- a/simvue/config/user.py +++ b/simvue/config/user.py @@ -4,6 +4,7 @@ """ +import contextlib import functools import http import logging @@ -14,7 +15,6 @@ import pydantic import semver import toml -from pandas.core.common import contextlib try: from typing import Self From d09c9a3a3936b446482decc062f3c2301c562c25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Zar=C4=99bski?= Date: Thu, 25 Jun 2026 15:33:34 +0100 Subject: [PATCH 3/3] Fixed exception messages and response handling for version --- simvue/config/user.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/simvue/config/user.py b/simvue/config/user.py index 912b343c..5c89ba3f 100644 --- a/simvue/config/user.py +++ b/simvue/config/user.py @@ -161,8 +161,8 @@ def _check_server( f"Exception retrieving server version:\n {err!s}", ) from err - _no_sim_version_str: str = "" - _no_sim_version: semver.Version | None = None + _nosim_version_str: str = "" + _nosim_version: semver.Version | None = None # Retrieve noSim version if applicable with contextlib.suppress(Exception): @@ -172,10 +172,10 @@ def _check_server( if _response.status_code == http.HTTPStatus.UNAUTHORIZED: raise AssertionError("Unauthorised token") - if _response.status_code != http.HTTPStatus.NOT_FOUND and ( - _no_sim_version_str := _response.json().get("version") + if _response.status_code == http.HTTPStatus.OK and ( + _nosim_version_str := _response.json().get("version") ): - _no_sim_version = semver.Version.parse(_no_sim_version_str) + _nosim_version = semver.Version.parse(_nosim_version_str) _version = semver.Version.parse(_version_str) @@ -192,10 +192,10 @@ def _check_server( raise AssertionError( f"Python API v{__version__} is not compatible " "with the current Simvue server version: " - f"{_version_str} < {SIMVUE_SERVER_UPPER_CONSTRAINT}", + f"{_version_str} < {SIMVUE_SERVER_LOWER_CONSTRAINT}", ) - return _version, _no_sim_version + return _version, _nosim_version @pydantic.validate_call def write(self, out_directory: pydantic.DirectoryPath) -> None: