aboutsummaryrefslogtreecommitdiff
path: root/extmod/uasyncio
diff options
context:
space:
mode:
authorKevin Köck <kevin.koeck@gmx.net>2020-03-30 08:41:42 +0200
committerDamien George <damien.p.george@gmail.com>2020-04-04 10:37:00 +1100
commit15f41c2dbf669e1a15ce2afb59eb29f5625d9973 (patch)
tree4c839dcaef47a54d55ee4331241bfb9fd21c8814 /extmod/uasyncio
parente97bb58f0ea0ec94847227982c7b4a8bfb27ed34 (diff)
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.
Diffstat (limited to 'extmod/uasyncio')
-rw-r--r--extmod/uasyncio/core.py25
1 files changed, 23 insertions, 2 deletions
diff --git a/extmod/uasyncio/core.py b/extmod/uasyncio/core.py
index dd0229ee4..e2f64119c 100644
--- a/extmod/uasyncio/core.py
+++ b/extmod/uasyncio/core.py
@@ -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):