Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 32 additions & 34 deletions scripts/sort_yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,26 @@

import sys
import argparse
from io import StringIO
from pathlib import Path

try:
import yaml
from ruamel.yaml import YAML
from ruamel.yaml.comments import CommentedMap, CommentedSeq
except ImportError:
print("ERROR: pyyaml is not installed. Run: pip install pyyaml", file=sys.stderr)
print("ERROR: ruamel.yaml is not installed. Run: pip install ruamel.yaml", file=sys.stderr)
sys.exit(1)


# ---------------------------------------------------------------------------
# Custom YAML Dumper
# YAML round-trip instance (loader + dumper), configured once
# ---------------------------------------------------------------------------

class _SortedDumper(yaml.Dumper):
"""YAML Dumper that produces consistent, human-readable output."""
pass


def _str_representer(dumper: yaml.Dumper, data: str):
"""Represent strings: use literal block style for multi-line, plain otherwise.
Strings that look like YAML scalars (booleans, numbers) are quoted.
"""
if "\n" in data:
return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|")
return dumper.represent_scalar("tag:yaml.org,2002:str", data)


_SortedDumper.add_representer(str, _str_representer)
_yaml = YAML(typ="rt")
_yaml.preserve_quotes = True
_yaml.width = 100
_yaml.indent(mapping=2, sequence=2, offset=0)
_yaml.allow_unicode = True


# ---------------------------------------------------------------------------
Expand All @@ -53,28 +45,36 @@ def _str_representer(dumper: yaml.Dumper, data: str):

def sort_recursive(obj):
"""Recursively sort dict keys alphabetically. Lists are preserved as-is."""
if isinstance(obj, dict):
return {k: sort_recursive(obj[k]) for k in sorted(obj.keys(), key=str)}
if isinstance(obj, list):
return [sort_recursive(item) for item in obj]
if isinstance(obj, CommentedMap):
sorted_map = CommentedMap()
for key in sorted(obj.keys(), key=str):
sorted_map[key] = sort_recursive(obj[key])
sorted_map.ca.items.update(obj.ca.items)
if obj.ca.comment is not None:
sorted_map.ca.comment = obj.ca.comment
sorted_map.ca.end = obj.ca.end
return sorted_map

if isinstance(obj, CommentedSeq):
new_seq = CommentedSeq(sort_recursive(item) for item in obj)
new_seq.ca.items.update(obj.ca.items)
if obj.ca.comment is not None:
new_seq.ca.comment = obj.ca.comment
new_seq.ca.end = obj.ca.end
return new_seq

return obj


def canonical(content: str) -> str:
"""Return the canonical (sorted + formatted) representation of a YAML string."""
data = yaml.safe_load(content)
data = _yaml.load(content)
if data is None:
return content
sorted_data = sort_recursive(data)
return yaml.dump(
sorted_data,
Dumper=_SortedDumper,
default_flow_style=False,
allow_unicode=True,
indent=2,
sort_keys=False, # we already sorted manually
width=100,
)
stream = StringIO()
_yaml.dump(sorted_data, stream)
return stream.getvalue()


def find_rule_files(root: Path) -> list[Path]:
Expand Down Expand Up @@ -172,5 +172,3 @@ def main():

if __name__ == "__main__":
main()


3 changes: 3 additions & 0 deletions setup/bash_setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ pip install --quiet -e engine/[dev]

VENV_PYTHON=$(which python)

echo "Installing ruamel.yaml..."
pip install --quiet ruamel.yaml

echo "Installing pre-commit..."
pip install pre-commit --index-url https://pypi.org/simple/ --quiet 2>/dev/null || \
pip install pre-commit --quiet 2>/dev/null || true
Expand Down
10 changes: 10 additions & 0 deletions setup/windows_setup.bat
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,16 @@ if !errorlevel! neq 0 (
exit /b 1
)

echo.
echo Installing ruamel.yaml...
python -m pip install ruamel.yaml --quiet
if !errorlevel! neq 0 (
echo Failed to install ruamel.yaml
cd ..
pause
exit /b 1
)

echo.
echo Installing pre-commit...
python -m pip install pre-commit --index-url https://pypi.org/simple/ --quiet 2>nul || python -m pip install pre-commit --quiet 2>nul
Expand Down