Se si desidera che il loader per lanciare un errore, allora si dovrebbe solo definire il proprio caricatore, con un costruttore che controlla se la chiave è già nella ¹ mappatura:
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)
e che getta una KeyError
.
Si noti che le parentesi graffe che si utilizzano nell'esempio non sono necessarie.
Non sono sicuro se questo funzionerà con merge keys.
¹ Ciò è stato fatto utilizzando ruamel.yaml di cui sono l'autore. ruamel.yaml
una versione avanzata di PyYAML e il codice del caricatore per quest'ultimo deve essere simile.
Eh, sembra che PyYAML dovrebbe già fare questo (chiavi duplicate non sono consentite dalle specifiche YAML) e che doesn in realtà è [un bug che è stato aperto per gli ultimi sette anni] (http://pyyaml.org/ticket/128). –
Quel biglietto è stato migrato [qui] (https://github.com/yaml/pyyaml/issues/41). Ancora aperto. sadpanda.jpg – wim