Skip to content

fix(app): pass Reflex app instance to lifespan task app parameter#6358

Open
BABTUNA wants to merge 1 commit intoreflex-dev:mainfrom
BABTUNA:fix-6331-lifespan-app-instance
Open

fix(app): pass Reflex app instance to lifespan task app parameter#6358
BABTUNA wants to merge 1 commit intoreflex-dev:mainfrom
BABTUNA:fix-6331-lifespan-app-instance

Conversation

@BABTUNA
Copy link
Copy Markdown

@BABTUNA BABTUNA commented Apr 22, 2026

All Submissions:

  • Have you followed the guidelines stated in [CONTRIBUTING.md](https://
    github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md) file?
  • Have you checked to ensure there aren't any other open Pull
    Requests
    for the desired
    changed?

Type of change

  • Bug fix (non-breaking change which fixes an issue)

New Feature Submission:

  • Does your submission pass the tests?
  • Have you linted your code locally prior to submission?

Changes To Core Features:

  • Have you added an explanation of what your changes do and why you'd
    like us to include them?
  • Have you written new tests for your core changes, as applicable?
  • Have you successfully ran tests with your changes locally?

Description

This fixes lifespan task app injection so tasks that declare an app
parameter receive the Reflex app instance (rx.App) instead of the
Starlette wrapper.

Before:

  • In _run_lifespan_tasks, tasks with an app parameter were called with
    the Starlette lifespan app object.
  • This made app inconsistent with expected Reflex app behavior.

After:

  • app parameter now receives self (the Reflex app instance).
  • Added optional starlette_app parameter support for tasks that
    explicitly want the Starlette instance.

Tests

Added regression test:

  • tests/units/app_mixins/test_lifespan.py
    • test_lifespan_task_app_param_receives_reflex_app_instance

Validation run:

  • uv run pytest tests/units/app_mixins/test_lifespan.py -q
  • uv run ruff check reflex/app_mixins/lifespan.py tests/units/app_mixins/ test_lifespan.py

Closes #6331

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 22, 2026

Greptile Summary

This PR fixes lifespan task parameter injection so that tasks declaring an app parameter now receive the Reflex app instance (self) rather than the Starlette wrapper, and adds an optional starlette_app parameter for tasks that explicitly need the Starlette object. The change is minimal and targeted, with a regression test covering the primary fix.

Confidence Score: 5/5

Safe to merge — the fix is correct and non-breaking; only remaining feedback is a P2 test coverage suggestion.

The core logic change is a one-line substitution (appself) and an additive starlette_app injection block. Both paths are straightforward and have no side-effects on existing callers. The single P2 finding (missing test for starlette_app injection) does not affect correctness.

No files require special attention.

Important Files Changed

Filename Overview
reflex/app_mixins/lifespan.py Renamed _run_lifespan_tasks parameter from app to starlette_app, updated app injection to pass self (Reflex instance), and added optional starlette_app injection support; logic is correct.
tests/units/app_mixins/test_lifespan.py New regression test verifies app parameter receives the Reflex instance; missing coverage for starlette_app parameter and the combined app+starlette_app case.

Sequence Diagram

sequenceDiagram
    participant SA as Starlette (ASGI)
    participant LM as LifespanMixin._run_lifespan_tasks(starlette_app)
    participant T as Lifespan Task

    SA->>LM: call with starlette_app instance
    LM->>LM: inspect.signature(task)
    alt task has "app" param
        LM->>LM: partial(task, app=self) — Reflex instance
    end
    alt task has "starlette_app" param
        LM->>LM: partial(task, starlette_app=starlette_app) — Starlette instance
    end
    LM->>T: task()
    T-->>LM: coroutine / context manager / plain return
Loading

Reviews (1): Last reviewed commit: "Pass Reflex app instance to lifespan tas..." | Re-trigger Greptile

Comment on lines +13 to +36
@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
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lifespan tasks get a Starlette instance instead of an rx.App instance

1 participant