diff options
-rw-r--r-- | _test/test_comments.py | 50 | ||||
-rw-r--r-- | _test/test_float.py | 48 | ||||
-rw-r--r-- | comments.py | 1 | ||||
-rw-r--r-- | constructor.py | 30 | ||||
-rw-r--r-- | cyaml.py | 2 | ||||
-rw-r--r-- | error.py | 2 | ||||
-rw-r--r-- | main.py | 4 | ||||
-rw-r--r-- | representer.py | 49 | ||||
-rw-r--r-- | scalarfloat.py | 8 | ||||
-rw-r--r-- | tokens.py | 1 |
10 files changed, 157 insertions, 38 deletions
diff --git a/_test/test_comments.py b/_test/test_comments.py index 2557b94..8c892c6 100644 --- a/_test/test_comments.py +++ b/_test/test_comments.py @@ -715,6 +715,14 @@ class TestEmptyValueBeforeComments: - e: f """) + def test_issue_25a1(self): + round_trip("""\ + - a: b + c: d + d: # foo + e: f + """) + def test_issue_25b(self): round_trip("""\ var1: #empty @@ -729,6 +737,15 @@ class TestEmptyValueBeforeComments: c: 3 # comment c """) + def test_issue_25c1(self): + round_trip("""\ + params: + a: 1 # comment a + b: # comment b + # extra + c: 3 # comment c + """) + def test_issue_25_00(self): round_trip("""\ params: @@ -736,6 +753,39 @@ class TestEmptyValueBeforeComments: b: # comment b """) + @pytest.mark.xfail(strict=True) + def test_issue_25_01(self): + round_trip("""\ + a: # comment 1 + # comment 2 + - b: # comment 3 + c: 1 # comment 4 + """) + + @pytest.mark.xfail(strict=True) + def test_issue_25_02(self): + round_trip("""\ + a: # comment 1 + # comment 2 + - b: 2 # comment 3 + """) + + @pytest.mark.xfail(strict=True) + def test_issue_25_03(self): + round_trip("""\ + a: # comment 1 + # comment 2 + - b: 2 # comment 3 + """) + + def test_issue_25_04(self): + round_trip("""\ + a: # comment 1 + # comment 2 + b: 1 # comment 3 + """) + + test_block_scalar_commented_line_template = """\ y: p diff --git a/_test/test_float.py b/_test/test_float.py index 0adde1d..98a1f78 100644 --- a/_test/test_float.py +++ b/_test/test_float.py @@ -16,19 +16,56 @@ class TestFloat: - 1.0 - 1.00 - 23.100 + - -1.0 + - -1.00 + - -23.100 + - 42. + - -42. + - +42. """) print(data) assert 0.999 < data[0] < 1.001 assert 0.999 < data[1] < 1.001 assert 23.099 < data[2] < 23.101 + assert 0.999 < -data[3] < 1.001 + assert 0.999 < -data[4] < 1.001 + assert 23.099 < -data[5] < 23.101 + assert 41.999 < data[6] < 42.001 + assert 41.999 < -data[7] < 42.001 + assert 41.999 < data[8] < 42.001 - @pytest.mark.xfail(strict=True) - def test_round_trip_non_exp_trailing_dot(self): + def test_round_trip_zeros_0(self): + data = round_trip("""\ + - 0. + - +0. + - -0. + - 0.0 + - +0.0 + - -0.0 + - 0.00 + - +0.00 + - -0.00 + """) + print(data) + for d in data: + assert -0.00001 < d < 0.00001 + + # @pytest.mark.xfail(strict=True) + def test_round_trip_zeros_1(self): + # not sure if this should be supported, but it is + data = round_trip("""\ + - 00.0 + - +00.0 + - -00.0 + """) + print(data) + for d in data: + assert -0.00001 < d < 0.00001 + + def Xtest_round_trip_non_exp_trailing_dot(self): data = round_trip("""\ - - 42. """) print(data) - assert 41.999 < data[0] < 42.001 def test_round_trip_exp_00(self): data = round_trip("""\ @@ -43,7 +80,7 @@ class TestFloat: for d in data: assert 41.99e56 < d < 42.01e56 - @pytest.mark.xfail(strict=True) + # @pytest.mark.xfail(strict=True) def test_round_trip_exp_00f(self): data = round_trip("""\ - 42.E56 @@ -94,6 +131,7 @@ class TestFloat: - 1.230e+34 - 1.023e+34 - -1.023e+34 + - 250e6 """) def test_yaml_1_1_no_dot(self): diff --git a/comments.py b/comments.py index f8e68a6..1e2bf82 100644 --- a/comments.py +++ b/comments.py @@ -177,6 +177,7 @@ class Tag(object): self.value = None def __repr__(self): + # type: () -> Any return '{0.__class__.__name__}({0.value!r})'.format(self) diff --git a/constructor.py b/constructor.py index 7e572ad..e91961e 100644 --- a/constructor.py +++ b/constructor.py @@ -1042,16 +1042,25 @@ class RoundTripConstructor(SafeConstructor): return sign * int(value_s) def construct_yaml_float(self, node): - # type: (Any) -> float + # type: (Any) -> Any + def leading_zeros(v): + # type: (Any) -> int + lead0 = 0 + idx = 0 + while idx < len(v) and v[idx] in '0.': + if v[idx] == '0': + lead0 += 1 + idx += 1 + return lead0 underscore = None - m_sign = False + m_sign = False # type: Any value_so = to_str(self.construct_scalar(node)) value_s = value_so.replace('_', '').lower() sign = +1 if value_s[0] == '-': sign = -1 if value_s[0] in '+-': - m_sign = True + m_sign = value_s[0] value_s = value_s[1:] if value_s == '.inf': return sign * self.inf_value @@ -1077,21 +1086,22 @@ class RoundTripConstructor(SafeConstructor): # value_s is lower case independent of input if '.' not in mantissa: warnings.warn(MantissaNoDotYAML1_1Warning(node, value_so)) + lead0 = leading_zeros(mantissa) width = len(mantissa) prec = mantissa.find('.') - if prec > width - 2: - prec = 0 if m_sign: width -= 1 e_width = len(exponent) e_sign = exponent[0] in '+-' # print('sf', width, prec, m_sign, exp, e_width, e_sign) - return ScalarFloat(sign * float(value_s), width=width, prec=prec, m_sign=m_sign, - exp=exp, e_width=e_width, e_sign=e_sign) + return ScalarFloat(sign * float(value_s), # type: ignore + width=width, prec=prec, m_sign=m_sign, + m_lead0=lead0, exp=exp, e_width=e_width, e_sign=e_sign) width = len(value_so) - prec = value_so.index('.') - return ScalarFloat(sign * float(value_s), width=width, prec=prec) - # return sign * float(value_s) + prec = value_so.index('.') # you can use index, this would not be float without dot + lead0 = leading_zeros(value_so) + return ScalarFloat(sign * float(value_s), # type: ignore + width=width, prec=prec, m_sign=m_sign, m_lead0=lead0) def construct_yaml_str(self, node): # type: (Any) -> Any @@ -92,7 +92,7 @@ class CSafeDumper(CEmitter, SafeRepresenter, Resolver): # type: ignore explicit_start=explicit_start, explicit_end=explicit_end, version=version, tags=tags) - self._emitter = self._serializer = self._representer = self + self._emitter = self._serializer = self._representer = self # type: ignore SafeRepresenter.__init__(self, default_style=default_style, default_flow_style=default_flow_style) Resolver.__init__(self) @@ -195,10 +195,12 @@ warnings.simplefilter('once', UnsafeLoaderWarning) class MantissaNoDotYAML1_1Warning(YAMLWarning): def __init__(self, node, flt_str): + # type: (Any, Any) -> None self.node = node self.flt = flt_str def __str__(self): + # type: () -> Any line = self.node.start_mark.line col = self.node.start_mark.column return """ @@ -383,10 +383,10 @@ class YAML(object): delattr(self, "_serializer") delattr(self, "_emitter") if transform: - val = stream.getvalue() + val = stream.getvalue() # type: ignore if self.encoding: val = val.decode(self.encoding) - fstream.write(transform(val)) # type: ignore + fstream.write(transform(val)) return None def get_serializer_representer_emitter(self, stream, tlca): diff --git a/representer.py b/representer.py index d2cc557..a4cf481 100644 --- a/representer.py +++ b/representer.py @@ -277,7 +277,7 @@ class SafeRepresenter(BaseRepresenter): def represent_bool(self, data): # type: (Any) -> Any try: - value = self.dumper.boolean_representation[bool(data)] + value = self.dumper.boolean_representation[bool(data)] # type: ignore except AttributeError: if data: value = u'true' @@ -309,15 +309,16 @@ class SafeRepresenter(BaseRepresenter): value = u'-.inf' else: value = to_unicode(repr(data)).lower() - if self.dumper.version == (1, 1) and u'.' not in value and u'e' in value: - # Note that in some cases `repr(data)` represents a float number - # without the decimal parts. For instance: - # >>> repr(1e17) - # '1e17' - # Unfortunately, this is not a valid float representation according - # to the definition of the `!!float` tag in YAML 1.1. We fix this by adding - # '.0' before the 'e' symbol. - value = value.replace(u'e', u'.0e', 1) + if self.dumper.version == (1, 1): # type: ignore + if u'.' not in value and u'e' in value: + # Note that in some cases `repr(data)` represents a float number + # without the decimal parts. For instance: + # >>> repr(1e17) + # '1e17' + # Unfortunately, this is not a valid float representation according + # to the definition of the `!!float` tag in YAML 1.1. We fix this by adding + # '.0' before the 'e' symbol. + value = value.replace(u'e', u'.0e', 1) return self.represent_scalar(u'tag:yaml.org,2002:float', value) def represent_list(self, data): @@ -763,21 +764,22 @@ class RoundTripRepresenter(SafeRepresenter): value = u'-.inf' if value: return self.represent_scalar(u'tag:yaml.org,2002:float', value) - if data._exp is None: + if data._exp is None and data._prec > 0 and data._prec == data._width - 1: + # no exponent, but trailing dot + value = '{}{:d}.'.format(data._m_sign if data._m_sign else '', abs(int(data))) + elif data._exp is None: + # no exponent, "normal" dot prec = data._prec if prec < 1: prec = 1 # print('dw2', data._width, prec) - value = '{:{}.{}f}'.format(data, data._width, data._width-prec-1) + ms = data._m_sign if data._m_sign else '' + # -1 for the dot + value = '{}{:0{}.{}f}'.format(ms, abs(data), data._width-len(ms), data._width-prec-1) while len(value) < data._width: value += '0' else: - # print('pr', data._width, prec) - # if data._prec > 0: - # prec = data._width - data.prec - # prec = data._prec - # if prec < 1: - # prec = 1 + # exponent m, es = '{:{}e}'.format(data, data._width).split('e') w = data._width if data._prec > 0 else (data._width + 1) if data < 0: @@ -791,16 +793,23 @@ class RoundTripRepresenter(SafeRepresenter): m1 = '+' + m1 esgn = '+' if data._e_sign else '' if data._prec < 0: # mantissa without dot - # print('ew2', m2, len(m2), e) if m2 != '0': e -= len(m2) else: m2 = '' + while (len(m1) + len(m2) - (1 if data._m_sign else 0)) < data._width: + m2 += '0' + e -= 1 value = m1 + m2 + data._exp + '{:{}0{}d}'.format(e, esgn, data._e_width) - elif data._prec == 0: # mantissa with trailind dot + elif data._prec == 0: # mantissa with trailing dot e -= len(m2) value = m1 + m2 + '.' + data._exp + '{:{}0{}d}'.format(e, esgn, data._e_width) else: + if data._m_lead0 > 0: + m2 = '0' * (data._m_lead0 - 1) + m1 + m2 + m1 = '0' + m2 = m2[:-data._m_lead0] # these should be zeros + e += data._m_lead0 while len(m1) < data._prec: m1 += m2[0] m2 = m2[1:] diff --git a/scalarfloat.py b/scalarfloat.py index 01d7080..4608094 100644 --- a/scalarfloat.py +++ b/scalarfloat.py @@ -7,6 +7,7 @@ if False: # MYPY __all__ = ["ScalarFloat", "ExponentialFloat", "ExponentialCapsFloat"] +import sys from .compat import no_limit_int # NOQA @@ -16,6 +17,7 @@ class ScalarFloat(float): width = kw.pop('width', None) # type: ignore prec = kw.pop('prec', None) # type: ignore m_sign = kw.pop('m_sign', None) # type: ignore + m_lead0 = kw.pop('m_lead0', 0) # type: ignore exp = kw.pop('exp', None) # type: ignore e_width = kw.pop('e_width', None) # type: ignore e_sign = kw.pop('e_sign', None) # type: ignore @@ -24,6 +26,7 @@ class ScalarFloat(float): v._width = width v._prec = prec v._m_sign = m_sign + v._m_lead0 = m_lead0 v._exp = exp v._e_width = e_width v._e_sign = e_sign @@ -65,6 +68,11 @@ class ScalarFloat(float): x._underscore = self._underscore[:] if self._underscore is not None else None # type: ignore # NOQA return x + def dump(self, out=sys.stdout): + # type: (Any) -> Any + print('ScalarFloat({}| w:{}, p:{}, s:{}, lz:{}|{}, w:{}, s:{})'.format( + self, self._width, self._prec, self._m_sign, self._m_lead0, # type: ignore + self._exp, self._e_width, self._e_sign), file=out) # type: ignore class ExponentialFloat(ScalarFloat): def __new__(cls, value, width=None, underscore=None): @@ -243,4 +243,5 @@ class CommentToken(Token): delattr(self, 'pre_done') def __repr__(self): + # type: () -> Any return 'CommentToken({!r})'.format(self.value) |