clan_lib: init flatten_data_structured
Avoids ambiguity issues with keys that contain dots or other separators.
This commit is contained in:
@@ -3,6 +3,26 @@ from typing import Any
|
|||||||
from clan_lib.persist.util import flatten_data, list_difference
|
from clan_lib.persist.util import flatten_data, list_difference
|
||||||
|
|
||||||
|
|
||||||
|
def flatten_data_structured(data: dict, parent_path: tuple = ()) -> dict:
|
||||||
|
"""Flatten using tuple keys instead of string concatenation
|
||||||
|
|
||||||
|
Avoids ambiguity issues with keys that contain dots or other separators.
|
||||||
|
|
||||||
|
"""
|
||||||
|
flattened = {}
|
||||||
|
for key, value in data.items():
|
||||||
|
current_path = (*parent_path, key)
|
||||||
|
|
||||||
|
if isinstance(value, dict):
|
||||||
|
if value:
|
||||||
|
flattened.update(flatten_data_structured(value, current_path))
|
||||||
|
else:
|
||||||
|
flattened[current_path] = {}
|
||||||
|
else:
|
||||||
|
flattened[current_path] = value
|
||||||
|
return flattened
|
||||||
|
|
||||||
|
|
||||||
def calculate_static_data(
|
def calculate_static_data(
|
||||||
all_values: dict[str, Any], persisted: dict[str, Any]
|
all_values: dict[str, Any], persisted: dict[str, Any]
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
|
|||||||
@@ -1,4 +1,68 @@
|
|||||||
from clan_lib.persist.static_data import calculate_static_data
|
from clan_lib.persist.static_data import calculate_static_data, flatten_data_structured
|
||||||
|
|
||||||
|
|
||||||
|
def test_flatten_data_structured() -> None:
|
||||||
|
data = {
|
||||||
|
"name": "example",
|
||||||
|
"settings": {
|
||||||
|
"optionA": True,
|
||||||
|
"optionB": {
|
||||||
|
"subOption1": 10,
|
||||||
|
"subOption2": 20,
|
||||||
|
},
|
||||||
|
"emptyDict": {},
|
||||||
|
},
|
||||||
|
"listSetting": [1, 2, 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
expected_flat = {
|
||||||
|
("name",): "example",
|
||||||
|
("settings", "optionA"): True,
|
||||||
|
("settings", "optionB", "subOption1"): 10,
|
||||||
|
("settings", "optionB", "subOption2"): 20,
|
||||||
|
("settings", "emptyDict"): {},
|
||||||
|
("listSetting",): [1, 2, 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
flattened = flatten_data_structured(data)
|
||||||
|
assert flattened == expected_flat
|
||||||
|
|
||||||
|
|
||||||
|
def test_flatten_data_structured_empty() -> None:
|
||||||
|
data: dict = {}
|
||||||
|
expected_flat: dict = {}
|
||||||
|
flattened = flatten_data_structured(data)
|
||||||
|
assert flattened == expected_flat
|
||||||
|
|
||||||
|
|
||||||
|
def test_flatten_data_structured_nested_empty() -> None:
|
||||||
|
data: dict = {
|
||||||
|
"level1": {
|
||||||
|
"level2": {
|
||||||
|
"level3": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
expected_flat: dict = {
|
||||||
|
("level1", "level2", "level3"): {},
|
||||||
|
}
|
||||||
|
flattened = flatten_data_structured(data)
|
||||||
|
assert flattened == expected_flat
|
||||||
|
|
||||||
|
|
||||||
|
def test_flatten_data_dot_in_keys() -> None:
|
||||||
|
data = {
|
||||||
|
"key.foo": "value1",
|
||||||
|
"key": {
|
||||||
|
"foo": "value2",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
expected_flat = {
|
||||||
|
("key.foo",): "value1",
|
||||||
|
("key", "foo"): "value2",
|
||||||
|
}
|
||||||
|
flattened = flatten_data_structured(data)
|
||||||
|
assert flattened == expected_flat
|
||||||
|
|
||||||
|
|
||||||
def test_calculate_static_data_basic() -> None:
|
def test_calculate_static_data_basic() -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user