Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions simvue/config/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,25 @@

logger = logging.getLogger(__name__)

BASE_MODEL_CONFIG = pydantic.ConfigDict(extra="forbid")


class MetadataSpecifications(pydantic.BaseModel):
environment: bool = os.environ.get("SIMVUE_DISABLE_ENVIRONMENT_METRICS") is None
git: bool = os.environ.get("SIMVUE_DISABLE_GIT_METRICS") is None
system: bool = os.environ.get("SIMVUE_DISABLE_SYSTEM_METRICS") is None
record_shell_vars: list[str] | None = None
custom: dict[str, str | int | float | bool] | None = None

model_config: typing.ClassVar[pydantic.ConfigDict] = BASE_MODEL_CONFIG


class CertificateSpecifications(pydantic.BaseModel):
storage_ca_cert: pydantic.FilePath | bool = True
server_ca_cert: pydantic.FilePath | bool = True
client_cert: pydantic.FilePath | None = None
client_key: pydantic.SecretStr | None = None
model_config: typing.ClassVar[pydantic.ConfigDict] = BASE_MODEL_CONFIG

@pydantic.model_validator(mode="before")
@classmethod
Expand All @@ -43,10 +56,9 @@ def check_for_cert_env(


class ServerSpecifications(pydantic.BaseModel):
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(
extra="forbid",
strict=True,
)
model_config: typing.ClassVar[pydantic.ConfigDict] = BASE_MODEL_CONFIG | {
"extra": "forbid"
}
url: pydantic.AnyHttpUrl | None
token: pydantic.SecretStr | None
env: dict[str, str] | None = None
Expand Down Expand Up @@ -78,6 +90,7 @@ def check_token(cls, v: pydantic.SecretStr | None) -> pydantic.SecretStr | None:

class OfflineSpecifications(pydantic.BaseModel):
cache: pathlib.Path | None = None
model_config: typing.ClassVar[pydantic.ConfigDict] = BASE_MODEL_CONFIG

@pydantic.field_validator("cache")
@classmethod
Expand All @@ -94,17 +107,20 @@ def check_valid_cache_path(cls, cache: pathlib.Path) -> pathlib.Path:
class MetricsSpecifications(pydantic.BaseModel):
system_metrics_interval: pydantic.PositiveInt | None = -1
enable_emission_metrics: bool = False
model_config: typing.ClassVar[pydantic.ConfigDict] = BASE_MODEL_CONFIG


class DefaultRunSpecifications(pydantic.BaseModel):
name: str | None = None
description: str | None = None
tags: list[str] | None = None
folder: str = pydantic.Field(default="/", pattern=sv_models.FOLDER_REGEX)
metadata: dict[str, str | int | float | bool] | None = None
metadata: MetadataSpecifications = MetadataSpecifications()
mode: typing.Literal["offline", "disabled", "online"] = "online"
record_shell_vars: list[str] | None = None
model_config: typing.ClassVar[pydantic.ConfigDict] = BASE_MODEL_CONFIG


class ClientGeneralOptions(pydantic.BaseModel):
debug: bool = False
model_config: typing.ClassVar[pydantic.ConfigDict] = BASE_MODEL_CONFIG
23 changes: 13 additions & 10 deletions simvue/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ def _node_js_env(repository: pathlib.Path) -> dict[str, typing.Any]:
return js_meta


def _environment_variables(glob_exprs: list[str]) -> dict[str, str]:
def _environment_variables(glob_exprs: set[str]) -> dict[str, str]:
"""Retrieve values for environment variables."""
_env_vars: list[str] = list(os.environ.keys())
_metadata: dict[str, str] = {}
Expand All @@ -283,20 +283,23 @@ def _environment_variables(glob_exprs: list[str]) -> dict[str, str]:


def environment(
*,
repository: pathlib.Path | None = None,
record_repo_environment: bool = True,
env_var_glob_exprs: set[str] | None = None,
) -> dict[str, typing.Any]:
"""Retrieve environment metadata."""
_environment_meta = {}
_repository: pathlib.Path = repository or pathlib.Path.cwd()
if _python_meta := _python_env(_repository):
_environment_meta["python"] = _python_meta
if _rust_meta := _rust_env(_repository):
_environment_meta["rust"] = _rust_meta
if _julia_meta := _julia_env(_repository):
_environment_meta["julia"] = _julia_meta
if _js_meta := _node_js_env(_repository):
_environment_meta["javascript"] = _js_meta
if record_repo_environment:
_repository: pathlib.Path = repository or pathlib.Path.cwd()
if _python_meta := _python_env(_repository):
_environment_meta["python"] = _python_meta
if _rust_meta := _rust_env(_repository):
_environment_meta["rust"] = _rust_meta
if _julia_meta := _julia_env(_repository):
_environment_meta["julia"] = _julia_meta
if _js_meta := _node_js_env(_repository):
_environment_meta["javascript"] = _js_meta
if env_var_glob_exprs:
_environment_meta["shell"] = _environment_variables(env_var_glob_exprs)
return _environment_meta
19 changes: 13 additions & 6 deletions simvue/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,7 @@ def init(
tags = (tags or []) + (self._user_config.run.tags or [])
folder = folder or self._user_config.run.folder
name = name or self._user_config.run.name
metadata = (metadata or {}) | (self._user_config.run.metadata or {})
metadata = (metadata or {}) | (self._user_config.run.metadata.custom or {})
record_shell_vars = record_shell_vars or self._user_config.run.record_shell_vars

self._term_color = not no_color
Expand Down Expand Up @@ -809,17 +809,24 @@ def init(
self._sv_obj.ttl = self._retention
self._sv_obj.status = self._status
self._sv_obj.tags = tags
self._sv_obj.metadata = (
(metadata or {})
| git_info(pathlib.Path.cwd())
| environment(env_var_glob_exprs=record_shell_vars)

_session_metadata = metadata or {}

_session_metadata |= environment(
env_var_glob_exprs=record_shell_vars,
record_repo_environment=self._user_config.run.metadata.environment,
)

if self._user_config.run.metadata.git:
_session_metadata |= git_info(pathlib.Path.cwd())

self._sv_obj.heartbeat_timeout = timeout
self._sv_obj.alerts = []
self._sv_obj.created = time.time()
self._sv_obj.notifications = notification
self._sv_obj.metadata = _session_metadata

if self._status == "running":
if self._status == "running" and self._user_config.run.metadata.system:
self._sv_obj.system = get_system()

self._data = self._sv_obj.staging
Expand Down
5 changes: 4 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,11 @@ def create_test_run_offline(request, monkeypatch: pytest.MonkeyPatch, prevent_sc


@pytest.fixture
def create_plain_run(request, prevent_script_exit, mocker) -> Generator[tuple[sv_run.Run, dict]]:
def create_plain_run(request, prevent_script_exit, mocker, monkeypatch) -> Generator[tuple[sv_run.Run, dict]]:
_ = prevent_script_exit
monkeypatch.setenv("SIMVUE_DISABLE_ENVIRONMENT_METRICS", "True")
monkeypatch.setenv("SIMVUE_DISABLE_GIT_METRICS", "True")
monkeypatch.setenv("SIMVUE_DISABLE_SYSTEM_METRICS", "True")
def testing_exit(status: int) -> None:
raise SystemExit(status)
with sv_run.Run() as run:
Expand Down
5 changes: 3 additions & 2 deletions tests/functional/test_run_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -1566,7 +1566,7 @@ def test_env_var_metadata() -> None:
}
os.environ.update(_recorded_env)
with simvue.Run() as run:
run.init(
_ = run.init(
name="test_reconnect",
folder="/simvue_unit_testing",
retention_period="2 minutes",
Expand All @@ -1575,6 +1575,7 @@ def test_env_var_metadata() -> None:
record_shell_vars={"SIMVUE_RUN_TEST_VAR_*"}
)
_recorded_meta = RunObject(identifier=run.id).metadata
assert "shell" in _recorded_meta
assert all(key in _recorded_meta.get("shell") for key in _recorded_env)

@pytest.mark.run
Expand Down Expand Up @@ -1630,7 +1631,7 @@ def test_run_environment_metadata(environment: str, mocker: pytest_mock.MockerFi
running=False,
visibility="tenant" if os.environ.get("CI") else None,
)
run.update_metadata(env_func(_target_dir))
run.update_metadata(env_func(repository=_target_dir))


@pytest.mark.online
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def test_js_env() -> None:
@pytest.mark.metadata
@pytest.mark.local
def test_environment() -> None:
metadata = sv_meta.environment(pathlib.Path(__file__).parents[1].joinpath("example_data"))
metadata = sv_meta.environment(repository=pathlib.Path(__file__).parents[1].joinpath("example_data"))
assert metadata["python"]["project"]["name"] == "example-repo"
assert metadata["rust"]["project"]["name"] == "example_project"
assert metadata["julia"]["project"]["name"] == "Julia Demo Project"
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading