microbit-foundation / micropython-microbit-v2

Temporary home for MicroPython for micro:bit v2 as we stablise it before pushing upstream
MIT License
42 stars 23 forks source link

Exceptions thrown in a function scheduled with `run_every` are not scrolled on the display #94

Closed microbit-carlos closed 2 years ago

microbit-carlos commented 2 years ago

For example, with this programme:

from microbit import *

@run_every(s=1)
def foo():
    raise Exception

while True:
    if button_a.is_pressed():
        foo()
    sleep(200)

An exception is thrown every second, which can be seen on the serial output, but nothing is scrolled on the micro:bit display.

When button A is pressed and the foo() function is called directly the exception is scrolled on the display correctly.

dpgeorge commented 2 years ago

I have a working patch that scrolls exceptions from run_every on the display. It does this asynchronously, ie in the background, otherwise the run-every function blocks everything else if it has an error.

Also, I think that functions that raise should have their run-every cancelled so they only display the error once. If you have an error in your run-every function then it's very unlikely that you want it to keep running and keeping erroring out.

microbit-carlos commented 2 years ago

I have a working patch that scrolls exceptions from run_every on the display. It does this asynchronously, ie in the background, otherwise the run-every function blocks everything else if it has an error.

Could that interact with the rest of the user code in a way in which the exception is not scrolled? e.g., if the user code happens to scrolls something on the display immediately after the exception is thrown, will the exception text stop and the other message take its place?

Also, I think that functions that raise should have their run-every cancelled so they only display the error once. If you have an error in your run-every function then it's very unlikely that you want it to keep running and keeping erroring out.

Yes, I think that's a good idea! I assume if there is a try/except capturing the exception the function would not be cancelled, right? This would avoid having to have code like this:

continue_logging = True

@run_every(s=10)
def log_data():
    global continue_logging
    if continue_logging:
        try:
            log.add(temp=temperature())
        except OSError:
            continue_logging = False
dpgeorge commented 2 years ago

Fixed by 52126a46e1519b9b60f3276ffcb80154c7da0cd5: if a run_every raises an uncaught exception then it is cancelled, and the exception is propagated out and stops the main.py from running, and scrolls on the display.

If at the REPL, the exception just prints to stdout and the run_every is cancelled.