From 46c3ab20049af16e97863e90e5761fae329f872a Mon Sep 17 00:00:00 2001
From: Paul Sokolovsky <pfalcon@users.sourceforge.net>
Date: Sat, 6 Dec 2014 14:29:09 +0200
Subject: [PATCH] modsys: Add sys.print_exception(exc, file=sys.stdout)
 function.

The function is modeled after traceback.print_exception(), but unbloated,
and put into existing module to save overhead on adding another module.
Compliant traceback.print_exception() is intended to be implemented in
micropython-lib in terms of sys.print_exception().

This change required refactoring mp_obj_print_exception() to take pfenv_t
interface arguments.

Addresses #751.
---
 bare-arm/main.c      |  5 +++--
 py/modsys.c          | 27 +++++++++++++++++++++++++++
 py/obj.c             | 16 ++++++++--------
 py/obj.h             |  2 +-
 py/pfenv.h           |  3 +++
 py/qstrdefs.h        |  1 +
 qemu-arm/main.c      |  5 +++--
 qemu-arm/test_main.c |  4 ++--
 stmhal/extint.c      |  3 ++-
 stmhal/pyexec.c      |  5 +++--
 stmhal/timer.c       |  3 ++-
 teensy/timer.c       |  3 ++-
 unix-cpy/main.c      |  3 ++-
 unix/main.c          |  5 +++--
 14 files changed, 62 insertions(+), 23 deletions(-)

diff --git a/bare-arm/main.c b/bare-arm/main.c
index 3c187e5fb..d9f83b4eb 100644
--- a/bare-arm/main.c
+++ b/bare-arm/main.c
@@ -14,6 +14,7 @@
 #include "runtime0.h"
 #include "runtime.h"
 #include "repl.h"
+#include "pfenv.h"
 
 void do_str(const char *src) {
     mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0);
@@ -38,7 +39,7 @@ void do_str(const char *src) {
 
     if (mp_obj_is_exception_instance(module_fun)) {
         // compile error
-        mp_obj_print_exception(module_fun);
+        mp_obj_print_exception(printf_wrapper, NULL, module_fun);
         return;
     }
 
@@ -48,7 +49,7 @@ void do_str(const char *src) {
         nlr_pop();
     } else {
         // uncaught exception
-        mp_obj_print_exception((mp_obj_t)nlr.ret_val);
+        mp_obj_print_exception(printf_wrapper, NULL, (mp_obj_t)nlr.ret_val);
     }
 }
 
diff --git a/py/modsys.c b/py/modsys.c
index d9be7d1bc..06bd4c80e 100644
--- a/py/modsys.c
+++ b/py/modsys.c
@@ -38,6 +38,8 @@
 #include "objstr.h"
 #include "mpz.h"
 #include "objint.h"
+#include "pfenv.h"
+#include "stream.h"
 
 #if MICROPY_PY_SYS
 
@@ -78,6 +80,25 @@ STATIC mp_obj_t mp_sys_exit(mp_uint_t n_args, const mp_obj_t *args) {
 }
 MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_sys_exit_obj, 0, 1, mp_sys_exit);
 
+STATIC mp_obj_t mp_sys_print_exception(mp_uint_t n_args, const mp_obj_t *args) {
+    #if MICROPY_PY_IO
+    mp_obj_t stream_obj = &mp_sys_stdout_obj;
+    if (n_args > 1) {
+        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]);
+    #else
+    mp_obj_print_exception(printf_wrapper, NULL, args[0]);
+    #endif
+
+    return mp_const_none;
+}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_sys_print_exception_obj, 1, 2, mp_sys_print_exception);
+
 STATIC const mp_map_elem_t mp_module_sys_globals_table[] = {
     { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_sys) },
 
@@ -118,6 +139,12 @@ STATIC const mp_map_elem_t mp_module_sys_globals_table[] = {
     { MP_OBJ_NEW_QSTR(MP_QSTR_stdout), (mp_obj_t)&mp_sys_stdout_obj },
     { MP_OBJ_NEW_QSTR(MP_QSTR_stderr), (mp_obj_t)&mp_sys_stderr_obj },
 #endif
+
+    /*
+     * Extensions to CPython
+     */
+
+    { MP_OBJ_NEW_QSTR(MP_QSTR_print_exception), (mp_obj_t)&mp_sys_print_exception_obj },
 };
 
 STATIC MP_DEFINE_CONST_DICT(mp_module_sys_globals, mp_module_sys_globals_table);
diff --git a/py/obj.c b/py/obj.c
index c869a6546..d2801a396 100644
--- a/py/obj.c
+++ b/py/obj.c
@@ -85,31 +85,31 @@ void mp_obj_print(mp_obj_t o_in, mp_print_kind_t kind) {
 }
 
 // helper function to print an exception with traceback
