From 6d80bf563e1550d5e54b3546b50b45efd76212d6 Mon Sep 17 00:00:00 2001 From: Xnot <28331593+Xnot@users.noreply.github.com> Date: Wed, 18 Jan 2023 07:14:47 +0000 Subject: [PATCH 1/2] preserve float formatting when pretty formatting --- pre_commit_hooks/pretty_format_json.py | 67 ++++++++++++++++++++++++- testing/resources/float_formatting.json | 12 +++++ tests/pretty_format_json_test.py | 1 + 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 testing/resources/float_formatting.json diff --git a/pre_commit_hooks/pretty_format_json.py b/pre_commit_hooks/pretty_format_json.py index 627a11cc..e9e18a0f 100644 --- a/pre_commit_hooks/pretty_format_json.py +++ b/pre_commit_hooks/pretty_format_json.py @@ -4,10 +4,74 @@ import json import sys from difflib import unified_diff +from typing import Any +from typing import Iterator from typing import Mapping from typing import Sequence +class FloatString(float): + def __init__(self, str_value: str): + self.str_value = str_value + + def __repr__(self): + return self.str_value + + +def float_parser(value: str) -> FloatString: + return FloatString(value) + + +class FloatPreservingEncoder(json.JSONEncoder): + def iterencode(self, o: Any, _one_shot: bool = ...) -> Iterator[str] | str: + if self.check_circular: + markers = {} + else: + markers = None + if self.ensure_ascii: + _encoder = json.encoder.encode_basestring_ascii + else: + _encoder = json.encoder.encode_basestring + + def floatstr( + o, + allow_nan=self.allow_nan, + _repr=FloatString.__repr__, + _inf=json.encoder.INFINITY, + _neginf=-json.encoder.INFINITY, + ): + + if o != o: + text = "NaN" + elif o == _inf: + text = "Infinity" + elif o == _neginf: + text = "-Infinity" + else: + return _repr(o) + + if not allow_nan: + raise ValueError( + "Out of range float values are not JSON compliant: " + repr(o) + ) + + return text + + _iterencode = json.encoder._make_iterencode( + markers, + self.default, + _encoder, + self.indent, + floatstr, + self.key_separator, + self.item_separator, + self.sort_keys, + self.skipkeys, + _one_shot, + ) + return _iterencode(o, 0) + + def _get_pretty_format( contents: str, indent: str, @@ -23,9 +87,10 @@ def pairs_first(pairs: Sequence[tuple[str, str]]) -> Mapping[str, str]: after.sort() return dict(before + after) json_pretty = json.dumps( - json.loads(contents, object_pairs_hook=pairs_first), + json.loads(contents, object_pairs_hook=pairs_first, parse_float=float_parser), indent=indent, ensure_ascii=ensure_ascii, + cls=FloatPreservingEncoder, ) return f'{json_pretty}\n' diff --git a/testing/resources/float_formatting.json b/testing/resources/float_formatting.json new file mode 100644 index 00000000..d0de1e4f --- /dev/null +++ b/testing/resources/float_formatting.json @@ -0,0 +1,12 @@ +{ + "exponential": 1e9, + "high_precision": 4.4257052820783003, + "list": [ + 1e9, + 4.4257052820783003 + ], + "mapping": { + "exponential": 1e9, + "high_precision": 4.4257052820783003 + } +} diff --git a/tests/pretty_format_json_test.py b/tests/pretty_format_json_test.py index 5ded724a..3e19fd8d 100644 --- a/tests/pretty_format_json_test.py +++ b/tests/pretty_format_json_test.py @@ -23,6 +23,7 @@ def test_parse_num_to_int(): ('unsorted_pretty_formatted_json.json', 1), ('non_ascii_pretty_formatted_json.json', 1), ('pretty_formatted_json.json', 0), + ('float_formatting.json', 0), ), ) def test_main(filename, expected_retval): From 20792c1e1f6d19a20bfeefb03c31df4483a7f999 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 18 Jan 2023 07:22:26 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- pre_commit_hooks/pretty_format_json.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pre_commit_hooks/pretty_format_json.py b/pre_commit_hooks/pretty_format_json.py index e9e18a0f..a51d7a79 100644 --- a/pre_commit_hooks/pretty_format_json.py +++ b/pre_commit_hooks/pretty_format_json.py @@ -42,17 +42,17 @@ def floatstr( ): if o != o: - text = "NaN" + text = 'NaN' elif o == _inf: - text = "Infinity" + text = 'Infinity' elif o == _neginf: - text = "-Infinity" + text = '-Infinity' else: return _repr(o) if not allow_nan: raise ValueError( - "Out of range float values are not JSON compliant: " + repr(o) + 'Out of range float values are not JSON compliant: ' + repr(o), ) return text