py: Add native json printing using existing print framework.

Also add start of ujson module with dumps implemented.  Enabled in unix
and stmhal ports.  Test passes on both.
This commit is contained in:
Damien George 2014-09-17 22:56:34 +01:00
parent 8a9b999f1c
commit 612045f53f
18 changed files with 227 additions and 15 deletions

71
extmod/modujson.c Normal file
View File

@ -0,0 +1,71 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2014 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "mpconfig.h"
#include "misc.h"
#include "qstr.h"
#include "obj.h"
#include "runtime.h"
#if MICROPY_PY_UJSON
STATIC mp_obj_t mod_ujson_dumps(mp_obj_t obj) {
vstr_t vstr;
vstr_init(&vstr, 8);
mp_obj_print_helper((void (*)(void *env, const char *fmt, ...))vstr_printf, &vstr, obj, PRINT_JSON);
mp_obj_t ret = mp_obj_new_str(vstr.buf, vstr.len, false);
vstr_clear(&vstr);
return ret;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ujson_dumps_obj, mod_ujson_dumps);
STATIC const mp_map_elem_t mp_module_ujson_globals_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_ujson) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_dumps), (mp_obj_t)&mod_ujson_dumps_obj },
};
STATIC const mp_obj_dict_t mp_module_ujson_globals = {
.base = {&mp_type_dict},
.map = {
.all_keys_are_qstrs = 1,
.table_is_fixed_array = 1,
.used = MP_ARRAY_SIZE(mp_module_ujson_globals_table),
.alloc = MP_ARRAY_SIZE(mp_module_ujson_globals_table),
.table = (mp_map_elem_t*)mp_module_ujson_globals_table,
},
};
const mp_obj_module_t mp_module_ujson = {
.base = { &mp_type_module },
.name = MP_QSTR_ujson,
.globals = (mp_obj_dict_t*)&mp_module_ujson_globals,
};
#endif //MICROPY_PY_UJSON

View File

@ -89,3 +89,4 @@ extern struct _dummy_t mp_sys_stderr_obj;
// extmod modules
extern const mp_obj_module_t mp_module_uctypes;
extern const mp_obj_module_t mp_module_zlibd;
extern const mp_obj_module_t mp_module_ujson;

View File

@ -200,6 +200,9 @@ STATIC const mp_map_elem_t mp_builtin_module_table[] = {
#if MICROPY_PY_ZLIBD
{ MP_OBJ_NEW_QSTR(MP_QSTR_zlibd), (mp_obj_t)&mp_module_zlibd },
#endif
#if MICROPY_PY_UJSON
{ MP_OBJ_NEW_QSTR(MP_QSTR_ujson), (mp_obj_t)&mp_module_ujson },
#endif
// extra builtin modules as defined by a port
MICROPY_PORT_BUILTIN_MODULES

View File

@ -390,6 +390,10 @@ typedef double mp_float_t;
#define MICROPY_PY_ZLIBD (0)
#endif
#ifndef MICROPY_PY_UJSON
#define MICROPY_PY_UJSON (0)
#endif
/*****************************************************************************/
/* Hooks for a port to add builtins */

View File

@ -187,7 +187,8 @@ typedef enum {
PRINT_STR = 0,
PRINT_REPR = 1,
PRINT_EXC = 2, // Special format for printing exception in unhandled exception message
PRINT_EXC_SUBCLASS = 4, // Internal flag for printing exception subclasses
PRINT_JSON = 3,
PRINT_EXC_SUBCLASS = 0x80, // Internal flag for printing exception subclasses
} mp_print_kind_t;
typedef void (*mp_print_fun_t)(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t o, mp_print_kind_t kind);

View File

@ -41,10 +41,18 @@ typedef struct _mp_obj_bool_t {
STATIC void bool_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
mp_obj_bool_t *self = self_in;
if (self->value) {
print(env, "True");
if (MICROPY_PY_UJSON && kind == PRINT_JSON) {
if (self->value) {
print(env, "true");
} else {
print(env, "false");
}
} else {
print(env, "False");
if (self->value) {
print(env, "True");
} else {
print(env, "False");
}
}
}

View File

@ -60,6 +60,9 @@ STATIC mp_map_elem_t *dict_iter_next(mp_obj_dict_t *dict, mp_uint_t *cur) {
STATIC void dict_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
mp_obj_dict_t *self = self_in;
bool first = true;
if (!(MICROPY_PY_UJSON && kind == PRINT_JSON)) {
kind = PRINT_REPR;
}
print(env, "{");
mp_uint_t cur = 0;
mp_map_elem_t *next = NULL;
@ -68,9 +71,9 @@ STATIC void dict_print(void (*print)(void *env, const char *fmt, ...), void *env
print(env, ", ");
}
first = false;
mp_obj_print_helper(print, env, next->key, PRINT_REPR);
mp_obj_print_helper(print, env, next->key, kind);
print(env, ": ");
mp_obj_print_helper(print, env, next->value, PRINT_REPR);
mp_obj_print_helper(print, env, next->value, kind);
}
print(env, "}");
}

View File

@ -49,12 +49,15 @@ STATIC mp_obj_t list_pop(mp_uint_t n_args, const mp_obj_t *args);
STATIC void list_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t o_in, mp_print_kind_t kind) {
mp_obj_list_t *o = o_in;
if (!(MICROPY_PY_UJSON && kind == PRINT_JSON)) {
kind = PRINT_REPR;
}
print(env, "[");
for (mp_uint_t i = 0; i < o->len; i++) {
if (i > 0) {
print(env, ", ");
}
mp_obj_print_helper(print, env, o->items[i], PRINT_REPR);
mp_obj_print_helper(print, env, o->items[i], kind);
}
print(env, "]");
}

