]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-141686: Break cycles created by JSONEncoder.iterencode (GH-141687)
authorBrandt Bucher <brandt@python.org>
Tue, 18 Nov 2025 17:51:18 +0000 (09:51 -0800)
committerGitHub <noreply@github.com>
Tue, 18 Nov 2025 17:51:18 +0000 (09:51 -0800)
Lib/json/encoder.py
Misc/NEWS.d/next/Library/2025-11-17-16-53-49.gh-issue-141686.V-xaoI.rst [new file with mode: 0644]

index 5cf6d64f3eade60b65f1324ae394454e97883c45..4c70e8b75ed1325c312b3732e64babbfb38b46f6 100644 (file)
@@ -264,17 +264,6 @@ class JSONEncoder(object):
 
 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 _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
                     # 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 _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
                 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 _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
                     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 _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
             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 _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
                 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 (file)
index 0000000..87e9cb8
--- /dev/null
@@ -0,0 +1,2 @@
+Break reference cycles created by each call to :func:`json.dump` or
+:meth:`json.JSONEncoder.iterencode`.