-void mp_obj_print_exception(mp_obj_t exc) {
+void mp_obj_print_exception(void (*print)(void *env, const char *fmt, ...), void *env, 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);
-            printf("Traceback (most recent call last):\n");
+            print(env, "Traceback (most recent call last):\n");
             for (int i = n - 3; i >= 0; i -= 3) {
 #if MICROPY_ENABLE_SOURCE_LINE
-                printf("  File \"%s\", line %d", qstr_str(values[i]), (int)values[i + 1]);
+                print(env, "  File \"%s\", line %d", qstr_str(values[i]), (int)values[i + 1]);
 #else
-                printf("  File \"%s\"", qstr_str(values[i]));
+                print(env, "  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) {
-                    printf("\n");
+                    print(env, "\n");
                 } else {
-                    printf(", in %s\n", qstr_str(block));
+                    print(env, ", in %s\n", qstr_str(block));
                 }
             }
         }
     }
-    mp_obj_print(exc, PRINT_EXC);
-    printf("\n");
+    mp_obj_print_helper(print, env, exc, PRINT_EXC);
+    print(env, "\n");
 }
 
 bool mp_obj_is_true(mp_obj_t arg) {
diff --git a/py/obj.h b/py/obj.h
index 3721bea92..6db76405e 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -418,7 +418,7 @@ mp_obj_t mp_instance_cast_to_native_base(mp_const_obj_t self_in, mp_const_obj_t
 
 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(mp_obj_t o, mp_print_kind_t kind);
-void mp_obj_print_exception(mp_obj_t exc);
+void mp_obj_print_exception(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t exc);
 
 bool mp_obj_is_true(mp_obj_t arg);
 
diff --git a/py/pfenv.h b/py/pfenv.h
index fea2c883e..98c1885f3 100644
--- a/py/pfenv.h
+++ b/py/pfenv.h
@@ -52,3 +52,6 @@ int pfenv_print_float(const pfenv_t *pfenv, mp_float_t f, char fmt, int flags, c
 
 //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, ...);
diff --git a/py/qstrdefs.h b/py/qstrdefs.h
index 449f44d3b..f183c256f 100644
--- a/py/qstrdefs.h
+++ b/py/qstrdefs.h
@@ -403,6 +403,7 @@ Q(version_info)
 #if MICROPY_PY_SYS_MAXSIZE
 Q(maxsize)
 #endif
+Q(print_exception)
 #endif
 
 #if MICROPY_PY_STRUCT
diff --git a/qemu-arm/main.c b/qemu-arm/main.c
index 8cbeaaa6e..11ba2f402 100644
--- a/qemu-arm/main.c
+++ b/qemu-arm/main.c
@@ -14,6 +14,7 @@
 #include "runtime0.h"
 #include "runtime.h"
 #include "repl.h"
+#include "pfenv.h"
 
 void do_str(const char *src) {
     mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0);
@@ -38,7 +39,7 @@ void do_str(const char *src) {
 
     if (mp_obj_is_exception_instance(module_fun)) {
         // compile error
-        mp_obj_print_exception(module_fun);
+        mp_obj_print_exception(printf_wrapper, NULL, module_fun);
         return;
     }
 
@@ -48,7 +49,7 @@ void do_str(const char *src) {
         nlr_pop();
     } else {
         // uncaught exception
-        mp_obj_print_exception((mp_obj_t)nlr.ret_val);
+        mp_obj_print_exception(printf_wrapper, NULL, (mp_obj_t)nlr.ret_val);
     }
 }
 
diff --git a/qemu-arm/test_main.c b/qemu-arm/test_main.c
index fb9513077..ea9b0408f 100644
--- a/qemu-arm/test_main.c
+++ b/qemu-arm/test_main.c
@@ -40,7 +40,7 @@ inline void do_str(const char *src) {
     mp_obj_t module_fun = mp_compile(pn, source_name, MP_EMIT_OPT_NONE, true);
 
     if (mp_obj_is_exception_instance(module_fun)) {
-        mp_obj_print_exception(module_fun);
+        mp_obj_print_exception(printf_wrapper, NULL, module_fun);
         tt_abort_msg("Compile error");
     }
 
@@ -49,7 +49,7 @@ inline void do_str(const char *src) {
         mp_call_function_0(module_fun);
         nlr_pop();
     } else {
-        mp_obj_print_exception((mp_obj_t)nlr.ret_val);
+        mp_obj_print_exception(printf_wrapper, NULL, (mp_obj_t)nlr.ret_val);
         tt_abort_msg("Uncaught exception");
     }
 end:
diff --git a/stmhal/extint.c b/stmhal/extint.c
index 4fab7c5f2..a67af65b0 100644
--- a/stmhal/extint.c
+++ b/stmhal/extint.c
@@ -37,6 +37,7 @@
 #include "gc.h"
 #include "obj.h"
 #include "runtime.h"
+#include "pfenv.h"
 
 #include "pin.h"
 #include "extint.h"
@@ -373,7 +374,7 @@ void Handle_EXTI_Irq(uint32_t line) {
                     v->callback_obj = mp_const_none;
                     extint_disable(line);
                     printf("Uncaught exception in ExtInt interrupt handler line %lu\n", line);
-                    mp_obj_print_exception((mp_obj_t)nlr.ret_val);
+                    mp_obj_print_exception(printf_wrapper, NULL, (mp_obj_t)nlr.ret_val);
                 }
                 gc_unlock();
             }
diff --git a/stmhal/pyexec.c b/stmhal/pyexec.c
index 36a496aa1..d62b2a373 100644
--- a/stmhal/pyexec.c
+++ b/stmhal/pyexec.c
@@ -48,6 +48,7 @@
 #include "pyexec.h"
 #include "pybstdio.h"
 #include "genhdr/py-version.h"
+#include "pfenv.h"
 
 pyexec_mode_kind_t pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL;
 STATIC bool repl_display_debugging_info = 0;
@@ -87,7 +88,7 @@ STATIC int parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t input_ki
         if (exec_flags & EXEC_FLAG_PRINT_EOF) {
             stdout_tx_strn("\x04", 1);
         }
-        mp_obj_print_exception(module_fun);
+        mp_obj_print_exception(printf_wrapper, NULL, module_fun);
         goto finish;
     }
 
@@ -116,7 +117,7 @@ STATIC int parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t input_ki
             // at the moment, the value of SystemExit is unused
             ret = PYEXEC_FORCED_EXIT;
         } else {
-            mp_obj_print_exception((mp_obj_t)nlr.ret_val);
+            mp_obj_print_exception(printf_wrapper, NULL, (mp_obj_t)nlr.ret_val);
             ret = 0;
         }
     }
