From cb2717724a23dd82d7777efbd08fe9d177f5eeb9 Mon Sep 17 00:00:00 2001 From: Kris-0605 Date: Thu, 9 May 2024 00:12:52 +0100 Subject: [PATCH 1/4] Add support for JSON encoding decimal.Decimal objects --- Lib/json/encoder.py | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/Lib/json/encoder.py b/Lib/json/encoder.py index 597849eca0524a3..c53ba8edc92e460 100644 --- a/Lib/json/encoder.py +++ b/Lib/json/encoder.py @@ -15,6 +15,11 @@ except ImportError: c_make_encoder = None +try: + from decimal import Decimal +except ImportError: + Decimal = None + ESCAPE = re.compile(r'[\x00-\x1f\\"\b\f\n\r\t]') ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])') HAS_UTF8 = re.compile(b'[\x80-\xff]') @@ -104,7 +109,7 @@ class JSONEncoder(object): key_separator = ': ' def __init__(self, *, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, - indent=None, separators=None, default=None): + indent=None, separators=None, default=None, support_decimal=False): """Constructor for JSONEncoder, with sensible defaults. If skipkeys is false, then it is a TypeError to attempt @@ -143,6 +148,9 @@ def __init__(self, *, skipkeys=False, ensure_ascii=True, that can't otherwise be serialized. It should return a JSON encodable version of the object or raise a ``TypeError``. + If support_decimal is true, then decimal.Decimal objects + will be encoded as real numbers, instead of raising a TypeError. + """ self.skipkeys = skipkeys @@ -151,6 +159,7 @@ def __init__(self, *, skipkeys=False, ensure_ascii=True, self.allow_nan = allow_nan self.sort_keys = sort_keys self.indent = indent + self.support_decimal = support_decimal if separators is not None: self.item_separator, self.key_separator = separators elif indent is not None: @@ -245,7 +254,7 @@ def floatstr(o, allow_nan=self.allow_nan, if (_one_shot and c_make_encoder is not None - and self.indent is None): + and self.indent is None and self.support_decimal is False): _iterencode = c_make_encoder( markers, self.default, _encoder, self.indent, self.key_separator, self.item_separator, self.sort_keys, @@ -254,11 +263,11 @@ def floatstr(o, allow_nan=self.allow_nan, _iterencode = _make_iterencode( markers, self.default, _encoder, self.indent, floatstr, self.key_separator, self.item_separator, self.sort_keys, - self.skipkeys, _one_shot) + self.skipkeys, _one_shot, self.support_decimal) return _iterencode(o, 0) def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, - _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot, + _key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot, support_decimal, ## HACK: hand-optimized bytecode; turn globals into locals ValueError=ValueError, dict=dict, @@ -270,6 +279,7 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, str=str, tuple=tuple, _intstr=int.__repr__, + Decimal=Decimal, ): if _indent is not None and not isinstance(_indent, str): @@ -315,6 +325,8 @@ def _iterencode_list(lst, _current_indent_level): elif isinstance(value, float): # see comment above for int yield buf + _floatstr(value) + elif isinstance(value, Decimal) and support_decimal and Decimal is not None: + yield buf + str(value) else: yield buf if isinstance(value, (list, tuple)): @@ -371,10 +383,12 @@ def _iterencode_dict(dct, _current_indent_level): elif isinstance(key, int): # see comment for int/float in _make_iterencode key = _intstr(key) + elif isinstance(key, Decimal) and support_decimal and Decimal is not None: + key = str(key) elif _skipkeys: continue else: - raise TypeError(f'keys must be str, int, float, bool or None, ' + raise TypeError(f'keys must be str, int, float, bool{", decimal.Decimal" if support_decimal else ""} or None, ' f'not {key.__class__.__name__}') if first: first = False @@ -396,6 +410,8 @@ def _iterencode_dict(dct, _current_indent_level): elif isinstance(value, float): # see comment for int/float in _make_iterencode yield _floatstr(value) + elif isinstance(value, Decimal) and support_decimal and Decimal is not None: + yield str(value) else: if isinstance(value, (list, tuple)): chunks = _iterencode_list(value, _current_indent_level) @@ -426,6 +442,8 @@ def _iterencode(o, _current_indent_level): elif isinstance(o, float): # see comment for int/float in _make_iterencode yield _floatstr(o) + elif isinstance(o, Decimal) and support_decimal and Decimal is not None: + yield str(o) elif isinstance(o, (list, tuple)): yield from _iterencode_list(o, _current_indent_level) elif isinstance(o, dict): From b37b1a288125a5b4531e3ff6ae6211ae94e58d58 Mon Sep 17 00:00:00 2001 From: Kris-0605 Date: Thu, 9 May 2024 01:14:55 +0100 Subject: [PATCH 2/4] Resolve merge conflict --- Lib/json/encoder.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Lib/json/encoder.py b/Lib/json/encoder.py index c53ba8edc92e460..d97efebbca0953b 100644 --- a/Lib/json/encoder.py +++ b/Lib/json/encoder.py @@ -252,9 +252,11 @@ def floatstr(o, allow_nan=self.allow_nan, return text - - if (_one_shot and c_make_encoder is not None - and self.indent is None and self.support_decimal is False): + if self.indent is None or isinstance(self.indent, str): + indent = self.indent + else: + indent = ' ' * self.indent + if _one_shot and c_make_encoder is not None and self.support_decimal is False: _iterencode = c_make_encoder( markers, self.default, _encoder, self.indent, self.key_separator, self.item_separator, self.sort_keys, From 15ac2f5565b5d88bdb03ac3c13655fd7edc2f0f6 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Thu, 9 May 2024 00:19:53 +0000 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2024-05-09-00-19-53.gh-issue-118810.XSKtWM.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2024-05-09-00-19-53.gh-issue-118810.XSKtWM.rst diff --git a/Misc/NEWS.d/next/Library/2024-05-09-00-19-53.gh-issue-118810.XSKtWM.rst b/Misc/NEWS.d/next/Library/2024-05-09-00-19-53.gh-issue-118810.XSKtWM.rst new file mode 100644 index 000000000000000..71880afbf5ab954 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-05-09-00-19-53.gh-issue-118810.XSKtWM.rst @@ -0,0 +1 @@ +Allow the json module to encode decimal.Decimal objects when the optional "support_decimal" parameter is set to true. From 69bc39047fd750cb3b87b9720eda283dbf58cc5c Mon Sep 17 00:00:00 2001 From: Kris-0605 Date: Thu, 9 May 2024 01:32:42 +0100 Subject: [PATCH 4/4] Revert "Resolve merge conflict" This reverts commit b37b1a288125a5b4531e3ff6ae6211ae94e58d58. --- Lib/json/encoder.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Lib/json/encoder.py b/Lib/json/encoder.py index d97efebbca0953b..c53ba8edc92e460 100644 --- a/Lib/json/encoder.py +++ b/Lib/json/encoder.py @@ -252,11 +252,9 @@ def floatstr(o, allow_nan=self.allow_nan, return text - if self.indent is None or isinstance(self.indent, str): - indent = self.indent - else: - indent = ' ' * self.indent - if _one_shot and c_make_encoder is not None and self.support_decimal is False: + + if (_one_shot and c_make_encoder is not None + and self.indent is None and self.support_decimal is False): _iterencode = c_make_encoder( markers, self.default, _encoder, self.indent, self.key_separator, self.item_separator, self.sort_keys,