from yaml.constructor import ConstructorError
from yaml.nodes import MappingNode
-from .exceptions import DataParsingError
+from .exceptions import DataParsingError, DataValidationError
from .renaming import Renamed, renamed
raise DataParsingError( # pylint: disable=raise-missing-from
f"failed to parse data, JSON: {je}, YAML: {ye}"
) from ye
+
+
+def data_combine(data: Dict[Any, Any], additional_data: Dict[Any, Any], object_path: str = "") -> Dict[Any, Any]:
+ """Combine dictionaries data"""
+ for key in additional_data:
+ if key in data:
+ # if both are dictionaries we can try to combine them deeper
+ if isinstance(data[key], (Dict, dict)) and isinstance(additional_data[key], (Dict, dict)):
+ data[key] = data_combine(data[key], additional_data[key], f"{object_path}/{key}").copy()
+ continue
+ # otherwise we cannot combine them
+ raise DataValidationError(f"duplicity key '{key}' with value in data", object_path)
+ val = additional_data[key]
+ data[key] = val.copy() if hasattr(val, "copy") else val
+ return data
--- /dev/null
+import copy
+from typing import Any, Dict
+
+import pytest
+from pytest import raises
+
+from knot_resolver.utils.modeling.exceptions import DataValidationError
+from knot_resolver.utils.modeling.parsing import data_combine
+
+# default data
+data_default = {"key1": {"inner11": False}}
+
+
+@pytest.mark.parametrize(
+ "val,res",
+ [
+ ({"key2": "value"}, {"key1": {"inner11": False}, "key2": "value"}),
+ ({"key2": {"inner21": True}}, {"key1": {"inner11": False}, "key2": {"inner21": True}}),
+ ({"key1": {"inner12": 5}}, {"key1": {"inner11": False, "inner12": 5}}),
+ ],
+)
+def test_data_combine_valid(val: Dict[Any, Any], res: Dict[Any, Any]) -> None:
+ data = copy.deepcopy(data_default)
+ assert data_combine(data, val) == res
+
+
+@pytest.mark.parametrize("val", [{"key1": "value"}, {"key1": {"inner11": False}}])
+def test_data_combine_invalid(val: Dict[Any, Any]) -> None:
+ data = copy.deepcopy(data_default)
+ with raises(DataValidationError):
+ data_combine(data, val)