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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
|
from __future__ import annotations
import sys
from types import TracebackType
from typing import Any, Awaitable, Callable, Optional
import trio
from ..config import Config
from ..typing import AppWrapper, ASGIReceiveCallable, ASGIReceiveEvent, ASGISendEvent, Scope
if sys.version_info < (3, 11):
from exceptiongroup import BaseExceptionGroup
async def _handle(
app: AppWrapper,
config: Config,
scope: Scope,
receive: ASGIReceiveCallable,
send: Callable[[Optional[ASGISendEvent]], Awaitable[None]],
sync_spawn: Callable,
call_soon: Callable,
) -> None:
try:
await app(scope, receive, send, sync_spawn, call_soon)
except trio.Cancelled:
raise
except BaseExceptionGroup as error:
_, other_errors = error.split(trio.Cancelled)
if other_errors is not None:
await config.log.exception("Error in ASGI Framework")
await send(None)
else:
raise
except Exception:
await config.log.exception("Error in ASGI Framework")
finally:
await send(None)
class TaskGroup:
def __init__(self) -> None:
self._nursery: Optional[trio._core._run.Nursery] = None
self._nursery_manager: Optional[trio._core._run.NurseryManager] = None
async def spawn_app(
self,
app: AppWrapper,
config: Config,
scope: Scope,
send: Callable[[Optional[ASGISendEvent]], Awaitable[None]],
) -> Callable[[ASGIReceiveEvent], Awaitable[None]]:
app_send_channel, app_receive_channel = trio.open_memory_channel(config.max_app_queue_size)
self._nursery.start_soon(
_handle,
app,
config,
scope,
app_receive_channel.receive,
send,
trio.to_thread.run_sync,
trio.from_thread.run,
)
return app_send_channel.send
def spawn(self, func: Callable, *args: Any) -> None:
self._nursery.start_soon(func, *args)
async def __aenter__(self) -> TaskGroup:
self._nursery_manager = trio.open_nursery()
self._nursery = await self._nursery_manager.__aenter__()
return self
async def __aexit__(self, exc_type: type, exc_value: BaseException, tb: TracebackType) -> None:
await self._nursery_manager.__aexit__(exc_type, exc_value, tb)
self._nursery_manager = None
self._nursery = None
|