1

I'm having a hard time escaping single quotes when adding new values to yaml with ruamel.yaml.

Below is what I am doing

import sys
from ruamel.yaml import YAML

yaml_doc = """\
Mappings:
  Values:
    '123': 'no'
"""

yaml = YAML()
yaml.preserve_quotes = True
data = yaml.load(yaml_doc)

new_value = data['Mappings']['Values']
new_value.insert(len(new_value), '456','' 'no'' ', comment="New Value")
new_value.insert(len(new_value), '789',' ''no' '', comment="New Value 2")
yaml.dump(data, sys.stdout)

With this code I always get a leading or trailing space after/before the no for the values I've inserted.

Mappings:
  Values:
    '123': 'no'
    '456': 'no '  # New Value
    '789': ' no' # New Value 2

How can I insert the no value with single quotes but no trailing/heading space?

Anthon
  • 69,918
  • 32
  • 186
  • 246
André Santos
  • 47
  • 1
  • 5

3 Answers3

2

I'm not sure if I fully understood what you are asking but you just inserted 3 string for the value and concatenated them. In

new_value.insert(len(new_value), '456','' 'no'' ', comment="New Value")

The '' 'no'' ' part translates to ''=<Empty String> 'no'=no ' '=<Space>.

So if you want a regurlar 'no' just do:

new_value.insert(len(new_value), '456','no', comment="New Value")
Stphane
  • 3,368
  • 5
  • 32
  • 47
andreygold
  • 192
  • 2
  • 13
  • That doesn't give the OP `'no'` in his YAML document, as he is clearly requesting (see last line of the post), but just `no` – Anthon May 21 '19 at 12:22
  • it is easily adjustable with `new_value.insert(len(new_value), '456','\'no\'', comment="New Value")` – andreygold May 21 '19 at 12:40
  • That will actually get you `"'no'"` in your YAML document. Although those are single quotes around the `no`, I assume the OP want to have the lines look similar to the last one of his input document. – Anthon May 21 '19 at 12:44
  • Are you sure that `new_value.insert(len(new_value), '456','no', comment="New Value")` will not do what he want to? he has this flag on `yaml.preserve_quotes = True` – andreygold May 21 '19 at 12:51
  • 1
    Yes I am sure. 1) Preserving only works on stuff loaded from a YAML document (to preserve it has to be there in the first place) 2) I am quite familiar with the internals of ruamel.yaml. 3) I tried the code, then copy-pasted the outcome in my previous comment ;-) – Anthon May 21 '19 at 13:07
0

If you would try:

print(repr('' 'no'' '))

it will give you:

'no '

as what you do is juxtapose the string '' with the string 'no' and the string ' ', which Python just concatenates to 'no ' before calling print. What you are trying to do is use YAML single quotes scalar escaping mechanism in Python, and that would only work if you YAML().load() the string from YAML.

If you want a single quoted scalar in your output, just like you have in your input, you should make scalar the same type as what you get using .load(). That is ruamel.yaml.scalarstring.SingleQuotedScalarString (as string subclass).

import sys
from ruamel.yaml import YAML
from ruamel.yaml.scalarstring import SingleQuotedScalarString as sq

yaml_doc = """\
Mappings:
  Values:
    '123': 'no'
"""

yaml = YAML()
yaml.preserve_quotes = True
data = yaml.load(yaml_doc)

new_value = data['Mappings']['Values']
new_value['456'] = sq('no')
new_value.yaml_add_eol_comment(comment="New Value", key='456', column=0)
new_value.insert(len(new_value), '789', sq('no'), comment="New Value 2")
yaml.dump(data, sys.stdout)

this gives:

Mappings:
  Values:
    '123': 'no'
    '456': 'no' # New Value
    '789': 'no' # New Value 2
Anthon
  • 69,918
  • 32
  • 186
  • 246
  • You need to use the `yaml_add_eol_comment` for the first added value, otherwise you get the extra space before the comment (and `.insert` doesn't have an option to set the column). Not sure what is going on there, probably a minor bug. – Anthon May 21 '19 at 13:13
  • I don't understand why you're going through all this work to have `'no'` in your YAML vs `no`. They mean the same thing. That's why this exercise has been so difficult for you and for others to understand. In either case, you'll end up with the quantity **no**, with no quotes, when the file is parsed. If you understand YAML syntax, this all becomes very simple. – CryptoFool May 22 '19 at 00:24
  • Do you have a reason to care what is in your YAML aside from what it will produce when parsed by a proper YAML interpreter? Do you want to end up with the quantity **'no'** being read in by the YAML parser and preserved with the quotes? That won't currently happen. – CryptoFool May 22 '19 at 00:28
  • @Steve There are multiple false claims in your comment. They mean the same thing for a proper, up-to-date, YAML parse, e.g. ruamel.yaml and other YAML 1.2 parsers. Although the spec has been out for ten years, some people and projects still use parsers based on the YAML 1.1 spec and for those `no` is loads as a boolean, and `'no'` a string. Preserving such quotes on load-modify-dump, whether for aesthetics or to prevent spurious changes in a file under revision control, that *will current happen* if you use ruamel.yaml. – Anthon May 22 '19 at 04:24
  • @Anthon, these are good points. There's no false claims in my comments though with regard to the current YAML spec. I did make the mistake of not allowing that older versions of YAML might be different. Thanks for educating me there. I still think jumping through hoops to write a simple YAML file this way looks like a code smell. And as for checking in a generated YAML file, that violates the DRY principle. But in none of this do I not allow that there may be good reasons to do all this. Hence my second comment , which was a question, and you're sorta answering that. – CryptoFool May 22 '19 at 22:51
  • Note that the OP does not say if he requires YAML that supports an outdated parser. He may in fact be misinformed as to the difference between the two formats (or rather, the lack thereof), and so saying "don't worry about the quotes" might be just the right answer. Who knows. I was just offering a different perspective besides blindly writing a bunch of hard-to-understand code to produce something of questionable purrpose. – CryptoFool May 22 '19 at 22:53
0

UPDATE: I was informed by @Anthon that earlier versions of YAML would treat no and 'no' as different values, considering no to be a boolean value. So please only take what I say below as applying to the current version of YAML. Also, maybe there's another motivation here beyond writing YAML that will produce a particular result from an up to date parser. In that case, you have a good answer above from @Anthon as to how to force those quotes into the resulting YAML via a writer that can't take the older YAML format into account (I'd suggest making sure that there is no such mode in the writer).

YAML is about conveying a particular data structure, with dictionaries, lists and values. When YAML is parsed, the resulting data structure is all you care about. If two YAML files are different but produce the same data structure, the differences shouldn't matter to you.

Here, I'm not sure what the OP wants. It appears that he either wants the word no to end up a few times in the data structure that his YAML represents, or maybe he wants 'no' to be the value that ends up in the data structure. Here are the most concise ways of writing each of those (in YAML 1.2):

no == no

'no' == "'no'"

There is no case here where the characters 'no' need to show up in the YAML in the way the OP is trying to force. All of this extra code is trying to force those characters into the YAML when there's no need to do so. That construct means the same thing as no. Just forget about the quotes.

CryptoFool
  • 21,719
  • 5
  • 26
  • 44