Krin123
Krin123

Reputation: 343

Force quotes in yaml.dump Python

Before I go in, yes I've looked into it already and know that YAML doesn't differentiate quotes and non-quotes and just takes the types as they are. Frankly I think it's nicer without the quotes, but unfortunately that's not what I need. So please try and understand that I have looked into the matter but still do need the quotes around my string objects.

In my code I have a dictionary:

data = {dic:[{A:''}, {B:''}, {C:''}, {D:''},...]}

the 'var*' are constructed through the PyQt4 QLineEdit class which the line of code extract the line looks like this:

var* = str(QtGui.QLineEdit().displayText())

So when I do data['dic'][index]['A'-'Z'] = var*

it becomes data = {dic:[{A: 'var1'}, {B:'var2'}, {C:'var3'}, {D:'var4'},...]}

then I dump all my data:

prettyData = yaml.dump(data, default_flow_style=False, width=10000000)

and print prettyData I get this:

dic:
  - A: var1
  - B: var2
  - C: var3
  - D: var4
  ...

I've tried many things to make them appear:

dic:
  - A: 'var1'
  - B: 'var2'
  - C: 'var3'
  - D: 'var4'
  ...

but haven't been successful yet. I've heard different opinions on the matter ranging from "not possible" to "just put quotes around them", which as you can see, in my case, I cannot do.

Any ideas on how to go about this?

Upvotes: 4

Views: 7663

Answers (2)

RMT
RMT

Reputation: 21

#!/usr/bin/env python2
# quoting_example.py
import yaml
import yaml.representer

def literal_presenter(dumper, data):
  if isinstance(data, str) and "\n" in data:
      return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|')
  return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='"')

yaml.add_representer(str, literal_presenter)
print yaml.dump({"key": "value", "multiline": "foo\nbar"}, default_flow_style=False)

And the result is:

$ python2 quoting_example.py
"key": "value"
"multiline": |-
  foo
  bar

Similarly, you can add representers for unicode, dicts (think sorted dicts), etc.

Upvotes: 2

Anthon
Anthon

Reputation: 76862

You can override the emitter for scalars and change the style of the values emitted on the fly. Depending on the rest of scalar values that you have, you might need to do some more tests to unset is_string. By the time process_scalar is called you don't know the original value any more, you just have an event with a (unicode) string value.

import sys
import ruamel.yaml as yaml

yaml_str = """\
dic:
  - A: var1    # need to get these strings quoted
  - B: var2
  - C: var3
  - D: var4
  - E: 3       # leave this alone
"""

# the scalar emitter from emitter.py
def process_scalar(self):
    if self.analysis is None:
        self.analysis = self.analyze_scalar(self.event.value)
    if self.style is None:
        self.style = self.choose_scalar_style()
    split = (not self.simple_key_context)
    # VVVVVVVVVVVVVVVVVVVV added
    if split:  # not a key
        is_string = True
        if self.event.value and self.event.value[0].isdigit():
            is_string = False
        # insert extra tests for scalars that should not be ?
        if is_string:
            self.style = "'"
    # ^^^^^^^^^^^^^^^^^^^^
    # if self.analysis.multiline and split    \
    #         and (not self.style or self.style in '\'\"'):
    #     self.write_indent()
    if self.style == '"':
        self.write_double_quoted(self.analysis.scalar, split)
    elif self.style == '\'':
        self.write_single_quoted(self.analysis.scalar, split)
    elif self.style == '>':
        self.write_folded(self.analysis.scalar)
    elif self.style == '|':
        self.write_literal(self.analysis.scalar)
    else:
        self.write_plain(self.analysis.scalar, split)
    self.analysis = None
    self.style = None
    if self.event.comment:
        self.write_post_comment(self.event)

data = yaml.load(yaml_str, Loader=yaml.RoundTripLoader)
dd = yaml.RoundTripDumper
dd.process_scalar = process_scalar
yaml.dump(data, sys.stdout, Dumper=dd)

and get as output¹:

dic:
- A: 'var1'    # need to get these strings quoted
- B: 'var2'
- C: 'var3'
- D: 'var4'
- E: 3         # leave this alone

¹ This was done using ruamel.yaml of which I am the author. That package is an enhancement of PyYAML. You might be able to do something similar with the latter, but it would drop the comments that are in the input.

Upvotes: 2

Related Questions