diff --git a/stmhal/timer.c b/stmhal/timer.c
index 5c99d51f1..e95fb5f9e 100644
--- a/stmhal/timer.c
+++ b/stmhal/timer.c
@@ -42,6 +42,7 @@
 #include "timer.h"
 #include "servo.h"
 #include "pin.h"
+#include "pfenv.h"
 
 /// \moduleref pyb
 /// \class Timer - periodically call a function
@@ -1245,7 +1246,7 @@ STATIC void timer_handle_irq_channel(pyb_timer_obj_t *tim, uint8_t channel, mp_o
                     } else {
                         printf("uncaught exception in Timer(%u) channel %u interrupt handler\n", tim->tim_id, channel);
                     }
-                    mp_obj_print_exception((mp_obj_t)nlr.ret_val);
+                    mp_obj_print_exception(printf_wrapper, NULL, (mp_obj_t)nlr.ret_val);
                 }
                 gc_unlock();
             }
diff --git a/teensy/timer.c b/teensy/timer.c
index 81450698b..a4051e608 100644
--- a/teensy/timer.c
+++ b/teensy/timer.c
@@ -35,6 +35,7 @@
 #include "qstr.h"
 #include "obj.h"
 #include "runtime.h"
+#include "pfenv.h"
 #include MICROPY_HAL_H
 #include "gc.h"
 #include "pin.h"
@@ -917,7 +918,7 @@ STATIC bool ftm_handle_irq_callback(pyb_timer_obj_t *self, mp_uint_t channel, mp
             printf("Uncaught exception in Timer(" UINT_FMT ") channel "
                    UINT_FMT " interrupt handler\n", self->tim_id, channel);
         }
-        mp_obj_print_exception((mp_obj_t)nlr.ret_val);
+        mp_obj_print_exception(printf_wrapper, NULL, (mp_obj_t)nlr.ret_val);
     }
     gc_unlock();
     return handled;
diff --git a/unix-cpy/main.c b/unix-cpy/main.c
index 03e5437c2..81f39ed27 100644
--- a/unix-cpy/main.c
+++ b/unix-cpy/main.c
@@ -40,6 +40,7 @@
 #include "parsehelper.h"
 #include "compile.h"
 #include "runtime.h"
+#include "pfenv.h"
 
 void do_file(const char *file) {
     mp_lexer_t *lex = mp_lexer_new_from_file(file);
@@ -80,7 +81,7 @@ void do_file(const char *file) {
             //printf("----------------\n");
 
             if (mp_obj_is_exception_instance(module_fun)) {
-                mp_obj_print_exception(module_fun);
+                mp_obj_print_exception(printf_wrapper, NULL, module_fun);
             }
         }
     }
diff --git a/unix/main.c b/unix/main.c
index 6733bbdda..2a0dc864c 100644
--- a/unix/main.c
+++ b/unix/main.c
@@ -53,6 +53,7 @@
 #include "genhdr/py-version.h"
 #include "input.h"
 #include "stackctrl.h"
+#include "pfenv.h"
 
 // Command line options, with their defaults
 STATIC bool compile_only = false;
@@ -100,7 +101,7 @@ STATIC int handle_uncaught_exception(mp_obj_t exc) {
     }
 
     // Report all other exceptions
-    mp_obj_print_exception(exc);
+    mp_obj_print_exception(printf_wrapper, NULL, exc);
     return 1;
 }
 
@@ -150,7 +151,7 @@ STATIC int execute_from_lexer(mp_lexer_t *lex, mp_parse_input_kind_t input_kind,
 
     if (mp_obj_is_exception_instance(module_fun)) {
         // compile error
-        mp_obj_print_exception(module_fun);
+        mp_obj_print_exception(printf_wrapper, NULL, module_fun);
         return 1;
     }
 
-- 
GitLab