How to prevent redefinition of keys in YAML?

Is there a way to make yaml.load an exception whenever a given key appears more than once in a single dictionary?

For example, parsing the following YAML will throw an exception because some_key appears twice:

 { some_key: 0, another_key: 1, some_key: 1 } 

Actually, the behavior described above corresponds to the simplest policy regarding key overrides. A somewhat more complicated policy could, for example, indicate that only overrides that change the value assigned to a key will result in an exception or may allow setting the severity level of the key override to a "warning" rather than an "error," etc. An ideal answer to this question will be able to support such options.

+6
source share
2 answers

If you want the bootloader to throw an error, you just need to define your own bootloader using a constructor that checks if the key is in the ยน mapping:

 import collections import ruamel.yaml as yaml from ruamel.yaml.reader import Reader from ruamel.yaml.scanner import Scanner from ruamel.yaml.parser_ import Parser from ruamel.yaml.composer import Composer from ruamel.yaml.constructor import Constructor from ruamel.yaml.resolver import Resolver from ruamel.yaml.nodes import MappingNode from ruamel.yaml.compat import PY2, PY3 class MyConstructor(Constructor): def construct_mapping(self, node, deep=False): if not isinstance(node, MappingNode): raise ConstructorError( None, None, "expected a mapping node, but found %s" % node.id, node.start_mark) mapping = {} for key_node, value_node in node.value: # keys can be list -> deep key = self.construct_object(key_node, deep=True) # lists are not hashable, but tuples are if not isinstance(key, collections.Hashable): if isinstance(key, list): key = tuple(key) if PY2: try: hash(key) except TypeError as exc: raise ConstructorError( "while constructing a mapping", node.start_mark, "found unacceptable key (%s)" % exc, key_node.start_mark) else: if not isinstance(key, collections.Hashable): raise ConstructorError( "while constructing a mapping", node.start_mark, "found unhashable key", key_node.start_mark) value = self.construct_object(value_node, deep=deep) # next two lines differ from original if key in mapping: raise KeyError mapping[key] = value return mapping class MyLoader(Reader, Scanner, Parser, Composer, MyConstructor, Resolver): def __init__(self, stream): Reader.__init__(self, stream) Scanner.__init__(self) Parser.__init__(self) Composer.__init__(self) MyConstructor.__init__(self) Resolver.__init__(self) yaml_str = """\ some_key: 0, another_key: 1, some_key: 1 """ data = yaml.load(yaml_str, Loader=MyLoader) print(data) 

and this raises a KeyError .

Note that the curly braces used in your example are not needed.

I'm not sure if this will work with key merging .


ยน This was done using ruamel.yaml , of which I am the author. ruamel.yaml extended version of PyYAML, and the loader code for the latter should be similar.

+1
source

Here's the equivalent code from Anthon if you are using pyyaml:

 import collections import yaml import sys from yaml.reader import Reader from yaml.scanner import Scanner from yaml.parser import Parser from yaml.composer import Composer from yaml.constructor import Constructor, ConstructorError from yaml.resolver import Resolver from yaml.nodes import MappingNode class NoDuplicateConstructor(Constructor): def construct_mapping(self, node, deep=False): if not isinstance(node, MappingNode): raise ConstructorError( None, None, "expected a mapping node, but found %s" % node.id, node.start_mark) mapping = {} for key_node, value_node in node.value: # keys can be list -> deep key = self.construct_object(key_node, deep=True) # lists are not hashable, but tuples are if not isinstance(key, collections.Hashable): if isinstance(key, list): key = tuple(key) if sys.version_info.major == 2: try: hash(key) except TypeError as exc: raise ConstructorError( "while constructing a mapping", node.start_mark, "found unacceptable key (%s)" % exc, key_node.start_mark) else: if not isinstance(key, collections.Hashable): raise ConstructorError( "while constructing a mapping", node.start_mark, "found unhashable key", key_node.start_mark) value = self.construct_object(value_node, deep=deep) # Actually do the check. if key in mapping: raise KeyError("Got duplicate key: {!r}".format(key)) mapping[key] = value return mapping class NoDuplicateLoader(Reader, Scanner, Parser, Composer, NoDuplicateConstructor, Resolver): def __init__(self, stream): Reader.__init__(self, stream) Scanner.__init__(self) Parser.__init__(self) Composer.__init__(self) NoDuplicateConstructor.__init__(self) Resolver.__init__(self) yaml_str = """\ some_key: 0, another_key: x: 1 """ data = yaml.load(yaml_str, Loader=NoDuplicateLoader) print(data) 
+1
source

All Articles