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

This commit is contained in:
Sebastián Ramírez 2025-11-19 17:50:18 +01:00 committed by GitHub
parent 566e3157a5
commit 85701631a0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 29 additions and 3 deletions

View File

@ -1,3 +1,4 @@
import dataclasses
import inspect
from contextlib import AsyncExitStack, contextmanager
from copy import copy, deepcopy
@ -428,7 +429,7 @@ def analyze_param(
if depends is not None and depends.dependency is None:
# Copy `depends` before mutating it
depends = copy(depends)
depends.dependency = type_annotation
depends = dataclasses.replace(depends, dependency=type_annotation)
# Handle non-param type annotations like Request
if lenient_issubclass(

View File

@ -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

View File

@ -0,0 +1,25 @@
# This is more or less a workaround to make Depends and Security hashable
# as other tools that use them depend on that
# Ref: https://github.com/fastapi/fastapi/pull/14320
from fastapi import Depends, Security
def dep():
pass
def test_depends_hashable():
dep() # just for coverage
d1 = Depends(dep)
d2 = Depends(dep)
d3 = Depends(dep, scope="function")
d4 = Depends(dep, scope="function")
s1 = Security(dep)
s2 = Security(dep)
assert hash(d1) == hash(d2)
assert hash(s1) == hash(s2)
assert hash(d1) != hash(d3)
assert hash(d3) == hash(d4)