You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Psych does not complain or raise an error when it encounters a duplicate key. Instead, it happily overwrites the previous value of the key. This is in contrast to the YAML spec, which maintains that the keys must be unique.
Well, Psych maintains the behavior that Syck had for this. Until now, nobody has had a problem with the way it works. I don't mind changing it, but it will not be backwards compatible.
Got it. I don't suppose Psych already has a place for configuration options, does it? I don't see one. Would that be a desirable approach? Then, for example, a Rails app in development mode could be more strict/fragile about the YAML than a production deployment.
So according to this tweet[0], we should add an option to enforce this behaviour (maybe the option is called 'strict').
In my opinion, with this option enabled we should raise a Psych::SyntaxError if we encounter the duplicate keys. This error is already a known error that can be raised on Psych.parse[1].
Psych has a number of interfaces that we would want this behaviour added to (cribbed from the docs[2]):
Right now none of these methods take an options hash. We can expand the signature with an optional options hash. I think this the right course of action, does anybody have any other suggestion?
Also, consider that the generation of YAML already has an options hash (in Psych.dump, but not Psych.dump_stream), so this isn't an unexpected behaviour, in my opinion.
@Nitrodist 👍 I think it's good. For a major release, I think we should change the filename to be an option like Psych.load(yaml, filename: name, strict: true) (but that's for a breaking major release). For now, lets add the options hash.
What is the state of this ? I don't really mind or care about the default but there should be a way to raise an error on duplicate keys, the consequences of a duplicate key are rather harsh since the first one will simply be entirely overwritten.
Reacted by 23tux, Tim Spriggs, Jordan Atwood, Alex Harvey, Eliott Wiener, Greg Campbell, Harry Lascelles, Mark Harviston, native-api and fraul
If you use a hash instead of a list then the performance shouldn't suffer nearly as much:
+ def end_mapping
+ mapping = pop
+ keys = {}
+ mapping.children.each_slice(2) do |(key_scalar, _)|
+ next if key_scalar.is_a?(Psych::Nodes::Sequence) or key_scalar.is_a?(Psych::Nodes::Alias) or key_scalar.is_a?(Psych::Nodes::Mapping)
+ key = key_scalar.value
+ raise Psych::Exception, "Same key exists on this level" if keys.key? key
+ keys[key] = nil
+ end
+ mapping
+ end
@tenderlove you mentioned backwards compatibility...
Instead raising an error, just (optionally) printing out a warning wouldn't brake backwards compatibility but help many people manage their yaml files. What do you think?
Just wasted 2 days investigating this very issue after a developer accidentally forgot to rename a job that was copy/pasted. Any thoughts on just printing a message as @wteuber suggests? Would save a lot of time - especially when multiple developers are working on automation.
http://www.yamllint.com/ also has the same problem: it doesn't report duplicate keys and instead silently deletes all but the last one and reports YAML as "valid".
Since multiple keys will likely be supported by default, permanently, I think there should be a guarantee about the order they will be processed. It seems to use a "first come, first served" model rather than an override model, i.e. the first key encountered is the one that's used, with subsequent duplicates being discarded rather than acting as overrides.
Would it make sense to document this and add a test to ensure it remains stable?
While I realise it's not strictly YAML-compliant, it's actually useful for merging YAMLs from multilpe sources, so if it's in the library anyway, may as well make it work deterministically.
That this is not an error is problematic, especially in the context of merge keys. Gitlab (working with Ruby for YAML) seems to allow this because of this bug, and the effect when mulitple mapped in merges have the same key (but different values) was allowed but impossible for me to trace what it means. I.e. is
<<: *a
<<: *b
the same as <<: [*a, *b] or as <<: [*b, *a] or as <<: *b or as <<: *a ? These all have different results depending on the keys in the mappings &a and &b and the mapping in which things are merged.
As pretty much anyone that has arrived here, I also have been bitten by this default behavior.
Working with a large Rails yml translation file, I didn't realize I declared a key that was already present in a lower part in the same file.
Couldn't understand why it wasn't loading properly. Took me a long time debugging the ActiveModel and i18n source code directly until I finally realized my mistake 😕
I'd love to see this feature implemented, either with a warning or an error, and IMHO Rails should even default to raising an error for i18n translation files in the future.
Reacted by Anthon van der Neut and Camille Goudeseune
Activity
tenderlove commentedon Aug 24, 2012
Well, Psych maintains the behavior that Syck had for this. Until now, nobody has had a problem with the way it works. I don't mind changing it, but it will not be backwards compatible.
rehevkor5 commentedon Aug 24, 2012
Got it. I don't suppose Psych already has a place for configuration options, does it? I don't see one. Would that be a desirable approach? Then, for example, a Rails app in development mode could be more strict/fragile about the YAML than a production deployment.
Nitrodist commentedon May 20, 2014
It's been about 2 years since this was brought up. Let's do it.
Nitrodist commentedon May 20, 2014
So according to this tweet[0], we should add an option to enforce this behaviour (maybe the option is called 'strict').
In my opinion, with this option enabled we should raise a
Psych::SyntaxErrorif we encounter the duplicate keys. This error is already a known error that can be raised onPsych.parse[1].Psych has a number of interfaces that we would want this behaviour added to (cribbed from the docs[2]):
(Object) Pysch.load(yaml, filename = nil)(Object) Psych.load_documents(yaml, &block)(Object) Psych.load_file(filename)(Object) Psych.load_stream(yaml, filename = nil)(Object) Psych.parse(yaml, filename = nil)(Object) Psych.parse_file(filename)(Object) Psych.parse_stream(yaml, filename = nil, &block)Right now none of these methods take an options hash. We can expand the signature with an optional options hash. I think this the right course of action, does anybody have any other suggestion?
Also, consider that the generation of YAML already has an options hash (in
Psych.dump, but notPsych.dump_stream), so this isn't an unexpected behaviour, in my opinion.[0] - https://twitter.com/tenderlove/status/468781805940641793
[1] - http://rubydoc.info/stdlib/psych/1.9.3/Psych#parse-class_method
[2] - http://rubydoc.info/stdlib/psych/1.9.3/Psych
Nitrodist commentedon May 20, 2014
Basically I'm proposing changing from this:
to this:
(and on all the other methods listed above)
tenderlove commentedon May 20, 2014
@Nitrodist 👍 I think it's good. For a major release, I think we should change the filename to be an option like
Psych.load(yaml, filename: name, strict: true)(but that's for a breaking major release). For now, lets add the options hash.Nitrodist commentedon May 20, 2014
We're having a hacknight tomorrow in Toronto. I'm hoping to work on a solution with @nwjsmith and have it ready by Thursday. 👐
perryventas commentedon Sep 1, 2015
@Nitrodist What was the outcome of your hacking night? 😄
Nitrodist commentedon Sep 17, 2015
@coin3d I was able to get it to 'work' but its efficiency left much to be desired. This is what I had:
wteuber commentedon Jun 24, 2016
I would have liked a warning or error message like this... I now used https://github.com/adrienverge/yamllint for checking for duplicate keys.
schmurfy commentedon Sep 5, 2017
What is the state of this ? I don't really mind or care about the default but there should be a way to raise an error on duplicate keys, the consequences of a duplicate key are rather harsh since the first one will simply be entirely overwritten.
imoverclocked commentedon Oct 26, 2017
If you use a hash instead of a list then the performance shouldn't suffer nearly as much:
any takers for testing and a PR?
alex-harvey-z3q commentedon Mar 23, 2018
Is this likely to be implemented any time soon?
wteuber commentedon Mar 25, 2018
@tenderlove you mentioned backwards compatibility...
Instead raising an error, just (optionally) printing out a warning wouldn't brake backwards compatibility but help many people manage their yaml files. What do you think?
imaginejosephishak commentedon Apr 23, 2019
Just wasted 2 days investigating this very issue after a developer accidentally forgot to rename a job that was copy/pasted. Any thoughts on just printing a message as @wteuber suggests? Would save a lot of time - especially when multiple developers are working on automation.
wteuber commentedon Apr 23, 2019
I helped programmatically avoiding this issue by writing and introducing a normalized YAML format in the projects I am involved in. Maybe you find this helpful, too: https://github.com/Sage/yaml_normalizer
@imaginejosephishak
native-api commentedon Jul 23, 2019
Travis CI user has had a problem because of the current behavior: https://travis-ci.community/t/build-no-longer-installing-apt-packages-ignoring-config/4306
native-api commentedon Jul 23, 2019
http://www.yamllint.com/ also has the same problem: it doesn't report duplicate keys and instead silently deletes all but the last one and reports YAML as "valid".
mahemoff commentedon Sep 15, 2019
Since multiple keys will likely be supported by default, permanently, I think there should be a guarantee about the order they will be processed. It seems to use a "first come, first served" model rather than an override model, i.e. the first key encountered is the one that's used, with subsequent duplicates being discarded rather than acting as overrides.
Would it make sense to document this and add a test to ensure it remains stable?
While I realise it's not strictly YAML-compliant, it's actually useful for merging YAMLs from multilpe sources, so if it's in the library anyway, may as well make it work deterministically.
AvdN commentedon Dec 2, 2021
That this is not an error is problematic, especially in the context of merge keys. Gitlab (working with Ruby for YAML) seems to allow this because of this bug, and the effect when mulitple mapped in merges have the same key (but different values) was allowed but impossible for me to trace what it means. I.e. is
the same as
<<: [*a, *b]or as<<: [*b, *a]or as<<: *bor as<<: *a? These all have different results depending on the keys in the mappings&aand&band the mapping in which things are merged.FWIW, PyYAML overwrites a key's value with any value with the same that occur later in the document text in a mapping.
https://github.com/yaml/pyyaml/blob/8cdff2c80573b8be8e8ad28929264a913a63aa33/lib/yaml/constructor.py#L132
matsubo commentedon Jun 2, 2022
I strongly recommend to implement this issue because I encountered issue related to this implementation.
Related issue
I encountered following issue.
psych.Reasons to implement
feliperaul commentedon Oct 23, 2023
As pretty much anyone that has arrived here, I also have been bitten by this default behavior.
Working with a large Rails yml translation file, I didn't realize I declared a key that was already present in a lower part in the same file.
Couldn't understand why it wasn't loading properly. Took me a long time debugging the ActiveModel and i18n source code directly until I finally realized my mistake 😕
I'd love to see this feature implemented, either with a warning or an error, and IMHO Rails should even default to raising an error for i18n translation files in the future.
wteuber commentedon Oct 30, 2023
I helped programmatically avoiding this issue by introducing normalized YAML files in projects I am involved in. Maybe you find this helpful, too: https://github.com/Sage/yaml_normalizer
@feliperaul