Skip to content

Commit

Permalink
Respect yamllint 'document_start' rule when autofixing yaml (#4184)
Browse files Browse the repository at this point in the history
Co-authored-by: Jonas Switala <jonas.switala@frequentis.com>
  • Loading branch information
elara-leitstellentechnik and frq-asgard-josi committed Jun 4, 2024
1 parent 528275b commit b5d027c
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 13 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ jobs:
env:
# Number of expected test passes, safety measure for accidental skip of
# tests. Update value if you add/remove tests.
PYTEST_REQPASS: 875
PYTEST_REQPASS: 877
steps:
- uses: actions/checkout@v4
with:
Expand Down
37 changes: 26 additions & 11 deletions src/ansiblelint/yaml_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,16 @@ def write_version_directive(self, version_text: Any) -> None:
class FormattedYAML(YAML):
"""A YAML loader/dumper that handles ansible content better by default."""

default_config = {
"explicit_start": True,
"explicit_end": False,
"width": 160,
"indent_sequences": True,
"preferred_quote": '"',
"min_spaces_inside": 0,
"max_spaces_inside": 1,
}

def __init__( # pylint: disable=too-many-arguments
self,
*,
Expand All @@ -828,6 +838,7 @@ def __init__( # pylint: disable=too-many-arguments
output: Any = None,
plug_ins: list[str] | None = None,
version: tuple[int, int] | None = None,
config: dict[str, bool | int | str] | None = None,
):
"""Return a configured ``ruamel.yaml.YAML`` instance.
Expand Down Expand Up @@ -897,7 +908,8 @@ def __init__( # pylint: disable=too-many-arguments

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

config = self._defaults_from_yamllint_config()
if not config:
config = self._defaults_from_yamllint_config()

# these settings are derived from yamllint config
self.explicit_start: bool = config["explicit_start"] # type: ignore[assignment]
Expand Down Expand Up @@ -950,15 +962,8 @@ def __init__( # pylint: disable=too-many-arguments
@staticmethod
def _defaults_from_yamllint_config() -> dict[str, bool | int | str]:
"""Extract FormattedYAML-relevant settings from yamllint config if possible."""
config = {
"explicit_start": True,
"explicit_end": False,
"width": 160,
"indent_sequences": True,
"preferred_quote": '"',
"min_spaces_inside": 0,
"max_spaces_inside": 1,
}
config = FormattedYAML.default_config

for rule, rule_config in load_yamllint_config().rules.items():
if not rule_config:
# rule disabled
Expand Down Expand Up @@ -1062,6 +1067,7 @@ def dumps(self, data: Any) -> str:
return self._post_process_yaml(
text,
strip_version_directive=strip_version_directive,
strip_explicit_start=not self.explicit_start,
)

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

@staticmethod
def _post_process_yaml(text: str, *, strip_version_directive: bool = False) -> str:
def _post_process_yaml(
text: str,
*,
strip_version_directive: bool = False,
strip_explicit_start: bool = False,
) -> str:
"""Handle known issues with ruamel.yaml dumping.
Make sure there's only one newline at the end of the file.
Expand All @@ -1166,6 +1177,10 @@ def _post_process_yaml(text: str, *, strip_version_directive: bool = False) -> s
if strip_version_directive and text.startswith("%YAML"):
text = text.split("\n", 1)[1]

# remove explicit document start
if strip_explicit_start and text.startswith("---"):
text = text.split("\n", 1)[1]

text = text.rstrip("\n") + "\n"

lines = text.splitlines(keepends=True)
Expand Down
28 changes: 27 additions & 1 deletion test/test_yaml_utils.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
"""Tests for yaml-related utility functions."""

# pylint: disable=too-many-lines
from __future__ import annotations

from io import StringIO
from pathlib import Path
from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING, Any, cast

import pytest
from ruamel.yaml.main import YAML
Expand Down Expand Up @@ -997,3 +998,28 @@ def test_yamllint_incompatible_config() -> None:
with (cwd(Path("examples/yamllint/incompatible-config")),):
config = ansiblelint.yaml_utils.load_yamllint_config()
assert config.incompatible


@pytest.mark.parametrize(
("yaml_version", "explicit_start"),
(
pytest.param((1, 1), True),
pytest.param((1, 1), False),
),
)
def test_document_start(
yaml_version: tuple[int, int] | None,
explicit_start: bool,
) -> None:
"""Ensure the explicit_start config option from .yamllint is applied correctly."""
config = ansiblelint.yaml_utils.FormattedYAML.default_config
config["explicit_start"] = explicit_start

yaml = ansiblelint.yaml_utils.FormattedYAML(
version=yaml_version,
config=cast(dict[str, bool | int | str], config),
)
assert (
yaml.dumps(yaml.load(_SINGLE_QUOTE_WITHOUT_INDENTS)).startswith("---")
== explicit_start
)

0 comments on commit b5d027c

Please # to comment.