← eval3_submission

fastapi_14372

resolved RESOLVED PASS · 11 tool calls · 24 s · fastapi/fastapi

Task input

♻️ Make the result of `Depends()` and `Security()` hashable, as a workaround for other tools interacting with these internal parts

♻️ Make the result of `Depends()` and `Security()` hashable, as a workaround for other tools interacting with these internal parts

---

This comes from https://github.com/fastapi/fastapi/pull/14320

As a note, FastAPI doesn't really have official support for extending its internals in ways that are not documented in the docs.

So this is not a long-term commitment to keep some specific behavior, but a quick workaround to make things easier for current tools that interact with FastAPI internals in some way.

---

At some point in the future, I'll refactor the internals of FastAPI to make them more explicitly private, and then also figure out the right ways to expose any points of connection with external components that currently need to interact with the internals in some way, and formalize that better.

---

Before that, I need to handle a few issues, bugs, features, refactors, deprecations, etc. And then I'll be able to come back to this idea of figuring out how and what to expose. :nerd_face: 

Tool calls (12)

#ToolArgumentsResult
1run_command
{
 "command": "grep -rn \"class Depends\" fastapi | head -20"
}
{"status": "ok", "stdout": "fastapi/params.py:766:class Depends:\n", "exit_code": 0}
full
{"status": "ok", "stdout": "fastapi/params.py:766:class Depends:\n", "exit_code": 0}
2run_command
{
 "command": "grep -rn \"class Security\" fastapi | head -20"
}
{"status": "ok", "stdout": "fastapi/dependencies/models.py:19:class SecurityRequirement:\nfastapi/security/oauth2.py:614:class SecurityScopes:\nfastapi/security
full
{"status": "ok", "stdout": "fastapi/dependencies/models.py:19:class SecurityRequirement:\nfastapi/security/oauth2.py:614:class SecurityScopes:\nfastapi/security/base.py:4:class SecurityBase:\nfastapi/params.py:773:class Security(Depends):\nfastapi/openapi/models.py:337:class SecuritySchemeType(Enum):\nfastapi/openapi/models.py:344:class SecurityBase(BaseModelWithConfig):\n", "exit_code": 0}
3read_file
{
 "filepath": "fastapi/params.py",
 "start_line": 760,
 "end_line": 785
}
{"status": "ok", "filepath": "fastapi/params.py", "content": " json_schema_extra=json_schema_extra,\n **extra,\n )\n\n\n@dataclass\nclass Depends:\n dependency:
full
{"status": "ok", "filepath": "fastapi/params.py", "content": "            json_schema_extra=json_schema_extra,\n            **extra,\n        )\n\n\n@dataclass\nclass Depends:\n    dependency: Optional[Callable[..., Any]] = None\n    use_cache: bool = True\n    scope: Union[Literal[\"function\", \"request\"], None] = None\n\n\n@dataclass\nclass Security(Depends):\n    scopes: Optional[Sequence[str]] = None\n", "start_line": 760, "end_line": 774, "total_lines": 774, "is_truncated": false}
4run_command
{
 "command": "python3 -c \"from fastapi.params import Depends, Security; d1 = Depends(lambda: 1); d2 = Depends(lambda: 1); s1 = Security(lambda: 1, scopes=['a']); s2 = Security(lambda: 1, scopes=['a']); print(f'Depends hashable: {hash(d1) == hash(d1)}'); print(f'Security hashable: {hash(s1) == hash(s1)}')\""
}
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recen
full
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recent call last):\n  File \"<string>\", line 1, in <module>\n    from fastapi.params import Depends, Security; d1 = Depends(lambda: 1); d2 = Depends(lambda: 1); s1 = Security(lambda: 1, scopes=['a']); s2 = Security(lambda: 1, scopes=['a']); print(f'Depends hashable: {hash(d1) == hash(d1)}'); print(f'Security hashable: {hash(s1) == hash(s1)}')\n                                                                                                                                                                                                               ~~~~^^^^\nTypeError: unhashable type: 'Depends'\n", "exit_code": 1}}
5edit_file
{
 "filepath": "fastapi/params.py",
 "new_string": "@dataclass(frozen=True)\\nclass Depends:",
 "old_string": "@dataclass\\nclass Depends:"
}
{"status": "error", "error_type": "FileEditError", "error_message": "old_string not found in file (tried exact, flexible and regex matching)"}
full
{"status": "error", "error_type": "FileEditError", "error_message": "old_string not found in file (tried exact, flexible and regex matching)"}
6read_file
{
 "end_line": 774,
 "filepath": "fastapi/params.py",
 "start_line": 766
}
{"status": "ok", "filepath": "fastapi/params.py", "content": "class Depends:\n dependency: Optional[Callable[..., Any]] = None\n use_cache: bool = True\n scope:
full
{"status": "ok", "filepath": "fastapi/params.py", "content": "class Depends:\n    dependency: Optional[Callable[..., Any]] = None\n    use_cache: bool = True\n    scope: Union[Literal[\"function\", \"request\"], None] = None\n\n\n@dataclass\nclass Security(Depends):\n    scopes: Optional[Sequence[str]] = None\n", "start_line": 766, "end_line": 774, "total_lines": 774, "is_truncated": false}
7read_file
{
 "end_line": 766,
 "filepath": "fastapi/params.py",
 "start_line": 760
}
{"status": "ok", "filepath": "fastapi/params.py", "content": " json_schema_extra=json_schema_extra,\n **extra,\n )\n\n\n@dataclass\nclass Depends:\n", "start_li
full
{"status": "ok", "filepath": "fastapi/params.py", "content": "            json_schema_extra=json_schema_extra,\n            **extra,\n        )\n\n\n@dataclass\nclass Depends:\n", "start_line": 760, "end_line": 766, "total_lines": 774, "is_truncated": false}
8edit_file
{
 "filepath": "fastapi/params.py",
 "new_string": "@dataclass(frozen=True)\nclass Depends:",
 "old_string": "@dataclass\nclass Depends:"
}
{"status": "ok", "filepath": "fastapi/params.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/params.py\n+++ b/fastapi/params.py\n@@ -762,7 +7
full
{"status": "ok", "filepath": "fastapi/params.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/params.py\n+++ b/fastapi/params.py\n@@ -762,7 +762,7 @@\n         )\n \n \n-@dataclass\n+@dataclass(frozen=True)\n class Depends:\n     dependency: Optional[Callable[..., Any]] = None\n     use_cache: bool = True\n", "is_truncated": false}
9run_command
{
 "command": "python3 -c \"from fastapi.params import Depends, Security; d1 = Depends(lambda: 1); d2 = Depends(lambda: 1); s1 = Security(lambda: 1, scopes=('a',)); s2 = Security(lambda: 1, scopes=('a',)); print(f'Depends hashable: {hash(d1) == hash(d1)}'); print(f'Security hashable: {hash(s1) == hash(s1)}')\""
}
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recen
full
{"status": "error", "error_type": "CommandError", "error_message": "Command failed with exit code 1", "details": {"stdout": "", "stderr": "Traceback (most recent call last):\n  File \"<string>\", line 1, in <module>\n    from fastapi.params import Depends, Security; d1 = Depends(lambda: 1); d2 = Depends(lambda: 1); s1 = Security(lambda: 1, scopes=('a',)); s2 = Security(lambda: 1, scopes=('a',)); print(f'Depends hashable: {hash(d1) == hash(d1)}'); print(f'Security hashable: {hash(s1) == hash(s1)}')\n    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n  File \"/workspace/fastapi/__init__.py\", line 7, in <module>\n    from .applications import FastAPI as FastAPI\n  File \"/workspace/fastapi/applications.py\", line 17, in <module>\n    from fastapi import routing\n  File \"/workspace/fastapi/routing.py\", line 28, in <module>\n    from fastapi import params, temp_pydantic_v1_params\n  File \"/workspace/fastapi/params.py\", line 772, in <module>\n    @dataclass\n     ^^^^^^^^^\n  File \"/root/.local/share/uv/python/cpython-3.13.14-linux-x86_64-gnu/lib/python3.13/dataclasses.py\", line 1353, in dataclass\n    return wrap(cls)\n  File \"/root/.local/share/uv/python/cpython-3.13.14-linux-x86_64-gnu/lib/python3.13/dataclasses.py\", line 1343, in wrap\n    return _process_class(cls, init, repr, eq, order, unsafe_hash,\n                          frozen, match_args, kw_only, slots,\n                          weakref_slot)\n  File \"/root/.local/share/uv/python/cpython-3.13.14-linux-x86_64-gnu/lib/python3.13/dataclasses.py\", line 1038, in _process_class\n    raise TypeError('cannot inherit non-frozen dataclass from a '\n                    'frozen one')\nTypeError: cannot inherit non-frozen dataclass from a frozen one\n", "exit_code": 1}}
10edit_file
{
 "filepath": "fastapi/params.py",
 "new_string": "@dataclass(frozen=True)\nclass Security(Depends):",
 "old_string": "@dataclass\nclass Security(Depends):"
}
{"status": "ok", "filepath": "fastapi/params.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/params.py\n+++ b/fastapi/params.py\n@@ -769,6 +7
full
{"status": "ok", "filepath": "fastapi/params.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/params.py\n+++ b/fastapi/params.py\n@@ -769,6 +769,6 @@\n     scope: Union[Literal[\"function\", \"request\"], None] = None\n \n \n-@dataclass\n+@dataclass(frozen=True)\n class Security(Depends):\n     scopes: Optional[Sequence[str]] = None\n", "is_truncated": false}
11run_command
{
 "command": "python3 -c \"from fastapi.params import Depends, Security; d1 = Depends(lambda: 1); d2 = Depends(lambda: 1); s1 = Security(lambda: 1, scopes=('a',)); s2 = Security(lambda: 1, scopes=('a',)); print(f'Depends hashable: {hash(d1) == hash(d1)}'); print(f'Security hashable: {hash(s1) == hash(s1)}')\""
}
{"status": "ok", "stdout": "Depends hashable: True\nSecurity hashable: True\n", "exit_code": 0}
full
{"status": "ok", "stdout": "Depends hashable: True\nSecurity hashable: True\n", "exit_code": 0}
12submit_patch
[]
{"status": "ok", "patch_size": 516, "files_changed": 1}
full
{"status": "ok", "patch_size": 516, "files_changed": 1}

Patch

diff --git a/fastapi/params.py b/fastapi/params.py
index 6a58d580..6d07df35 100644
--- a/fastapi/params.py
+++ b/fastapi/params.py
@@ -762,13 +762,13 @@ class File(Form):  # type: ignore[misc]
         )
 
 
-@dataclass
+@dataclass(frozen=True)
 class Depends:
     dependency: Optional[Callable[..., Any]] = None
     use_cache: bool = True
     scope: Union[Literal["function", "request"], None] = None
 
 
-@dataclass
+@dataclass(frozen=True)
 class Security(Depends):
     scopes: Optional[Sequence[str]] = None

Test output

show
.                                                                        [100%]
1 passed in 0.47s