Skip to content

Commit 88d8956

Browse files
authored
Merge pull request #3143 from tannewt/improve_json
Add support to json.load for any object with readinto
2 parents 72f6d7e + 372bcf8 commit 88d8956

File tree

3 files changed

+62
-2
lines changed

3 files changed

+62
-2
lines changed

extmod/modujson.c

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
#include <stdio.h>
77

8+
#include "py/binary.h"
9+
#include "py/objarray.h"
810
#include "py/objlist.h"
911
#include "py/objstringio.h"
1012
#include "py/parsenum.h"
@@ -53,6 +55,8 @@ typedef struct _ujson_stream_t {
5355
mp_obj_t stream_obj;
5456
mp_uint_t (*read)(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode);
5557
int errcode;
58+
mp_obj_t python_readinto[2 + 1];
59+
mp_obj_array_t bytearray_obj;
5660
byte cur;
5761
} ujson_stream_t;
5862

@@ -73,9 +77,39 @@ STATIC byte ujson_stream_next(ujson_stream_t *s) {
7377
return s->cur;
7478
}
7579

80+
STATIC mp_uint_t ujson_python_readinto(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode) {
81+
ujson_stream_t* s = obj;
82+
s->bytearray_obj.items = buf;
83+
s->bytearray_obj.len = size;
84+
*errcode = 0;
85+
mp_obj_t ret = mp_call_method_n_kw(1, 0, s->python_readinto);
86+
if (ret == mp_const_none) {
87+
*errcode = MP_EAGAIN;
88+
return MP_STREAM_ERROR;
89+
}
90+
return mp_obj_get_int(ret);
91+
}
92+
7693
STATIC mp_obj_t _mod_ujson_load(mp_obj_t stream_obj, bool return_first_json) {
77-
const mp_stream_p_t *stream_p = mp_get_stream_raise(stream_obj, MP_STREAM_OP_READ);
78-
ujson_stream_t s = {stream_obj, stream_p->read, 0, 0};
94+
const mp_stream_p_t *stream_p = mp_proto_get(MP_QSTR_protocol_stream, stream_obj);
95+
ujson_stream_t s;
96+
if (stream_p == NULL) {
97+
mp_load_method(stream_obj, MP_QSTR_readinto, s.python_readinto);
98+
s.bytearray_obj.base.type = &mp_type_bytearray;
99+
s.bytearray_obj.typecode = BYTEARRAY_TYPECODE;
100+
s.bytearray_obj.free = 0;
101+
// len and items are set at read time
102+
s.python_readinto[2] = MP_OBJ_FROM_PTR(&s.bytearray_obj);
103+
s.stream_obj = &s;
104+
s.read = ujson_python_readinto;
105+
} else {
106+
stream_p = mp_get_stream_raise(stream_obj, MP_STREAM_OP_READ);
107+
s.stream_obj = stream_obj;
108+
s.read = stream_p->read;
109+
s.errcode = 0;
110+
s.cur = 0;
111+
}
112+
79113
JSON_DEBUG("got JSON stream\n");
80114
vstr_t vstr;
81115
vstr_init(&vstr, 8);

tests/extmod/ujson_load_readinto.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import ujson as json
2+
3+
# Test that json can load from any object with readinto
4+
5+
class Buffer:
6+
def __init__(self, data):
7+
self._data = data
8+
self._i = 0
9+
10+
def readinto(self, buf):
11+
end = self._i + len(buf)
12+
remaining = len(self._data) - self._i
13+
end = min(end, len(self._data))
14+
l = min(len(buf), remaining)
15+
buf[:l] = self._data[self._i:end]
16+
self._i += l
17+
return l
18+
19+
print(json.load(Buffer(b'null')))
20+
print(json.load(Buffer(b'"abc\\u0064e"')))
21+
print(json.load(Buffer(b'[false, true, 1, -2]')))
22+
print(json.load(Buffer(b'{"a":true}')))
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
None
2+
abcde
3+
[False, True, 1, -2]
4+
{'a': True}

0 commit comments

Comments
 (0)