Skip to content

Commit 5aaebe6

Browse files
authored
Merge pull request #663 from simvue-io/feature/api-repro
Introduction of Lower Level API and Support for Simvue Server v3
2 parents d347e95 + 1a604e1 commit 5aaebe6

File tree

104 files changed

+8916
-2507
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+8916
-2507
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,5 @@ offline/
147147

148148
# Vagrant
149149
Vagrantfile
150+
151+
.sourcery*

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
* Add support for defining Simvue run defaults using `tool.simvue` in a project `pyproject.toml` file.
66
* Drop support for INI based configuration files.
77
* Retrieve all metric values if `max_points` is unspecified or set to `None`.
8+
* Add support for PyTorch in Python 3.13
9+
* Create lower level API for directly interacting with the Simvue RestAPI endpoints.
10+
* **Removes support for Python <3.10 due to dependency constraints.**
11+
* Separates `create_alert` into specific methods `create_event_alert` etc.
812
## [v1.1.4](https://github.com/simvue-io/client/releases/tag/v1.1.4) - 2024-12-11
913

1014
* Remove incorrect identifier reference for latest Simvue servers during reconnection.

README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,15 @@ if __name__ == "__main__":
7676
run.save_file('params.in', 'input')
7777

7878
# Add an alert (the alert definition will be created if necessary)
79-
run.create_alert(name='loss-too-high', # Name
80-
source='metrics', # Source
81-
rule='is above', # Rule
82-
metric='loss', # Metric
83-
frequency=1, # Frequency
84-
window=1, # Window
85-
threshold=10, # Threshold
86-
notification='email') # Notification type
79+
run.create_metric_threshold_alert(
80+
name='loss-too-high', # Name
81+
rule='is above', # Rule
82+
metric='loss', # Metric
83+
frequency=1, # Frequency
84+
window=1, # Window
85+
threshold=10, # Threshold
86+
notification='email' # Notification type
87+
)
8788

8889
...
8990

