diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_json.py b/graalpython/com.oracle.graal.python.test/src/tests/test_json.py index b94d05e36a..cf85d43747 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_json.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_json.py @@ -39,6 +39,7 @@ import json import os +import sys import unittest BIGINT_JSON_DATA = ''' @@ -109,6 +110,43 @@ def test_encode_surrogate(self): s = json.dumps({'foo': "\uda6a"}, ensure_ascii=False) assert s == '{"foo": "\uda6a"}' + def test_dump_skipkeys_invalid_middle_key(self): + assert json.dumps({"first": 1, b"bad": 2, "last": 3}, skipkeys=True) == '{"first": 1, "last": 3}' + + def test_dump_skipkeys_invalid_trailing_key(self): + assert json.dumps({"first": 1, b"bad": 2}, skipkeys=True) == '{"first": 1}' + + def test_dump_skipkeys_default_returned_dict(self): + class InvalidKey: + pass + + class Unsupported: + pass + + def default(obj): + if isinstance(obj, Unsupported): + return {"first": 1, InvalidKey(): 2, "last": 3} + raise TypeError + + assert json.dumps(Unsupported(), default=default, skipkeys=True) == '{"first": 1, "last": 3}' + + @unittest.skipUnless(sys.implementation.name == "graalpy", "fixed only in later CPython versions, bug gh-110941") + def test_dump_empty_storage_dict_subclass_with_items(self): + class StaticDict(dict): + def keys(self): + return ["a", "b"] + + def values(self): + return [1, 2] + + def items(self): + return zip(self.keys(), self.values()) + + def __len__(self): + return 2 + + assert json.dumps(StaticDict()) == '{"a": 1, "b": 2}' + def test_object_hook_nested(self): def hook(obj): return "hooked" @@ -116,3 +154,30 @@ def hook(obj): assert json.loads('{"outer": {"inner": {"leaf": 1}}}', object_hook=hook) == "hooked" assert json.loads('{"outer": {"inner": {"leaf": 1}}}', object_pairs_hook=hook) == "hooked" + def test_object_hook_nested_list_and_object(self): + def hook(obj): + return obj + + payload = ( + '{"seq": 2, "type": "request", "command": "attach", ' + '"arguments": {"justMyCode": true, "name": "Test", "type": "python", ' + '"program": "/tmp/code.py", "args": [], ' + '"connect": {"host": "127.0.0.1", "port": 5681}, ' + '"debugOptions": ["ShowReturnValue"]}}' + ) + expected = { + "seq": 2, + "type": "request", + "command": "attach", + "arguments": { + "justMyCode": True, + "name": "Test", + "type": "python", + "program": "/tmp/code.py", + "args": [], + "connect": {"host": "127.0.0.1", "port": 5681}, + "debugOptions": ["ShowReturnValue"], + }, + } + assert json.loads(payload, object_hook=hook) == expected + assert json.loads(payload, object_pairs_hook=dict) == expected diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONEncoderBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONEncoderBuiltins.java index b57df9ae59..fde91351ef 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONEncoderBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONEncoderBuiltins.java @@ -218,12 +218,15 @@ PTuple call(VirtualFrame frame, PJSONEncoder self, Object obj, @SuppressWarnings PJSONEncoder.FastEncode fastEncode = self.fastEncode; outer: while (true) { boolean skip = false; - if (state != STATE_INITIAL && state != STATE_DEFAULT_FN && !first) { + if ((state == STATE_BUILTIN_LIST || state == STATE_GENERIC_LIST) && !first) { appendStringNode.execute(builder, self.itemSeparator); } if (state == STATE_BUILTIN_DICT || state == STATE_GENERIC_DICT) { boolean isString = isString(key); if (isString || isSimpleObj(key, inliningTarget, getClassNode, isSubtypeNode)) { + if (!first) { + appendStringNode.execute(builder, self.itemSeparator); + } if (!isString) { appendCodePointNode.execute(builder, '"'); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONScannerBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONScannerBuiltins.java index cae62531f6..ba2b705988 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONScannerBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONScannerBuiltins.java @@ -583,6 +583,9 @@ private Object scanOnceUnicode(VirtualFrame frame, BoundaryCallData boundaryCall return topOfStack; } else { Object parent = stack.peek(); + if (parent instanceof TruffleString) { + parent = stack.get(stack.size() - 2); + } if (parent instanceof PList parentList) { currentListStorage = (ObjectSequenceStorage) parentList.getSequenceStorage(); nextState = ScannerState.list;