#!/usr/bin/env python3 """ Verify that it is possible to round-trip native_functions.yaml via ruamel under some configuration. Keeping native_functions.yaml consistent in this way allows us to run codemods on the file using ruamel without introducing line noise. Note that we don't want to normalize the YAML file, as that would to lots of spurious lint failures. Anything that ruamel understands how to roundtrip, e.g., whitespace and comments, is OK! ruamel is a bit picky about inconsistent indentation, so you will have to indent your file properly. Also, if you are working on changing the syntax of native_functions.yaml, you may find that you want to use some format that is not what ruamel prefers. If so, it is OK to modify this script (instead of reformatting native_functions.yaml)--the point is simply to make sure that there is *some* configuration of ruamel that can round trip the YAML, not to be prescriptive about it. """ from __future__ import annotations import argparse import json import sys from enum import Enum from io import StringIO from typing import NamedTuple import ruamel.yaml # type: ignore[import] class LintSeverity(str, Enum): ERROR = "error" WARNING = "warning" ADVICE = "advice" DISABLED = "disabled" class LintMessage(NamedTuple): path: str | None line: int | None char: int | None code: str severity: LintSeverity name: str original: str | None replacement: str | None description: str | None if __name__ == "__main__": parser = argparse.ArgumentParser( description="native functions linter", fromfile_prefix_chars="@", ) parser.add_argument( "--native-functions-yml", required=True, help="location of native_functions.yaml", ) args = parser.parse_args() with open(args.native_functions_yml) as f: contents = f.read() yaml = ruamel.yaml.YAML() # type: ignore[attr-defined] yaml.preserve_quotes = True # type: ignore[assignment] yaml.width = 1000 # type: ignore[assignment] yaml.boolean_representation = ["False", "True"] # type: ignore[attr-defined] try: r = yaml.load(contents) except Exception as err: msg = LintMessage( path=None, line=None, char=None, code="NATIVEFUNCTIONS", severity=LintSeverity.ERROR, name="YAML load failure", original=None, replacement=None, description=f"Failed due to {err.__class__.__name__}:\n{err}", ) print(json.dumps(msg._asdict()), flush=True) sys.exit(0) # Cuz ruamel's author intentionally didn't include conversion to string # https://stackoverflow.com/questions/47614862/best-way-to-use-ruamel-yaml-to-dump-to-string-not-to-stream string_stream = StringIO() yaml.dump(r, string_stream) new_contents = string_stream.getvalue() string_stream.close() if contents != new_contents: msg = LintMessage( path=args.native_functions_yml, line=None, char=None, code="NATIVEFUNCTIONS", severity=LintSeverity.ERROR, name="roundtrip inconsistency", original=contents, replacement=new_contents, description=( "YAML roundtrip failed; run `lintrunner --take NATIVEFUNCTIONS -a` to apply the suggested changes. " "If you think this is in error, please see tools/linter/adapters/nativefunctions_linter.py" ), ) print(json.dumps(msg._asdict()), flush=True)