Fix json serialization: no longer call str(obj) on str subclasses.
Replace PyUnicodeWriter_WriteStr() with PyUnicodeWriter_WriteASCII()
and private _PyUnicodeWriter_WriteStr().
d[1337] = "true.dat"
self.assertEqual(self.dumps(d, sort_keys=True), '{"1337": "true.dat"}')
+ def test_dumps_str_subclass(self):
+ # Don't call obj.__str__() on str subclasses
+
+ # str subclass which returns a different string on str(obj)
+ class StrSubclass(str):
+ def __str__(self):
+ return "StrSubclass"
+
+ obj = StrSubclass('ascii')
+ self.assertEqual(self.dumps(obj), '"ascii"')
+ self.assertEqual(self.dumps([obj]), '["ascii"]')
+ self.assertEqual(self.dumps({'key': obj}), '{"key": "ascii"}')
+
+ obj = StrSubclass('escape\n')
+ self.assertEqual(self.dumps(obj), '"escape\\n"')
+ self.assertEqual(self.dumps([obj]), '["escape\\n"]')
+ self.assertEqual(self.dumps({'key': obj}), '{"key": "escape\\n"}')
+
+ obj = StrSubclass('nonascii:é')
+ self.assertEqual(self.dumps(obj, ensure_ascii=False),
+ '"nonascii:é"')
+ self.assertEqual(self.dumps([obj], ensure_ascii=False),
+ '["nonascii:é"]')
+ self.assertEqual(self.dumps({'key': obj}, ensure_ascii=False),
+ '{"key": "nonascii:é"}')
+ self.assertEqual(self.dumps(obj), '"nonascii:\\u00e9"')
+ self.assertEqual(self.dumps([obj]), '["nonascii:\\u00e9"]')
+ self.assertEqual(self.dumps({'key': obj}),
+ '{"key": "nonascii:\\u00e9"}')
+
class TestPyDump(TestDump, PyTest): pass
from test.support import bigaddrspacetest
+# str subclass which returns a different string on str(obj)
+class StrSubclass(str):
+ def __str__(self):
+ return "StrSubclass"
+
CASES = [
('/\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\x08\x0c\n\r\t`1~!@#$%^&*()_+-=[]{}|;:\',./<>?', '"/\\\\\\"\\ucafe\\ubabe\\uab98\\ufcde\\ubcda\\uef4a\\b\\f\\n\\r\\t`1~!@#$%^&*()_+-=[]{}|;:\',./<>?"'),
('\u0123\u4567\u89ab\ucdef\uabcd\uef4a', '"\\u0123\\u4567\\u89ab\\ucdef\\uabcd\\uef4a"'),
('\U0001d120', '"\\ud834\\udd20"'),
('\u03b1\u03a9', '"\\u03b1\\u03a9"'),
("`1~!@#$%^&*()_+-={':[,]}|;.</>?", '"`1~!@#$%^&*()_+-={\':[,]}|;.</>?"'),
+ # Don't call obj.__str__() on str subclasses
+ (StrSubclass('ascii'), '"ascii"'),
]
class TestEncodeBasestringAscii:
neg_inf = NEG_INF
nan = NAN
+class StringEnum(str, Enum):
+ COLOR = "color"
+
class TestEnum:
def test_floats(self):
self.assertEqual(nd['j'], NEG_INF)
self.assertTrue(isnan(nd['n']))
+ def test_str_enum(self):
+ obj = StringEnum.COLOR
+ self.assertEqual(self.dumps(obj), '"color"')
+ self.assertEqual(self.dumps([obj]), '["color"]')
+ self.assertEqual(self.dumps({'key': obj}), '{"key": "color"}')
+
class TestPyEnum(TestEnum, PyTest): pass
class TestCEnum(TestEnum, CTest): pass
--- /dev/null
+:mod:`json`: Fix serialization: no longer call ``str(obj)`` on :class:`str`
+subclasses. Patch by Victor Stinner.
if (PyUnicodeWriter_WriteChar(writer, '"') < 0) {
return -1;
}
- if (PyUnicodeWriter_WriteStr(writer, pystr) < 0) {
+ // gh-148241: Avoid PyUnicodeWriter_WriteStr() which calls str(obj)
+ // on str subclasses
+ assert(PyUnicode_IS_ASCII(pystr));
+ if (PyUnicodeWriter_WriteASCII(writer, input, input_chars) < 0) {
return -1;
}
return PyUnicodeWriter_WriteChar(writer, '"');
if (PyUnicodeWriter_WriteChar(writer, '"') < 0) {
return -1;
}
- if (PyUnicodeWriter_WriteStr(writer, pystr) < 0) {
+ // gh-148241: Avoid PyUnicodeWriter_WriteStr() which calls str(obj)
+ // on str subclasses
+ if (_PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, pystr) < 0) {
return -1;
}
return PyUnicodeWriter_WriteChar(writer, '"');