examples/Geant4/geant4_simvue.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
@click.option("--momentum", type=float, default=10)
2828
@click.option("--events", type=int, default=100)
2929
def geant4_simvue_example(
30-
g4_binary: str, config: typing.Optional[str], ci: bool, momentum: float, events: int
30+
g4_binary: str, config: str | None, ci: bool, momentum: float, events: int
3131
) -> None:
3232
@mp_file_parse.file_parser
3333
def root_file_parser(

poetry.lock

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

pyproject.toml

Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
[tool.poetry]
1+
[project]
22
name = "simvue"
3-
version = "1.1.4"
3+
version = "2.0.0a0"
44
description = "Simulation tracking and monitoring"
5-
authors = ["Simvue Development Team <[email protected]>"]
5+
authors = [
6+
{name = "Simvue Development Team", email = "[email protected]"}
7+
]
68
license = "Apache v2"
9+
requires-python = ">=3.10,<3.14"
710
readme = "README.md"
8-
homepage = "https://simvue.io"
9-
repository = "https://github.com/simvue-io/python-api"
10-
documentation = "https://docs.simvue.io"
1111
classifiers = [
1212
"Development Status :: 5 - Production/Stable",
1313
"Intended Audience :: Science/Research",
@@ -33,34 +33,37 @@ keywords = [
3333
"alerting",
3434
"metrics-gathering"
3535
]
36+
dependencies = [
37+
"requests (>=2.32.3,<3.0.0)",
38+
"pydantic (>=2.10.6,<3.0.0)",
39+
"tabulate (>=0.9.0,<0.10.0)",
40+
"msgpack (>=1.1.0,<2.0.0)",
41+
"pyjwt (>=2.10.1,<3.0.0)",
42+
"pandas (>=2.2.3,<3.0.0)",
43+
"toml (>=0.10.2,<0.11.0)",
44+
"click (>=8.1.8,<9.0.0)",
45+
"gitpython (>=3.1.44,<4.0.0)",
46+
"humanfriendly (>=10.0,<11.0)",
47+
"randomname (>=0.2.1,<0.3.0)",
48+
"codecarbon (>=2.8.3,<3.0.0)",
49+
"numpy (>=2.2.2,<3.0.0)",
50+
"flatdict (>=4.0.1,<5.0.0)",
51+
"semver (>=3.0.4,<4.0.0)",
52+
"email-validator (>=2.2.0,<3.0.0)",
53+
"psutil (>=6.1.1,<7.0.0)",
54+
"tenacity (>=9.0.0,<10.0.0)",
55+
"typing-extensions (>=4.12.2,<5.0.0) ; python_version < \"3.11\"",
56+
]
3657

37-
[tool.poetry.dependencies]
38-
python = "^3.10,<3.14"
39-
dill = "^0.3.7"
40-
requests = "^2.31.0"
41-
msgpack = "^1.0.7"
42-
tenacity = ">=8.2.3,<10.0.0"
43-
PyJWT = "^2.8.0"
44-
psutil = ">=5.9.8,<7.0.0"
45-
pydantic = "^2.5.3"
46-
pandas = "^2.2.0"
47-
plotly = {version = ">=5.18,<7.0", optional = true}
48-
matplotlib = {version = "^3.8.2", optional = true}
49-
typing_extensions = { version = "^4.11.0", python = "<3.10" }
50-
toml = "^0.10.2"
51-
click = "^8.1.7"
52-
gitpython = "^3.1.43"
53-
humanfriendly = "^10.0"
54-
tabulate = "^0.9.0"
55-
randomname = "^0.2.1"
56-
codecarbon = "^2.7.1"
57-
numpy = "^2.1.2"
58-
semver = "^3.0.2"
58+
[project.urls]
59+
homepage = "https://simvue.io"
60+
repository = "https://github.com/simvue-io/python-api"
61+
documentation = "https://docs.simvue.io"
5962

60-
[tool.poetry.extras]
61-
plot = ["matplotlib", "plotly"]
63+
[project.optional-dependencies]
64+
plot = ["plotly (>=6.0.0,<7.0.0)", "matplotlib (>=3.10.0,<4.0.0)"]
6265

63-
[tool.poetry.scripts]
66+
[project.scripts]
6467
simvue_sender = "simvue.bin.sender:run"
6568

6669
[tool.poetry.group.dev.dependencies]
@@ -72,16 +75,13 @@ pytest-mock = "^3.14.0"
7275
pytest-sugar = "^1.0.0"
7376
pytest-xdist = "^3.6.1"
7477
jinja2 = "^3.1.4"
78+
types-requests = "^2.32.0.20241016"
79+
interrogate = "^1.7.0"
7580

7681
[build-system]
7782
requires = ["poetry-core"]
7883
build-backend = "poetry.core.masonry.api"
7984

80-
[[tool.poetry.source]]
81-
name = "PyPI"
82-
priority = "primary"
83-
84-
8585
[tool.ruff]
8686
lint.extend-select = ["C901", "T201"]
8787
lint.mccabe.max-complexity = 11
@@ -93,6 +93,7 @@ testpaths = [
9393
"tests"
9494
]
9595
markers = [
96+
"codecarbon: tests for emission metrics",
9697
"client: tests of Simvue client",
9798
"converters: tests for Simvue object converters",
9899
"dispatch: test data dispatcher",
@@ -104,11 +105,17 @@ markers = [
104105
"api: tests of RestAPI functionality",
105106
"unix: tests for UNIX systems only",
106107
"metadata: tests of metadata gathering functions",
107-
"proxies: tests for remote/offline Simvue proxies"
108+
"proxies: tests for remote/offline Simvue proxies",
109+
"online: tests for online functionality",
110+
"offline: tests for offline functionality",
111+
"local: tests of functionality which do not involve a server or writing to an offline cache file"
108112
]
109113

110114
[tool.interrogate]
111115
ignore-init-method = true
112116
fail-under = 95
113117
verbose = 1
114118
exclude = ["docs", "tests"]
119+
120+
[tool.mypy]
121+
ignore_missing_imports = true

simvue/api/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"""
2+
Simvue API
3+
==========
4+
5+
Module contains methods for interacting with a Simvue server
6+
including accessing/updating objects.
7+
8+
"""

simvue/api/objects/__init__.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""
2+
Simvue API Objects
3+
==================
4+
5+
The following module defines objects which provide exact representations
6+
of information accessible via the Simvue RestAPI, this provides a lower
7+
level interface towards the development of additional tools/frameworks.
8+
9+
"""
10+
11+
from .administrator import Tenant as Tenant, User as User
12+
from .alert import (
13+
Alert as Alert,
14+
EventsAlert as EventsAlert,
15+
MetricsThresholdAlert as MetricsThresholdAlert,
16+
MetricsRangeAlert as MetricsRangeAlert,
17+
UserAlert as UserAlert,
18+
)
19+
from .storage import (
20+
S3Storage as S3Storage,
21+
FileStorage as FileStorage,
22+
Storage as Storage,
23+
)
24+
from .artifact import (
25+
FileArtifact as FileArtifact,
26+
ObjectArtifact as ObjectArtifact,
27+
Artifact as Artifact,
28+
)
29+
30+
from .stats import Stats as Stats
31+
from .run import Run as Run
32+
from .tag import Tag as Tag
33+
from .folder import Folder as Folder, get_folder_from_path as get_folder_from_path
34+
from .events import Events as Events
35+
from .metrics import Metrics as Metrics
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""
2+
Simvue Admin Objects
3+
====================
4+
5+
These are Simvue objects only accessible to an administrator of
6+
the server.
7+
8+
"""
9+
10+
from .tenant import Tenant as Tenant
11+
from .user import User as User
12+
13+
__all__ = [
14+
"Tenant",
15+
"User",
16+
]
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
"""
2+
Simvue Tenants
3+
==============
4+
5+
Contains a class for remotely connecting to Simvue tenants, or defining
6+
a new tenant given relevant arguments.
7+
8+
"""
9+
10+
try:
11+
from typing import Self
12+
except ImportError:
13+
from typing_extensions import Self
14+
import pydantic
15+
16+
from simvue.api.objects.base import write_only, SimvueObject, staging_check
17+
18+
19+
class Tenant(SimvueObject):
20+
"""Class for interacting with a tenant instance on the server."""
21+
22+
@classmethod
23+
@pydantic.validate_call
24+
def new(
25+
cls,
26+
*,
27+
name: str,
28+
is_enabled: bool = True,
29+
max_request_rate: int = 0,
30+
max_runs: int = 0,
31+
max_data_volume: int = 0,
32+
offline: bool = False,
33+
) -> Self:
34+
"""Create a new tenant on the Simvue server.
35+
36+
Requires administrator privileges.
37+
38+
Parameters
39+
----------
40+
name: str
41+
the name for this tenant
42+
is_enabled: bool, optional
43+
whether to enable the tenant on creation, default is True
44+
max_request_rate: int, optional
45+
the maximum request rate allowed for this tenant, default is no limit.
46+
max_runs: int, optional
47+
the maximum number of runs allowed within this tenant, default is no limit.
48+
max_data_volume: int, optional
49+
the maximum volume of data allowed within this tenant, default is no limit.
50+
offline: bool, optional
51+
create in offline mode, default is False.
52+
53+
Returns
54+
-------
55+
Tenant
56+
a tenant instance with staged changes
57+
58+
"""
59+
return Tenant(
60+
name=name,
61+
is_enabled=is_enabled,
62+
max_request_rate=max_request_rate,
63+
max_runs=max_runs,
64+
max_data_volume=max_data_volume,
65+
_read_only=False,
66+
_offline=offline,
67+
)
68+
69+
@property
70+
def name(self) -> str:
71+
"""Retrieve the name of the tenant"""
72+
return self._get_attribute("name")
73+
74+
@name.setter
75+
@write_only
76+
@pydantic.validate_call
77+
def name(self, name: str) -> None:
78+
"""Change name of tenant"""
79+
self._staging["name"] = name
80+
81+
@property
82+
@staging_check
83+
def is_enabled(self) -> bool:
84+
"""Retrieve if tenant is enabled"""
85+
return self._get_attribute("is_enabled")
86+
87+
@is_enabled.setter
88+
@write_only
89+
@pydantic.validate_call
90+
def is_enabled(self, is_enabled: bool) -> None:
91+
"""Enable/disable tenant"""
92+
self._staging["is_enabled"] = is_enabled
93+
94+
@property
95+
@staging_check
96+
def max_request_rate(self) -> int:
97+
"""Retrieve the tenant's maximum request rate"""
98+
return self._get_attribute("max_request_rate")
99+
100+
@max_request_rate.setter
101+
@write_only
102+
@pydantic.validate_call
103+
def max_request_rate(self, max_request_rate: int) -> None:
104+
"""Update tenant's maximum request rate"""
105+
self._staging["max_request_rate"] = max_request_rate
106+
107+
@property
108+
@staging_check
109+
def max_runs(self) -> int:
110+
"""Retrieve the tenant's maximum runs"""
111+
return self._get_attribute("max_runs")
112+
113+
@max_runs.setter
114+
@write_only
115+
@pydantic.validate_call
116+
def max_runs(self, max_runs: int) -> None:
117+
"""Update tenant's maximum runs"""
118+
self._staging["max_runs"] = max_runs
119+
120+
@property
121+
@staging_check
122+
def max_data_volume(self) -> int:
123+
"""Retrieve the tenant's maximum data volume"""
124+
return self._get_attribute("max_data_volume")
125+
126+
@max_data_volume.setter
127+
@write_only
128+
@pydantic.validate_call
129+
def max_data_volume(self, max_data_volume: int) -> None:
130+
"""Update tenant's maximum data volume"""
131+
self._staging["max_data_volume"] = max_data_volume

0 commit comments

Comments
 (0)