Explorar o código

using task logger to report exceptions in coroutines

Rodja Trappe %!s(int64=3) %!d(string=hai) anos
pai
achega
cb83871430
Modificáronse 2 ficheiros con 53 adicións e 4 borrados
  1. 1 4
      nicegui/nicegui.py
  2. 52 0
      nicegui/task_logger.py

+ 1 - 4
nicegui/nicegui.py

@@ -7,12 +7,9 @@ import justpy as jp
 from .timer import Timer
 from . import globals
 from . import binding
+from .task_logger import create_task
 
 
-def create_task(coro, name: str) -> asyncio.tasks.Task:
-    loop = asyncio.get_event_loop()
-    return loop.create_task(coro, name=name)
-
 @jp.app.on_event('startup')
 def startup():
     globals.tasks.extend(create_task(t.coro, name=t.name) for t in Timer.prepared_coroutines)

+ 52 - 0
nicegui/task_logger.py

@@ -0,0 +1,52 @@
+'''original copied from https://quantlane.com/blog/ensure-asyncio-task-exceptions-get-logged/'''
+
+from typing import Any, Awaitable, Optional, TypeVar
+
+import asyncio
+import functools
+import logging
+
+
+T = TypeVar('T')
+
+
+def create_task(
+    coroutine: Awaitable[T],
+    *,
+    loop: Optional[asyncio.AbstractEventLoop] = None,
+    name: str = 'unnamed task',
+) -> 'asyncio.Task[T]':  # This type annotation has to be quoted for Python < 3.9, see https://www.python.org/dev/peps/pep-0585/
+    '''
+    This helper function wraps a ``loop.create_task(coroutine())`` call and ensures there is
+    an exception handler added to the resulting task. If the task raises an exception it is logged
+    using the provided ``logger``, with additional context provided by ``message`` and optionally
+    ``message_args``.
+    '''
+
+    logger = logging.getLogger(__name__)
+    message = 'Task raised an exception'
+    message_args = ()
+    if loop is None:
+        loop = asyncio.get_running_loop()
+    task = loop.create_task(coroutine, name=name)
+    task.add_done_callback(
+        functools.partial(_handle_task_result, logger=logger, message=message, message_args=message_args)
+    )
+    return task
+
+
+def _handle_task_result(
+    task: asyncio.Task,
+    *,
+    logger: logging.Logger,
+    message: str,
+    message_args: tuple[Any, ...] = (),
+) -> None:
+    try:
+        task.result()
+    except asyncio.CancelledError:
+        raise  # Task cancellation should not be logged as an error.
+    # Ad the pylint ignore: we want to handle all exceptions here so that the result of the task
+    # is properly logged. There is no point re-raising the exception in this callback.
+    except Exception:  # pylint: disable=broad-except
+        logger.exception(message, *message_args)