From 69762d93df041294e58f38d7d7ae228110b95378 Mon Sep 17 00:00:00 2001 From: Walter Oggioni Date: Fri, 18 Sep 2026 02:33:01 +0000 Subject: [PATCH] Document that mixin setup/shutdown loops may not be running Under RSGI __rsgi_init__ is called before the loop starts, so asyncio.get_running_loop() raises RuntimeError inside setup(). Spell out that mixins must use the loop they are given, since the ASGI lifespan incidentally runs with a live loop. --- packages/kaya-core/src/kaya/core/_mixin.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/kaya-core/src/kaya/core/_mixin.py b/packages/kaya-core/src/kaya/core/_mixin.py index fd9444b..f743939 100644 --- a/packages/kaya-core/src/kaya/core/_mixin.py +++ b/packages/kaya-core/src/kaya/core/_mixin.py @@ -29,9 +29,22 @@ class KayaMixin(ABC): pass def setup(self, loop: AbstractEventLoop) -> None: - """Called on lifespan startup (default: no-op).""" + """Called on lifespan startup (default: no-op). + + Under RSGI (granian) the loop is NOT yet running when this is + called: schedule work on the passed ``loop`` (e.g. + ``loop.create_task``) instead of calling + ``asyncio.get_running_loop()``, which raises ``RuntimeError`` + there. Under ASGI the lifespan runs inside a coroutine, so a + running loop happens to be available — do not rely on that. + """ pass def shutdown(self, loop: AbstractEventLoop) -> None: - """Called on lifespan shutdown (default: no-op).""" + """Called on lifespan shutdown (default: no-op). + + As with :meth:`setup`, use the passed ``loop``; it may already be + stopped under RSGI, so ``asyncio.get_running_loop()`` is not + reliable here either. + """ pass