From 19b6d3589a285031e410aa08e09655c60f5e17a4 Mon Sep 17 00:00:00 2001 From: Todd Gamblin Date: Thu, 8 Apr 2021 07:37:16 -0700 Subject: bugfix: `spack config blame` should print all lines of config (#22598) * bugfix: fix representation of null in spack_yaml output Nulls were previously printed differently by `spack config blame config` and `spack config get config`. Fix this in the `spack_yaml` dumpers. * bugfix: `spack config blame` should print all lines of config `spack config blame` was not printing all lines of configuration because there were no annotations for empty lines in the YAML dump output. Fix this by removing empty lines. --- lib/spack/spack/test/util/spack_yaml.py | 28 ++++++++++++++++++++++++++++ lib/spack/spack/util/spack_yaml.py | 23 ++++++++++++++++------- 2 files changed, 44 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/spack/spack/test/util/spack_yaml.py b/lib/spack/spack/test/util/spack_yaml.py index 530226ad8d..34a923093f 100644 --- a/lib/spack/spack/test/util/spack_yaml.py +++ b/lib/spack/spack/test/util/spack_yaml.py @@ -65,3 +65,31 @@ def test_config_blame_with_override(config): check_blame('verify_ssl', config_file, 13) check_blame('checksum', config_file, 14) check_blame('dirty', config_file, 15) + + +def test_config_blame_defaults(): + """check blame for an element from an override scope""" + files = {} + + def get_file_lines(filename): + if filename not in files: + with open(filename, "r") as f: + files[filename] = [""] + f.read().split("\n") + return files[filename] + + config_blame = config_cmd("blame", "config") + for line in config_blame.split("\n"): + # currently checking only simple lines with dict keys + match = re.match(r"^([^:]+):(\d+)\s+([^:]+):\s+(.*)", line) + + # check that matches are on the lines they say they are + if match: + filename, line, key, val = match.groups() + line = int(line) + + if val.lower() in ("true", "false"): + val = val.lower() + + lines = get_file_lines(filename) + assert key in lines[line] + assert val in lines[line] diff --git a/lib/spack/spack/util/spack_yaml.py b/lib/spack/spack/util/spack_yaml.py index e27c48860b..24eea88b77 100644 --- a/lib/spack/spack/util/spack_yaml.py +++ b/lib/spack/spack/util/spack_yaml.py @@ -13,6 +13,7 @@ """ import ctypes +import re import sys from typing import List # novm @@ -185,6 +186,12 @@ class OrderedLineDumper(RoundTripDumper): """Make the dumper NEVER print YAML aliases.""" return True + def represent_data(self, data): + result = super(OrderedLineDumper, self).represent_data(data) + if data is None: + result.value = syaml_str("null") + return result + def represent_str(self, data): if hasattr(data, 'override') and data.override: data = data + ':' @@ -262,19 +269,18 @@ class LineAnnotationDumper(OrderedLineDumper): def represent_data(self, data): """Force syaml_str to be passed through with marks.""" result = super(LineAnnotationDumper, self).represent_data(data) - if isinstance(result.value, string_types): + if data is None: + result.value = syaml_str("null") + elif isinstance(result.value, string_types): result.value = syaml_str(data) if markable(result.value): mark(result.value, data) return result - def write_stream_start(self): - super(LineAnnotationDumper, self).write_stream_start() - _annotations.append(colorize('@K{---}')) - def write_line_break(self): super(LineAnnotationDumper, self).write_line_break() - if not self.saved: + if self.saved is None: + _annotations.append(colorize('@K{---}')) return # append annotations at the end of each line @@ -322,7 +328,10 @@ def dump_annotated(data, stream=None, *args, **kwargs): sio = StringIO() yaml.dump(data, sio, *args, **kwargs) - lines = sio.getvalue().rstrip().split('\n') + + # write_line_break() is not called by YAML for empty lines, so we + # skip empty lines here with \n+. + lines = re.split(r"\n+", sio.getvalue().rstrip()) getvalue = None if stream is None: -- cgit v1.2.3-70-g09d2