Browse Source

♻️ Refactor dependencies, allow a default scope of None

pull/14262/head
Sebastián Ramírez 9 months ago
parent
commit
87bec2962c
  1. 16
      fastapi/dependencies/models.py
  2. 17
      fastapi/dependencies/utils.py
  3. 4
      fastapi/param_functions.py
  4. 2
      fastapi/params.py
  5. 3
      fastapi/types.py

16
fastapi/dependencies/models.py

@ -1,8 +1,10 @@
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any, Callable, List, Optional, Sequence, Tuple from functools import cached_property
from typing import Any, Callable, List, Optional, Sequence, Union
from fastapi._compat import ModelField from fastapi._compat import ModelField
from fastapi.security.base import SecurityBase from fastapi.security.base import SecurityBase
from fastapi.types import DependencyCacheKey
from typing_extensions import Literal from typing_extensions import Literal
@ -32,14 +34,12 @@ class Dependant:
security_scopes: Optional[List[str]] = None security_scopes: Optional[List[str]] = None
use_cache: bool = True use_cache: bool = True
path: Optional[str] = None path: Optional[str] = None
scope: Literal["function", "request"] = "request" scope: Union[Literal["function", "request"], None] = None
cache_key: Tuple[Optional[Callable[..., Any]], Tuple[str, ...], str] = field(
init=False
)
def __post_init__(self) -> None: @cached_property
self.cache_key = ( def cache_key(self) -> DependencyCacheKey:
return (
self.call, self.call,
tuple(sorted(set(self.security_scopes or []))), tuple(sorted(set(self.security_scopes or []))),
self.scope, self.scope or "",
) )

17
fastapi/dependencies/utils.py

