File tree Expand file tree Collapse file tree 3 files changed +57
-1
lines changed
Expand file tree Collapse file tree 3 files changed +57
-1
lines changed Original file line number Diff line number Diff line change 3636
3737def load_yaml (source : str | Path ) -> t .Dict :
3838 try :
39- return load (source , render_jinja = False )
39+ return load (
40+ source , render_jinja = False , allow_duplicate_keys = True , keep_last_duplicate_key = True
41+ )
4042 except DuplicateKeyError as ex :
4143 raise ConfigError (f"{ source } : { ex } " if isinstance (source , Path ) else f"{ ex } " )
4244
Original file line number Diff line number Diff line change 88from pathlib import Path
99
1010from ruamel import yaml
11+ from ruamel .yaml .constructor import SafeConstructor
1112
1213from sqlmesh .core .constants import VAR
1314from sqlmesh .utils .errors import SQLMeshError
@@ -32,12 +33,30 @@ def YAML(typ: t.Optional[str] = "safe") -> yaml.YAML:
3233 return yaml_obj
3334
3435
36+ class SafeConstructorOverride (SafeConstructor ):
37+ def check_mapping_key (
38+ self ,
39+ node : t .Any ,
40+ key_node : t .Any ,
41+ mapping : t .Any ,
42+ key : t .Any ,
43+ value : t .Any ,
44+ ) -> bool :
45+ """This function normally returns True if key is unique.
46+
47+ It is only used by the construct_mapping function. By always returning True,
48+ keys will always be updated and so the last value will be kept for mappings.
49+ """
50+ return True
51+
52+
3553def load (
3654 source : str | Path ,
3755 raise_if_empty : bool = True ,
3856 render_jinja : bool = True ,
3957 allow_duplicate_keys : bool = False ,
4058 variables : t .Optional [t .Dict [str , t .Any ]] = None ,
59+ keep_last_duplicate_key : bool = False ,
4160) -> t .Dict :
4261 """Loads a YAML object from either a raw string or a file."""
4362 path : t .Optional [Path ] = None
@@ -56,6 +75,8 @@ def load(
5675 )
5776
5877 yaml = YAML ()
78+ if allow_duplicate_keys and keep_last_duplicate_key :
79+ yaml .Constructor = SafeConstructorOverride
5980 yaml .allow_duplicate_keys = allow_duplicate_keys
6081 contents = yaml .load (source )
6182 if contents is None :
Original file line number Diff line number Diff line change @@ -45,3 +45,36 @@ def test_yaml() -> None:
4545
4646 decimal_value = Decimal (123.45 )
4747 assert yaml .load (yaml .dump (decimal_value )) == str (decimal_value )
48+
49+
50+ def test_load_keep_last_duplicate_key () -> None :
51+ input_str = """
52+ name: first_name
53+ name: second_name
54+ name: third_name
55+
56+ foo: bar
57+
58+ mapping:
59+ key: first_value
60+ key: third_value
61+
62+ sequence:
63+ - one
64+ - two
65+ """
66+ # Default behavior of ruamel is to keep the first key encountered
67+ assert yaml .load (input_str , allow_duplicate_keys = True ) == {
68+ "name" : "first_name" ,
69+ "foo" : "bar" ,
70+ "mapping" : {"key" : "first_value" },
71+ "sequence" : ["one" , "two" ],
72+ }
73+
74+ # Test keeping last key
75+ assert yaml .load (input_str , allow_duplicate_keys = True , keep_last_duplicate_key = True ) == {
76+ "name" : "third_name" ,
77+ "foo" : "bar" ,
78+ "mapping" : {"key" : "third_value" },
79+ "sequence" : ["one" , "two" ],
80+ }
You can’t perform that action at this time.
0 commit comments