Skip to content

Commit cfa797c

Browse files
bpo-24641: Improved error message for JSON unserializible keys. (#4364)
Also updated an example for default() in the module docstring. Removed quotes around type name in other error messages.
1 parent 5b48dc6 commit cfa797c

File tree

4 files changed

+19
-13
lines changed

4 files changed

+19
-13
lines changed

Lib/json/__init__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@
7676
>>> def encode_complex(obj):
7777
... if isinstance(obj, complex):
7878
... return [obj.real, obj.imag]
79-
... raise TypeError(repr(obj) + " is not JSON serializable")
79+
... raise TypeError(f'Object of type {obj.__class__.__name__} '
80+
... f'is not JSON serializable')
8081
...
8182
>>> json.dumps(2 + 1j, default=encode_complex)
8283
'[2.0, 1.0]'
@@ -344,8 +345,8 @@ def loads(s, *, encoding=None, cls=None, object_hook=None, parse_float=None,
344345
s, 0)
345346
else:
346347
if not isinstance(s, (bytes, bytearray)):
347-
raise TypeError('the JSON object must be str, bytes or bytearray, '
348-
'not {!r}'.format(s.__class__.__name__))
348+
raise TypeError(f'the JSON object must be str, bytes or bytearray, '
349+
f'not {s.__class__.__name__}')
349350
s = s.decode(detect_encoding(s), 'surrogatepass')
350351

351352
if (cls is None and object_hook is None and

Lib/json/encoder.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ def default(self, o):
176176
return JSONEncoder.default(self, o)
177177
178178
"""
179-
raise TypeError("Object of type '%s' is not JSON serializable" %
180-
o.__class__.__name__)
179+
raise TypeError(f'Object of type {o.__class__.__name__} '
180+
f'is not JSON serializable')
181181

182182
def encode(self, o):
183183
"""Return a JSON string representation of a Python data structure.
@@ -373,7 +373,8 @@ def _iterencode_dict(dct, _current_indent_level):
373373
elif _skipkeys:
374374
continue
375375
else:
376-
raise TypeError("key " + repr(key) + " is not a string")
376+
raise TypeError(f'keys must be str, int, float, bool or None, '
377+
f'not {key.__class__.__name__}')
377378
if first:
378379
first = False
379380
else:

Lib/test/test_json/test_fail.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,15 @@ def test_failures(self):
9393

9494
def test_non_string_keys_dict(self):
9595
data = {'a' : 1, (1, 2) : 2}
96+
with self.assertRaisesRegex(TypeError,
97+
'keys must be str, int, float, bool or None, not tuple'):
98+
self.dumps(data)
9699

97-
#This is for c encoder
98-
self.assertRaises(TypeError, self.dumps, data)
99-
100-
#This is for python encoder
101-
self.assertRaises(TypeError, self.dumps, data, indent=True)
100+
def test_not_serializable(self):
101+
import sys
102+
with self.assertRaisesRegex(TypeError,
103+
'Object of type module is not JSON serializable'):
104+
self.dumps(sys)
102105

103106
def test_truncated_input(self):
104107
test_cases = [

Modules/_json.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1650,8 +1650,9 @@ encoder_listencode_dict(PyEncoderObject *s, _PyAccu *acc,
16501650
continue;
16511651
}
16521652
else {
1653-
/* TODO: include repr of key */
1654-
PyErr_SetString(PyExc_TypeError, "keys must be a string");
1653+
PyErr_Format(PyExc_TypeError,
1654+
"keys must be str, int, float, bool or None, "
1655+
"not %.100s", key->ob_type->tp_name);
16551656
goto bail;
16561657
}
16571658

0 commit comments

Comments
 (0)