extmod/uasyncio: Add global exception handling methods.

This commit adds support for global exception handling in uasyncio
according to the CPython error handling:
https://docs.python.org/3/library/asyncio-eventloop.html#error-handling-api

This allows a program to receive exceptions from detached tasks and log
them to an appropriate location, instead of them being printed to the REPL.

The implementation preallocates a context dictionary so in case of an
exception there shouldn't be any RAM allocation.

The approach here is compatible with CPython except that in CPython the
exception handler is called once the task that threw an uncaught exception
is freed, whereas in MicroPython the exception handler is called
immediately when the exception is thrown.
This commit is contained in:
Kevin Köck 2020-03-30 08:41:42 +02:00 committed by Damien George
parent e97bb58f0e
commit 15f41c2dbf
1 changed files with 23 additions and 2 deletions

View File

@ -23,6 +23,10 @@ class TimeoutError(Exception):
pass
# Used when calling Loop.call_exception_handler
_exc_context = {"message": "Task exception wasn't retrieved", "exception": None, "future": None}
################################################################################
# Sleep functions
@ -199,8 +203,9 @@ def run_until_complete(main_task=None):
t.waiting = None # Free waiting queue head
# Print out exception for detached tasks
if not waiting and not isinstance(er, excs_stop):
print("task raised exception:", t.coro)
sys.print_exception(er)
_exc_context["exception"] = er
_exc_context["future"] = t
Loop.call_exception_handler(_exc_context)
# Indicate task is done
t.coro = None
@ -222,6 +227,8 @@ _stop_task = None
class Loop:
_exc_handler = None
def create_task(coro):
return create_task(coro)
@ -244,6 +251,20 @@ class Loop:
def close():
pass
def set_exception_handler(handler):
Loop._exc_handler = handler
def get_exception_handler():
return Loop._exc_handler
def default_exception_handler(loop, context):
print(context["message"])
print("future:", context["future"], "coro=", context["future"].coro)
sys.print_exception(context["exception"])
def call_exception_handler(context):
(Loop._exc_handler or Loop.default_exception_handler)(Loop, context)
# The runq_len and waitq_len arguments are for legacy uasyncio compatibility
def get_event_loop(runq_len=0, waitq_len=0):