https://github.com/python/cpython/commit/4cfa695c953e5dfdab99ade81cee960ddf4b106d
commit: 4cfa695c953e5dfdab99ade81cee960ddf4b106d
branch: main
author: Brandt Bucher <[email protected]>
committer: brandtbucher <[email protected]>
date: 2025-11-18T09:51:18-08:00
summary:
GH-141686: Break cycles created by JSONEncoder.iterencode (GH-141687)
files:
A Misc/NEWS.d/next/Library/2025-11-17-16-53-49.gh-issue-141686.V-xaoI.rst
M Lib/json/encoder.py
diff --git a/Lib/json/encoder.py b/Lib/json/encoder.py
index 5cf6d64f3eade6..4c70e8b75ed132 100644
--- a/Lib/json/encoder.py
+++ b/Lib/json/encoder.py
@@ -264,17 +264,6 @@ def floatstr(o, allow_nan=self.allow_nan,
def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
_key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot,
- ## HACK: hand-optimized bytecode; turn globals into locals
- ValueError=ValueError,
- dict=dict,
- float=float,
- id=id,
- int=int,
- isinstance=isinstance,
- list=list,
- str=str,
- tuple=tuple,
- _intstr=int.__repr__,
):
def _iterencode_list(lst, _current_indent_level):
@@ -311,7 +300,7 @@ def _iterencode_list(lst, _current_indent_level):
# Subclasses of int/float may override __repr__, but we
still
# want to encode them as integers/floats in JSON. One
example
# within the standard library is IntEnum.
- yield buf + _intstr(value)
+ yield buf + int.__repr__(value)
elif isinstance(value, float):
# see comment above for int
yield buf + _floatstr(value)
@@ -374,7 +363,7 @@ def _iterencode_dict(dct, _current_indent_level):
key = 'null'
elif isinstance(key, int):
# see comment for int/float in _make_iterencode
- key = _intstr(key)
+ key = int.__repr__(key)
elif _skipkeys:
continue
else:
@@ -399,7 +388,7 @@ def _iterencode_dict(dct, _current_indent_level):
yield 'false'
elif isinstance(value, int):
# see comment for int/float in _make_iterencode
- yield _intstr(value)
+ yield int.__repr__(value)
elif isinstance(value, float):
# see comment for int/float in _make_iterencode
yield _floatstr(value)
@@ -434,7 +423,7 @@ def _iterencode(o, _current_indent_level):
yield 'false'
elif isinstance(o, int):
# see comment for int/float in _make_iterencode
- yield _intstr(o)
+ yield int.__repr__(o)
elif isinstance(o, float):
# see comment for int/float in _make_iterencode
yield _floatstr(o)
@@ -458,4 +447,13 @@ def _iterencode(o, _current_indent_level):
raise
if markers is not None:
del markers[markerid]
- return _iterencode
+
+ def _iterencode_once(o, _current_indent_level):
+ nonlocal _iterencode, _iterencode_dict, _iterencode_list
+ try:
+ yield from _iterencode(o, _current_indent_level)
+ finally:
+ # Break reference cycles due to mutually recursive closures:
+ del _iterencode, _iterencode_dict, _iterencode_list
+
+ return _iterencode_once
diff --git
a/Misc/NEWS.d/next/Library/2025-11-17-16-53-49.gh-issue-141686.V-xaoI.rst
b/Misc/NEWS.d/next/Library/2025-11-17-16-53-49.gh-issue-141686.V-xaoI.rst
new file mode 100644
index 00000000000000..87e9cb8d69bfbc
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-11-17-16-53-49.gh-issue-141686.V-xaoI.rst
@@ -0,0 +1,2 @@
+Break reference cycles created by each call to :func:`json.dump` or
+:meth:`json.JSONEncoder.iterencode`.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]