View File

@ -38,7 +38,11 @@ typedef struct _mp_obj_none_t {
} mp_obj_none_t;
STATIC void none_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
print(env, "None");
if (MICROPY_PY_UJSON && kind == PRINT_JSON) {
print(env, "null");
} else {
print(env, "None");
}
}
STATIC mp_obj_t none_unary_op(mp_uint_t op, mp_obj_t o_in) {

View File

@ -92,9 +92,41 @@ void mp_str_print_quoted(void (*print)(void *env, const char *fmt, ...), void *e
print(env, "%c", quote_char);
}
#if MICROPY_PY_UJSON
STATIC void str_print_json(void (*print)(void *env, const char *fmt, ...), void *env, const byte *str_data, mp_uint_t str_len) {
print(env, "\"");
for (const byte *s = str_data, *top = str_data + str_len; s < top; s++) {
if (*s == '"' || *s == '\\' || *s == '/') {
print(env, "\\%c", *s);
} else if (32 <= *s && *s <= 126) {
print(env, "%c", *s);
} else if (*s == '\b') {
print(env, "\\b");
} else if (*s == '\f') {
print(env, "\\f");
} else if (*s == '\n') {
print(env, "\\n");
} else if (*s == '\r') {
print(env, "\\r");
} else if (*s == '\t') {
print(env, "\\t");
} else {
print(env, "\\u%04x", *s);
}
}
print(env, "\"");
}
#endif
STATIC void str_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
GET_STR_DATA_LEN(self_in, str_data, str_len);
bool is_bytes = MP_OBJ_IS_TYPE(self_in, &mp_type_bytes);
#if MICROPY_PY_UJSON
if (kind == PRINT_JSON) {
str_print_json(print, env, str_data, str_len);
return;
}
#endif
if (kind == PRINT_STR && !is_bytes) {
print(env, "%.*s", str_len, str_data);
} else {

View File

@ -91,8 +91,44 @@ STATIC void uni_print_quoted(void (*print)(void *env, const char *fmt, ...), voi
print(env, "%c", quote_char);
}
#if MICROPY_PY_UJSON
STATIC void uni_print_json(void (*print)(void *env, const char *fmt, ...), void *env, const byte *str_data, uint str_len) {
print(env, "\"");
const byte *s = str_data, *top = str_data + str_len;
while (s < top) {
unichar ch;
ch = utf8_get_char(s);
s = utf8_next_char(s);
if (ch == '"' || ch == '\\' || ch == '/') {
print(env, "\\%c", ch);
} else if (32 <= ch && ch <= 126) {
print(env, "%c", ch);
} else if (*s == '\b') {
print(env, "\\b");
} else if (*s == '\f') {
print(env, "\\f");
} else if (*s == '\n') {
print(env, "\\n");
} else if (*s == '\r') {
print(env, "\\r");
} else if (*s == '\t') {
print(env, "\\t");
} else {
print(env, "\\u%04x", ch);
}
}
print(env, "\"");
}
#endif
STATIC void uni_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
GET_STR_DATA_LEN(self_in, str_data, str_len);
#if MICROPY_PY_UJSON
if (kind == PRINT_JSON) {
uni_print_json(print, env, str_data, str_len);
return;
}
#endif
if (kind == PRINT_STR) {
print(env, "%.*s", str_len, str_data);
} else {

View File

@ -43,17 +43,26 @@ STATIC mp_obj_t mp_obj_new_tuple_iterator(mp_obj_tuple_t *tuple, mp_uint_t cur);
void mp_obj_tuple_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t o_in, mp_print_kind_t kind) {
mp_obj_tuple_t *o = o_in;
print(env, "(");
if (MICROPY_PY_UJSON && kind == PRINT_JSON) {
print(env, "[");
} else {
print(env, "(");
kind = PRINT_REPR;
}
for (mp_uint_t i = 0; i < o->len; i++) {
if (i > 0) {
print(env, ", ");
}
mp_obj_print_helper(print, env, o->items[i], PRINT_REPR);
mp_obj_print_helper(print, env, o->items[i], kind);
}
if (o->len == 1) {
print(env, ",");
if (MICROPY_PY_UJSON && kind == PRINT_JSON) {
print(env, "]");
} else {
if (o->len == 1) {
print(env, ",");
}
print(env, ")");
}
print(env, ")");
}
STATIC mp_obj_t mp_obj_tuple_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {

View File

@ -112,6 +112,7 @@ PY_O_BASENAME = \
pfenv_printf.o \
../extmod/moductypes.o \
../extmod/modzlibd.o \
../extmod/modujson.o \
# prepend the build destination prefix to the py object files
PY_O = $(addprefix $(PY_BUILD)/, $(PY_O_BASENAME))

View File

@ -463,3 +463,8 @@ Q(deleter)
Q(zlibd)
Q(decompress)
#endif
#if MICROPY_PY_UJSON
Q(ujson)
Q(dumps)
#endif

View File

@ -57,6 +57,7 @@
#define MICROPY_PY_IO_FILEIO (1)
#define MICROPY_PY_UCTYPES (1)
#define MICROPY_PY_ZLIBD (1)
#define MICROPY_PY_UJSON (1)
#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1)
#define MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE (0)

View File

@ -0,0 +1,23 @@
try:
import ujson as json
except ImportError:
import json
print(json.dumps(False))
print(json.dumps(True))
print(json.dumps(None))
print(json.dumps(1))
print(json.dumps(1.2))
print(json.dumps('abc'))
print(json.dumps('\x01\x7e\x7f\x80\u1234'))
print(json.dumps([]))
print(json.dumps([1]))
print(json.dumps([1, 2]))
print(json.dumps([1, True]))
print(json.dumps(()))
print(json.dumps((1,)))
print(json.dumps((1, 2)))
print(json.dumps((1, (2, 3))))
print(json.dumps({}))
print(json.dumps({"a":1}))
print(json.dumps({"a":(2,[3,None])}))

View File

@ -3,6 +3,7 @@
import os
import subprocess
import sys
import platform
import argparse
from glob import glob
@ -37,6 +38,11 @@ def run_tests(pyb, tests, args):
if pyb is not None:
skip_tests.add('float/float_divmod.py') # tested by float/float_divmod_relaxed.py instead
# Some tests are known to fail on 64-bit machines
if pyb is None and platform.architecture()[0] == '64bit':
skip_tests.add('extmod/uctypes_ptr_le.py')
skip_tests.add('extmod/uctypes_ptr_native_le.py')
# Some tests are known to fail with native emitter
# Remove them from the below when they work
if args.emit == 'native':
@ -153,10 +159,10 @@ def main():
if args.test_dirs is None:
if pyb is None:
# run PC tests
test_dirs = ('basics', 'micropython', 'float', 'import', 'io', 'misc', 'unicode', 'unix')
test_dirs = ('basics', 'micropython', 'float', 'import', 'io', 'misc', 'unicode', 'extmod', 'unix')
else:
# run pyboard tests
test_dirs = ('basics', 'micropython', 'float', 'misc', 'pyb', 'pybnative', 'inlineasm')
test_dirs = ('basics', 'micropython', 'float', 'misc', 'extmod', 'pyb', 'pybnative', 'inlineasm')
else:
# run tests from these directories
test_dirs = args.test_dirs

View File

@ -58,6 +58,7 @@
#define MICROPY_PY_UCTYPES (1)
#define MICROPY_PY_ZLIBD (1)
#define MICROPY_PY_UJSON (1)
// Define to MICROPY_ERROR_REPORTING_DETAILED to get function, etc.
// names in exception messages (may require more RAM).