Skip to content

Commit

Permalink
Sort csm subsystem lists (#430)
Browse files Browse the repository at this point in the history
  • Loading branch information
vhirtham authored Jul 16, 2021
1 parent a142185 commit 699e4e2
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@

### ASDF

- sort `List[str]` before serialization of most `weldx` classes to avoid random reordering in the same file and enforce
consistency. [[#430]](https://github.com/BAMWelDX/weldx/pull/430)

### deprecations

- `lcs_coords_from_ts` will be removed in version 0.5.0 [[#426]](https://github.com/BAMWelDX/weldx/pull/426)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ class CoordinateSystemManagerSubsystem:
subsystems: List[str]
members: List[str]

def __post_init__(self):
"""Sort the string lists."""
self.subsystems = sorted(self.subsystems)
self.members = sorted(self.members)


class CoordinateSystemManagerSubsystemASDF(WeldxType):
"""Serialization class for a CoordinateSystemManagerSubsystem instance"""
Expand Down
12 changes: 12 additions & 0 deletions weldx/asdf/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ def dataclass_serialization_class(
to_tree_mod: Callable = None,
from_tree_mod: Callable = None,
validators: dict = None,
sort_string_lists: bool = True,
) -> Type:
"""Generate a asdf serialization class for a python dataclass.
Expand Down Expand Up @@ -338,6 +339,17 @@ def _noop(tree):
if from_tree_mod is None:
from_tree_mod = _noop

if sort_string_lists:
original_to_tree_mod = to_tree_mod

def _sort_string_list(tree):
for k, v in tree.items():
if isinstance(v, list) and all(isinstance(item, str) for item in v):
tree[k] = sorted(v)
return original_to_tree_mod(tree)

to_tree_mod = _sort_string_list

class _SerializationClass(WeldxType):
name = class_name
version = v
Expand Down
87 changes: 86 additions & 1 deletion weldx/tests/asdf_tests/test_asdf_util.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
"""tests for asdf utility functions."""
from dataclasses import dataclass
from typing import List

import pytest

from weldx import WeldxFile
from weldx.asdf.util import get_yaml_header
from weldx.asdf.util import dataclass_serialization_class, get_yaml_header


@pytest.fixture(scope="function")
Expand Down Expand Up @@ -40,3 +43,85 @@ def test_get_yaml_header(create_file_and_buffer, index, parse):
else:
assert isinstance(header, str)
assert "asdf_library" in header


def _to_tree_mod(tree):
tree["a"] += ["d"]
return tree


def _from_tree_mod(tree):
tree["a"] += ["e"]
return tree


@pytest.mark.parametrize(
"val_a, exp_val_a_tree, exp_val_a_dc, to_tree_mod, from_tree_mod,"
"sort_string_lists",
[
(["c", "b", "a"], ["a", "b", "c"], ["a", "b", "c"], None, None, True),
(["c", "b", "a"], ["c", "b", "a"], ["c", "b", "a"], None, None, False),
# not a pure string list -> no sorting
(["c", 1, "a"], ["c", 1, "a"], ["c", 1, "a"], None, None, True),
(["c", 1, "a"], ["c", 1, "a"], ["c", 1, "a"], None, None, False),
# check to_tree_mod is called
(["c", "b"], ["b", "c", "d"], ["b", "c", "d"], _to_tree_mod, None, True),
(["c", "b"], ["c", "b", "d"], ["c", "b", "d"], _to_tree_mod, None, False),
# check_from_tree_mod is called
(["c"], ["c", "d"], ["c", "d", "e"], _to_tree_mod, _from_tree_mod, False),
(["c"], ["c"], ["c", "e"], None, _from_tree_mod, False),
],
)
def test_dataclass_serialization_class(
val_a, exp_val_a_tree, exp_val_a_dc, to_tree_mod, from_tree_mod, sort_string_lists
):
"""Test the `dataclass_serialization_class` function.
The test defines a dataclass and its corresponding serialization class using
`dataclass_serialization_class`. It first calls to_tree to get tree from the
generated serialization class. Afterwards the tree is used with the `from_tree`
method to construct a new dataclass instance. The results of the function calls are
checked against the expected values.
Parameters
----------
val_a :
Initial value of the dataclasses' variable a
exp_val_a_tree :
Expected value of the variable a in the tree after `to_tree` was run
exp_val_a_dc :
Expected value of the variable a of the reconstructed dataclass
to_tree_mod :
The value passed as corresponding function parameter
from_tree_mod :
The value passed as corresponding function parameter
sort_string_lists
The value passed as corresponding function parameter
"""

@dataclass
class _DataClass:
a: List[str]
b: int = 1

dc = _DataClass(a=val_a, b=2)

dataclass_asdf = dataclass_serialization_class(
class_type=_DataClass,
class_name="Test",
version="1.0.0",
sort_string_lists=sort_string_lists,
to_tree_mod=to_tree_mod,
from_tree_mod=from_tree_mod,
)
tree = dataclass_asdf.to_tree(dc, None)

assert tree["b"] == 2
assert tree["a"] == exp_val_a_tree

dc_restored = dataclass_asdf.from_tree(tree, None)

assert dc_restored.b == 2
assert dc_restored.a == exp_val_a_dc

0 comments on commit 699e4e2

Please sign in to comment.