Skip to content

Conversation

@luzzodev
Copy link
Contributor

@luzzodev luzzodev commented Nov 6, 2025

Summary

This PR enables proper support for scope="function" on APIRoute dependencies by passing the dependency’s scope through to get_dependant. Previously, parameter-less dependencies created via get_parameterless_sub_dependant ignored depends.scope, which led to unexpected behavior for function-scoped dependencies.

Motivation

Users expect scope="function" to work as defined, even when registered at the route level. Because depends.scope wasn’t forwarded, function scope behaved like the default(request). This change aligns behavior with user expectations and documentation around dependency scopes.
(Related: #14296)

@luzzodev luzzodev added the bug Something isn't working label Nov 6, 2025
@YuriiMotov
Copy link
Member

Hi! 👋

I think the test logic looks a bit complicated. Can we try to simplify it?

My suggestion:

from typing import Any

from fastapi import APIRouter, Depends, FastAPI, HTTPException
from fastapi.testclient import TestClient

app = FastAPI()


def raise_after_yield() -> Any:
    yield
    raise HTTPException(status_code=503, detail="Exception after yield")


router = APIRouter()


@router.get("/")
def get_index():
    return {"status": "ok"}


app.include_router(
    prefix="/router-scope-function",
    router=router,
    dependencies=[Depends(raise_after_yield, scope="function")],
)

app.include_router(
    prefix="/router-scope-request",
    router=router,
    dependencies=[Depends(raise_after_yield, scope="request")],
)


client = TestClient(app)


def test_router_level_dep_scope_function() -> None:
    response = client.get("/router-scope-function/")
    assert response.status_code == 503
    assert response.json() == {"detail": "Exception after yield"}


def test_router_level_dep_scope_request() -> None:
    client = TestClient(app, raise_server_exceptions=False)
    response = client.get("/router-scope-request/")
    assert response.status_code == 200
    assert response.json() == {"status": "ok"}


def test_app_level_dep_scope_function() -> None:
    app = FastAPI(dependencies=[Depends(raise_after_yield, scope="function")])

    @app.get("/app-scope-function")
    def get_app_scope_function():
        return {"status": "ok"}

    client = TestClient(app)

    response = client.get("/app-scope-function")
    assert response.status_code == 503
    assert response.json() == {"detail": "Exception after yield"}


def test_app_level_dep_scope_request() -> None:
    app = FastAPI(dependencies=[Depends(raise_after_yield, scope="request")])

    @app.get("/app-scope-request")
    def get_app_scope_request():
        return {"status": "ok"}

    client = TestClient(app, raise_server_exceptions=False)

    response = client.get("/app-scope-request")
    assert response.status_code == 200
    assert response.json() == {"status": "ok"}

Also, I think we should put these tests into existing test_dependency_yield_scope.py and test_dependency_yield_scope_websockets.py

What do you think?

@YuriiMotov
Copy link
Member

YuriiMotov commented Nov 6, 2025

How about changing the title to

🐛 Fix `Depends(func, scope='function')` for top level (parameterless) dependencies

?

@luzzodev
Copy link
Contributor Author

luzzodev commented Nov 6, 2025

I totally agree with all suggestions. I was unsure to use dependencies on include_router but seems reasonable. I’ll update the PR later this evening 😁

@luzzodev luzzodev changed the title Fix Depends(scope='function') when defined on ApiRouter. 🐛 Fix Depends(func, scope='function') for top level (parameterless) dependencies Nov 6, 2025
Copy link
Member

@YuriiMotov YuriiMotov left a comment

Choose a reason for hiding this comment

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

LGTM!

Thanks!

Copy link
Member

@tiangolo tiangolo left a comment

Choose a reason for hiding this comment

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

Awesome! Great job, thank you @luzzodev! 🚀

And thanks a lot @YuriiMotov! 🙌

This will be available in the next hours in FastAPI version 0.121.1 🎉

@tiangolo tiangolo merged commit 282f372 into fastapi:master Nov 8, 2025
30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants