From 74c9a61a158ec1da3ace45f832f80d1b4e14d60e Mon Sep 17 00:00:00 2001 From: "D. Bohdan" Date: Sun, 13 Oct 2024 08:49:45 +0000 Subject: [PATCH] fix(yaml): emit `null`, not empty scalars This is the null representation users expect. It was how Remarshal worked with PyYAML. The choice of how to implement it is per the suggestion of the developer of ruamel.yaml on https://stackoverflow.com/questions/44313992/. --- src/remarshal/main.py | 6 ++++++ tests/bool-null-key.yaml | 2 +- tests/null.json | 3 +++ tests/null.yaml | 1 + tests/test_remarshal.py | 17 ++++++++++++++++- 5 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 tests/null.json create mode 100644 tests/null.yaml diff --git a/src/remarshal/main.py b/src/remarshal/main.py index a9a336e..229033d 100755 --- a/src/remarshal/main.py +++ b/src/remarshal/main.py @@ -646,6 +646,10 @@ def stringify_null(x: Any) -> Any: raise ValueError(msg) +def _yaml_represent_none(self, data): + return self.represent_scalar("tag:yaml.org,2002:null", "null") + + def _encode_yaml(data: Document, *, yaml_options: YAMLOptions) -> str: yaml = ruamel.yaml.YAML() yaml.default_flow_style = False @@ -654,6 +658,8 @@ def _encode_yaml(data: Document, *, yaml_options: YAMLOptions) -> str: yaml.indent = yaml_options.indent yaml.width = yaml_options.width + yaml.representer.add_representer(type(None), _yaml_represent_none) + try: out = StringIO() yaml.dump( diff --git a/tests/bool-null-key.yaml b/tests/bool-null-key.yaml index 28dd2ab..13b59dc 100644 --- a/tests/bool-null-key.yaml +++ b/tests/bool-null-key.yaml @@ -1,4 +1,4 @@ True: foo false: oof another: bar -NULL: "nothin'" +NULL: nothin' diff --git a/tests/null.json b/tests/null.json new file mode 100644 index 0000000..91011d4 --- /dev/null +++ b/tests/null.json @@ -0,0 +1,3 @@ +{ + "foo": null +} diff --git a/tests/null.yaml b/tests/null.yaml new file mode 100644 index 0000000..8ff5e87 --- /dev/null +++ b/tests/null.yaml @@ -0,0 +1 @@ +foo: null diff --git a/tests/test_remarshal.py b/tests/test_remarshal.py index 6259b5f..5de29f4 100755 --- a/tests/test_remarshal.py +++ b/tests/test_remarshal.py @@ -1,6 +1,6 @@ #! /usr/bin/env python # Remarshal, a utility to convert between serialization formats. -# Copyright (c) 2014-2020, 2023 D. Bohdan +# Copyright (c) 2014-2020, 2023-2024 D. Bohdan # License: MIT from __future__ import annotations @@ -364,6 +364,11 @@ def test_binary_to_yaml(self, convert_and_read) -> None: def test_binary_to_cbor(self, convert_and_read) -> None: convert_and_read("bin.msgpack", "msgpack", "cbor") + def test_yaml_null(self, convert_and_read) -> None: + output = convert_and_read("null.json", "json", "yaml") + reference = read_file("null.yaml") + assert output == reference + def test_yaml_style_default(self, convert_and_read) -> None: output = convert_and_read("long-line.json", "json", "yaml") reference = read_file("long-line-default.yaml") @@ -544,6 +549,16 @@ def test_yaml2toml_bool_null_key(self, convert_and_read) -> None: reference = read_file("bool-null-key.toml") assert output == reference + def test_yaml2yaml_bool_null_key(self, convert_and_read) -> None: + output = convert_and_read( + "bool-null-key.yaml", + "yaml", + "yaml", + ) + reference = read_file("bool-null-key.yaml") + + assert output == reference.lower() + def test_yaml2toml_timestamp_key(self, convert_and_read) -> None: output = convert_and_read( "timestamp-key.yaml",