From 2128c8b56a6324020bf9ad468e91a2f4549156fc Mon Sep 17 00:00:00 2001 From: guibog Date: Thu, 29 Feb 2024 15:15:25 +0800 Subject: [PATCH] Add preserve_block_seqs_indents --- _test/test_block_seq_local_indent.py | 134 +++++++++++++++++++++++++++ lib/ruyaml/emitter.py | 11 ++- lib/ruyaml/events.py | 18 +++- lib/ruyaml/main.py | 2 + lib/ruyaml/nodes.py | 18 +++- lib/ruyaml/representer.py | 11 +++ lib/ruyaml/serializer.py | 1 + 7 files changed, 191 insertions(+), 4 deletions(-) create mode 100644 _test/test_block_seq_local_indent.py diff --git a/_test/test_block_seq_local_indent.py b/_test/test_block_seq_local_indent.py new file mode 100644 index 00000000..9b19295c --- /dev/null +++ b/_test/test_block_seq_local_indent.py @@ -0,0 +1,134 @@ + +import pytest +import io +import ruyaml # NOQA +from .roundtrip import round_trip, dedent + +from ruyaml import YAML +from ruyaml.util import load_yaml_guess_indent +from ruyaml.scalarbool import ScalarBoolean +from ruyaml.comments import CommentedSeq +from ruyaml.representer import RoundTripRepresenter, ScalarNode +from ruyaml.constructor import RoundTripConstructor +from typing import Text, Any, Dict, List # NOQA + + +class ScalarNodePositioned(ScalarNode): + lc_position = None + + +class ScalarBooleanStable(ScalarBoolean): + + def __new__(cls: Any, boolval, origrepr, anchor=None) -> Any: + b = ScalarBoolean.__new__(cls, boolval, anchor=anchor) + b.original_bool_repr = origrepr + return b + + +#def represent_list2(self, data): +# dedede +# RoundTripRepresenter.add_representer(CommentedSeq, represent_list2) + + +class BetterRoundTripConstructor(RoundTripConstructor): + keep_bool_repr = True + + +class BetterRoundTripRepresenter(RoundTripRepresenter): + def represent_bool(self, data, anchor=None): + ret = self.represent_scalar('tag:yaml.org,2002:bool', data.original_bool_repr, anchor=anchor) + return ret + + def TOTO_represent_sequence(self, tag, data): + node = super().represent_sequence(tag, data) + print(44, self, tag, data, node) + # print(45, data.lc, data.lc.data) + local_block_seq_ident = -1 + + +#BetterRoundTripConstructor.add_constructor('tag:yaml.org,2002:bool', BetterRoundTripConstructor.construct_yaml_bool) +#BetterRoundTripRepresenter.add_representer(ScalarBooleanStable, BetterRoundTripRepresenter.represent_bool) + + +def round_trip_stabler( + inp, + outp=None, +): + if outp is None: + outp = inp + doutp = dedent(outp) + yaml = ruyaml.YAML() + yaml.preserve_quotes = True + yaml.preserve_block_seqs_indents = True + data = yaml.load(doutp) + buf = io.StringIO() + yaml.dump(data, stream=buf) + res = buf.getvalue() + assert res == doutp + + +class TestStability: + + def test_blockseq1(self): + round_trip( + """ + a: + - a1 + - a2 + """ + ) + + @pytest.mark.xfail(strict=True) + def test_blockseq2(self): + round_trip( + """ + a: + - a1 + - a2 + """ + ) + + @pytest.mark.xfail(strict=True) + def test_blockseq3(self): + round_trip( + """ + a: + - a1 + - a2 + b: + - b1 + - b2 + """ + ) + +class TestStabilityStabler: + + def test_blockseq1(self): + round_trip_stabler( + """ + a: + - a1 + - a2 + """ + ) + + def test_blockseq2(self): + round_trip_stabler( + """ + a: + - a1 + - a2 + """ + ) + + def test_blockseq3(self): + round_trip_stabler( + """ + a: + - a1 + - a2 + b: + - b1 + - b2 + """ + ) diff --git a/lib/ruyaml/emitter.py b/lib/ruyaml/emitter.py index d5fe1a16..22483c78 100644 --- a/lib/ruyaml/emitter.py +++ b/lib/ruyaml/emitter.py @@ -117,6 +117,7 @@ def __init__( top_level_colon_align=None, prefix_colon=None, brace_single_entry_mapping_in_flow_sequence=None, + preserve_block_seqs_indents=None, dumper=None, ): # type: (StreamType, Any, Optional[int], Optional[int], Optional[bool], Any, Optional[int], Optional[bool], Any, Optional[bool], Any) -> None # NOQA @@ -185,6 +186,8 @@ def __init__( # set to False to get "\Uxxxxxxxx" for non-basic unicode like emojis self.unicode_supplementary = sys.maxunicode > 0xFFFF self.sequence_dash_offset = block_seq_indent if block_seq_indent else 0 + self.preserve_block_seqs_indents = preserve_block_seqs_indents + self.current_local_block_seq_indent = None self.top_level_colon_align = top_level_colon_align self.best_sequence_indent = 2 self.requested_indent = indent # specific for literal zero indent @@ -446,6 +449,7 @@ def expect_node(self, root=False, sequence=False, mapping=False, simple_key=Fals self.expect_scalar() elif isinstance(self.event, SequenceStartEvent): # nprint('@', self.indention, self.no_newline, self.column) + self.current_local_block_seq_indent = self.event.block_seq_indent i2, n2 = self.indention, self.no_newline # NOQA if self.event.comment: if self.event.flow_style is False and self.event.comment: @@ -669,9 +673,12 @@ def expect_block_sequence_item(self, first=False): self.write_pre_comment(self.event) nonl = self.no_newline if self.column == 0 else False self.write_indent() - ind = self.sequence_dash_offset # if len(self.indents) > 1 else 0 + cur_dash_offset = self.sequence_dash_offset + if self.current_local_block_seq_indent and self.preserve_block_seqs_indents: + cur_dash_offset = self.current_local_block_seq_indent + ind = cur_dash_offset # if len(self.indents) > 1 else 0 self.write_indicator(' ' * ind + '-', True, indention=True) - if nonl or self.sequence_dash_offset + 2 > self.best_sequence_indent: + if nonl or cur_dash_offset + 2 > self.best_sequence_indent: self.no_newline = True self.states.append(self.expect_block_sequence_item) self.expect_node(sequence=True) diff --git a/lib/ruyaml/events.py b/lib/ruyaml/events.py index 558d2db2..7af91a18 100644 --- a/lib/ruyaml/events.py +++ b/lib/ruyaml/events.py @@ -186,7 +186,23 @@ def __init__( class SequenceStartEvent(CollectionStartEvent): - __slots__ = () + __slots__ = ('block_seq_indent', ) + + def __init__( + self, + anchor, + tag, + implicit, + start_mark=None, + end_mark=None, + flow_style=None, + comment=None, + nr_items=None, + block_seq_indent=None, + ): + # type: (Any, Any, Any, Any, Any, Any, Any, Optional[int]) -> None + CollectionStartEvent.__init__(self, anchor, tag, implicit, start_mark, end_mark, flow_style, comment, nr_items) + self.block_seq_indent = block_seq_indent class SequenceEndEvent(CollectionEndEvent): diff --git a/lib/ruyaml/main.py b/lib/ruyaml/main.py index fd0a4fbd..19288c9f 100644 --- a/lib/ruyaml/main.py +++ b/lib/ruyaml/main.py @@ -169,6 +169,7 @@ def __init__( self.version = None self.preserve_quotes = None self.preserve_bools = None + self.preserve_block_seqs_indents = None self.allow_duplicate_keys = False # duplicate keys in map, set self.encoding = 'utf-8' self.explicit_start = None @@ -273,6 +274,7 @@ def emitter(self): line_break=self.line_break, prefix_colon=self.prefix_colon, brace_single_entry_mapping_in_flow_sequence=self.brace_single_entry_mapping_in_flow_sequence, # NOQA + preserve_block_seqs_indents=self.preserve_block_seqs_indents, dumper=self, ) setattr(self, attr, _emitter) diff --git a/lib/ruyaml/nodes.py b/lib/ruyaml/nodes.py index e9c01888..311f3297 100644 --- a/lib/ruyaml/nodes.py +++ b/lib/ruyaml/nodes.py @@ -121,9 +121,25 @@ def __init__( class SequenceNode(CollectionNode): - __slots__ = () + __slots__ = ('block_seq_indent', ) id = 'sequence' + def __init__( + self, + tag, + value, + start_mark=None, + end_mark=None, + flow_style=None, + comment=None, + anchor=None, + ): + # type: (Any, Any, Any, Any, Any, Any, Any) -> None + CollectionNode.__init__( + self, tag, value, start_mark, end_mark, flow_style, comment, anchor + ) + self.block_seq_indent = None + class MappingNode(CollectionNode): __slots__ = ('merge',) diff --git a/lib/ruyaml/representer.py b/lib/ruyaml/representer.py index d332e195..29812f5c 100644 --- a/lib/ruyaml/representer.py +++ b/lib/ruyaml/representer.py @@ -815,8 +815,19 @@ def represent_sequence(self, tag, sequence, flow_style=None): node.flow_style = self.default_flow_style else: node.flow_style = best_style + self.set_block_seq_indent(node, sequence) return node + def set_block_seq_indent(self, node, sequence): + local_block_seq_indent = 10000 + if not isinstance(sequence, CommentedSeq): + return + if not sequence.lc.data: + return + for lc_item in sequence.lc.data.values(): + local_block_seq_indent = min(local_block_seq_indent, lc_item[1] - 2) # Why '2'? + node.block_seq_indent = local_block_seq_indent + def merge_comments(self, node, comments): # type: (Any, Any) -> Any if comments is None: diff --git a/lib/ruyaml/serializer.py b/lib/ruyaml/serializer.py index 9f9425f9..83742d0d 100644 --- a/lib/ruyaml/serializer.py +++ b/lib/ruyaml/serializer.py @@ -208,6 +208,7 @@ def serialize_node(self, node, parent, index): implicit, flow_style=node.flow_style, comment=node.comment, + block_seq_indent=node.block_seq_indent, ) ) index = 0