Skip to content

Commit 3ea5c3f

Browse files
authored
Merge pull request #623 from simvue-io/hotfix/v1.1-no-server-check-when-offline
Remove server version check from offline mode
2 parents ea8a1fb + ad0960f commit 3ea5c3f

File tree

9 files changed

+138
-66
lines changed

9 files changed

+138
-66
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
# Change log
22

33
## Unreleased
4+
45
* Fixed bug with `requirements.txt` metadata read.
6+
* Added Simvue server version check.
7+
* Remove checking of server version in offline mode and add default run mode to configuration options.
8+
* Fix offline mode class initialisation, and propagation of configuration.
9+
510
## [v1.1.2](https://github.com/simvue-io/client/releases/tag/v1.1.2) - 2024-11-06
611

712
* Fix bug in offline mode directory retrieval.
13+
814
## [v1.1.1](https://github.com/simvue-io/client/releases/tag/v1.1.1) - 2024-10-22
915

1016
* Add missing `offline.cache` key to TOML config.

poetry.lock

Lines changed: 15 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ humanfriendly = "^10.0"
5555
tabulate = "^0.9.0"
5656
randomname = "^0.2.1"
5757
codecarbon = "^2.7.1"
58+
semver = "^3.0.2"
5859

5960
[tool.poetry.extras]
6061
plot = ["matplotlib", "plotly"]

