diff --git a/_test/test_bools.py b/_test/test_bools.py new file mode 100644 index 0000000..d6620b3 --- /dev/null +++ b/_test/test_bools.py @@ -0,0 +1,77 @@ + +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 + + +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_bools = True + data = yaml.load(doutp) + buf = io.StringIO() + yaml.dump(data, stream=buf) + res = buf.getvalue() + assert res == doutp + + +class TestStability: + + def test_lowercase_boolean(self): + round_trip( + """ + - true + """ + ) + + @pytest.mark.xfail(strict=True) + def test_uppercase_boolean(self): + round_trip( + """ + - True + """ + ) + + # @pytest.mark.xfail(strict=True) # Why not failing?? + def test_yes_boolean(self): + round_trip( + """ + - yes + """ + ) + + def test_lowercase_boolean2(self): + round_trip_stabler( + """ + - true + """ + ) + + def test_uppercase_boolean2(self): + round_trip_stabler( + """ + - True + """ + ) + + def test_yes_boolean2(self): + round_trip_stabler( + """ + - yes + """ + ) diff --git a/lib/ruyaml/constructor.py b/lib/ruyaml/constructor.py index 2c89807..104a090 100644 --- a/lib/ruyaml/constructor.py +++ b/lib/ruyaml/constructor.py @@ -75,7 +75,7 @@ class BaseConstructor: yaml_constructors = {} # type: Dict[Any, Any] yaml_multi_constructors = {} # type: Dict[Any, Any] - def __init__(self, preserve_quotes=None, loader=None): + def __init__(self, preserve_quotes=None, loader=None, preserve_bools=None): # type: (Optional[bool], Any) -> None self.loader = loader if ( @@ -91,6 +91,7 @@ def __init__(self, preserve_quotes=None, loader=None): self.state_generators = [] # type: List[Any] self.deep_construct = False self._preserve_quotes = preserve_quotes + self._preserve_bools = preserve_bools self.allow_duplicate_keys = False @property @@ -1862,8 +1863,8 @@ def construct_yaml_timestamp(self, node, values=None): def construct_yaml_bool(self, node): # type: (Any) -> Any b = SafeConstructor.construct_yaml_bool(self, node) - if node.anchor: - return ScalarBoolean(b, anchor=node.anchor) + if node.anchor or self._preserve_bools: + return ScalarBoolean(b, anchor=node.anchor, orig_repr=node.value) return b diff --git a/lib/ruyaml/main.py b/lib/ruyaml/main.py index 4e54d2b..fd0a4fb 100644 --- a/lib/ruyaml/main.py +++ b/lib/ruyaml/main.py @@ -168,6 +168,7 @@ def __init__( self.prefix_colon = None self.version = None self.preserve_quotes = None + self.preserve_bools = None self.allow_duplicate_keys = False # duplicate keys in map, set self.encoding = 'utf-8' self.explicit_start = None @@ -242,7 +243,9 @@ def constructor(self): # type: () -> Any attr = '_' + sys._getframe().f_code.co_name if not hasattr(self, attr): - cnst = self.Constructor(preserve_quotes=self.preserve_quotes, loader=self) + cnst = self.Constructor(preserve_quotes=self.preserve_quotes, + preserve_bools=self.preserve_bools, + loader=self) cnst.allow_duplicate_keys = self.allow_duplicate_keys setattr(self, attr, cnst) return getattr(self, attr) diff --git a/lib/ruyaml/representer.py b/lib/ruyaml/representer.py index 5ce9855..d332e19 100644 --- a/lib/ruyaml/representer.py +++ b/lib/ruyaml/representer.py @@ -254,13 +254,16 @@ def represent_binary(self, data): def represent_bool(self, data, anchor=None): # type: (Any, Optional[Any]) -> Any - try: - value = self.dumper.boolean_representation[bool(data)] # type: ignore - except AttributeError: - if data: - value = 'true' - else: - value = 'false' + if getattr(data, 'yaml_orig_repr', None) is not None: + value = data.yaml_orig_repr + else: + try: + value = self.dumper.boolean_representation[bool(data)] # type: ignore + except AttributeError: + if data: + value = 'true' + else: + value = 'false' return self.represent_scalar('tag:yaml.org,2002:bool', value, anchor=anchor) def represent_int(self, data): diff --git a/lib/ruyaml/scalarbool.py b/lib/ruyaml/scalarbool.py index 8cae835..6da413b 100644 --- a/lib/ruyaml/scalarbool.py +++ b/lib/ruyaml/scalarbool.py @@ -18,10 +18,15 @@ class ScalarBoolean(int): + yaml_orig_repr = None + def __new__(cls, *args, **kw): # type: (Any, Any, Any) -> Any anchor = kw.pop('anchor', None) + orig_repr = kw.pop('orig_repr', None) b = int.__new__(cls, *args, **kw) + if orig_repr is not None: + b.yaml_orig_repr = orig_repr if anchor is not None: b.yaml_set_anchor(anchor, always_dump=True) return b