|
1 | 1 | """Utility helpers to simplify working with yaml-based data."""
|
| 2 | +# pylint: disable=too-many-lines |
2 | 3 | import functools
|
3 | 4 | import logging
|
4 | 5 | import os
|
|
20 | 21 | )
|
21 | 22 |
|
22 | 23 | import ruamel.yaml.events
|
23 |
| -from ruamel.yaml.comments import CommentedMap, CommentedSeq |
| 24 | +from ruamel.yaml.comments import CommentedMap, CommentedSeq, Format |
24 | 25 | from ruamel.yaml.constructor import RoundTripConstructor
|
25 | 26 | from ruamel.yaml.emitter import Emitter, ScalarAnalysis
|
26 | 27 |
|
@@ -886,13 +887,58 @@ def loads(self, stream: str) -> Any:
|
886 | 887 | def dumps(self, data: Any) -> str:
|
887 | 888 | """Dump YAML document to string (including its preamble_comment)."""
|
888 | 889 | preamble_comment: Optional[str] = getattr(data, "preamble_comment", None)
|
| 890 | + self._prevent_wrapping_flow_style(data) |
889 | 891 | with StringIO() as stream:
|
890 | 892 | if preamble_comment:
|
891 | 893 | stream.write(preamble_comment)
|
892 | 894 | self.dump(data, stream)
|
893 | 895 | text = stream.getvalue()
|
894 | 896 | return self._post_process_yaml(text)
|
895 | 897 |
|
| 898 | + def _prevent_wrapping_flow_style(self, data: Any) -> None: |
| 899 | + if not isinstance(data, (CommentedMap, CommentedSeq)): |
| 900 | + return |
| 901 | + for key, value, parent_path in nested_items_path(data): |
| 902 | + if not isinstance(value, (CommentedMap, CommentedSeq)): |
| 903 | + continue |
| 904 | + fa: Format = value.fa # pylint: disable=invalid-name |
| 905 | + if fa.flow_style(): |
| 906 | + predicted_indent = self._predict_indent_length(parent_path, key) |
| 907 | + predicted_width = len(str(value)) |
| 908 | + if predicted_indent + predicted_width > self.width: |
| 909 | + # this flow-style map will probably get line-wrapped, |
| 910 | + # so, switch it to block style to avoid the line wrap. |
| 911 | + fa.set_block_style() |
| 912 | + |
| 913 | + def _predict_indent_length( |
| 914 | + self, parent_path: List[Union[str, int]], key: Any |
| 915 | + ) -> int: |
| 916 | + indent = 0 |
| 917 | + |
| 918 | + # each parent_key type tells us what the indent is for the next level. |
| 919 | + for parent_key in parent_path: |
| 920 | + if isinstance(parent_key, int) and indent == 0: |
| 921 | + # root level is a sequence |
| 922 | + indent += self.sequence_dash_offset |
| 923 | + elif isinstance(parent_key, int): |
| 924 | + # next level is a sequence |
| 925 | + indent += cast(int, self.sequence_indent) |
| 926 | + elif isinstance(parent_key, str): |
| 927 | + # next level is a map |
| 928 | + indent += cast(int, self.map_indent) |
| 929 | + |
| 930 | + if isinstance(key, int) and indent == 0: |
| 931 | + # flow map is an item in a root-level sequence |
| 932 | + indent += self.sequence_dash_offset |
| 933 | + elif isinstance(key, int) and indent > 0: |
| 934 | + # flow map is in a sequence |
| 935 | + indent += cast(int, self.sequence_indent) |
| 936 | + elif isinstance(key, str): |
| 937 | + # flow map is in a map |
| 938 | + indent += len(key + ": ") |
| 939 | + |
| 940 | + return indent |
| 941 | + |
896 | 942 | # ruamel.yaml only preserves empty (no whitespace) blank lines
|
897 | 943 | # (ie "/n/n" becomes "/n/n" but "/n /n" becomes "/n").
|
898 | 944 | # So, we need to identify whitespace-only lines to drop spaces before reading.
|
|
0 commit comments