I wish to set some specific key values, all of which are lists, to be flow style and everything else to block style.
I can see that I can set the style of any specific CommentedSeq via CommentedSeq.fa.set_flow_style() which could be done post-loading the yaml data but I'd rather set up a custom representer to handle this.
The following function seems to work post-loading:
from ruamel.yaml import YAML
from ruamel.yamlments import CommentedMap, CommentedSeq
import sys
# Keys to enforce flow style for
keys_with_flow_style = ["categories", "classification"]
def set_flow_style_recursive(yamldata, flowkeys):
if isinstance(yamldata, (dict, CommentedMap)):
for k,v in yamldata.items():
if k in flowkeys:
if isinstance(v, (dict, CommentedMap)):
set_flow_style_recursive(v, flowkeys)
elif isinstance(v, (CommentedSeq)):
v.fa.set_flow_style()
set_flow_style(v, flowkeys)
elif isinstance(v, (dict, CommentedMap)):
set_flow_style_recursive(v, flowkeys)
elif isinstance(yamldata, (list, CommentedSeq)):
for item in yamldata:
set_flow_style_recursive(item, flowkeys)
yaml_data = """
data:
categories:
- CLNT
- TXN
- MKDT
- REFD
- PERS
- CODE
classification:
- RES
description: This application processes data
"""
yaml = YAML().load(yaml_data)
set_flow_style_recursive(yaml, keys_with_flow_style)
YAML().dump(yaml, syst.stdout)
It prints :
"""
data:
categories: [CLNT, TXN, MKDT, REFD, PERS, CODE]
classification: [RES]
description: This application processes data
"""
What I can't figure out is how to turn this into a Representer so the flow style is applied to specific key-values automatically on loading (and by default everything else is block style). Is anyone here familiar with that?
I wish to set some specific key values, all of which are lists, to be flow style and everything else to block style.
I can see that I can set the style of any specific CommentedSeq via CommentedSeq.fa.set_flow_style() which could be done post-loading the yaml data but I'd rather set up a custom representer to handle this.
The following function seems to work post-loading:
from ruamel.yaml import YAML
from ruamel.yaml.comments import CommentedMap, CommentedSeq
import sys
# Keys to enforce flow style for
keys_with_flow_style = ["categories", "classification"]
def set_flow_style_recursive(yamldata, flowkeys):
if isinstance(yamldata, (dict, CommentedMap)):
for k,v in yamldata.items():
if k in flowkeys:
if isinstance(v, (dict, CommentedMap)):
set_flow_style_recursive(v, flowkeys)
elif isinstance(v, (CommentedSeq)):
v.fa.set_flow_style()
set_flow_style(v, flowkeys)
elif isinstance(v, (dict, CommentedMap)):
set_flow_style_recursive(v, flowkeys)
elif isinstance(yamldata, (list, CommentedSeq)):
for item in yamldata:
set_flow_style_recursive(item, flowkeys)
yaml_data = """
data:
categories:
- CLNT
- TXN
- MKDT
- REFD
- PERS
- CODE
classification:
- RES
description: This application processes data
"""
yaml = YAML().load(yaml_data)
set_flow_style_recursive(yaml, keys_with_flow_style)
YAML().dump(yaml, syst.stdout)
It prints :
"""
data:
categories: [CLNT, TXN, MKDT, REFD, PERS, CODE]
classification: [RES]
description: This application processes data
"""
What I can't figure out is how to turn this into a Representer so the flow style is applied to specific key-values automatically on loading (and by default everything else is block style). Is anyone here familiar with that?
Share Improve this question edited Jan 20 at 9:19 pinkfloydfan asked Jan 20 at 8:48 pinkfloydfanpinkfloydfan 534 bronze badges 1 |1 Answer
Reset to default 0Representing in YAML is used to go from data to YAML, i.e. during dumping. You indicate you want to do this during loading, so that would require an alternative constructor.
The alternative constructor would have to be for the YAML mapping (to Python dict), as during construction of the list from the YAML sequence, you don't have the information about whether that list is going to be a value for a dictionary key (at least not without adjusting the mapping constructor as well).
IMO the best place to put this is in the method creating the Python dict: RoundTripConstructor.construct_yaml_map()
which is relatively small. You could alternativey change the method .construct_mapping()
it uses,
which would prevent you from having to run over the keys of the dict afterwards to find a match. But that
method is much longer, resulting in more copied code (and a greater chance it changes with a new release).
import sys
import ruamel.yaml
yaml_str = """\
data:
categories:
- CLNT
- TXN
- MKDT
- REFD
- PERS
- CODE
classification:
- RES
description: This application processes data
"""
class FlowStyleValueCreator:
def __init__(self, *args):
self._flow_style_value_keys = args
ruamel.yaml.constructor.RoundTripConstructor.add_constructor('tag:yaml.org,2002:map', self.construct_yaml_map)
def construct_yaml_map(self, constructor, node):
data = ruamel.yaml.comments.CommentedMap()
data._yaml_set_line_col(node.start_mark.line, node.start_mark.column)
yield data
constructor.construct_mapping(node, data, deep=True)
for k, v in data.items():
if k in self._flow_style_value_keys and isinstance(v, ruamel.yaml.comments.CommentedSeq):
v.fa.set_flow_style()
constructor.set_collection_style(data, node)
fsvc = FlowStyleValueCreator('categories', 'classification')
yaml = ruamel.yaml.YAML()
yaml.indent(sequence=4, offset=2)
yaml.preserve_quotes = True
data = yaml.load(yaml_str)
yaml.dump(data, sys.stdout)
which gives:
data:
categories: [CLNT, TXN, MKDT, REFD, PERS, CODE]
classification: [RES]
description: This application processes data
Which looks like your output except for the surrounding tripe-quotes (which I think your code does not actually print).
The yaml.indent()
is there to keep the extra indentation your block style sequences have (in case they are not forced to flow style).
The yield
in construct_yaml_map()
is there to handle self-recursive data structures (using YAML anchors/aliases)
list
,dict
) added by the program default to block style (when usingYAML()
orYAML(typ='rt')
) – Anthon Commented Jan 20 at 13:38