forked from weaselab/weaseljson
Python bindings: add missing on_key_data callback (#7)
Closes #6 Reviewed-on: weaselab/weaseljson#7 Co-authored-by: Weaselbot <weaselbot@weaselab.dev> Co-committed-by: Weaselbot <weaselbot@weaselab.dev>
This commit is contained in:
@@ -163,6 +163,13 @@ target_link_libraries(mytest PRIVATE ${PROJECT_NAME} doctest nanobench simdjson)
|
|||||||
target_compile_options(mytest PRIVATE ${TEST_FLAGS})
|
target_compile_options(mytest PRIVATE ${TEST_FLAGS})
|
||||||
doctest_discover_tests(mytest WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
doctest_discover_tests(mytest WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||||
|
|
||||||
|
find_package(Python3 COMPONENTS Interpreter)
|
||||||
|
if(Python3_Interpreter_FOUND)
|
||||||
|
add_test(NAME python_bindings
|
||||||
|
COMMAND ${Python3_EXECUTABLE}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/test_python_bindings.py)
|
||||||
|
endif()
|
||||||
|
|
||||||
include(CMakePushCheckState)
|
include(CMakePushCheckState)
|
||||||
include(CheckCXXCompilerFlag)
|
include(CheckCXXCompilerFlag)
|
||||||
cmake_push_check_state()
|
cmake_push_check_state()
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
import weaseljson
|
||||||
|
|
||||||
|
|
||||||
|
class Recorder(weaseljson.WeaselJsonCallbacksBase):
|
||||||
|
def __init__(self):
|
||||||
|
self.keys = []
|
||||||
|
self.strings = []
|
||||||
|
self.numbers = []
|
||||||
|
self.events = []
|
||||||
|
self._current = bytearray()
|
||||||
|
|
||||||
|
def _flush(self, target, data, done):
|
||||||
|
self._current.extend(data)
|
||||||
|
if done:
|
||||||
|
target.append(bytes(self._current))
|
||||||
|
self._current = bytearray()
|
||||||
|
|
||||||
|
def on_begin_object(self):
|
||||||
|
self.events.append("begin_object")
|
||||||
|
|
||||||
|
def on_end_object(self):
|
||||||
|
self.events.append("end_object")
|
||||||
|
|
||||||
|
def on_begin_array(self):
|
||||||
|
self.events.append("begin_array")
|
||||||
|
|
||||||
|
def on_end_array(self):
|
||||||
|
self.events.append("end_array")
|
||||||
|
|
||||||
|
def on_key_data(self, data, done):
|
||||||
|
self._flush(self.keys, data, done)
|
||||||
|
|
||||||
|
def on_string_data(self, data, done):
|
||||||
|
self._flush(self.strings, data, done)
|
||||||
|
|
||||||
|
def on_number_data(self, data, done):
|
||||||
|
self._flush(self.numbers, data, done)
|
||||||
|
|
||||||
|
def on_true_literal(self):
|
||||||
|
self.events.append("true")
|
||||||
|
|
||||||
|
def on_false_literal(self):
|
||||||
|
self.events.append("false")
|
||||||
|
|
||||||
|
def on_null_literal(self):
|
||||||
|
self.events.append("null")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_all(parser, data):
|
||||||
|
for i in range(len(data)):
|
||||||
|
status = parser.parse(data[i : i + 1])
|
||||||
|
if status != weaseljson.WeaselJsonStatus.AGAIN:
|
||||||
|
return status
|
||||||
|
return parser.parse(b"")
|
||||||
|
|
||||||
|
|
||||||
|
def test_object_keys_routed_correctly():
|
||||||
|
recorder = Recorder()
|
||||||
|
with weaseljson.WeaselJsonParser(recorder) as parser:
|
||||||
|
status = parse_all(
|
||||||
|
parser, json.dumps({"hello": "world", "foo": "bar"}).encode()
|
||||||
|
)
|
||||||
|
|
||||||
|
assert status == weaseljson.WeaselJsonStatus.OK, status
|
||||||
|
assert recorder.keys == [b"hello", b"foo"], recorder.keys
|
||||||
|
assert recorder.strings == [b"world", b"bar"], recorder.strings
|
||||||
|
|
||||||
|
|
||||||
|
def test_mixed_values():
|
||||||
|
recorder = Recorder()
|
||||||
|
with weaseljson.WeaselJsonParser(recorder) as parser:
|
||||||
|
status = parse_all(
|
||||||
|
parser,
|
||||||
|
json.dumps({"answer": 42, "yes": True, "no": False, "nil": None}).encode(),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert status == weaseljson.WeaselJsonStatus.OK, status
|
||||||
|
assert recorder.keys == [b"answer", b"yes", b"no", b"nil"], recorder.keys
|
||||||
|
assert recorder.numbers == [b"42"], recorder.numbers
|
||||||
|
assert recorder.events.count("true") == 1
|
||||||
|
assert recorder.events.count("false") == 1
|
||||||
|
assert recorder.events.count("null") == 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_object_keys_routed_correctly()
|
||||||
|
test_mixed_values()
|
||||||
|
print("python bindings ok")
|
||||||
+13
-1
@@ -15,6 +15,7 @@ class WeaselJsonCallbacks(ctypes.Structure):
|
|||||||
("on_begin_object", event_callback),
|
("on_begin_object", event_callback),
|
||||||
("on_end_object", event_callback),
|
("on_end_object", event_callback),
|
||||||
("on_string_data", data_callback),
|
("on_string_data", data_callback),
|
||||||
|
("on_key_data", data_callback),
|
||||||
("on_begin_array", event_callback),
|
("on_begin_array", event_callback),
|
||||||
("on_end_array", event_callback),
|
("on_end_array", event_callback),
|
||||||
("on_number_data", data_callback),
|
("on_number_data", data_callback),
|
||||||
@@ -41,6 +42,9 @@ class WeaselJsonCallbacksBase:
|
|||||||
def on_string_data(self, data, done):
|
def on_string_data(self, data, done):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def on_key_data(self, data, done):
|
||||||
|
pass
|
||||||
|
|
||||||
def on_begin_array(self):
|
def on_begin_array(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -151,6 +155,12 @@ def on_string_data(p, buf, len, done):
|
|||||||
self.on_string_data(bytes(ctypes.string_at(buf, len)), bool(done))
|
self.on_string_data(bytes(ctypes.string_at(buf, len)), bool(done))
|
||||||
|
|
||||||
|
|
||||||
|
@ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, ctypes.c_int)
|
||||||
|
def on_key_data(p, buf, len, done):
|
||||||
|
self = ctypes.cast(p, ctypes.POINTER(ctypes.py_object)).contents.value
|
||||||
|
self.on_key_data(bytes(ctypes.string_at(buf, len)), bool(done))
|
||||||
|
|
||||||
|
|
||||||
@ctypes.CFUNCTYPE(None, ctypes.c_void_p)
|
@ctypes.CFUNCTYPE(None, ctypes.c_void_p)
|
||||||
def on_begin_array(p):
|
def on_begin_array(p):
|
||||||
self = ctypes.cast(p, ctypes.POINTER(ctypes.py_object)).contents.value
|
self = ctypes.cast(p, ctypes.POINTER(ctypes.py_object)).contents.value
|
||||||
@@ -191,6 +201,7 @@ c_callbacks = WeaselJsonCallbacks(
|
|||||||
on_begin_object,
|
on_begin_object,
|
||||||
on_end_object,
|
on_end_object,
|
||||||
on_string_data,
|
on_string_data,
|
||||||
|
on_key_data,
|
||||||
on_begin_array,
|
on_begin_array,
|
||||||
on_end_array,
|
on_end_array,
|
||||||
on_number_data,
|
on_number_data,
|
||||||
@@ -206,7 +217,8 @@ class MyCallbacks(WeaselJsonCallbacksBase):
|
|||||||
print(data)
|
print(data)
|
||||||
|
|
||||||
|
|
||||||
with WeaselJsonParser(MyCallbacks()) as parser:
|
if __name__ == "__main__":
|
||||||
|
with WeaselJsonParser(MyCallbacks()) as parser:
|
||||||
raw = json.dumps({"hello": "world", "foo": 42}).encode()
|
raw = json.dumps({"hello": "world", "foo": 42}).encode()
|
||||||
i = 0
|
i = 0
|
||||||
stride = 1
|
stride = 1
|
||||||
|
|||||||
Reference in New Issue
Block a user