← eval8_submission_v1l

fastapi_14301

resolved RESOLVED PASS · 10 tool calls · 95 s · fastapi/fastapi

Task input

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

**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)

Tool calls (11)

#ToolArgumentsResult
1run_command
{
 "command": "grep -rn \"get_parameterless_sub_dependant\" . | head -20"
}
{"status": "ok", "stdout": "./fastapi/dependencies/utils.py:124:def get_parameterless_sub_dependant(*, depends: params.Depends, path: str) -> Dependant:\n./fast
full
{"status": "ok", "stdout": "./fastapi/dependencies/utils.py:124:def get_parameterless_sub_dependant(*, depends: params.Depends, path: str) -> Dependant:\n./fastapi/routing.py:44:    get_parameterless_sub_dependant,\n./fastapi/routing.py:489:                get_parameterless_sub_dependant(depends=depends, path=self.path_format),\n./fastapi/routing.py:642:                get_parameterless_sub_dependant(depends=depends, path=self.path_format),\n", "stderr": "", "exit_code": 0}
2run_command
{
 "command": "sed -n '120,140p' fastapi/dependencies/utils.py"
}
{"status": "ok", "stdout": " logger.error(multipart_not_installed_error)\n raise RuntimeError(multipart_not_installed_error) from None\n\n\ndef get_parameterles
full
{"status": "ok", "stdout": "            logger.error(multipart_not_installed_error)\n            raise RuntimeError(multipart_not_installed_error) from None\n\n\ndef get_parameterless_sub_dependant(*, depends: params.Depends, path: str) -> Dependant:\n    assert callable(depends.dependency), (\n        \"A parameter-less dependency must have a callable dependency\"\n    )\n    use_security_scopes: List[str] = []\n    if isinstance(depends, params.Security) and depends.scopes:\n        use_security_scopes.extend(depends.scopes)\n    return get_dependant(\n        path=path, call=depends.dependency, security_scopes=use_security_scopes\n    )\n\n\ndef get_flat_dependant(\n    dependant: Dependant,\n    *,\n    skip_repeats: bool = False,\n    visited: Optional[List[DependencyCacheKey]] = None,\n", "stderr": "", "exit_code": 0}
3run_command
{
 "command": "grep -n \"def get_dependant\" fastapi/dependencies/utils.py"
}
{"status": "ok", "stdout": "226:def get_dependant(\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "226:def get_dependant(\n", "stderr": "", "exit_code": 0}
4run_command
{
 "command": "sed -n '226,250p' fastapi/dependencies/utils.py"
}
{"status": "ok", "stdout": "def get_dependant(\n *,\n path: str,\n call: Callable[..., Any],\n name: Optional[str] = None,\n security_scopes: Optional[List[str]
full
{"status": "ok", "stdout": "def get_dependant(\n    *,\n    path: str,\n    call: Callable[..., Any],\n    name: Optional[str] = None,\n    security_scopes: Optional[List[str]] = None,\n    use_cache: bool = True,\n    scope: Union[Literal[\"function\", \"request\"], None] = None,\n) -> Dependant:\n    dependant = Dependant(\n        call=call,\n        name=name,\n        path=path,\n        security_scopes=security_scopes,\n        use_cache=use_cache,\n        scope=scope,\n    )\n    path_param_names = get_path_param_names(path)\n    endpoint_signature = get_typed_signature(call)\n    signature_params = endpoint_signature.parameters\n    if isinstance(call, SecurityBase):\n        use_scopes: List[str] = []\n        if isinstance(call, (OAuth2, OpenIdConnect)):\n            use_scopes = security_scopes or use_scopes\n        security_requirement = SecurityRequirement(\n", "stderr": "", "exit_code": 0}
5edit_file
{
 "filepath": "fastapi/dependencies/utils.py",
 "new_string": "    return get_dependant(\n        path=path,\n        call=depends.dependency,\n        security_scopes=use_security_scopes,\n        scope=depends.scope,\n    )",
 "old_string": "    return get_dependant(\n        path=path, call=depends.dependency, security_scopes=use_security_scopes\n    )"
}
{"status": "ok", "filepath": "fastapi/dependencies/utils.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/dependencies/utils.py\n+++ b/fastapi
full
{"status": "ok", "filepath": "fastapi/dependencies/utils.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/dependencies/utils.py\n+++ b/fastapi/dependencies/utils.py\n@@ -129,7 +129,10 @@\n     if isinstance(depends, params.Security) and depends.scopes:\n         use_security_scopes.extend(depends.scopes)\n     return get_dependant(\n-        path=path, call=depends.dependency, security_scopes=use_security_scopes\n+        path=path,\n+        call=depends.dependency,\n+        security_scopes=use_security_scopes,\n+        scope=depends.scope,\n     )\n \n \n", "is_truncated": false}
6run_command
{
 "command": "python -c \"from fastapi.dependencies.utils import get_parameterless_sub_dependant; from fastapi import Depends; def test_dep(): pass; dep = Depends(test_dep, scope='function'); result = get_parameterless_sub_dependant(depends=dep, path='test'); print(f'Scope: {result.scope}')\""
}
{"status": "error", "error_type": "CommandError", "error_message": " File \"<string>\", line 1\n from fastapi.dependencies.utils import get_parameterless_sub_de
full
{"status": "error", "error_type": "CommandError", "error_message": "  File \"<string>\", line 1\n    from fastapi.dependencies.utils import get_parameterless_sub_dependant; from fastapi import Depends; def test_dep(): pass; dep = Depends(test_dep, scope='function'); result = get_parameterless_sub_dependant(depends=dep, path='test'); print(f'Scope: {result.scope}')\n                                                                                                         ^^^\nSyntaxError: invalid syntax\n", "details": {"stdout": "", "stderr": "  File \"<string>\", line 1\n    from fastapi.dependencies.utils import get_parameterless_sub_dependant; from fastapi import Depends; def test_dep(): pass; dep = Depends(test_dep, scope='function'); result = get_parameterless_sub_dependant(depends=dep, path='test'); print(f'Scope: {result.scope}')\n                                                                                                         ^^^\nSyntaxError: invalid syntax\n", "exit_code": 1}}
7run_command
{
 "command": "python3 -c \"\nfrom fastapi.dependencies.utils import get_parameterless_sub_dependant\nfrom fastapi import Depends\n\ndef test_dep():\n    return 'hello'\n\ndep = Depends(test_dep, scope='function')\nresult = get_parameterless_sub_dependant(depends=dep, path='test')\nprint(f'Scope: {result.scope}')\n\""
}
{"status": "ok", "stdout": "Scope: function\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "Scope: function\n", "stderr": "", "exit_code": 0}
8run_command
{
 "command": "cd /workspace && git status --short && git diff --stat"
}
{"status": "ok", "stdout": " M fastapi/dependencies/utils.py\n fastapi/dependencies/utils.py | 5 ++++-\n 1 file changed, 4 insertions(+), 1 deletion(-)\n", "std
full
{"status": "ok", "stdout": " M fastapi/dependencies/utils.py\n fastapi/dependencies/utils.py | 5 ++++-\n 1 file changed, 4 insertions(+), 1 deletion(-)\n", "stderr": "", "exit_code": 0}
9run_command
{
 "command": "python3 -c \"from fastapi.dependencies.utils import get_parameterless_sub_dependant; from fastapi import Depends; def test_dep(): pass; dep = Depends(test_dep, scope='function'); result = get_parameterless_sub_dependant(depends=dep, path='test'); print(f'Scope: {result.scope}')\""
}
{"status": "error", "error_type": "CommandError", "error_message": " File \"<string>\", line 1\n from fastapi.dependencies.utils import get_parameterless_sub_de
full
{"status": "error", "error_type": "CommandError", "error_message": "  File \"<string>\", line 1\n    from fastapi.dependencies.utils import get_parameterless_sub_dependant; from fastapi import Depends; def test_dep(): pass; dep = Depends(test_dep, scope='function'); result = get_parameterless_sub_dependant(depends=dep, path='test'); print(f'Scope: {result.scope}')\n                                                                                                         ^^^\nSyntaxError: invalid syntax\n", "details": {"stdout": "", "stderr": "  File \"<string>\", line 1\n    from fastapi.dependencies.utils import get_parameterless_sub_dependant; from fastapi import Depends; def test_dep(): pass; dep = Depends(test_dep, scope='function'); result = get_parameterless_sub_dependant(depends=dep, path='test'); print(f'Scope: {result.scope}')\n                                                                                                         ^^^\nSyntaxError: invalid syntax\n", "exit_code": 1}}
10run_command
{
 "command": "python3 -c \"\nfrom fastapi.dependencies.utils import get_parameterless_sub_dependant\nfrom fastapi import Depends\n\ndef test_dep():\n    return 'hello'\n\ndep = Depends(test_dep, scope='function')\nresult = get_parameterless_sub_dependant(depends=dep, path='test')\nprint(f'Scope: {result.scope}')\n\""
}
{"status": "ok", "stdout": "Scope: function\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "Scope: function\n", "stderr": "", "exit_code": 0}
11submit_patch
{}
{"status": "ok", "patch_size": 647, "files_changed": 1}
full
{"status": "ok", "patch_size": 647, "files_changed": 1}

Patch

diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py
index c5c6b69b..1981e9e1 100644
--- a/fastapi/dependencies/utils.py
+++ b/fastapi/dependencies/utils.py
@@ -129,7 +129,10 @@ def get_parameterless_sub_dependant(*, depends: params.Depends, path: str) -> De
     if isinstance(depends, params.Security) and depends.scopes:
         use_security_scopes.extend(depends.scopes)
     return get_dependant(
-        path=path, call=depends.dependency, security_scopes=use_security_scopes
+        path=path,
+        call=depends.dependency,
+        security_scopes=use_security_scopes,
+        scope=depends.scope,
     )
 
 

Test output

show
...........                                                              [100%]
=============================== warnings summary ===============================
../../../../../../kaggle/tmp/envs/overlays/starlette-0.49.3-py3-none-any/starlette/testclient.py:51
  /kaggle/tmp/envs/overlays/starlette-0.49.3-py3-none-any/starlette/testclient.py:51: DeprecationWarning: The anyio.abc.BlockingPortal alias is deprecated, use anyio.from_thread.BlockingPortal instead.
    _PortalFactoryType = Callable[[], AbstractContextManager[anyio.abc.BlockingPortal]]

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
11 passed, 1 warning in 0.74s