File size: 1,007 Bytes
732b14f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
"""Shared asyncio task registry so background jobs are not garbage-collected."""

from __future__ import annotations

import asyncio
import logging

logger = logging.getLogger(__name__)

_background_tasks: set[asyncio.Task] = set()  # type: ignore[type-arg]


def _log_task_outcome(task: asyncio.Task) -> None:  # type: ignore[type-arg]
    """Surface exceptions from fire-and-forget tasks (handlers should still catch)."""
    try:
        exc = task.exception()
    except asyncio.CancelledError:
        return
    if exc is not None:
        logger.error(
            "Unhandled exception in background task: %s",
            exc,
            exc_info=exc,
        )


def spawn_background_task(coro) -> asyncio.Task:  # type: ignore[no-untyped-def]
    """Schedule ``coro`` and retain the task until it completes."""
    task = asyncio.create_task(coro)
    _background_tasks.add(task)
    task.add_done_callback(_background_tasks.discard)
    task.add_done_callback(_log_task_outcome)
    return task