Custom exception cannot be re-raised from called function
Consider this code:
class MyEx(Exception): pass
def do_raise():
raise
try:
raise MyEx()
except MyEx:
try:
do_raise()
except MyEx:
print("Ok")
else:
print("not Ok")
On MicroPython it fails with
Traceback (most recent call last):
File "/home/mb/test.py", line 12, in <module>
File "/home/mb/test.py", line 10, in <module>
File "/home/mb/test.py", line 4, in do_raise
RuntimeError: No active exception to reraise
Things I noticed:
If the raise is done directly in the handler instead of do_raise, it works correctly (prints Ok).
If the exception is Exception instead of MyEx, it also works correctly.
esp32/ble: Fatal crashes in BLE ISR handler
The ESP32 crashes and reboots when an Python exception is raised in another function as part of the Bluetooth ISR callback. Minimal reproduction:
import ubluetooth
def raises_exception():
raise RuntimeError('The things! They are wrong!')
def ble_callback(event, data):
try:
raises_exception()
except Exception:
print('We caught it!')
print('The end.')
ble = ubluetooth.BLE()
ble.irq(ble_callback)
ble.active(True)
ble.gap_advertise(100 * 1000, adv_data=b'\x02\x01\x06\x08\tExample', connectable=True)
Note: If I raise exception and catch an exception within the same function, everything works fine:
>>> import ubluetooth
>>> def ble_callback(event, data):
... try:
... raise RuntimeError('The things! They are wrong!')
... except Exception:
... print('We caught it!')
... print('The end.')
...
>>> ble = ubluetooth.BLE()
>>> ble.irq(ble_callback)
>>> ble.active(True)
We caught it!
The end.
We caught it!
The end.
True
>>> ble.gap_advertise(100 * 1000, adv_data=b'\x02\x01\x06\x08\tExample', connectable=True)
>>>
However, if I raise the exception in a different function, it crashes.
MicroPython v1.20.0-378-g63f46f08d-dirty on 2023-08-15; custom board with ESP32S3
Type "help()" for more information.
>>> import ubluetooth
>>> def raises_exception():
... raise RuntimeError('The things! They are wrong!')
...
>>> def ble_callback(event, data):
... try:
... raises_exception()
... except Exception:
... print('We caught it!')
... print('The end.')
...
>>> ble = ubluetooth.BLE()
>>> ble.irq(ble_callback)
>>> ble.active(True)
We caught it!
The end.
Guru Meditation Error: Core 1 panic'ed (LoadProhibited). Exception was unhandled.
Core 1 register dump:
PC : 0x42041d62 PS : 0x00060330 A0 : 0x8200bda9 A1 : 0x3fcb5c90
A2 : 0x3fcb5d50 A3 : 0x3fcb5ff0 A4 : 0x00000002 A5 : 0x00000588
A6 : 0x3c1143f4 A7 : 0x3fcb5cc0 A8 : 0x00002018 A9 : 0x3fcb5c70
A10 : 0x3fcb5ff0 A11 : 0x3fcab95c A12 : 0x00060320 A13 : 0x3fcb5cf0
A14 : 0x00000192 A15 : 0x3c128838 SAR : 0x0000001e EXCCAUSE: 0x0000001c
EXCVADDR: 0x00002018 LBEG : 0x400570e8 LEND : 0x400570f3 LCOUNT : 0x00000000
Backtrace:
0x42041d5f: nlr_pop_jump_callback at <mpy>/py/nlr.c:61
(inlined by) nlr_call_jump_callbacks at <mpy>/py/nlr.c:79
0x4200bda6: nlr_jump at <mpy>/py/nlrsetjmp.c:32 (discriminator 2)
0x4200eaa8: fun_bc_call at <mpy>/py/objfun.c:330
0x42016081: mp_call_function_n_kw at <mpy>/py/runtime.c:707
0x4037985a: mp_execute_bytecode at <mpy>/py/vm.c:957
0x4200ea88: fun_bc_call at <mpy>/py/objfun.c:273
0x42016081: mp_call_function_n_kw at <mpy>/py/runtime.c:707
0x4201611e: mp_call_function_2 at <mpy>/py/runtime.c:692
0x4201a1fa: invoke_irq_handler_run at <mpy>/extmod/modbluetooth.c:1211
0x4201a27c: invoke_irq_handler_run_protected at <mpy>/extmod/modbluetooth.c:1238
0x4201a31e: invoke_irq_handler at <mpy>/extmod/modbluetooth.c:1286
0x4201a57c: mp_bluetooth_gap_on_set_secret at <mpy>/extmod/modbluetooth.c:1351
0x42021794: load_irk at <mpy>/extmod/nimble/modbluetooth_nimble.c:307
(inlined by) sync_cb at <mpy>/extmod/nimble/modbluetooth_nimble.c:336
0x42048e29: ble_hs_sync at <idf>/components/bt/host/nimble/nimble/nimble/host/src/ble_hs.c:377
(inlined by) ble_hs_sync at <idf>/components/bt/host/nimble/nimble/nimble/host/src/ble_hs.c:348
0x420490d6: ble_hs_start at <idf>/components/bt/host/nimble/nimble/nimble/host/src/ble_hs.c:696
0x420490df: ble_hs_event_start_stage2 at <idf>/components/bt/host/nimble/nimble/nimble/host/src/ble_hs.c:596
0x40383de5: npl_freertos_event_run at <idf>/components/bt/host/nimble/nimble/porting/npl/freertos/src/npl_os_freertos.c:450
0x4037a47d: ble_npl_event_run at <idf>/components/bt/host/nimble/nimble/porting/npl/freertos/include/nimble/nimble_npl_os.h:185
(inlined by) nimble_port_run at <idf>/components/bt/host/nimble/nimble/porting/nimble/src/nimble_port.c:248
0x4202d306: ble_host_task at <mpy>/ports/esp32/mpnimbleport.c:43
Reproduces on both a S3 and C3. Manually bisected: 18caf49a7fab84f55fdf170eb5ca1c27206ccc76 (last version that builds against IDF4 without changes) does not have the issue, e4650125b88a35f074097f16d84a8f49bd22ac06 (builds against IDF5) does have it. Above logs are against latest master.
EDIT: For reference, working log on 18caf49a7fab84f55fdf170eb5ca1c27206ccc76 outputs identical to the version raising the exception within the same function:
>>> import ubluetooth
>>> def raises_exception():
... raise RuntimeError('The things! They are wrong!')
...
>>> def ble_callback(event, data):
... try:
... raises_exception()
... except Exception:
... print('We caught it!')
... print('The end.')
...
>>> ble = ubluetooth.BLE()
>>> ble.irq(ble_callback)
>>> ble.active(True)
We caught it!
The end.
We caught it!
The end.
True
>>> ble.gap_advertise(100 * 1000, adv_data=b'\x02\x01\x06\x08\tExample', connectable=True)
>>>