3535if TYPE_CHECKING :
3636 # noinspection PyProtectedMember
3737 from ruamel .yaml .comments import LineCol
38- from ruamel .yaml .compat import StreamTextType
38+ from ruamel .yaml .compat import StreamTextType , VersionType
3939 from ruamel .yaml .nodes import ScalarNode
4040 from ruamel .yaml .representer import RoundTripRepresenter
4141 from ruamel .yaml .tokens import CommentToken
4444
4545_logger = logging .getLogger (__name__ )
4646
47+
4748YAMLLINT_CONFIG = """
4849extends: default
4950rules:
@@ -750,13 +751,14 @@ def write_version_directive(self, version_text: Any) -> None:
750751class FormattedYAML (YAML ):
751752 """A YAML loader/dumper that handles ansible content better by default."""
752753
753- def __init__ (
754+ def __init__ ( # pylint: disable=too-many-arguments
754755 self ,
755756 * ,
756757 typ : str | None = None ,
757758 pure : bool = False ,
758759 output : Any = None ,
759760 plug_ins : list [str ] | None = None ,
761+ version : VersionType | None = None ,
760762 ):
761763 """Return a configured ``ruamel.yaml.YAML`` instance.
762764
@@ -817,8 +819,12 @@ def __init__(
817819 - name: Task
818820 """
819821 # Default to reading/dumping YAML 1.1 (ruamel.yaml defaults to 1.2)
820- self ._yaml_version_default : tuple [int , int ] = (1 , 1 )
821- self ._yaml_version : str | tuple [int , int ] = self ._yaml_version_default
822+ if version :
823+ if isinstance (version , str ):
824+ x , y = version .split ("." , maxsplit = 1 )
825+ version = (int (x ), int (y ))
826+ self ._yaml_version_default : VersionType = version
827+ self ._yaml_version : VersionType = self ._yaml_version_default
822828
823829 super ().__init__ (typ = typ , pure = pure , output = output , plug_ins = plug_ins )
824830
@@ -874,6 +880,9 @@ def __init__(
874880 # (see https://stackoverflow.com/a/44314840/1134951)
875881 # self.Representer.add_representer(
876882
883+ if version :
884+ self .version = version
885+
877886 @staticmethod
878887 def _defaults_from_yamllint_config () -> dict [str , bool | int | str ]:
879888 """Extract FormattedYAML-relevant settings from yamllint config if possible."""
@@ -920,16 +929,18 @@ def _defaults_from_yamllint_config() -> dict[str, bool | int | str]:
920929
921930 return cast (dict [str , Union [bool , int , str ]], config )
922931
923- @property # type: ignore[override]
924- def version (self ) -> str | tuple [ int , int ] :
932+ @property
933+ def version (self ) -> VersionType | None :
925934 """Return the YAML version used to parse or dump.
926935
927936 Ansible uses PyYAML which only supports YAML 1.1. ruamel.yaml defaults to 1.2.
928937 So, we have to make sure we dump yaml files using YAML 1.1.
929938 We can relax the version requirement once ansible uses a version of PyYAML
930939 that includes this PR: https://github.com/yaml/pyyaml/pull/555
931940 """
932- return self ._yaml_version
941+ if hasattr (self , "_yaml_version" ):
942+ return self ._yaml_version
943+ return None
933944
934945 @version .setter
935946 def version (self , value : str | tuple [int , int ] | None ) -> None :
@@ -939,7 +950,11 @@ def version(self, value: str | tuple[int, int] | None) -> None:
939950 So, if a file does not include the directive, it sets this to None.
940951 But, None effectively resets the parsing version to YAML 1.2 (ruamel's default).
941952 """
942- self ._yaml_version = value if value is not None else self ._yaml_version_default
953+ if value is not None :
954+ self ._yaml_version = value
955+ elif hasattr (self , "_yaml_version_default" ):
956+ self ._yaml_version = self ._yaml_version_default
957+ # We do nothing if the object did not had a previous default version defined
943958
944959 def load (self , stream : Path | StreamTextType ) -> Any :
945960 """Load YAML content from a string while avoiding known ruamel.yaml issues."""
@@ -977,7 +992,11 @@ def dumps(self, data: Any) -> str:
977992 stream .write (preamble_comment )
978993 self .dump (data , stream )
979994 text = stream .getvalue ()
980- return self ._post_process_yaml (text )
995+ strip_version_directive = hasattr (self , "_yaml_version_default" )
996+ return self ._post_process_yaml (
997+ text ,
998+ strip_version_directive = strip_version_directive ,
999+ )
9811000
9821001 def _prevent_wrapping_flow_style (self , data : Any ) -> None :
9831002 if not isinstance (data , (CommentedMap , CommentedSeq )):
@@ -1065,7 +1084,7 @@ def _pre_process_yaml(self, text: str) -> tuple[str, str | None]:
10651084 return text , "" .join (preamble_comments ) or None
10661085
10671086 @staticmethod
1068- def _post_process_yaml (text : str ) -> str :
1087+ def _post_process_yaml (text : str , * , strip_version_directive : bool = False ) -> str :
10691088 """Handle known issues with ruamel.yaml dumping.
10701089
10711090 Make sure there's only one newline at the end of the file.
@@ -1077,6 +1096,10 @@ def _post_process_yaml(text: str) -> str:
10771096
10781097 Make sure null list items don't end in a space.
10791098 """
1099+ # remove YAML directive
1100+ if strip_version_directive and text .startswith ("%YAML" ):
1101+ text = text .split ("\n " , 1 )[1 ]
1102+
10801103 text = text .rstrip ("\n " ) + "\n "
10811104
10821105 lines = text .splitlines (keepends = True )
0 commit comments