diff options
author | Kevin Köck <kevin.koeck@gmx.net> | 2020-03-30 08:41:42 +0200 |
---|---|---|
committer | Damien George <damien.p.george@gmail.com> | 2020-04-04 10:37:00 +1100 |
commit | 15f41c2dbf669e1a15ce2afb59eb29f5625d9973 (patch) | |
tree | 4c839dcaef47a54d55ee4331241bfb9fd21c8814 /extmod/uasyncio | |
parent | e97bb58f0ea0ec94847227982c7b4a8bfb27ed34 (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.py | 25 |
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): |