Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions reflex/app_mixins/lifespan.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def get_lifespan_tasks(self) -> tuple[asyncio.Task | Callable, ...]:
return tuple(self._lifespan_tasks)

@contextlib.asynccontextmanager
async def _run_lifespan_tasks(self, app: Starlette):
async def _run_lifespan_tasks(self, starlette_app: Starlette):
self._lifespan_tasks_started = True
running_tasks = []
try:
Expand All @@ -98,7 +98,11 @@ async def _run_lifespan_tasks(self, app: Starlette):
else:
signature = inspect.signature(task)
if "app" in signature.parameters:
task = functools.partial(task, app=app)
task = functools.partial(task, app=self)
if "starlette_app" in signature.parameters:
task = functools.partial(
task, starlette_app=starlette_app
)
t_ = task()
if isinstance(t_, contextlib._AsyncGeneratorContextManager):
await stack.enter_async_context(t_)
Expand Down
36 changes: 36 additions & 0 deletions tests/units/app_mixins/test_lifespan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""Unit tests for lifespan mixin behavior."""

from __future__ import annotations

import asyncio

import pytest
from starlette.applications import Starlette

from reflex.app_mixins.lifespan import LifespanMixin


@pytest.mark.asyncio
async def test_lifespan_task_app_param_receives_reflex_app_instance():
"""Lifespan tasks should receive the Reflex app instance, not Starlette."""

class DummyApp(LifespanMixin):
"""Minimal test app based on the lifespan mixin."""

app = DummyApp()
received: dict[str, object] = {}

def lifespan_task(app):
"""Record the app argument injected by the lifespan runner.

Args:
app: App object injected by the lifespan runner.
"""
received["app"] = app

app.register_lifespan_task(lifespan_task)

async with app._run_lifespan_tasks(Starlette()):
await asyncio.sleep(0)

assert received["app"] is app
Comment on lines +13 to +36
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing test coverage for starlette_app injection

The PR introduces starlette_app as a new injectable parameter for lifespan tasks, but the test suite only verifies the app (Reflex instance) injection path. A complementary test covering starlette_app injection — and ideally a task that declares both parameters simultaneously — would complete coverage for the new feature.