]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-141510, PEP 814: Add frozendict support to json (#144903)
authorVictor Stinner <vstinner@python.org>
Sat, 21 Feb 2026 11:22:47 +0000 (12:22 +0100)
committerGitHub <noreply@github.com>
Sat, 21 Feb 2026 11:22:47 +0000 (12:22 +0100)
Lib/json/encoder.py
Lib/test/test_json/test_dump.py
Misc/NEWS.d/next/Library/2026-02-17-11-15-17.gh-issue-141510.ZmqEUb.rst [new file with mode: 0644]
Modules/_json.c

index 4c70e8b75ed1325c312b3732e64babbfb38b46f6..718b3254241c56535d372a338919e8936fde1979 100644 (file)
@@ -79,7 +79,7 @@ class JSONEncoder(object):
     +-------------------+---------------+
     | Python            | JSON          |
     +===================+===============+
-    | dict              | object        |
+    | dict, frozendict  | object        |
     +-------------------+---------------+
     | list, tuple       | array         |
     +-------------------+---------------+
@@ -308,7 +308,7 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
                     yield buf
                     if isinstance(value, (list, tuple)):
                         chunks = _iterencode_list(value, _current_indent_level)
-                    elif isinstance(value, dict):
+                    elif isinstance(value, (dict, frozendict)):
                         chunks = _iterencode_dict(value, _current_indent_level)
                     else:
                         chunks = _iterencode(value, _current_indent_level)
@@ -395,7 +395,7 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
                 else:
                     if isinstance(value, (list, tuple)):
                         chunks = _iterencode_list(value, _current_indent_level)
-                    elif isinstance(value, dict):
+                    elif isinstance(value, (dict, frozendict)):
                         chunks = _iterencode_dict(value, _current_indent_level)
                     else:
                         chunks = _iterencode(value, _current_indent_level)
@@ -429,7 +429,7 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
             yield _floatstr(o)
         elif isinstance(o, (list, tuple)):
             yield from _iterencode_list(o, _current_indent_level)
-        elif isinstance(o, dict):
+        elif isinstance(o, (dict, frozendict)):
             yield from _iterencode_dict(o, _current_indent_level)
         else:
             if markers is not None:
index 39470754003bb6f11b26a6fe5296e761a2648bed..9880698455ca5e4484131d486cfe7f01163f1585 100644 (file)
@@ -12,6 +12,18 @@ class TestDump:
     def test_dumps(self):
         self.assertEqual(self.dumps({}), '{}')
 
+    def test_dumps_dict(self):
+        self.assertEqual(self.dumps({'x': 1, 'y': 2}),
+                         '{"x": 1, "y": 2}')
+        self.assertEqual(self.dumps(frozendict({'x': 1, 'y': 2})),
+                         '{"x": 1, "y": 2}')
+        lst = [{'x': 1}, frozendict(y=2)]
+        self.assertEqual(self.dumps(lst),
+                         '[{"x": 1}, {"y": 2}]')
+        data = {'x': dict(a=1), 'y': frozendict(b=2)}
+        self.assertEqual(self.dumps(data),
+                         '{"x": {"a": 1}, "y": {"b": 2}}')
+
     def test_dump_skipkeys(self):
         v = {b'invalid_key': False, 'valid_key': True}
         with self.assertRaises(TypeError):
diff --git a/Misc/NEWS.d/next/Library/2026-02-17-11-15-17.gh-issue-141510.ZmqEUb.rst b/Misc/NEWS.d/next/Library/2026-02-17-11-15-17.gh-issue-141510.ZmqEUb.rst
new file mode 100644 (file)
index 0000000..59a8b41
--- /dev/null
@@ -0,0 +1,2 @@
+The :mod:`json` module now supports the :class:`frozendict` type. Patch by
+Victor Stinner.
index c7e62c4fe55c64be271dd9f613882905655767e7..cbede8f44dc065be98dab9395cd10626aeef075f 100644 (file)
@@ -1599,7 +1599,7 @@ encoder_listencode_obj(PyEncoderObject *s, PyUnicodeWriter *writer,
         _Py_LeaveRecursiveCall();
         return rv;
     }
-    else if (PyDict_Check(obj)) {
+    else if (PyAnyDict_Check(obj)) {
         if (_Py_EnterRecursiveCall(" while encoding a JSON object"))
             return -1;
         rv = encoder_listencode_dict(s, writer, obj, indent_level, indent_cache);
@@ -1838,7 +1838,7 @@ encoder_listencode_dict(PyEncoderObject *s, PyUnicodeWriter *writer,
             goto bail;
     }
 
-    if (s->sort_keys || !PyDict_CheckExact(dct)) {
+    if (s->sort_keys || !PyAnyDict_CheckExact(dct)) {
         PyObject *items = PyMapping_Items(dct);
         if (items == NULL || (s->sort_keys && PyList_Sort(items) < 0)) {
             Py_XDECREF(items);