Skip to content

Commit ffc854f

Browse files
peterzugerdpgeorge
authored andcommitted
extmod/modujson: Add support for dump/dumps separators keyword-argument.
Optionally enabled via MICROPY_PY_UJSON_SEPARATORS. Enabled by default. For dump, make sure mp_get_stream_raise is called after mod_ujson_separators since CPython does it in this order (if both separators and stream are invalid, separators will raise an exception first). Add separators argument in the docs as well. Signed-off-by: Peter Züger <zueger.peter@icloud.com> Signed-off-by: Damien George <damien@micropython.org>
1 parent 8616129 commit ffc854f

File tree

7 files changed

+99
-6
lines changed

7 files changed

+99
-6
lines changed

docs/library/ujson.rst

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,20 @@ data format.
1212
Functions
1313
---------
1414

15-
.. function:: dump(obj, stream)
15+
.. function:: dump(obj, stream, separators=None)
1616

1717
Serialise *obj* to a JSON string, writing it to the given *stream*.
1818

19-
.. function:: dumps(obj)
19+
If specified, separators should be an ``(item_separator, key_separator)``
20+
tuple. The default is ``(', ', ': ')``. To get the most compact JSON
21+
representation, you should specify ``(',', ':')`` to eliminate whitespace.
22+
23+
.. function:: dumps(obj, separators=None)
2024

2125
Return *obj* represented as a JSON string.
2226

27+
The arguments have the same meaning as in `dump`.
28+
2329
.. function:: load(stream)
2430

2531
Parse the given *stream*, interpreting it as a JSON string and

extmod/modujson.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,62 @@
3434

3535
#if MICROPY_PY_UJSON
3636

37+
#if MICROPY_PY_UJSON_SEPARATORS
38+
39+
enum {
40+
DUMP_MODE_TO_STRING = 1,
41+
DUMP_MODE_TO_STREAM = 2,
42+
};
43+
44+
STATIC mp_obj_t mod_ujson_dump_helper(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, unsigned int mode) {
45+
enum { ARG_separators };
46+
static const mp_arg_t allowed_args[] = {
47+
{ MP_QSTR_separators, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} },
48+
};
49+
50+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
51+
mp_arg_parse_all(n_args - mode, pos_args + mode, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
52+
53+
mp_print_ext_t print_ext;
54+
55+
if (args[ARG_separators].u_obj == mp_const_none) {
56+
print_ext.item_separator = ", ";
57+
print_ext.key_separator = ": ";
58+
} else {
59+
mp_obj_t *items;
60+
mp_obj_get_array_fixed_n(args[ARG_separators].u_obj, 2, &items);
61+
print_ext.item_separator = mp_obj_str_get_str(items[0]);
62+
print_ext.key_separator = mp_obj_str_get_str(items[1]);
63+
}
64+
65+
if (mode == DUMP_MODE_TO_STRING) {
66+
// dumps(obj)
67+
vstr_t vstr;
68+
vstr_init_print(&vstr, 8, &print_ext.base);
69+
mp_obj_print_helper(&print_ext.base, pos_args[0], PRINT_JSON);
70+
return mp_obj_new_str_from_vstr(&mp_type_str, &vstr);
71+
} else {
72+
// dump(obj, stream)
73+
print_ext.base.data = MP_OBJ_TO_PTR(pos_args[1]);
74+
print_ext.base.print_strn = mp_stream_write_adaptor;
75+
mp_get_stream_raise(pos_args[1], MP_STREAM_OP_WRITE);
76+
mp_obj_print_helper(&print_ext.base, pos_args[0], PRINT_JSON);
77+
return mp_const_none;
78+
}
79+
}
80+
81+
STATIC mp_obj_t mod_ujson_dump(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
82+
return mod_ujson_dump_helper(n_args, pos_args, kw_args, DUMP_MODE_TO_STREAM);
83+
}
84+
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_ujson_dump_obj, 2, mod_ujson_dump);
85+
86+
STATIC mp_obj_t mod_ujson_dumps(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
87+
return mod_ujson_dump_helper(n_args, pos_args, kw_args, DUMP_MODE_TO_STRING);
88+
}
89+
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_ujson_dumps_obj, 1, mod_ujson_dumps);
90+
91+
#else
92+
3793
STATIC mp_obj_t mod_ujson_dump(mp_obj_t obj, mp_obj_t stream) {
3894
mp_get_stream_raise(stream, MP_STREAM_OP_WRITE);
3995
mp_print_t print = {MP_OBJ_TO_PTR(stream), mp_stream_write_adaptor};
@@ -51,6 +107,8 @@ STATIC mp_obj_t mod_ujson_dumps(mp_obj_t obj) {
51107
}
52108
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ujson_dumps_obj, mod_ujson_dumps);
53109

110+
#endif
111+
54112
// The function below implements a simple non-recursive JSON parser.
55113
//
56114
// The JSON specification is at http://www.ietf.org/rfc/rfc4627.txt

