fde014c987
That was a constant paper cut for me: every time someone uses edit-deliverables it may result in release highlights being reformatted to create lines over 80 char. You can see that happening at: https://review.openstack.org/#/c/650911/1/deliverables/rocky/cloudkitty.yaml where it messes up perfectly-good yaml and turns it into 88-char-long lines. I experimented with various settings (you would think width=80 would do, but no). Forcing dump width at 66 characters seems to produce the desired results. Change-Id: I83934a0f7d6eeaec47b5dcb1a6bfb8c53ed65799
132 lines
4.6 KiB
Python
132 lines
4.6 KiB
Python
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import collections
|
|
import re
|
|
|
|
import six
|
|
import yaml
|
|
import yamlordereddictloader
|
|
|
|
|
|
_LIKE_A_NUMBER = re.compile('^[0-9]+.[0-9]+$')
|
|
|
|
|
|
def _has_newline(data):
|
|
if "\n" in data or "\r" in data:
|
|
return True
|
|
return False
|
|
|
|
|
|
class PrettySafeDumper(yaml.dumper.SafeDumper):
|
|
"""Yaml dumper that tries to not alter original formats (too much)."""
|
|
|
|
BINARY_ENCODING = 'utf-8'
|
|
|
|
def represent_ordereddict(self, data):
|
|
values = []
|
|
node = yaml.nodes.MappingNode(
|
|
'tag:yaml.org,2002:map', values, flow_style=None)
|
|
for key, value in data.items():
|
|
key_item = self.represent_data(key)
|
|
value_item = self.represent_data(value)
|
|
values.append((key_item, value_item))
|
|
return node
|
|
|
|
def ignore_aliases(self, data):
|
|
# Never output alias references; always repeat the data.
|
|
return True
|
|
|
|
def represent_bool(self, data):
|
|
if data:
|
|
value = 'yes'
|
|
else:
|
|
value = 'no'
|
|
return self.represent_scalar('tag:yaml.org,2002:bool', value)
|
|
|
|
def choose_scalar_style(self):
|
|
# Avoid messing up dict keys...
|
|
if self.states[-1] == self.expect_block_mapping_simple_value:
|
|
self.event.style = 'plain'
|
|
return super(PrettySafeDumper, self).choose_scalar_style()\
|
|
if self.event.style != 'plain' else ("'" if ' ' in
|
|
self.event.value else None)
|
|
|
|
def represent_string(self, data):
|
|
if isinstance(data, six.binary_type):
|
|
data = data.decode(self.BINARY_ENCODING)
|
|
style = "plain"
|
|
if _has_newline(data):
|
|
style = "|"
|
|
elif _LIKE_A_NUMBER.match(data):
|
|
style = '"'
|
|
return yaml.representer.ScalarNode('tag:yaml.org,2002:str',
|
|
data, style=style)
|
|
|
|
def represent_undefined(self, data):
|
|
if isinstance(data, collections.OrderedDict):
|
|
return self.represent_odict(data)
|
|
else:
|
|
return super(PrettySafeDumper, self).represent_undefined(data)
|
|
|
|
# Override this method to always indent. Otherwise when a list is
|
|
# emitted, the items nested under it are not and we have inconsistent
|
|
# formatting.
|
|
# https://stackoverflow.com/questions/25108581/python-yaml-dump-bad-indentation
|
|
def increase_indent(self, flow=False, indentless=False):
|
|
return super(PrettySafeDumper, self).increase_indent(flow, False)
|
|
|
|
|
|
# NOTE(harlowja): at some point this may not be needed...
|
|
# See: http://pyyaml.org/ticket/29
|
|
PrettySafeDumper.add_representer(collections.OrderedDict,
|
|
PrettySafeDumper.represent_ordereddict)
|
|
PrettySafeDumper.add_representer(None,
|
|
PrettySafeDumper.represent_undefined)
|
|
|
|
# NOTE(dhellmann): The representer functions in the base class are
|
|
# specified by class.method-name so we have to re-register the
|
|
# representer for bool if we want to override it.
|
|
PrettySafeDumper.add_representer(bool,
|
|
PrettySafeDumper.represent_bool)
|
|
|
|
|
|
# Ensure we use our own routine here, because the style that comes by
|
|
# default is sort of wonky and messes up the values....
|
|
for str_type in [six.binary_type, six.text_type]:
|
|
PrettySafeDumper.add_representer(str_type,
|
|
PrettySafeDumper.represent_string)
|
|
|
|
|
|
def dumps(obj):
|
|
"""Dump a python object -> blob and apply our pretty styling."""
|
|
buff = six.StringIO()
|
|
yaml.dump_all([obj], buff,
|
|
explicit_start=True,
|
|
indent=2,
|
|
default_flow_style=False,
|
|
line_break="\n",
|
|
width=66,
|
|
Dumper=PrettySafeDumper,
|
|
allow_unicode=True)
|
|
return buff.getvalue()
|
|
|
|
|
|
def loads(blob):
|
|
"""Load a yaml blob and retain key ordering."""
|
|
# This does use load, which is unsafe, but should be ok
|
|
# for what we are loading here in this program; we should
|
|
# be able to fix that in the future (if it matters).
|
|
return yaml.load(blob, Loader=yamlordereddictloader.Loader)
|