simvue/config/parameters.py

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,13 @@
77
"""
88

99
import logging
10-
import os
1110
import time
1211
import pydantic
1312
import typing
1413
import pathlib
15-
import http
16-
import functools
1714

1815
import simvue.models as sv_models
1916
from simvue.utilities import get_expiry
20-
from simvue.version import __version__
21-
from simvue.api import get
2217

2318

2419
logger = logging.getLogger(__file__)
@@ -42,37 +37,6 @@ def check_token(cls, v: typing.Any) -> str:
4237
raise AssertionError("Simvue token has expired")
4338
return value
4439

45-
@classmethod
46-
@functools.lru_cache
47-
def _check_server(cls, token: str, url: str) -> None:
48-
headers: dict[str, str] = {
49-
"Authorization": f"Bearer {token}",
50-
"User-Agent": f"Simvue Python client {__version__}",
51-
}
52-
try:
53-
response = get(f"{url}/api/version", headers)
54-
55-
if response.status_code != http.HTTPStatus.OK or not response.json().get(
56-
"version"
57-
):
58-
raise AssertionError
59-
60-
if response.status_code == http.HTTPStatus.UNAUTHORIZED:
61-
raise AssertionError("Unauthorised token")
62-
63-
except Exception as err:
64-
raise AssertionError(f"Exception retrieving server version: {str(err)}")
65-
66-
@pydantic.model_validator(mode="after")
67-
@classmethod
68-
def check_valid_server(cls, values: "ServerSpecifications") -> bool:
69-
if os.environ.get("SIMVUE_NO_SERVER_CHECK"):
70-
return values
71-
72-
cls._check_server(values.token, values.url)
73-
74-
return values
75-
7640

7741
class OfflineSpecifications(pydantic.BaseModel):
7842
cache: typing.Optional[pathlib.Path] = None
@@ -89,6 +53,7 @@ class DefaultRunSpecifications(pydantic.BaseModel):
8953
tags: typing.Optional[list[str]] = None
9054
folder: str = pydantic.Field("/", pattern=sv_models.FOLDER_REGEX)
9155
metadata: typing.Optional[dict[str, typing.Union[str, int, float, bool]]] = None
56+
mode: typing.Literal["offline", "disabled", "online"] = "online"
9257

9358

9459
class ClientGeneralOptions(pydantic.BaseModel):

simvue/config/user.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@
1414
import configparser
1515
import contextlib
1616
import warnings
17-
17+
import http
1818
import pydantic
1919
import toml
20+
import semver
2021

2122
import simvue.utilities as sv_util
2223
from simvue.config.parameters import (
@@ -31,9 +32,16 @@
3132
CONFIG_INI_FILE_NAMES,
3233
DEFAULT_OFFLINE_DIRECTORY,
3334
)
35+
from simvue.version import __version__
36+
from simvue.api import get
3437

3538
logger = logging.getLogger(__name__)
3639

40+
SIMVUE_SERVER_UPPER_CONSTRAINT: typing.Optional[semver.Version] = semver.Version.parse(
41+
"1.0.0"
42+
)
43+
SIMVUE_SERVER_LOWER_CONSTRAINT: typing.Optional[semver.Version] = None
44+
3745

3846
class SimvueConfiguration(pydantic.BaseModel):
3947
# Hide values as they contain token and URL
@@ -70,12 +78,65 @@ def _parse_ini_config(cls, ini_file: pathlib.Path) -> dict[str, dict[str, str]]:
7078

7179
return config_dict
7280

81+
@classmethod
82+
@functools.lru_cache
83+
def _check_server(
84+
cls, token: str, url: str, mode: typing.Literal["offline", "online", "disabled"]
85+
) -> None:
86+
if mode in ("offline", "disabled"):
87+
return
88+
89+
headers: dict[str, str] = {
90+
"Authorization": f"Bearer {token}",
91+
"User-Agent": f"Simvue Python client {__version__}",
92+
}
93+
try:
94+
response = get(f"{url}/api/version", headers)
95+
96+
if response.status_code != http.HTTPStatus.OK or not (
97+
_version_str := response.json().get("version")
98+
):
99+
raise AssertionError
100+
101+
if response.status_code == http.HTTPStatus.UNAUTHORIZED:
102+
raise AssertionError("Unauthorised token")
103+
104+
except Exception as err:
105+
raise AssertionError(f"Exception retrieving server version: {str(err)}")
106+
107+
_version = semver.Version.parse(_version_str)
108+
109+
if (
110+
SIMVUE_SERVER_UPPER_CONSTRAINT
111+
and _version >= SIMVUE_SERVER_UPPER_CONSTRAINT
112+
):
113+
raise AssertionError(
114+
f"Python API v{_version_str} is not compatible with Simvue server versions "
115+
f">= {SIMVUE_SERVER_UPPER_CONSTRAINT}"
116+
)
117+
if SIMVUE_SERVER_LOWER_CONSTRAINT and _version < SIMVUE_SERVER_LOWER_CONSTRAINT:
118+
raise AssertionError(
119+
f"Python API v{_version_str} is not compatible with Simvue server versions "
120+
f"< {SIMVUE_SERVER_LOWER_CONSTRAINT}"
121+
)
122+
123+
@pydantic.model_validator(mode="after")
124+
@classmethod
125+
def check_valid_server(cls, values: "SimvueConfiguration") -> bool:
126+
if os.environ.get("SIMVUE_NO_SERVER_CHECK"):
127+
return values
128+
129+
cls._check_server(values.server.token, values.server.url, values.run.mode)
130+
131+
return values
132+
73133
@classmethod
74134
@sv_util.prettify_pydantic
75135
def fetch(
76136
cls,
77137
server_url: typing.Optional[str] = None,
78138
server_token: typing.Optional[str] = None,
139+
mode: typing.Optional[typing.Literal["offline", "online", "disabled"]] = None,
79140
) -> "SimvueConfiguration":
80141
"""Retrieve the Simvue configuration from this project
81142
@@ -88,6 +149,8 @@ def fetch(
88149
override the URL used for this session
89150
server_token : str, optional
90151
override the token used for this session
152+
mode : 'online' | 'offline' | 'disabled'
153+
set the run mode for this session
91154
92155
Return
93156
------
@@ -115,6 +178,8 @@ def fetch(
115178

116179
_config_dict["offline"] = _config_dict.get("offline", {})
117180

181+
_config_dict["run"] = _config_dict.get("run", {})
182+
118183
# Allow override of specification of offline directory via environment variable
119184
if not (_default_dir := os.environ.get("SIMVUE_OFFLINE_DIRECTORY")):
120185
_default_dir = _config_dict["offline"].get(
@@ -134,6 +199,8 @@ def fetch(
134199
"SIMVUE_TOKEN", server_token or _config_dict["server"].get("token")
135200
)
136201

202+
_run_mode = mode or _config_dict["run"].get("mode")
203+
137204
if not _server_url:
138205
raise RuntimeError("No server URL was specified")
139206

@@ -142,6 +209,7 @@ def fetch(
142209

143210
_config_dict["server"]["token"] = _server_token
144211
_config_dict["server"]["url"] = _server_url
212+
_config_dict["run"]["mode"] = _run_mode
145213

146214
return SimvueConfiguration(**_config_dict)
147215

simvue/factory/proxy/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ def Simvue(
2323
suppress_errors: bool = True,
2424
) -> "SimvueBaseClass":
2525
if mode == "offline":
26-
return Offline(name=name, uniq_id=uniq_id, suppress_errors=suppress_errors)
26+
return Offline(
27+
name=name, uniq_id=uniq_id, suppress_errors=suppress_errors, config=config
28+
)
2729
else:
2830
return Remote(
2931
name=name, uniq_id=uniq_id, config=config, suppress_errors=suppress_errors

simvue/factory/proxy/offline.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,15 @@ class Offline(SimvueBaseClass):
2828
"""
2929

3030
def __init__(
31-
self, name: typing.Optional[str], uniq_id: str, suppress_errors: bool = True
31+
self,
32+
name: typing.Optional[str],
33+
uniq_id: str,
34+
config: SimvueConfiguration,
35+
suppress_errors: bool = True,
3236
) -> None:
33-
super().__init__(name, uniq_id, suppress_errors)
37+
super().__init__(name=name, uniq_id=uniq_id, suppress_errors=suppress_errors)
3438

35-
_offline_dir = SimvueConfiguration.fetch().offline.cache
39+
_offline_dir = config.offline.cache
3640
self._directory: str = os.path.join(_offline_dir, self._uuid)
3741

3842
os.makedirs(self._directory, exist_ok=True)

0 commit comments

Comments
 (0)