From 41eee0d5ea73fe772ebce669a86efa2b3f67e60d Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 21 May 2024 16:37:08 -0400 Subject: [PATCH 1/3] Add tests for chained exceptions (failing) --- tests/circuitpython/traceback_test_chained.py | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/tests/circuitpython/traceback_test_chained.py b/tests/circuitpython/traceback_test_chained.py index dac99e00b454d..15d383b0b8e78 100644 --- a/tests/circuitpython/traceback_test_chained.py +++ b/tests/circuitpython/traceback_test_chained.py @@ -72,3 +72,61 @@ def print_exc_info(e, chain=True): print_exc_info(e, chain=False) print_exc_info(e) print() + +class SomeException(RuntimeError): + pass + +try: + try: + raise Exception("inner") + except Exception as inner: + raise SomeException("outer") from inner +except Exception as e: + print_exc_info(e) + +try: + try: + raise Exception("inner") + except Exception as inner: + l = inner + raise SomeException("outer") from l +except Exception as e: + print_exc_info(e) +print() + +try: + try: + raise SomeException("inner") + except Exception as inner: + raise Exception("outer") from inner +except Exception as e: + print_exc_info(e) + +try: + try: + raise SomeException("inner") + except Exception as inner: + l = inner + raise Exception("outer") from l +except Exception as e: + print(e, e.__cause__, e.__context__) + print_exc_info(e) +print() + +try: + try: + raise SomeException("inner") + except Exception as inner: + raise SomeException("outer") from inner +except Exception as e: + print_exc_info(e) + +try: + try: + raise SomeException("inner") + except Exception as inner: + l = inner + raise SomeException("outer") from l +except Exception as e: + print_exc_info(e) +print() From 49cbbd198a522e4bd0aa63be160995766447a7ee Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 21 May 2024 16:40:58 -0400 Subject: [PATCH 2/3] Fix chaining non-built-in exceptions --- py/obj.c | 20 +++-- tests/circuitpython/traceback_test_chained.py | 1 - .../traceback_test_chained.py.exp | 75 +++++++++++++++++++ 3 files changed, 90 insertions(+), 6 deletions(-) diff --git a/py/obj.c b/py/obj.c index f981d27cda2b8..aa206c1a931e9 100644 --- a/py/obj.c +++ b/py/obj.c @@ -143,18 +143,28 @@ void mp_obj_print(mp_obj_t o_in, mp_print_kind_t kind) { mp_obj_print_helper(MP_PYTHON_PRINTER, o_in, kind); } +// CIRCUITPY-CHANGE +static mp_obj_t mp_load_attr_or_none(mp_obj_t base, qstr attr) { + mp_obj_t dest[2]; + mp_load_method_protected(base, attr, dest, true); + return dest[0] == MP_OBJ_NULL ? mp_const_none : dest[0]; +} + // CIRCUITPY-CHANGE static void mp_obj_print_inner_exception(const mp_print_t *print, mp_obj_t self_in, mp_int_t limit) { #if MICROPY_CPYTHON_EXCEPTION_CHAIN mp_obj_exception_t *self = mp_obj_exception_get_native(self_in); mp_rom_error_text_t msg = MP_ERROR_TEXT("During handling of the above exception, another exception occurred:"); - mp_obj_exception_t *inner = NULL; - if (self->cause) { + mp_obj_t inner_obj = mp_const_none; + if (!self->suppress_context) { + inner_obj = mp_load_attr_or_none(self_in, MP_QSTR___context__); + } + if (inner_obj == mp_const_none) { msg = MP_ERROR_TEXT("The above exception was the direct cause of the following exception:"); - inner = self->cause; - } else if (!self->suppress_context) { - inner = self->context; + inner_obj = mp_load_attr_or_none(self_in, MP_QSTR___cause__); } + mp_obj_exception_t *inner = mp_obj_is_exception_instance(inner_obj) ? + mp_obj_exception_get_native(inner_obj) : NULL; if (inner && !inner->marked) { inner->marked = true; mp_obj_print_exception_with_limit(print, MP_OBJ_FROM_PTR(inner), limit); diff --git a/tests/circuitpython/traceback_test_chained.py b/tests/circuitpython/traceback_test_chained.py index 15d383b0b8e78..3b96eb7a3ca9e 100644 --- a/tests/circuitpython/traceback_test_chained.py +++ b/tests/circuitpython/traceback_test_chained.py @@ -109,7 +109,6 @@ class SomeException(RuntimeError): l = inner raise Exception("outer") from l except Exception as e: - print(e, e.__cause__, e.__context__) print_exc_info(e) print() diff --git a/tests/circuitpython/traceback_test_chained.py.exp b/tests/circuitpython/traceback_test_chained.py.exp index a979adc24c040..498be1f0b7989 100644 --- a/tests/circuitpython/traceback_test_chained.py.exp +++ b/tests/circuitpython/traceback_test_chained.py.exp @@ -74,3 +74,78 @@ ZeroDivisionError: division by zero ------------------------------------------------------------------------ +------------------------------------------------------------------------ +Traceback (most recent call last): + File "circuitpython/traceback_test_chained.py", line 81, in +Exception: inner + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "circuitpython/traceback_test_chained.py", line 83, in +SomeException: outer +------------------------------------------------------------------------ + +------------------------------------------------------------------------ +Traceback (most recent call last): + File "circuitpython/traceback_test_chained.py", line 89, in +Exception: inner + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "circuitpython/traceback_test_chained.py", line 92, in +SomeException: outer +------------------------------------------------------------------------ + + +------------------------------------------------------------------------ +Traceback (most recent call last): + File "circuitpython/traceback_test_chained.py", line 99, in +RuntimeError: inner + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "circuitpython/traceback_test_chained.py", line 101, in +Exception: outer +------------------------------------------------------------------------ + +------------------------------------------------------------------------ +Traceback (most recent call last): + File "circuitpython/traceback_test_chained.py", line 107, in +RuntimeError: inner + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "circuitpython/traceback_test_chained.py", line 110, in +Exception: outer +------------------------------------------------------------------------ + + +------------------------------------------------------------------------ +Traceback (most recent call last): + File "circuitpython/traceback_test_chained.py", line 117, in +RuntimeError: inner + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "circuitpython/traceback_test_chained.py", line 119, in +SomeException: outer +------------------------------------------------------------------------ + +------------------------------------------------------------------------ +Traceback (most recent call last): + File "circuitpython/traceback_test_chained.py", line 125, in +RuntimeError: inner + +The above exception was the direct cause of the following exception: + +Traceback (most recent call last): + File "circuitpython/traceback_test_chained.py", line 128, in +SomeException: outer +------------------------------------------------------------------------ + + From 94db02f3695d32b6d7df7ef4ccdb582b49a6b25c Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 21 May 2024 16:49:10 -0400 Subject: [PATCH 3/3] function may be unused --- py/obj.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/py/obj.c b/py/obj.c index aa206c1a931e9..85deaff8e6806 100644 --- a/py/obj.c +++ b/py/obj.c @@ -144,11 +144,13 @@ void mp_obj_print(mp_obj_t o_in, mp_print_kind_t kind) { } // CIRCUITPY-CHANGE +#if MICROPY_CPYTHON_EXCEPTION_CHAIN static mp_obj_t mp_load_attr_or_none(mp_obj_t base, qstr attr) { mp_obj_t dest[2]; mp_load_method_protected(base, attr, dest, true); return dest[0] == MP_OBJ_NULL ? mp_const_none : dest[0]; } +#endif // CIRCUITPY-CHANGE static void mp_obj_print_inner_exception(const mp_print_t *print, mp_obj_t self_in, mp_int_t limit) {