py/mpconfig.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1380,6 +1380,11 @@ typedef double mp_float_t;
13801380
#define MICROPY_PY_UJSON (0)
13811381
#endif
13821382

1383+
// Whether to support the "separators" argument to dump, dumps
1384+
#ifndef MICROPY_PY_UJSON_SEPARATORS
1385+
#define MICROPY_PY_UJSON_SEPARATORS (1)
1386+
#endif
1387+
13831388
#ifndef MICROPY_PY_URE
13841389
#define MICROPY_PY_URE (0)
13851390
#endif

py/mpprint.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ typedef struct _mp_print_t {
5252
mp_print_strn_t print_strn;
5353
} mp_print_t;
5454

55+
typedef struct _mp_print_ext_t {
56+
mp_print_t base;
57+
const char *item_separator;
58+
const char *key_separator;
59+
}mp_print_ext_t;
60+
61+
#define MP_PRINT_GET_EXT(print) ((mp_print_ext_t *)print)
62+
5563
// All (non-debug) prints go through one of the two interfaces below.
5664
// 1) Wrapper for platform print function, which wraps MP_PLAT_PRINT_STRN.
5765
extern const mp_print_t mp_plat_print;

py/objdict.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,15 @@ STATIC mp_map_elem_t *dict_iter_next(mp_obj_dict_t *dict, size_t *cur) {
6969
STATIC void dict_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
7070
mp_obj_dict_t *self = MP_OBJ_TO_PTR(self_in);
7171
bool first = true;
72+
const char *item_separator = ", ";
73+
const char *key_separator = ": ";
7274
if (!(MICROPY_PY_UJSON && kind == PRINT_JSON)) {
7375
kind = PRINT_REPR;
76+
} else {
77+
#if MICROPY_PY_UJSON_SEPARATORS
78+
item_separator = MP_PRINT_GET_EXT(print)->item_separator;
79+
key_separator = MP_PRINT_GET_EXT(print)->key_separator;
80+
#endif
7481
}
7582
if (MICROPY_PY_COLLECTIONS_ORDEREDDICT && self->base.type != &mp_type_dict && kind != PRINT_JSON) {
7683
mp_printf(print, "%q(", self->base.type->name);
@@ -80,7 +87,7 @@ STATIC void dict_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_
8087
mp_map_elem_t *next = NULL;
8188
while ((next = dict_iter_next(self, &cur)) != NULL) {
8289
if (!first) {
83-
mp_print_str(print, ", ");
90+
mp_print_str(print, item_separator);
8491
}
8592
first = false;
8693
bool add_quote = MICROPY_PY_UJSON && kind == PRINT_JSON && !mp_obj_is_str_or_bytes(next->key);
@@ -91,7 +98,7 @@ STATIC void dict_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_
9198
if (add_quote) {
9299
mp_print_str(print, "\"");
93100
}
94-
mp_print_str(print, ": ");
101+
mp_print_str(print, key_separator);
95102
mp_obj_print_helper(print, next->value, kind);
96103
}
97104
mp_print_str(print, "}");

py/objlist.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,18 @@ STATIC mp_obj_t list_pop(size_t n_args, const mp_obj_t *args);
4444

4545
STATIC void list_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
4646
mp_obj_list_t *o = MP_OBJ_TO_PTR(o_in);
47+
const char *item_separator = ", ";
4748
if (!(MICROPY_PY_UJSON && kind == PRINT_JSON)) {
4849
kind = PRINT_REPR;
50+
} else {
51+
#if MICROPY_PY_UJSON_SEPARATORS
52+
item_separator = MP_PRINT_GET_EXT(print)->item_separator;
53+
#endif
4954
}
5055
mp_print_str(print, "[");
5156
for (size_t i = 0; i < o->len; i++) {
5257
if (i > 0) {
53-
mp_print_str(print, ", ");
58+
mp_print_str(print, item_separator);
5459
}
5560
mp_obj_print_helper(print, o->items[i], kind);
5661
}

py/objtuple.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,19 @@
3939

4040
void mp_obj_tuple_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
4141
mp_obj_tuple_t *o = MP_OBJ_TO_PTR(o_in);
42+
const char *item_separator = ", ";
4243
if (MICROPY_PY_UJSON && kind == PRINT_JSON) {
4344
mp_print_str(print, "[");
45+
#if MICROPY_PY_UJSON_SEPARATORS
46+
item_separator = MP_PRINT_GET_EXT(print)->item_separator;
47+
#endif
4448
} else {
4549
mp_print_str(print, "(");
4650
kind = PRINT_REPR;
4751
}
4852
for (size_t i = 0; i < o->len; i++) {
4953
if (i > 0) {
50-
mp_print_str(print, ", ");
54+
mp_print_str(print, item_separator);
5155
}
5256
mp_obj_print_helper(print, o->items[i], kind);
5357
}

0 commit comments

Comments
 (0)