@ -60,6 +60,7 @@ from fastapi.logger import logger
from fastapi.security.base import SecurityBase from fastapi.security.base import SecurityBase
from fastapi.security.oauth2 import OAuth2, SecurityScopes from fastapi.security.oauth2 import OAuth2, SecurityScopes
from fastapi.security.open_id_connect_url import OpenIdConnect from fastapi.security.open_id_connect_url import OpenIdConnect
from fastapi.types import DependencyCacheKey
from fastapi.utils import create_model_field, get_path_param_names from fastapi.utils import create_model_field, get_path_param_names
from pydantic import BaseModel from pydantic import BaseModel
from pydantic.fields import FieldInfo from pydantic.fields import FieldInfo
@ -138,14 +139,11 @@ def get_parameterless_sub_dependant(*, depends: params.Depends, path: str) -> De
) )
CacheKey = Tuple[Optional[Callable[..., Any]], Tuple[str, ...]]
def get_flat_dependant( def get_flat_dependant(
dependant: Dependant, dependant: Dependant,
*, *,
skip_repeats: bool = False, skip_repeats: bool = False,
visited: Optional[List[CacheKey]] = None, visited: Optional[List[DependencyCacheKey]] = None,
) -> Dependant: ) -> Dependant:
if visited is None: if visited is None:
visited = [] visited = []
@ -238,7 +236,7 @@ def get_dependant(
name: Optional[str] = None, name: Optional[str] = None,
security_scopes: Optional[List[str]] = None, security_scopes: Optional[List[str]] = None,
use_cache: bool = True, use_cache: bool = True,
scope: Literal["function", "request"] = "request", scope: Union[Literal["function", "request"], None] = None,
) -> Dependant: ) -> Dependant:
dependant = Dependant( dependant = Dependant(
call=call, call=call,
@ -254,7 +252,7 @@ def get_dependant(
if isinstance(call, SecurityBase): if isinstance(call, SecurityBase):
use_scopes: List[str] = [] use_scopes: List[str] = []
if isinstance(call, (OAuth2, OpenIdConnect)): if isinstance(call, (OAuth2, OpenIdConnect)):
use_scopes = security_scopes use_scopes = security_scopes or use_scopes
security_requirement = SecurityRequirement( security_requirement = SecurityRequirement(
security_scheme=call, scopes=use_scopes security_scheme=call, scopes=use_scopes
) )
@ -581,7 +579,7 @@ class SolvedDependency:
errors: List[Any] errors: List[Any]
background_tasks: Optional[StarletteBackgroundTasks] background_tasks: Optional[StarletteBackgroundTasks]
response: Response response: Response
dependency_cache: Dict[Tuple[Callable[..., Any], Tuple[str]], Any] dependency_cache: Dict[DependencyCacheKey, Any]
async def solve_dependencies( async def solve_dependencies(
@ -592,7 +590,7 @@ async def solve_dependencies(
background_tasks: Optional[StarletteBackgroundTasks] = None, background_tasks: Optional[StarletteBackgroundTasks] = None,
response: Optional[Response] = None, response: Optional[Response] = None,
dependency_overrides_provider: Optional[Any] = None, dependency_overrides_provider: Optional[Any] = None,
dependency_cache: Optional[Dict[Tuple[Callable[..., Any], Tuple[str]], Any]] = None, dependency_cache: Optional[Dict[DependencyCacheKey, Any]] = None,
# TODO: remove this parameter later, no longer used, not removing it yet as some # TODO: remove this parameter later, no longer used, not removing it yet as some
# people might be monkey patching this function (although that's not supported) # people might be monkey patching this function (although that's not supported)
async_exit_stack: AsyncExitStack, async_exit_stack: AsyncExitStack,
@ -616,9 +614,6 @@ async def solve_dependencies(
dependency_cache = {} dependency_cache = {}
for sub_dependant in dependant.dependencies: for sub_dependant in dependant.dependencies:
sub_dependant.call = cast(Callable[..., Any], sub_dependant.call) sub_dependant.call = cast(Callable[..., Any], sub_dependant.call)
sub_dependant.cache_key = cast(
Tuple[Callable[..., Any], Tuple[str]], sub_dependant.cache_key
)
call = sub_dependant.call call = sub_dependant.call
use_sub_dependant = sub_dependant use_sub_dependant = sub_dependant
if ( if (

4
fastapi/param_functions.py

@ -2246,7 +2246,7 @@ def Depends( # noqa: N802
), ),
] = True, ] = True,
scope: Annotated[ scope: Annotated[
Literal["function", "request"], Union[Literal["function", "request"], None],
Doc( Doc(
""" """
Mainly for dependencies with `yield`, define when the dependency function Mainly for dependencies with `yield`, define when the dependency function
@ -2264,7 +2264,7 @@ def Depends( # noqa: N802
function will be executed *around* the **request** and response cycle. function will be executed *around* the **request** and response cycle.
""" """
), ),
] = "request", ] = None,
) -> Any: ) -> Any:
""" """
Declare a FastAPI dependency. Declare a FastAPI dependency.

2
fastapi/params.py

@ -766,7 +766,7 @@ class File(Form): # type: ignore[misc]
class Depends: class Depends:
dependency: Optional[Callable[..., Any]] = None dependency: Optional[Callable[..., Any]] = None
use_cache: bool = True use_cache: bool = True
scope: Literal["function", "request"] = "request" scope: Union[Literal["function", "request"], None] = None
@dataclass @dataclass

3
fastapi/types.py

@ -1,6 +1,6 @@
import types import types
from enum import Enum from enum import Enum
from typing import Any, Callable, Dict, Set, Type, TypeVar, Union from typing import Any, Callable, Dict, Optional, Set, Tuple, Type, TypeVar, Union
from pydantic import BaseModel from pydantic import BaseModel
@ -8,3 +8,4 @@ DecoratedCallable = TypeVar("DecoratedCallable", bound=Callable[..., Any])
UnionType = getattr(types, "UnionType", Union) UnionType = getattr(types, "UnionType", Union)
ModelNameMap = Dict[Union[Type[BaseModel], Type[Enum]], str] ModelNameMap = Dict[Union[Type[BaseModel], Type[Enum]], str]
IncEx = Union[Set[int], Set[str], Dict[int, Any], Dict[str, Any]] IncEx = Union[Set[int], Set[str], Dict[int, Any], Dict[str, Any]]
DependencyCacheKey = Tuple[Optional[Callable[..., Any]], Tuple[str, ...], str]

Loading…
Cancel
Save