Skip to content

Commit 9b209fc

Browse files
authored
Merge pull request #692 from simvue-io/wk9874/update_metadata_tags_offline
Added improved merging of staging dicts
2 parents 9531b62 + 58a623b commit 9b209fc

File tree

5 files changed

+83
-11
lines changed

5 files changed

+83
-11
lines changed

poetry.lock

Lines changed: 17 additions & 3 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
@@ -53,6 +53,7 @@ dependencies = [
5353
"psutil (>=6.1.1,<7.0.0)",
5454
"tenacity (>=9.0.0,<10.0.0)",
5555
"typing-extensions (>=4.12.2,<5.0.0) ; python_version < \"3.11\"",
56+
"deepmerge (>=2.0,<3.0)",
5657
]
5758

5859
[project.urls]

simvue/api/objects/base.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import msgpack
1818
import pydantic
1919

20+
from simvue.utilities import staging_merger
2021
from simvue.config.user import SimvueConfiguration
2122
from simvue.exception import ObjectNotFoundError
2223
from simvue.version import __version__
@@ -524,7 +525,8 @@ def _cache(self) -> None:
524525
with self._local_staging_file.open() as in_f:
525526
_local_data = json.load(in_f)
526527

527-
_local_data |= self._staging
528+
staging_merger.merge(_local_data, self._staging)
529+
528530
with self._local_staging_file.open("w", encoding="utf-8") as out_f:
529531
json.dump(_local_data, out_f, indent=2)
530532

simvue/utilities.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
import os
1212
import pathlib
1313
import typing
14-
1514
import jwt
15+
from deepmerge import Merger
1616

1717
from datetime import timezone
1818
from simvue.models import DATETIME_FORMAT
@@ -396,3 +396,18 @@ def get_mimetype_for_file(file_path: pathlib.Path) -> str:
396396
"""Return MIME type for the given file"""
397397
_guess, *_ = mimetypes.guess_type(file_path)
398398
return _guess or "application/octet-stream"
399+
400+
401+
# Create a new Merge strategy for merging local file and staging attributes
402+
staging_merger = Merger(
403+
# pass in a list of tuple, with the
404+
# strategies you are looking to apply
405+
# to each type.
406+
[(list, ["override"]), (dict, ["merge"]), (set, ["union"])],
407+
# next, choose the fallback strategies,
408+
# applied to all other types:
409+
["override"],
410+
# finally, choose the strategies in
411+
# the case where the types conflict:
412+
["override"],
413+
)

tests/functional/test_run_class.py

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -220,9 +220,14 @@ def test_offline_tags(create_plain_run_offline: tuple[sv_run.Run, dict]) -> None
220220

221221
@pytest.mark.run
222222
def test_update_metadata_running(create_test_run: tuple[sv_run.Run, dict]) -> None:
223-
METADATA = {"a": 10, "b": 1.2, "c": "word"}
223+
METADATA = {"a": 1, "b": 1.2, "c": "word", "d": "new"}
224224
run, _ = create_test_run
225-
run.update_metadata(METADATA)
225+
# Add an initial set of metadata
226+
run.update_metadata({"a": 10, "b": 1.2, "c": "word"})
227+
# Try updating a second time, check original dict isnt overwritten
228+
run.update_metadata({"d": "new"})
229+
# Try updating an already defined piece of metadata
230+
run.update_metadata({"a": 1})
226231
run.close()
227232
time.sleep(1.0)
228233
client = sv_cl.Client()
@@ -234,9 +239,14 @@ def test_update_metadata_running(create_test_run: tuple[sv_run.Run, dict]) -> No
234239

235240
@pytest.mark.run
236241
def test_update_metadata_created(create_pending_run: tuple[sv_run.Run, dict]) -> None:
237-
METADATA = {"a": 10, "b": 1.2, "c": "word"}
242+
METADATA = {"a": 1, "b": 1.2, "c": "word", "d": "new"}
238243
run, _ = create_pending_run
239-
run.update_metadata(METADATA)
244+
# Add an initial set of metadata
245+
run.update_metadata({"a": 10, "b": 1.2, "c": "word"})
246+
# Try updating a second time, check original dict isnt overwritten
247+
run.update_metadata({"d": "new"})
248+
# Try updating an already defined piece of metadata
249+
run.update_metadata({"a": 1})
240250
time.sleep(1.0)
241251
client = sv_cl.Client()
242252
run_info = client.get_run(run.id)
@@ -250,13 +260,20 @@ def test_update_metadata_created(create_pending_run: tuple[sv_run.Run, dict]) ->
250260
def test_update_metadata_offline(
251261
create_plain_run_offline: tuple[sv_run.Run, dict],
252262
) -> None:
253-
METADATA = {"a": 10, "b": 1.2, "c": "word"}
263+
METADATA = {"a": 1, "b": 1.2, "c": "word", "d": "new"}
254264
run, _ = create_plain_run_offline
255265
run_name = run._name
256-
run.update_metadata(METADATA)
266+
# Add an initial set of metadata
267+
run.update_metadata({"a": 10, "b": 1.2, "c": "word"})
268+
# Try updating a second time, check original dict isnt overwritten
269+
run.update_metadata({"d": "new"})
270+
# Try updating an already defined piece of metadata
271+
run.update_metadata({"a": 1})
272+
257273
sv_send.sender(os.environ["SIMVUE_OFFLINE_DIRECTORY"], 2, 10)
258274
run.close()
259275
time.sleep(1.0)
276+
260277
client = sv_cl.Client()
261278
run_info = client.get_run(client.get_run_id_from_name(run_name))
262279

@@ -655,6 +672,29 @@ def test_update_tags_created(
655672
assert sorted(run_data.tags) == sorted(tags + ["additional"])
656673

657674

675+
@pytest.mark.offline
676+
@pytest.mark.run
677+
def test_update_tags_offline(
678+
create_plain_run_offline: typing.Tuple[sv_run.Run, dict],
679+
) -> None:
680+
simvue_run, _ = create_plain_run_offline
681+
run_name = simvue_run._name
682+
683+
simvue_run.set_tags(["simvue_client_unit_tests",])
684+
685+
simvue_run.update_tags(["additional"])
686+
687+
sv_send.sender(os.environ["SIMVUE_OFFLINE_DIRECTORY"], 2, 10)
688+
simvue_run.close()
689+
time.sleep(1.0)
690+
691+
client = sv_cl.Client()
692+
run_data = client.get_run(client.get_run_id_from_name(run_name))
693+
694+
time.sleep(1)
695+
run_data = client.get_run(simvue_run._id)
696+
assert sorted(run_data.tags) == sorted(["simvue_client_unit_tests", "additional"])
697+
658698
@pytest.mark.run
659699
@pytest.mark.parametrize("object_type", ("DataFrame", "ndarray"))
660700
def test_save_object(

0 commit comments

Comments
 (0)