Skip to content

Commit 48aaad8

Browse files
use existing exception; include full path to duplicates
1 parent 528ee80 commit 48aaad8

File tree

4 files changed

+37
-20
lines changed

4 files changed

+37
-20
lines changed

src/layered_config_tree/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
ConfigurationError,
1313
ConfigurationKeyError,
1414
DuplicatedConfigurationError,
15-
DuplicateKeysInYAMLError,
1615
ImproperAccessError,
1716
)
1817
from layered_config_tree.main import ConfigNode, LayeredConfigTree, load_yaml

src/layered_config_tree/exceptions.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,3 @@ def __init__(
4747
self.source = source
4848
self.value = value
4949
super().__init__(message, name)
50-
51-
52-
class DuplicateKeysInYAMLError(ConfigurationError):
53-
"""Error raised when a YAML file contains duplicate keys."""
54-
55-
pass

src/layered_config_tree/main.py

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
ConfigurationError,
4040
ConfigurationKeyError,
4141
DuplicatedConfigurationError,
42-
DuplicateKeysInYAMLError,
4342
ImproperAccessError,
4443
)
4544
from layered_config_tree.types import InputData
@@ -711,10 +710,22 @@ class SafeLoader(yaml.SafeLoader):
711710
"""A yaml.SafeLoader that restricts duplicate keys."""
712711

713712
def construct_mapping(
714-
self, node: yaml.MappingNode, deep: bool = False
713+
self,
714+
node: yaml.MappingNode,
715+
deep: bool = False,
716+
path: list[str] | None = None,
715717
) -> dict[Hashable, Any]:
716718
"""Constructs the standard mapping after checking for duplicates.
717719
720+
Parameters
721+
----------
722+
node
723+
The YAML mapping node to construct.
724+
deep
725+
Whether or not to recursively construct mappings.
726+
path
727+
The path to the current node in the YAML document.
728+
718729
Raises
719730
------
720731
DuplicateKeysInYAMLError
@@ -729,15 +740,28 @@ def construct_mapping(
729740
fixing a duplicate in one level of the document will not prevent this method
730741
from raising an error for duplicates in another upon subsequent loads.
731742
"""
732-
mapping = []
733-
duplicates = []
734-
for key_node, _value_node in node.value:
743+
path = [] if path is None else path
744+
seen = set()
745+
duplicates = {}
746+
for key_node, value_node in node.value:
735747
key = self.construct_object(key_node, deep=deep) # type: ignore[no-untyped-call]
736-
if key in mapping:
737-
duplicates.append(key)
738-
mapping.append(key)
748+
full_path = path + [key]
749+
if key in seen:
750+
duplicates[key] = full_path
751+
else:
752+
seen.add(key)
753+
# update path
754+
if isinstance(value_node, yaml.MappingNode):
755+
self.construct_mapping(value_node, deep, full_path)
739756
if duplicates:
740-
raise DuplicateKeysInYAMLError(
741-
f"Duplicate key(s) detected in YAML file being loaded: {duplicates}"
757+
formatted_duplicates = "\n".join(
758+
[f"* {'-'.join(map(str, v))}" for v in duplicates.values()]
759+
)
760+
raise DuplicatedConfigurationError(
761+
f"Duplicate key(s) detected in YAML file being loaded:\n{formatted_duplicates}",
762+
name=f"duplicates_{'_'.join(duplicates)}",
763+
layer=None,
764+
source=None,
765+
value=None,
742766
)
743767
return super().construct_mapping(node, deep)

tests/test_ingestion.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import pytest
55

6-
from layered_config_tree import ConfigurationError, LayeredConfigTree
6+
from layered_config_tree import DuplicatedConfigurationError, LayeredConfigTree
77

88
TEST_YAML_ONE = """
99
test_section:
@@ -67,9 +67,9 @@ def test_load_yaml_duplicates(tmp_path: Path, duplicates: bool) -> None:
6767
lct = LayeredConfigTree()
6868
if duplicates:
6969
with pytest.raises(
70-
ConfigurationError,
70+
DuplicatedConfigurationError,
7171
match=re.escape(
72-
"Duplicate key(s) detected in YAML file being loaded: ['size', 'traits']"
72+
"Duplicate key(s) detected in YAML file being loaded:\n* cats-garfield-size\n* cats-garfield-traits"
7373
),
7474
):
7575
lct.update(tmp_file)

0 commit comments

Comments
 (0)