Skip to content

Commit b5d027c

Browse files
elara-leitstellentechnikfrq-asgard-josi
elara-leitstellentechnik
andauthored
Respect yamllint 'document_start' rule when autofixing yaml (ansible#4184)
Co-authored-by: Jonas Switala <[email protected]>
1 parent 528275b commit b5d027c

File tree

3 files changed

+54
-13
lines changed

3 files changed

+54
-13
lines changed

.github/workflows/tox.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ jobs:
7272
env:
7373
# Number of expected test passes, safety measure for accidental skip of
7474
# tests. Update value if you add/remove tests.
75-
PYTEST_REQPASS: 875
75+
PYTEST_REQPASS: 877
7676
steps:
7777
- uses: actions/checkout@v4
7878
with:

src/ansiblelint/yaml_utils.py

+26-11
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,16 @@ def write_version_directive(self, version_text: Any) -> None:
820820
class FormattedYAML(YAML):
821821
"""A YAML loader/dumper that handles ansible content better by default."""
822822

823+
default_config = {
824+
"explicit_start": True,
825+
"explicit_end": False,
826+
"width": 160,
827+
"indent_sequences": True,
828+
"preferred_quote": '"',
829+
"min_spaces_inside": 0,
830+
"max_spaces_inside": 1,
831+
}
832+
823833
def __init__( # pylint: disable=too-many-arguments
824834
self,
825835
*,
@@ -828,6 +838,7 @@ def __init__( # pylint: disable=too-many-arguments
828838
output: Any = None,
829839
plug_ins: list[str] | None = None,
830840
version: tuple[int, int] | None = None,
841+
config: dict[str, bool | int | str] | None = None,
831842
):
832843
"""Return a configured ``ruamel.yaml.YAML`` instance.
833844
@@ -897,7 +908,8 @@ def __init__( # pylint: disable=too-many-arguments
897908

898909
# NB: We ignore some mypy issues because ruamel.yaml typehints are not great.
899910

900-
config = self._defaults_from_yamllint_config()
911+
if not config:
912+
config = self._defaults_from_yamllint_config()
901913

902914
# these settings are derived from yamllint config
903915
self.explicit_start: bool = config["explicit_start"] # type: ignore[assignment]
@@ -950,15 +962,8 @@ def __init__( # pylint: disable=too-many-arguments
950962
@staticmethod
951963
def _defaults_from_yamllint_config() -> dict[str, bool | int | str]:
952964
"""Extract FormattedYAML-relevant settings from yamllint config if possible."""
953-
config = {
954-
"explicit_start": True,
955-
"explicit_end": False,
956-
"width": 160,
957-
"indent_sequences": True,
958-
"preferred_quote": '"',
959-
"min_spaces_inside": 0,
960-
"max_spaces_inside": 1,
961-
}
965+
config = FormattedYAML.default_config
966+
962967
for rule, rule_config in load_yamllint_config().rules.items():
963968
if not rule_config:
964969
# rule disabled
@@ -1062,6 +1067,7 @@ def dumps(self, data: Any) -> str:
10621067
return self._post_process_yaml(
10631068
text,
10641069
strip_version_directive=strip_version_directive,
1070+
strip_explicit_start=not self.explicit_start,
10651071
)
10661072

10671073
def _prevent_wrapping_flow_style(self, data: Any) -> None:
@@ -1150,7 +1156,12 @@ def _pre_process_yaml(self, text: str) -> tuple[str, str | None]:
11501156
return text, "".join(preamble_comments) or None
11511157

11521158
@staticmethod
1153-
def _post_process_yaml(text: str, *, strip_version_directive: bool = False) -> str:
1159+
def _post_process_yaml(
1160+
text: str,
1161+
*,
1162+
strip_version_directive: bool = False,
1163+
strip_explicit_start: bool = False,
1164+
) -> str:
11541165
"""Handle known issues with ruamel.yaml dumping.
11551166
11561167
Make sure there's only one newline at the end of the file.
@@ -1166,6 +1177,10 @@ def _post_process_yaml(text: str, *, strip_version_directive: bool = False) -> s
11661177
if strip_version_directive and text.startswith("%YAML"):
11671178
text = text.split("\n", 1)[1]
11681179

1180+
# remove explicit document start
1181+
if strip_explicit_start and text.startswith("---"):
1182+
text = text.split("\n", 1)[1]
1183+
11691184
text = text.rstrip("\n") + "\n"
11701185

11711186
lines = text.splitlines(keepends=True)

test/test_yaml_utils.py

+27-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
"""Tests for yaml-related utility functions."""
22

3+
# pylint: disable=too-many-lines
34
from __future__ import annotations
45

56
from io import StringIO
67
from pathlib import Path
7-
from typing import TYPE_CHECKING, Any
8+
from typing import TYPE_CHECKING, Any, cast
89

910
import pytest
1011
from ruamel.yaml.main import YAML
@@ -997,3 +998,28 @@ def test_yamllint_incompatible_config() -> None:
997998
with (cwd(Path("examples/yamllint/incompatible-config")),):
998999
config = ansiblelint.yaml_utils.load_yamllint_config()
9991000
assert config.incompatible
1001+
1002+
1003+
@pytest.mark.parametrize(
1004+
("yaml_version", "explicit_start"),
1005+
(
1006+
pytest.param((1, 1), True),
1007+
pytest.param((1, 1), False),
1008+
),
1009+
)
1010+
def test_document_start(
1011+
yaml_version: tuple[int, int] | None,
1012+
explicit_start: bool,
1013+
) -> None:
1014+
"""Ensure the explicit_start config option from .yamllint is applied correctly."""
1015+
config = ansiblelint.yaml_utils.FormattedYAML.default_config
1016+
config["explicit_start"] = explicit_start
1017+
1018+
yaml = ansiblelint.yaml_utils.FormattedYAML(
1019+
version=yaml_version,
1020+
config=cast(dict[str, bool | int | str], config),
1021+
)
1022+
assert (
1023+
yaml.dumps(yaml.load(_SINGLE_QUOTE_WITHOUT_INDENTS)).startswith("---")
1024+
== explicit_start
1025+
)

0 commit comments

Comments
 (0)