Commit 7f9d1d6a authored by Damien George's avatar Damien George
Browse files

py: Overhaul and simplify printf/pfenv mechanism.

Previous to this patch the printing mechanism was a bit of a tangled
mess.  This patch attempts to consolidate printing into one interface.

All (non-debug) printing now uses the mp_print* family of functions,
mainly mp_printf.  All these functions take an mp_print_t structure as
their first argument, and this structure defines the printing backend
through the "print_strn" function of said structure.

Printing from the uPy core can reach the platform-defined print code via
two paths: either through mp_sys_stdout_obj (defined pert port) in
conjunction with mp_stream_write; or through the mp_plat_print structure
which uses the MP_PLAT_PRINT_STRN macro to define how string are printed
on the platform.  The former is only used when MICROPY_PY_IO is defined.

With this new scheme printing is generally more efficient (less layers
to go through, less arguments to pass), and, given an mp_print_t*
structure, one can call mp_print_str for efficiency instead of
mp_printf("%s", ...).  Code size is also reduced by around 200 bytes on
Thumb2 archs.
parent 56beb017
......@@ -147,6 +147,8 @@ typedef struct _vstr_t {
void vstr_init(vstr_t *vstr, size_t alloc);
void vstr_init_len(vstr_t *vstr, size_t len);
void vstr_init_fixed_buf(vstr_t *vstr, size_t alloc, char *buf);
struct _mp_print_t;
void vstr_init_print(vstr_t *vstr, size_t alloc, struct _mp_print_t *print);
void vstr_clear(vstr_t *vstr);
vstr_t *vstr_new(void);
vstr_t *vstr_new_size(size_t alloc);
......
......@@ -35,7 +35,6 @@
#include "py/runtime.h"
#include "py/builtin.h"
#include "py/stream.h"
#include "py/pfenv.h"
#if MICROPY_PY_BUILTINS_FLOAT
#include <math.h>
......@@ -413,9 +412,7 @@ STATIC mp_obj_t mp_builtin_print(mp_uint_t n_args, const mp_obj_t *args, mp_map_
stream_obj = file_elem->value;
}
pfenv_t pfenv;
pfenv.data = stream_obj;
pfenv.print_strn = (void (*)(void *, const char *, mp_uint_t))mp_stream_write;
mp_print_t print = {stream_obj, (mp_print_strn_t)mp_stream_write};
#endif
for (mp_uint_t i = 0; i < n_args; i++) {
if (i > 0) {
......@@ -426,7 +423,7 @@ STATIC mp_obj_t mp_builtin_print(mp_uint_t n_args, const mp_obj_t *args, mp_map_
#endif
}
#if MICROPY_PY_IO
mp_obj_print_helper((void (*)(void *env, const char *fmt, ...))pfenv_printf, &pfenv, args[i], PRINT_STR);
mp_obj_print_helper(&print, args[i], PRINT_STR);
#else
mp_obj_print(args[i], PRINT_STR);
#endif
......@@ -443,10 +440,8 @@ MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_print_obj, 0, mp_builtin_print);
STATIC mp_obj_t mp_builtin___repl_print__(mp_obj_t o) {
if (o != mp_const_none) {
#if MICROPY_PY_IO
pfenv_t pfenv;
pfenv.data = &mp_sys_stdout_obj;
pfenv.print_strn = (void (*)(void *, const char *, mp_uint_t))mp_stream_write;
mp_obj_print_helper((void (*)(void *env, const char *fmt, ...))pfenv_printf, &pfenv, o, PRINT_REPR);
mp_print_t print = {&mp_sys_stdout_obj, (mp_print_strn_t)mp_stream_write};
mp_obj_print_helper(&print, o, PRINT_REPR);
mp_stream_write(&mp_sys_stdout_obj, "\n", 1);
#else
mp_obj_print(o, PRINT_REPR);
......@@ -459,8 +454,9 @@ MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin___repl_print___obj, mp_builtin___repl_print
STATIC mp_obj_t mp_builtin_repr(mp_obj_t o_in) {
vstr_t vstr;
vstr_init(&vstr, 16);
mp_obj_print_helper((void (*)(void *env, const char *fmt, ...))vstr_printf, &vstr, o_in, PRINT_REPR);
mp_print_t print;
vstr_init_print(&vstr, 16, &print);
mp_obj_print_helper(&print, o_in, PRINT_REPR);
return mp_obj_new_str_from_vstr(&mp_type_str, &vstr);
}
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_repr_obj, mp_builtin_repr);
......
......@@ -31,7 +31,6 @@
#include "py/objtuple.h"
#include "py/objstr.h"
#include "py/objint.h"
#include "py/pfenv.h"
#include "py/stream.h"
#if MICROPY_PY_SYS
......@@ -78,12 +77,10 @@ STATIC mp_obj_t mp_sys_print_exception(mp_uint_t n_args, const mp_obj_t *args) {
stream_obj = args[1];
}
pfenv_t pfenv;
pfenv.data = stream_obj;
pfenv.print_strn = (void (*)(void *, const char *, mp_uint_t))mp_stream_write;
mp_obj_print_exception((void (*)(void *env, const char *fmt, ...))pfenv_printf, &pfenv, args[0]);
mp_print_t print = {stream_obj, (mp_print_strn_t)mp_stream_write};
mp_obj_print_exception(&print, args[0]);
#else
mp_obj_print_exception(printf_wrapper, NULL, args[0]);
mp_obj_print_exception(&mp_plat_print, args[0]);
#endif
return mp_const_none;
......
......@@ -691,7 +691,7 @@ typedef double mp_float_t;
#define MICROPY_MAKE_POINTER_CALLABLE(p) (p)
#endif
// If these MP_PLAT_* macros are overridden then the memory allocated by them
// If these MP_PLAT_*_EXEC macros are overridden then the memory allocated by them
// must be somehow reachable for marking by the GC, since the native code
// generators store pointers to GC managed memory in the code.
#ifndef MP_PLAT_ALLOC_EXEC
......@@ -702,6 +702,11 @@ typedef double mp_float_t;
#define MP_PLAT_FREE_EXEC(ptr, size) m_del(byte, ptr, size)
#endif
// This macro is used to do all output (except when MICROPY_PY_IO is defined)
#ifndef MP_PLAT_PRINT_STRN
#define MP_PLAT_PRINT_STRN(str, len) printf("%.*s", (int)len, str)
#endif
#ifndef MP_SSIZE_MAX
#define MP_SSIZE_MAX SSIZE_MAX
#endif
......
......@@ -3,7 +3,7 @@
*
* The MIT License (MIT)
*
* Copyright (c) 2013, 2014 Damien P. George
* Copyright (c) 2013-2015 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
......@@ -24,11 +24,16 @@
* THE SOFTWARE.
*/
#include <assert.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "py/mpprint.h"
#include "py/obj.h"
#include "py/objint.h"
#include "py/pfenv.h"
#include "py/runtime.h"
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
#include <stdio.h>
......@@ -41,11 +46,22 @@
static const char pad_spaces[] = " ";
static const char pad_zeroes[] = "0000000000000000";
void pfenv_vstr_add_strn(void *data, const char *str, mp_uint_t len) {
vstr_add_strn(data, str, len);
STATIC void plat_print_strn(void *env, const char *str, mp_uint_t len) {
(void)env;
MP_PLAT_PRINT_STRN(str, len);
}
int pfenv_print_strn(const pfenv_t *pfenv, const char *str, mp_uint_t len, int flags, char fill, int width) {
const mp_print_t mp_plat_print = {NULL, plat_print_strn};
int mp_print_str(const mp_print_t *print, const char *str) {
mp_uint_t len = strlen(str);
if (len) {
print->print_strn(print->data, str, len);
}
return len;
}
int mp_print_strn(const mp_print_t *print, const char *str, mp_uint_t len, int flags, char fill, int width) {
int left_pad = 0;
int right_pad = 0;
int pad = width - len;
......@@ -53,7 +69,7 @@ int pfenv_print_strn(const pfenv_t *pfenv, const char *str, mp_uint_t len, int f
int total_chars_printed = 0;
const char *pad_chars;
if (!fill || fill == ' ' ) {
if (!fill || fill == ' ') {
pad_chars = pad_spaces;
pad_size = sizeof(pad_spaces) - 1;
} else if (fill == '0') {
......@@ -82,12 +98,12 @@ int pfenv_print_strn(const pfenv_t *pfenv, const char *str, mp_uint_t len, int f
if (p > pad_size) {
p = pad_size;
}
pfenv->print_strn(pfenv->data, pad_chars, p);
print->print_strn(print->data, pad_chars, p);
left_pad -= p;
}
}
if (len) {
pfenv->print_strn(pfenv->data, str, len);
print->print_strn(print->data, str, len);
total_chars_printed += len;
}
if (right_pad > 0) {
......@@ -97,7 +113,7 @@ int pfenv_print_strn(const pfenv_t *pfenv, const char *str, mp_uint_t len, int f
if (p > pad_size) {
p = pad_size;
}
pfenv->print_strn(pfenv->data, pad_chars, p);
print->print_strn(print->data, pad_chars, p);
right_pad -= p;
}
}
......@@ -109,8 +125,8 @@ int pfenv_print_strn(const pfenv_t *pfenv, const char *str, mp_uint_t len, int f
#define INT_BUF_SIZE (sizeof(mp_int_t) * 4)
// This function is used by stmhal port to implement printf.
// It needs to be a separate function to pfenv_print_mp_int, since converting to a mp_int looses the MSB.
int pfenv_print_int(const pfenv_t *pfenv, mp_uint_t x, int sgn, int base, int base_char, int flags, char fill, int width) {
// It needs to be a separate function to mp_print_mp_int, since converting to a mp_int looses the MSB.
int mp_print_int(const mp_print_t *print, mp_uint_t x, int sgn, int base, int base_char, int flags, char fill, int width) {
char sign = 0;
if (sgn) {
if ((mp_int_t)x < 0) {
......@@ -156,12 +172,12 @@ int pfenv_print_int(const pfenv_t *pfenv, mp_uint_t x, int sgn, int base, int ba
int len = 0;
if (flags & PF_FLAG_PAD_AFTER_SIGN) {
if (sign) {
len += pfenv_print_strn(pfenv, &sign, 1, flags, fill, 1);
len += mp_print_strn(print, &sign, 1, flags, fill, 1);
width--;
}
if (prefix_char) {
len += pfenv_print_strn(pfenv, "0", 1, flags, fill, 1);
len += pfenv_print_strn(pfenv, &prefix_char, 1, flags, fill, 1);
len += mp_print_strn(print, "0", 1, flags, fill, 1);
len += mp_print_strn(print, &prefix_char, 1, flags, fill, 1);
width -= 2;
}
} else {
......@@ -174,11 +190,11 @@ int pfenv_print_int(const pfenv_t *pfenv, mp_uint_t x, int sgn, int base, int ba
}
}
len += pfenv_print_strn(pfenv, b, buf + INT_BUF_SIZE - b, flags, fill, width);
len += mp_print_strn(print, b, buf + INT_BUF_SIZE - b, flags, fill, width);
return len;
}
int pfenv_print_mp_int(const pfenv_t *pfenv, mp_obj_t x, int base, int base_char, int flags, char fill, int width, int prec) {
int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char, int flags, char fill, int width, int prec) {
if (!MP_OBJ_IS_INT(x)) {
// This will convert booleans to int, or raise an error for
// non-integer types.
......@@ -282,16 +298,16 @@ int pfenv_print_mp_int(const pfenv_t *pfenv, mp_obj_t x, int base, int base_char
int len = 0;
if (spaces_before) {
len += pfenv_print_strn(pfenv, "", 0, 0, ' ', spaces_before);
len += mp_print_strn(print, "", 0, 0, ' ', spaces_before);
}
if (flags & PF_FLAG_PAD_AFTER_SIGN) {
// pad after sign implies pad after prefix as well.
if (sign) {
len += pfenv_print_strn(pfenv, &sign, 1, 0, 0, 1);
len += mp_print_strn(print, &sign, 1, 0, 0, 1);
width--;
}
if (prefix_len) {
len += pfenv_print_strn(pfenv, prefix, prefix_len, 0, 0, 1);
len += mp_print_strn(print, prefix, prefix_len, 0, 0, 1);
width -= prefix_len;
}
}
......@@ -299,10 +315,10 @@ int pfenv_print_mp_int(const pfenv_t *pfenv, mp_obj_t x, int base, int base_char
width = prec;
}
len += pfenv_print_strn(pfenv, str, fmt_size, flags, fill, width);
len += mp_print_strn(print, str, fmt_size, flags, fill, width);
if (spaces_after) {
len += pfenv_print_strn(pfenv, "", 0, 0, ' ', spaces_after);
len += mp_print_strn(print, "", 0, 0, ' ', spaces_after);
}
if (buf != stack_buf) {
......@@ -312,7 +328,7 @@ int pfenv_print_mp_int(const pfenv_t *pfenv, mp_obj_t x, int base, int base_char
}
#if MICROPY_PY_BUILTINS_FLOAT
int pfenv_print_float(const pfenv_t *pfenv, mp_float_t f, char fmt, int flags, char fill, int width, int prec) {
int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, int flags, char fill, int width, int prec) {
char buf[32];
char sign = '\0';
int chrs = 0;
......@@ -361,7 +377,7 @@ int pfenv_print_float(const pfenv_t *pfenv, mp_float_t f, char fmt, int flags, c
if (*s <= '9' || (flags & PF_FLAG_PAD_NAN_INF)) {
// We have a number, or we have a inf/nan and PAD_NAN_INF is set
// With '{:06e}'.format(float('-inf')) you get '-00inf'
chrs += pfenv_print_strn(pfenv, &buf[0], 1, 0, 0, 1);
chrs += mp_print_strn(print, &buf[0], 1, 0, 0, 1);
width--;
len--;
}
......@@ -373,8 +389,168 @@ int pfenv_print_float(const pfenv_t *pfenv, mp_float_t f, char fmt, int flags, c
// so suppress the zero fill.
fill = ' ';
}
chrs += pfenv_print_strn(pfenv, s, len, flags, fill, width);
chrs += mp_print_strn(print, s, len, flags, fill, width);
return chrs;
}
#endif
int mp_printf(const mp_print_t *print, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
int ret = mp_vprintf(print, fmt, ap);
va_end(ap);
return ret;
}
int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) {
int chrs = 0;
for (;;) {
{
const char *f = fmt;
while (*f != '\0' && *f != '%') {
++f; // XXX UTF8 advance char
}
if (f > fmt) {
print->print_strn(print->data, fmt, f - fmt);
chrs += f - fmt;
fmt = f;
}
}
if (*fmt == '\0') {
break;
}
// move past % character
++fmt;
// parse flags, if they exist
int flags = 0;
char fill = ' ';
while (*fmt != '\0') {
if (*fmt == '-') flags |= PF_FLAG_LEFT_ADJUST;
else if (*fmt == '+') flags |= PF_FLAG_SHOW_SIGN;
else if (*fmt == ' ') flags |= PF_FLAG_SPACE_SIGN;
else if (*fmt == '!') flags |= PF_FLAG_NO_TRAILZ;
else if (*fmt == '0') {
flags |= PF_FLAG_PAD_AFTER_SIGN;
fill = '0';
} else break;
++fmt;
}
// parse width, if it exists
int width = 0;
for (; '0' <= *fmt && *fmt <= '9'; ++fmt) {
width = width * 10 + *fmt - '0';
}
// parse precision, if it exists
int prec = -1;
if (*fmt == '.') {
++fmt;
if (*fmt == '*') {
++fmt;
prec = va_arg(args, int);
} else {
prec = 0;
for (; '0' <= *fmt && *fmt <= '9'; ++fmt) {
prec = prec * 10 + *fmt - '0';
}
}
if (prec < 0) {
prec = 0;
}
}
// parse long specifiers (current not used)
//bool long_arg = false;
if (*fmt == 'l') {
++fmt;
//long_arg = true;
}
if (*fmt == '\0') {
break;
}
switch (*fmt) {
case 'b':
if (va_arg(args, int)) {
chrs += mp_print_strn(print, "true", 4, flags, fill, width);
} else {
chrs += mp_print_strn(print, "false", 5, flags, fill, width);
}
break;
case 'c':
{
char str = va_arg(args, int);
chrs += mp_print_strn(print, &str, 1, flags, fill, width);
break;
}
case 's':
{
const char *str = va_arg(args, const char*);
if (str) {
if (prec < 0) {
prec = strlen(str);
}
chrs += mp_print_strn(print, str, prec, flags, fill, width);
} else {
chrs += mp_print_strn(print, "(null)", 6, flags, fill, width);
}
break;
}
case 'u':
chrs += mp_print_int(print, va_arg(args, int), 0, 10, 'a', flags, fill, width);
break;
case 'd':
chrs += mp_print_int(print, va_arg(args, int), 1, 10, 'a', flags, fill, width);
break;
case 'x':
chrs += mp_print_int(print, va_arg(args, int), 0, 16, 'a', flags, fill, width);
break;
case 'X':
chrs += mp_print_int(print, va_arg(args, int), 0, 16, 'A', flags, fill, width);
break;
case 'p':
case 'P': // don't bother to handle upcase for 'P'
chrs += mp_print_int(print, va_arg(args, unsigned int), 0, 16, 'a', flags, fill, width);
break;
#if MICROPY_PY_BUILTINS_FLOAT
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
{
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
mp_float_t f = va_arg(args, double);
chrs += mp_print_float(print, f, *fmt, flags, fill, width, prec);
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
// Currently mp_print_float uses snprintf, but snprintf
// itself may be implemented in terms of mp_vprintf() for
// some ports. So, for extra caution, this case is handled
// with assert below. Note that currently ports which
// use MICROPY_FLOAT_IMPL_DOUBLE, don't call mp_vprintf()
// with float format specifier at all.
// TODO: resolve this completely
assert(0);
//#error Calling mp_print_float with double not supported from within printf
#else
#error Unknown MICROPY FLOAT IMPL
#endif
break;
}
#endif
default:
print->print_strn(print->data, fmt, 1);
chrs += 1;
break;
}
++fmt;
}
return chrs;
}
......@@ -23,12 +23,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef __MICROPY_INCLUDED_PY_PFENV_H__
#define __MICROPY_INCLUDED_PY_PFENV_H__
#ifndef __MICROPY_INCLUDED_PY_MPPRINT_H__
#define __MICROPY_INCLUDED_PY_MPPRINT_H__
#include <stdarg.h>
#include "py/obj.h"
#include "py/mpconfig.h"
#define PF_FLAG_LEFT_ADJUST (0x001)
#define PF_FLAG_SHOW_SIGN (0x002)
......@@ -42,24 +40,28 @@
#define PF_FLAG_PAD_NAN_INF (0x200)
#define PF_FLAG_SHOW_OCTAL_LETTER (0x400)
typedef struct _pfenv_t {
typedef void (*mp_print_strn_t)(void *data, const char *str, mp_uint_t len);
typedef struct _mp_print_t {
void *data;
void (*print_strn)(void *, const char *str, mp_uint_t len);
} pfenv_t;
mp_print_strn_t print_strn;
} mp_print_t;
void pfenv_vstr_add_strn(void *data, const char *str, mp_uint_t len);
// Wrapper for platform print function, which wraps MP_PLAT_PRINT_STRN.
// All (non-debug) prints go through this interface (except some which
// go through mp_sys_stdout_obj if MICROPY_PY_IO is defined).
extern const mp_print_t mp_plat_print;
int pfenv_print_strn(const pfenv_t *pfenv, const char *str, mp_uint_t len, int flags, char fill, int width);
int pfenv_print_int(const pfenv_t *pfenv, mp_uint_t x, int sgn, int base, int base_char, int flags, char fill, int width);
int pfenv_print_mp_int(const pfenv_t *pfenv, mp_obj_t x, int base, int base_char, int flags, char fill, int width, int prec);
int mp_print_str(const mp_print_t *print, const char *str);
int mp_print_strn(const mp_print_t *print, const char *str, mp_uint_t len, int flags, char fill, int width);
int mp_print_int(const mp_print_t *print, mp_uint_t x, int sgn, int base, int base_char, int flags, char fill, int width);
#if MICROPY_PY_BUILTINS_FLOAT
int pfenv_print_float(const pfenv_t *pfenv, mp_float_t f, char fmt, int flags, char fill, int width, int prec);
int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, int flags, char fill, int width, int prec);
#endif
int pfenv_vprintf(const pfenv_t *pfenv, const char *fmt, va_list args);
int pfenv_printf(const pfenv_t *pfenv, const char *fmt, ...);
// Wrapper for system printf
void printf_wrapper(void *env, const char *fmt, ...);
int mp_printf(const mp_print_t *print, const char *fmt, ...);
#ifdef va_start
int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args);
#endif
#endif // __MICROPY_INCLUDED_PY_PFENV_H__
#endif // __MICROPY_INCLUDED_PY_MPPRINT_H__
......@@ -36,7 +36,7 @@
#include "py/runtime0.h"
#include "py/runtime.h"
#include "py/stackctrl.h"
#include "py/pfenv.h"
//#include "py/pfenv.h"
#include "py/stream.h" // for mp_obj_print
mp_obj_type_t *mp_obj_get_type(mp_const_obj_t o_in) {
......@@ -54,20 +54,20 @@ const char *mp_obj_get_type_str(mp_const_obj_t o_in) {
return qstr_str(mp_obj_get_type(o_in)->name);
}
void mp_obj_print_helper(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t o_in, mp_print_kind_t kind) {
void mp_obj_print_helper(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
// There can be data structures nested too deep, or just recursive
MP_STACK_CHECK();
#ifndef NDEBUG
if (o_in == NULL) {
print(env, "(nil)");
mp_print_str(print, "(nil)");
return;
}
#endif
mp_obj_type_t *type = mp_obj_get_type(o_in);
if (type->print != NULL) {
type->print(print, env, o_in, kind);
type->print((mp_print_t*)print, o_in, kind);
} else {
print(env, "<%s>", qstr_str(type->name));
mp_printf(print, "<%s>", qstr_str(type->name));
}
}
......@@ -75,41 +75,41 @@ void mp_obj_print(mp_obj_t o_in, mp_print_kind_t kind) {
#if MICROPY_PY_IO
// defined per port; type of these is irrelevant, just need pointer
extern struct _mp_dummy_t mp_sys_stdout_obj;
pfenv_t pfenv;
pfenv.data = &mp_sys_stdout_obj;
pfenv.print_strn = (void (*)(void *, const char *, mp_uint_t))mp_stream_write;
mp_obj_print_helper((void (*)(void *env, const char *fmt, ...))pfenv_printf, &pfenv, o_in, kind);
mp_print_t print;
print.data = &mp_sys_stdout_obj;
print.print_strn = (mp_print_strn_t)mp_stream_write;
mp_obj_print_helper(&print, o_in, kind);
#else
mp_obj_print_helper(printf_wrapper, NULL, o_in, kind);
mp_obj_print_helper(&mp_plat_print, o_in, kind);
#endif
}
// helper function to print an exception with traceback
void mp_obj_print_exception(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t exc) {
void mp_obj_print_exception(const mp_print_t *print, mp_obj_t exc) {
if (mp_obj_is_exception_instance(exc)) {
mp_uint_t n, *values;
mp_obj_exception_get_traceback(exc, &n, &values);
if (n > 0) {
assert(n % 3 == 0);
print(env, "Traceback (most recent call last):\n");
mp_print_str(print, "Traceback (most recent call last):\n");
for (int i = n - 3; i >= 0; i -= 3) {
#if MICROPY_ENABLE_SOURCE_LINE
print(env, " File \"%s\", line %d", qstr_str(values[i]), (int)values[i + 1]);
mp_printf(print, " File \"%s\", line %d", qstr_str(values[i]), (int)values[i + 1]);
#else
print(env, " File \"%s\"", qstr_str(values[i]));
mp_printf(print, " File \"%s\"", qstr_str(values[i]));
#endif
// the block name can be NULL if it's unknown
qstr block = values[i + 2];
if (block == MP_QSTR_NULL) {
print(env, "\n");
mp_print_str(print, "\n");
} else {
print(env, ", in %s\n", qstr_str(block));
mp_printf(print, ", in %s\n", qstr_str(block));
}
}
}
}
mp_obj_print_helper(print, env, exc, PRINT_EXC);
print(env, "\n");
mp_obj_print_helper(print, exc, PRINT_EXC);
mp_print_str(print, "\n");
}
bool mp_obj_is_true(mp_obj_t arg) {
......
......@@ -29,6 +29,7 @@
#include "py/mpconfig.h"
#include "py/misc.h"
#include "py/qstr.h"
#include "py/mpprint.h"
// All Micro Python objects are at least this type
// It must be of pointer size
......@@ -260,7 +261,7 @@ typedef enum {
PRINT_EXC_SUBCLASS = 0x80, // Internal flag for printing exception subclasses
} mp_print_kind_t;