Sync fastapi docs from b5ca1324 on 2025-12-07
Issue Manager / issue-manager (push) Has been cancelled
Build Docs / changes (push) Has been cancelled
Build Docs / langs (push) Has been cancelled
Build Docs / build-docs (push) Has been cancelled
Build Docs / docs-all-green (push) Has been cancelled
Conflict detector / main (push) Has been cancelled
Test Redistribute / test-redistribute (fastapi) (push) Has been cancelled
Test Redistribute / test-redistribute (fastapi-slim) (push) Has been cancelled
Test Redistribute / test-redistribute-alls-green (push) Has been cancelled
Test / lint (push) Has been cancelled
Test / test (pydantic-v1, 3.10) (push) Has been cancelled
Test / test (pydantic-v1, 3.11) (push) Has been cancelled
Test / test (pydantic-v1, 3.13) (push) Has been cancelled
Test / test (pydantic-v1, 3.8) (push) Has been cancelled
Test / test (pydantic-v1, 3.9) (push) Has been cancelled
Test / test (pydantic-v2, 3.10) (push) Has been cancelled
Test / test (pydantic-v2, 3.11) (push) Has been cancelled
Test / test (pydantic-v2, 3.12) (push) Has been cancelled
Test / test (pydantic-v2, 3.13) (push) Has been cancelled
Test / test (pydantic-v2, 3.14) (push) Has been cancelled
Test / test (pydantic-v2, 3.8) (push) Has been cancelled
Test / test (pydantic-v2, 3.9) (push) Has been cancelled
Test / coverage-combine (push) Has been cancelled
Test / check (push) Has been cancelled
Label Approved / label-approved (push) Has been cancelled
FastAPI People Contributors / job (push) Has been cancelled
FastAPI People Sponsors / job (push) Has been cancelled
Update Topic Repos / topic-repos (push) Has been cancelled
FastAPI People / job (push) Has been cancelled
Test / test (pydantic-v1, 3.12) (push) Has been cancelled
Issue Manager / issue-manager (push) Has been cancelled
Build Docs / changes (push) Has been cancelled
Build Docs / langs (push) Has been cancelled
Build Docs / build-docs (push) Has been cancelled
Build Docs / docs-all-green (push) Has been cancelled
Conflict detector / main (push) Has been cancelled
Test Redistribute / test-redistribute (fastapi) (push) Has been cancelled
Test Redistribute / test-redistribute (fastapi-slim) (push) Has been cancelled
Test Redistribute / test-redistribute-alls-green (push) Has been cancelled
Test / lint (push) Has been cancelled
Test / test (pydantic-v1, 3.10) (push) Has been cancelled
Test / test (pydantic-v1, 3.11) (push) Has been cancelled
Test / test (pydantic-v1, 3.13) (push) Has been cancelled
Test / test (pydantic-v1, 3.8) (push) Has been cancelled
Test / test (pydantic-v1, 3.9) (push) Has been cancelled
Test / test (pydantic-v2, 3.10) (push) Has been cancelled
Test / test (pydantic-v2, 3.11) (push) Has been cancelled
Test / test (pydantic-v2, 3.12) (push) Has been cancelled
Test / test (pydantic-v2, 3.13) (push) Has been cancelled
Test / test (pydantic-v2, 3.14) (push) Has been cancelled
Test / test (pydantic-v2, 3.8) (push) Has been cancelled
Test / test (pydantic-v2, 3.9) (push) Has been cancelled
Test / coverage-combine (push) Has been cancelled
Test / check (push) Has been cancelled
Label Approved / label-approved (push) Has been cancelled
FastAPI People Contributors / job (push) Has been cancelled
FastAPI People Sponsors / job (push) Has been cancelled
Update Topic Repos / topic-repos (push) Has been cancelled
FastAPI People / job (push) Has been cancelled
Test / test (pydantic-v1, 3.12) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
import inspect
|
||||
import sys
|
||||
from dataclasses import dataclass, field
|
||||
from functools import cached_property, partial
|
||||
from typing import Any, Callable, List, Optional, Union
|
||||
|
||||
from fastapi._compat import ModelField
|
||||
from fastapi.security.base import SecurityBase
|
||||
from fastapi.types import DependencyCacheKey
|
||||
from typing_extensions import Literal
|
||||
|
||||
if sys.version_info >= (3, 13): # pragma: no cover
|
||||
from inspect import iscoroutinefunction
|
||||
else: # pragma: no cover
|
||||
from asyncio import iscoroutinefunction
|
||||
|
||||
|
||||
def _unwrapped_call(call: Optional[Callable[..., Any]]) -> Any:
|
||||
if call is None:
|
||||
return call # pragma: no cover
|
||||
unwrapped = inspect.unwrap(_impartial(call))
|
||||
return unwrapped
|
||||
|
||||
|
||||
def _impartial(func: Callable[..., Any]) -> Callable[..., Any]:
|
||||
while isinstance(func, partial):
|
||||
func = func.func
|
||||
return func
|
||||
|
||||
|
||||
@dataclass
|
||||
class Dependant:
|
||||
path_params: List[ModelField] = field(default_factory=list)
|
||||
query_params: List[ModelField] = field(default_factory=list)
|
||||
header_params: List[ModelField] = field(default_factory=list)
|
||||
cookie_params: List[ModelField] = field(default_factory=list)
|
||||
body_params: List[ModelField] = field(default_factory=list)
|
||||
dependencies: List["Dependant"] = field(default_factory=list)
|
||||
name: Optional[str] = None
|
||||
call: Optional[Callable[..., Any]] = None
|
||||
request_param_name: Optional[str] = None
|
||||
websocket_param_name: Optional[str] = None
|
||||
http_connection_param_name: Optional[str] = None
|
||||
response_param_name: Optional[str] = None
|
||||
background_tasks_param_name: Optional[str] = None
|
||||
security_scopes_param_name: Optional[str] = None
|
||||
own_oauth_scopes: Optional[List[str]] = None
|
||||
parent_oauth_scopes: Optional[List[str]] = None
|
||||
use_cache: bool = True
|
||||
path: Optional[str] = None
|
||||
scope: Union[Literal["function", "request"], None] = None
|
||||
|
||||
@cached_property
|
||||
def oauth_scopes(self) -> List[str]:
|
||||
scopes = self.parent_oauth_scopes.copy() if self.parent_oauth_scopes else []
|
||||
# This doesn't use a set to preserve order, just in case
|
||||
for scope in self.own_oauth_scopes or []:
|
||||
if scope not in scopes:
|
||||
scopes.append(scope)
|
||||
return scopes
|
||||
|
||||
@cached_property
|
||||
def cache_key(self) -> DependencyCacheKey:
|
||||
scopes_for_cache = (
|
||||
tuple(sorted(set(self.oauth_scopes or []))) if self._uses_scopes else ()
|
||||
)
|
||||
return (
|
||||
self.call,
|
||||
scopes_for_cache,
|
||||
self.computed_scope or "",
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def _uses_scopes(self) -> bool:
|
||||
if self.own_oauth_scopes:
|
||||
return True
|
||||
if self.security_scopes_param_name is not None:
|
||||
return True
|
||||
if self._is_security_scheme:
|
||||
return True
|
||||
for sub_dep in self.dependencies:
|
||||
if sub_dep._uses_scopes:
|
||||
return True
|
||||
return False
|
||||
|
||||
@cached_property
|
||||
def _is_security_scheme(self) -> bool:
|
||||
if self.call is None:
|
||||
return False # pragma: no cover
|
||||
unwrapped = _unwrapped_call(self.call)
|
||||
return isinstance(unwrapped, SecurityBase)
|
||||
|
||||
# Mainly to get the type of SecurityBase, but it's the same self.call
|
||||
@cached_property
|
||||
def _security_scheme(self) -> SecurityBase:
|
||||
unwrapped = _unwrapped_call(self.call)
|
||||
assert isinstance(unwrapped, SecurityBase)
|
||||
return unwrapped
|
||||
|
||||
@cached_property
|
||||
def _security_dependencies(self) -> List["Dependant"]:
|
||||
security_deps = [dep for dep in self.dependencies if dep._is_security_scheme]
|
||||
return security_deps
|
||||
|
||||
@cached_property
|
||||
def is_gen_callable(self) -> bool:
|
||||
if self.call is None:
|
||||
return False # pragma: no cover
|
||||
if inspect.isgeneratorfunction(
|
||||
_impartial(self.call)
|
||||
) or inspect.isgeneratorfunction(_unwrapped_call(self.call)):
|
||||
return True
|
||||
if inspect.isclass(_unwrapped_call(self.call)):
|
||||
return False
|
||||
dunder_call = getattr(_impartial(self.call), "__call__", None) # noqa: B004
|
||||
if dunder_call is None:
|
||||
return False # pragma: no cover
|
||||
if inspect.isgeneratorfunction(
|
||||
_impartial(dunder_call)
|
||||
) or inspect.isgeneratorfunction(_unwrapped_call(dunder_call)):
|
||||
return True
|
||||
dunder_unwrapped_call = getattr(_unwrapped_call(self.call), "__call__", None) # noqa: B004
|
||||
if dunder_unwrapped_call is None:
|
||||
return False # pragma: no cover
|
||||
if inspect.isgeneratorfunction(
|
||||
_impartial(dunder_unwrapped_call)
|
||||
) or inspect.isgeneratorfunction(_unwrapped_call(dunder_unwrapped_call)):
|
||||
return True
|
||||
return False
|
||||
|
||||
@cached_property
|
||||
def is_async_gen_callable(self) -> bool:
|
||||
if self.call is None:
|
||||
return False # pragma: no cover
|
||||
if inspect.isasyncgenfunction(
|
||||
_impartial(self.call)
|
||||
) or inspect.isasyncgenfunction(_unwrapped_call(self.call)):
|
||||
return True
|
||||
if inspect.isclass(_unwrapped_call(self.call)):
|
||||
return False
|
||||
dunder_call = getattr(_impartial(self.call), "__call__", None) # noqa: B004
|
||||
if dunder_call is None:
|
||||
return False # pragma: no cover
|
||||
if inspect.isasyncgenfunction(
|
||||
_impartial(dunder_call)
|
||||
) or inspect.isasyncgenfunction(_unwrapped_call(dunder_call)):
|
||||
return True
|
||||
dunder_unwrapped_call = getattr(_unwrapped_call(self.call), "__call__", None) # noqa: B004
|
||||
if dunder_unwrapped_call is None:
|
||||
return False # pragma: no cover
|
||||
if inspect.isasyncgenfunction(
|
||||
_impartial(dunder_unwrapped_call)
|
||||
) or inspect.isasyncgenfunction(_unwrapped_call(dunder_unwrapped_call)):
|
||||
return True
|
||||
return False
|
||||
|
||||
@cached_property
|
||||
def is_coroutine_callable(self) -> bool:
|
||||
if self.call is None:
|
||||
return False # pragma: no cover
|
||||
if inspect.isroutine(_impartial(self.call)) and iscoroutinefunction(
|
||||
_impartial(self.call)
|
||||
):
|
||||
return True
|
||||
if inspect.isroutine(_unwrapped_call(self.call)) and iscoroutinefunction(
|
||||
_unwrapped_call(self.call)
|
||||
):
|
||||
return True
|
||||
if inspect.isclass(_unwrapped_call(self.call)):
|
||||
return False
|
||||
dunder_call = getattr(_impartial(self.call), "__call__", None) # noqa: B004
|
||||
if dunder_call is None:
|
||||
return False # pragma: no cover
|
||||
if iscoroutinefunction(_impartial(dunder_call)) or iscoroutinefunction(
|
||||
_unwrapped_call(dunder_call)
|
||||
):
|
||||
return True
|
||||
dunder_unwrapped_call = getattr(_unwrapped_call(self.call), "__call__", None) # noqa: B004
|
||||
if dunder_unwrapped_call is None:
|
||||
return False # pragma: no cover
|
||||
if iscoroutinefunction(
|
||||
_impartial(dunder_unwrapped_call)
|
||||
) or iscoroutinefunction(_unwrapped_call(dunder_unwrapped_call)):
|
||||
return True
|
||||
return False
|
||||
|
||||
@cached_property
|
||||
def computed_scope(self) -> Union[str, None]:
|
||||
if self.scope:
|
||||
return self.scope
|
||||
if self.is_gen_callable or self.is_async_gen_callable:
|
||||
return "request"
|
||||
return None
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user