Browse Source

🎨 Update internal types for Python 3.10 (#14898)

pull/14902/head
Sebastián Ramírez 4 months ago
committed by GitHub
parent
commit
3da206c06d
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 26
      fastapi/_compat/v2.py
  2. 354
      fastapi/applications.py
  3. 3
      fastapi/background.py
  4. 10
      fastapi/datastructures.py
  5. 32
      fastapi/dependencies/models.py
  6. 13
      fastapi/encoders.py
  7. 14
      fastapi/exceptions.py
  8. 8
      fastapi/openapi/docs.py
  9. 326
      fastapi/openapi/models.py
  10. 31
      fastapi/openapi/utils.py
  11. 380
      fastapi/param_functions.py
  12. 446
      fastapi/params.py
  13. 412
      fastapi/routing.py
  14. 26
      fastapi/security/api_key.py
  15. 36
      fastapi/security/http.py
  16. 42
      fastapi/security/oauth2.py
  17. 8
      fastapi/security/open_id_connect_url.py
  18. 5
      fastapi/security/utils.py
  19. 7
      fastapi/types.py
  20. 18
      fastapi/utils.py

26
fastapi/_compat/v2.py

@ -8,8 +8,11 @@ from functools import lru_cache
from typing import ( from typing import (
Annotated, Annotated,
Any, Any,
Literal,
Union, Union,
cast, cast,
get_args,
get_origin,
) )
from fastapi._compat import lenient_issubclass, shared from fastapi._compat import lenient_issubclass, shared
@ -32,7 +35,6 @@ from pydantic_core import Url as Url
from pydantic_core.core_schema import ( from pydantic_core.core_schema import (
with_info_plain_validator_function as with_info_plain_validator_function, with_info_plain_validator_function as with_info_plain_validator_function,
) )
from typing_extensions import Literal, get_args, get_origin
RequiredParam = PydanticUndefined RequiredParam = PydanticUndefined
Undefined = PydanticUndefined Undefined = PydanticUndefined
@ -83,7 +85,7 @@ class ModelField:
field_info: FieldInfo field_info: FieldInfo
name: str name: str
mode: Literal["validation", "serialization"] = "validation" mode: Literal["validation", "serialization"] = "validation"
config: Union[ConfigDict, None] = None config: ConfigDict | None = None
@property @property
def alias(self) -> str: def alias(self) -> str:
@ -91,14 +93,14 @@ class ModelField:
return a if a is not None else self.name return a if a is not None else self.name
@property @property
def validation_alias(self) -> Union[str, None]: def validation_alias(self) -> str | None:
va = self.field_info.validation_alias va = self.field_info.validation_alias
if isinstance(va, str) and va: if isinstance(va, str) and va:
return va return va
return None return None
@property @property
def serialization_alias(self) -> Union[str, None]: def serialization_alias(self) -> str | None:
sa = self.field_info.serialization_alias sa = self.field_info.serialization_alias
return sa or None return sa or None
@ -143,7 +145,7 @@ class ModelField:
value: Any, value: Any,
values: dict[str, Any] = {}, # noqa: B006 values: dict[str, Any] = {}, # noqa: B006
*, *,
loc: tuple[Union[int, str], ...] = (), loc: tuple[int | str, ...] = (),
) -> tuple[Any, list[dict[str, Any]]]: ) -> tuple[Any, list[dict[str, Any]]]:
try: try:
return ( return (
@ -160,8 +162,8 @@ class ModelField:
value: Any, value: Any,
*, *,
mode: Literal["json", "python"] = "json", mode: Literal["json", "python"] = "json",
include: Union[IncEx, None] = None, include: IncEx | None = None,
exclude: Union[IncEx, None] = None, exclude: IncEx | None = None,
by_alias: bool = True, by_alias: bool = True,
exclude_unset: bool = False, exclude_unset: bool = False,
exclude_defaults: bool = False, exclude_defaults: bool = False,
@ -202,7 +204,7 @@ def get_schema_from_model_field(
], ],
separate_input_output_schemas: bool = True, separate_input_output_schemas: bool = True,
) -> dict[str, Any]: ) -> dict[str, Any]:
override_mode: Union[Literal["validation"], None] = ( override_mode: Literal["validation"] | None = (
None None
if (separate_input_output_schemas or _has_computed_fields(field)) if (separate_input_output_schemas or _has_computed_fields(field))
else "validation" else "validation"
@ -318,7 +320,7 @@ def serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:
return shared.sequence_annotation_to_type[origin_type](value) # type: ignore[no-any-return,index] return shared.sequence_annotation_to_type[origin_type](value) # type: ignore[no-any-return,index]
def get_missing_field_error(loc: tuple[Union[int, str], ...]) -> dict[str, Any]: def get_missing_field_error(loc: tuple[int | str, ...]) -> dict[str, Any]:
error = ValidationError.from_exception_data( error = ValidationError.from_exception_data(
"Field required", [{"type": "missing", "loc": loc, "input": {}}] "Field required", [{"type": "missing", "loc": loc, "input": {}}]
).errors(include_url=False)[0] ).errors(include_url=False)[0]
@ -360,7 +362,7 @@ def get_cached_model_fields(model: type[BaseModel]) -> list[ModelField]:
# Duplicate of several schema functions from Pydantic v1 to make them compatible with # Duplicate of several schema functions from Pydantic v1 to make them compatible with
# Pydantic v2 and allow mixing the models # Pydantic v2 and allow mixing the models
TypeModelOrEnum = Union[type["BaseModel"], type[Enum]] TypeModelOrEnum = type["BaseModel"] | type[Enum]
TypeModelSet = set[TypeModelOrEnum] TypeModelSet = set[TypeModelOrEnum]
@ -377,7 +379,7 @@ def get_model_name_map(unique_models: TypeModelSet) -> dict[TypeModelOrEnum, str
def get_flat_models_from_model( def get_flat_models_from_model(
model: type["BaseModel"], known_models: Union[TypeModelSet, None] = None model: type["BaseModel"], known_models: TypeModelSet | None = None
) -> TypeModelSet: ) -> TypeModelSet:
known_models = known_models or set() known_models = known_models or set()
fields = get_model_fields(model) fields = get_model_fields(model)
@ -426,7 +428,7 @@ def get_flat_models_from_fields(
def _regenerate_error_with_loc( def _regenerate_error_with_loc(
*, errors: Sequence[Any], loc_prefix: tuple[Union[str, int], ...] *, errors: Sequence[Any], loc_prefix: tuple[str | int, ...]
) -> list[dict[str, Any]]: ) -> list[dict[str, Any]]:
updated_loc_errors: list[Any] = [ updated_loc_errors: list[Any] = [
{**err, "loc": loc_prefix + err.get("loc", ())} for err in errors {**err, "loc": loc_prefix + err.get("loc", ())} for err in errors

354
fastapi/applications.py

File diff suppressed because it is too large

3
fastapi/background.py

@ -1,4 +1,5 @@
from typing import Annotated, Any, Callable from collections.abc import Callable
from typing import Annotated, Any
from annotated_doc import Doc from annotated_doc import Doc
from starlette.background import BackgroundTasks as StarletteBackgroundTasks from starlette.background import BackgroundTasks as StarletteBackgroundTasks

10
fastapi/datastructures.py

@ -1,10 +1,8 @@
from collections.abc import Mapping from collections.abc import Callable, Mapping
from typing import ( from typing import (
Annotated, Annotated,
Any, Any,
BinaryIO, BinaryIO,
Callable,
Optional,
TypeVar, TypeVar,
cast, cast,
) )
@ -58,11 +56,11 @@ class UploadFile(StarletteUploadFile):
BinaryIO, BinaryIO,
Doc("The standard Python file object (non-async)."), Doc("The standard Python file object (non-async)."),
] ]
filename: Annotated[Optional[str], Doc("The original file name.")] filename: Annotated[str | None, Doc("The original file name.")]
size: Annotated[Optional[int], Doc("The size of the file in bytes.")] size: Annotated[int | None, Doc("The size of the file in bytes.")]
headers: Annotated[Headers, Doc("The headers of the request.")] headers: Annotated[Headers, Doc("The headers of the request.")]
content_type: Annotated[ content_type: Annotated[
Optional[str], Doc("The content type of the request, from the headers.") str | None, Doc("The content type of the request, from the headers.")
] ]
async def write( async def write(

32
fastapi/dependencies/models.py

@ -1,13 +1,13 @@
import inspect import inspect
import sys import sys
from collections.abc import Callable
from dataclasses import dataclass, field from dataclasses import dataclass, field
from functools import cached_property, partial from functools import cached_property, partial
from typing import Any, Callable, Optional, Union from typing import Any, Literal
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 fastapi.types import DependencyCacheKey
from typing_extensions import Literal
if sys.version_info >= (3, 13): # pragma: no cover if sys.version_info >= (3, 13): # pragma: no cover
from inspect import iscoroutinefunction from inspect import iscoroutinefunction
@ -15,7 +15,7 @@ else: # pragma: no cover
from asyncio import iscoroutinefunction from asyncio import iscoroutinefunction
def _unwrapped_call(call: Optional[Callable[..., Any]]) -> Any: def _unwrapped_call(call: Callable[..., Any] | None) -> Any:
if call is None: if call is None:
return call # pragma: no cover return call # pragma: no cover
unwrapped = inspect.unwrap(_impartial(call)) unwrapped = inspect.unwrap(_impartial(call))
@ -36,19 +36,19 @@ class Dependant:
cookie_params: list[ModelField] = field(default_factory=list) cookie_params: list[ModelField] = field(default_factory=list)
body_params: list[ModelField] = field(default_factory=list) body_params: list[ModelField] = field(default_factory=list)
dependencies: list["Dependant"] = field(default_factory=list) dependencies: list["Dependant"] = field(default_factory=list)
name: Optional[str] = None name: str | None = None
call: Optional[Callable[..., Any]] = None call: Callable[..., Any] | None = None
request_param_name: Optional[str] = None request_param_name: str | None = None
websocket_param_name: Optional[str] = None websocket_param_name: str | None = None
http_connection_param_name: Optional[str] = None http_connection_param_name: str | None = None
response_param_name: Optional[str] = None response_param_name: str | None = None
background_tasks_param_name: Optional[str] = None background_tasks_param_name: str | None = None
security_scopes_param_name: Optional[str] = None security_scopes_param_name: str | None = None
own_oauth_scopes: Optional[list[str]] = None own_oauth_scopes: list[str] | None = None
parent_oauth_scopes: Optional[list[str]] = None parent_oauth_scopes: list[str] | None = None
use_cache: bool = True use_cache: bool = True
path: Optional[str] = None path: str | None = None
scope: Union[Literal["function", "request"], None] = None scope: Literal["function", "request"] | None = None
@cached_property @cached_property
def oauth_scopes(self) -> list[str]: def oauth_scopes(self) -> list[str]:
@ -185,7 +185,7 @@ class Dependant:
return False return False
@cached_property @cached_property
def computed_scope(self) -> Union[str, None]: def computed_scope(self) -> str | None:
if self.scope: if self.scope:
return self.scope return self.scope
if self.is_gen_callable or self.is_async_gen_callable: if self.is_gen_callable or self.is_async_gen_callable:

13
fastapi/encoders.py

@ -1,6 +1,7 @@
import dataclasses import dataclasses
import datetime import datetime
from collections import defaultdict, deque from collections import defaultdict, deque
from collections.abc import Callable
from decimal import Decimal from decimal import Decimal
from enum import Enum from enum import Enum
from ipaddress import ( from ipaddress import (
@ -14,7 +15,7 @@ from ipaddress import (
from pathlib import Path, PurePath from pathlib import Path, PurePath
from re import Pattern from re import Pattern
from types import GeneratorType from types import GeneratorType
from typing import Annotated, Any, Callable, Optional, Union from typing import Annotated, Any
from uuid import UUID from uuid import UUID
from annotated_doc import Doc from annotated_doc import Doc
@ -33,13 +34,13 @@ from ._compat import (
# Taken from Pydantic v1 as is # Taken from Pydantic v1 as is
def isoformat(o: Union[datetime.date, datetime.time]) -> str: def isoformat(o: datetime.date | datetime.time) -> str:
return o.isoformat() return o.isoformat()
# Adapted from Pydantic v1 # Adapted from Pydantic v1
# TODO: pv2 should this return strings instead? # TODO: pv2 should this return strings instead?
def decimal_encoder(dec_value: Decimal) -> Union[int, float]: def decimal_encoder(dec_value: Decimal) -> int | float:
""" """
Encodes a Decimal as int if there's no exponent, otherwise float Encodes a Decimal as int if there's no exponent, otherwise float
@ -118,7 +119,7 @@ def jsonable_encoder(
), ),
], ],
include: Annotated[ include: Annotated[
Optional[IncEx], IncEx | None,
Doc( Doc(
""" """
Pydantic's `include` parameter, passed to Pydantic models to set the Pydantic's `include` parameter, passed to Pydantic models to set the
@ -127,7 +128,7 @@ def jsonable_encoder(
), ),
] = None, ] = None,
exclude: Annotated[ exclude: Annotated[
Optional[IncEx], IncEx | None,
Doc( Doc(
""" """
Pydantic's `exclude` parameter, passed to Pydantic models to set the Pydantic's `exclude` parameter, passed to Pydantic models to set the
@ -177,7 +178,7 @@ def jsonable_encoder(
), ),
] = False, ] = False,
custom_encoder: Annotated[ custom_encoder: Annotated[
Optional[dict[Any, Callable[[Any], Any]]], dict[Any, Callable[[Any], Any]] | None,
Doc( Doc(
""" """
Pydantic's `custom_encoder` parameter, passed to Pydantic models to define Pydantic's `custom_encoder` parameter, passed to Pydantic models to define

14
fastapi/exceptions.py

@ -1,5 +1,5 @@
from collections.abc import Mapping, Sequence from collections.abc import Mapping, Sequence
from typing import Annotated, Any, Optional, TypedDict, Union from typing import Annotated, Any, TypedDict
from annotated_doc import Doc from annotated_doc import Doc
from pydantic import BaseModel, create_model from pydantic import BaseModel, create_model
@ -68,7 +68,7 @@ class HTTPException(StarletteHTTPException):
), ),
] = None, ] = None,
headers: Annotated[ headers: Annotated[
Optional[Mapping[str, str]], Mapping[str, str] | None,
Doc( Doc(
""" """
Any headers to send to the client in the response. Any headers to send to the client in the response.
@ -137,7 +137,7 @@ class WebSocketException(StarletteWebSocketException):
), ),
], ],
reason: Annotated[ reason: Annotated[
Union[str, None], str | None,
Doc( Doc(
""" """
The reason to close the WebSocket connection. The reason to close the WebSocket connection.
@ -176,7 +176,7 @@ class ValidationException(Exception):
self, self,
errors: Sequence[Any], errors: Sequence[Any],
*, *,
endpoint_ctx: Optional[EndpointContext] = None, endpoint_ctx: EndpointContext | None = None,
) -> None: ) -> None:
self._errors = errors self._errors = errors
self.endpoint_ctx = endpoint_ctx self.endpoint_ctx = endpoint_ctx
@ -215,7 +215,7 @@ class RequestValidationError(ValidationException):
errors: Sequence[Any], errors: Sequence[Any],
*, *,
body: Any = None, body: Any = None,
endpoint_ctx: Optional[EndpointContext] = None, endpoint_ctx: EndpointContext | None = None,
) -> None: ) -> None:
super().__init__(errors, endpoint_ctx=endpoint_ctx) super().__init__(errors, endpoint_ctx=endpoint_ctx)
self.body = body self.body = body
@ -226,7 +226,7 @@ class WebSocketRequestValidationError(ValidationException):
self, self,
errors: Sequence[Any], errors: Sequence[Any],
*, *,
endpoint_ctx: Optional[EndpointContext] = None, endpoint_ctx: EndpointContext | None = None,
) -> None: ) -> None:
super().__init__(errors, endpoint_ctx=endpoint_ctx) super().__init__(errors, endpoint_ctx=endpoint_ctx)
@ -237,7 +237,7 @@ class ResponseValidationError(ValidationException):
errors: Sequence[Any], errors: Sequence[Any],
*, *,
body: Any = None, body: Any = None,
endpoint_ctx: Optional[EndpointContext] = None, endpoint_ctx: EndpointContext | None = None,
) -> None: ) -> None:
super().__init__(errors, endpoint_ctx=endpoint_ctx) super().__init__(errors, endpoint_ctx=endpoint_ctx)
self.body = body self.body = body

8
fastapi/openapi/docs.py

@ -1,5 +1,5 @@
import json import json
from typing import Annotated, Any, Optional from typing import Annotated, Any
from annotated_doc import Doc from annotated_doc import Doc
from fastapi.encoders import jsonable_encoder from fastapi.encoders import jsonable_encoder
@ -85,7 +85,7 @@ def get_swagger_ui_html(
), ),
] = "https://fastapi.tiangolo.com/img/favicon.png", ] = "https://fastapi.tiangolo.com/img/favicon.png",
oauth2_redirect_url: Annotated[ oauth2_redirect_url: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
The OAuth2 redirect URL, it is normally automatically handled by FastAPI. The OAuth2 redirect URL, it is normally automatically handled by FastAPI.
@ -96,7 +96,7 @@ def get_swagger_ui_html(
), ),
] = None, ] = None,
init_oauth: Annotated[ init_oauth: Annotated[
Optional[dict[str, Any]], dict[str, Any] | None,
Doc( Doc(
""" """
A dictionary with Swagger UI OAuth2 initialization configurations. A dictionary with Swagger UI OAuth2 initialization configurations.
@ -107,7 +107,7 @@ def get_swagger_ui_html(
), ),
] = None, ] = None,
swagger_ui_parameters: Annotated[ swagger_ui_parameters: Annotated[
Optional[dict[str, Any]], dict[str, Any] | None,
Doc( Doc(
""" """
Configuration parameters for Swagger UI. Configuration parameters for Swagger UI.

326
fastapi/openapi/models.py

@ -1,6 +1,6 @@
from collections.abc import Iterable, Mapping from collections.abc import Callable, Iterable, Mapping
from enum import Enum from enum import Enum
from typing import Annotated, Any, Callable, Optional, Union from typing import Annotated, Any, Literal, Optional, Union
from fastapi._compat import with_info_plain_validator_function from fastapi._compat import with_info_plain_validator_function
from fastapi.logger import logger from fastapi.logger import logger
@ -10,7 +10,7 @@ from pydantic import (
Field, Field,
GetJsonSchemaHandler, GetJsonSchemaHandler,
) )
from typing_extensions import Literal, TypedDict from typing_extensions import TypedDict
from typing_extensions import deprecated as typing_deprecated from typing_extensions import deprecated as typing_deprecated
try: try:
@ -59,37 +59,37 @@ class BaseModelWithConfig(BaseModel):
class Contact(BaseModelWithConfig): class Contact(BaseModelWithConfig):
name: Optional[str] = None name: str | None = None
url: Optional[AnyUrl] = None url: AnyUrl | None = None
email: Optional[EmailStr] = None email: EmailStr | None = None
class License(BaseModelWithConfig): class License(BaseModelWithConfig):
name: str name: str
identifier: Optional[str] = None identifier: str | None = None
url: Optional[AnyUrl] = None url: AnyUrl | None = None
class Info(BaseModelWithConfig): class Info(BaseModelWithConfig):
title: str title: str
summary: Optional[str] = None summary: str | None = None
description: Optional[str] = None description: str | None = None
termsOfService: Optional[str] = None termsOfService: str | None = None
contact: Optional[Contact] = None contact: Contact | None = None
license: Optional[License] = None license: License | None = None
version: str version: str
class ServerVariable(BaseModelWithConfig): class ServerVariable(BaseModelWithConfig):
enum: Annotated[Optional[list[str]], Field(min_length=1)] = None enum: Annotated[list[str] | None, Field(min_length=1)] = None
default: str default: str
description: Optional[str] = None description: str | None = None
class Server(BaseModelWithConfig): class Server(BaseModelWithConfig):
url: Union[AnyUrl, str] url: AnyUrl | str
description: Optional[str] = None description: str | None = None
variables: Optional[dict[str, ServerVariable]] = None variables: dict[str, ServerVariable] | None = None
class Reference(BaseModel): class Reference(BaseModel):
@ -98,19 +98,19 @@ class Reference(BaseModel):
class Discriminator(BaseModel): class Discriminator(BaseModel):
propertyName: str propertyName: str
mapping: Optional[dict[str, str]] = None mapping: dict[str, str] | None = None
class XML(BaseModelWithConfig): class XML(BaseModelWithConfig):
name: Optional[str] = None name: str | None = None
namespace: Optional[str] = None namespace: str | None = None
prefix: Optional[str] = None prefix: str | None = None
attribute: Optional[bool] = None attribute: bool | None = None
wrapped: Optional[bool] = None wrapped: bool | None = None
class ExternalDocumentation(BaseModelWithConfig): class ExternalDocumentation(BaseModelWithConfig):
description: Optional[str] = None description: str | None = None
url: AnyUrl url: AnyUrl
@ -123,80 +123,80 @@ SchemaType = Literal[
class Schema(BaseModelWithConfig): class Schema(BaseModelWithConfig):
# Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-the-json-schema-core-vocabu # Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-the-json-schema-core-vocabu
# Core Vocabulary # Core Vocabulary
schema_: Optional[str] = Field(default=None, alias="$schema") schema_: str | None = Field(default=None, alias="$schema")
vocabulary: Optional[str] = Field(default=None, alias="$vocabulary") vocabulary: str | None = Field(default=None, alias="$vocabulary")
id: Optional[str] = Field(default=None, alias="$id") id: str | None = Field(default=None, alias="$id")
anchor: Optional[str] = Field(default=None, alias="$anchor") anchor: str | None = Field(default=None, alias="$anchor")
dynamicAnchor: Optional[str] = Field(default=None, alias="$dynamicAnchor") dynamicAnchor: str | None = Field(default=None, alias="$dynamicAnchor")
ref: Optional[str] = Field(default=None, alias="$ref") ref: str | None = Field(default=None, alias="$ref")
dynamicRef: Optional[str] = Field(default=None, alias="$dynamicRef") dynamicRef: str | None = Field(default=None, alias="$dynamicRef")
defs: Optional[dict[str, "SchemaOrBool"]] = Field(default=None, alias="$defs") defs: dict[str, "SchemaOrBool"] | None = Field(default=None, alias="$defs")
comment: Optional[str] = Field(default=None, alias="$comment") comment: str | None = Field(default=None, alias="$comment")
# Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-a-vocabulary-for-applying-s # Ref: JSON Schema 2020-12: https://json-schema.org/draft/2020-12/json-schema-core.html#name-a-vocabulary-for-applying-s
# A Vocabulary for Applying Subschemas # A Vocabulary for Applying Subschemas
allOf: Optional[list["SchemaOrBool"]] = None allOf: list["SchemaOrBool"] | None = None
anyOf: Optional[list["SchemaOrBool"]] = None anyOf: list["SchemaOrBool"] | None = None
oneOf: Optional[list["SchemaOrBool"]] = None oneOf: list["SchemaOrBool"] | None = None
not_: Optional["SchemaOrBool"] = Field(default=None, alias="not") not_: Optional["SchemaOrBool"] = Field(default=None, alias="not")
if_: Optional["SchemaOrBool"] = Field(default=None, alias="if") if_: Optional["SchemaOrBool"] = Field(default=None, alias="if")
then: Optional["SchemaOrBool"] = None then: Optional["SchemaOrBool"] = None
else_: Optional["SchemaOrBool"] = Field(default=None, alias="else") else_: Optional["SchemaOrBool"] = Field(default=None, alias="else")
dependentSchemas: Optional[dict[str, "SchemaOrBool"]] = None dependentSchemas: dict[str, "SchemaOrBool"] | None = None
prefixItems: Optional[list["SchemaOrBool"]] = None prefixItems: list["SchemaOrBool"] | None = None
items: Optional["SchemaOrBool"] = None items: Optional["SchemaOrBool"] = None
contains: Optional["SchemaOrBool"] = None contains: Optional["SchemaOrBool"] = None
properties: Optional[dict[str, "SchemaOrBool"]] = None properties: dict[str, "SchemaOrBool"] | None = None
patternProperties: Optional[dict[str, "SchemaOrBool"]] = None patternProperties: dict[str, "SchemaOrBool"] | None = None
additionalProperties: Optional["SchemaOrBool"] = None additionalProperties: Optional["SchemaOrBool"] = None
propertyNames: Optional["SchemaOrBool"] = None propertyNames: Optional["SchemaOrBool"] = None
unevaluatedItems: Optional["SchemaOrBool"] = None unevaluatedItems: Optional["SchemaOrBool"] = None
unevaluatedProperties: Optional["SchemaOrBool"] = None unevaluatedProperties: Optional["SchemaOrBool"] = None
# Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-structural # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-structural
# A Vocabulary for Structural Validation # A Vocabulary for Structural Validation
type: Optional[Union[SchemaType, list[SchemaType]]] = None type: SchemaType | list[SchemaType] | None = None
enum: Optional[list[Any]] = None enum: list[Any] | None = None
const: Optional[Any] = None const: Any | None = None
multipleOf: Optional[float] = Field(default=None, gt=0) multipleOf: float | None = Field(default=None, gt=0)
maximum: Optional[float] = None maximum: float | None = None
exclusiveMaximum: Optional[float] = None exclusiveMaximum: float | None = None
minimum: Optional[float] = None minimum: float | None = None
exclusiveMinimum: Optional[float] = None exclusiveMinimum: float | None = None
maxLength: Optional[int] = Field(default=None, ge=0) maxLength: int | None = Field(default=None, ge=0)
minLength: Optional[int] = Field(default=None, ge=0) minLength: int | None = Field(default=None, ge=0)
pattern: Optional[str] = None pattern: str | None = None
maxItems: Optional[int] = Field(default=None, ge=0) maxItems: int | None = Field(default=None, ge=0)
minItems: Optional[int] = Field(default=None, ge=0) minItems: int | None = Field(default=None, ge=0)
uniqueItems: Optional[bool] = None uniqueItems: bool | None = None
maxContains: Optional[int] = Field(default=None, ge=0) maxContains: int | None = Field(default=None, ge=0)
minContains: Optional[int] = Field(default=None, ge=0) minContains: int | None = Field(default=None, ge=0)
maxProperties: Optional[int] = Field(default=None, ge=0) maxProperties: int | None = Field(default=None, ge=0)
minProperties: Optional[int] = Field(default=None, ge=0) minProperties: int | None = Field(default=None, ge=0)
required: Optional[list[str]] = None required: list[str] | None = None
dependentRequired: Optional[dict[str, set[str]]] = None dependentRequired: dict[str, set[str]] | None = None
# Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-vocabularies-for-semantic-c # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-vocabularies-for-semantic-c
# Vocabularies for Semantic Content With "format" # Vocabularies for Semantic Content With "format"
format: Optional[str] = None format: str | None = None
# Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-the-conten # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-the-conten
# A Vocabulary for the Contents of String-Encoded Data # A Vocabulary for the Contents of String-Encoded Data
contentEncoding: Optional[str] = None contentEncoding: str | None = None
contentMediaType: Optional[str] = None contentMediaType: str | None = None
contentSchema: Optional["SchemaOrBool"] = None contentSchema: Optional["SchemaOrBool"] = None
# Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-basic-meta # Ref: JSON Schema Validation 2020-12: https://json-schema.org/draft/2020-12/json-schema-validation.html#name-a-vocabulary-for-basic-meta
# A Vocabulary for Basic Meta-Data Annotations # A Vocabulary for Basic Meta-Data Annotations
title: Optional[str] = None title: str | None = None
description: Optional[str] = None description: str | None = None
default: Optional[Any] = None default: Any | None = None
deprecated: Optional[bool] = None deprecated: bool | None = None
readOnly: Optional[bool] = None readOnly: bool | None = None
writeOnly: Optional[bool] = None writeOnly: bool | None = None
examples: Optional[list[Any]] = None examples: list[Any] | None = None
# Ref: OpenAPI 3.1.0: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#schema-object # Ref: OpenAPI 3.1.0: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#schema-object
# Schema Object # Schema Object
discriminator: Optional[Discriminator] = None discriminator: Discriminator | None = None
xml: Optional[XML] = None xml: XML | None = None
externalDocs: Optional[ExternalDocumentation] = None externalDocs: ExternalDocumentation | None = None
example: Annotated[ example: Annotated[
Optional[Any], Any | None,
typing_deprecated( typing_deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead." "although still supported. Use examples instead."
@ -206,14 +206,14 @@ class Schema(BaseModelWithConfig):
# Ref: https://json-schema.org/draft/2020-12/json-schema-core.html#name-json-schema-documents # Ref: https://json-schema.org/draft/2020-12/json-schema-core.html#name-json-schema-documents
# A JSON Schema MUST be an object or a boolean. # A JSON Schema MUST be an object or a boolean.
SchemaOrBool = Union[Schema, bool] SchemaOrBool = Schema | bool
class Example(TypedDict, total=False): class Example(TypedDict, total=False):
summary: Optional[str] summary: str | None
description: Optional[str] description: str | None
value: Optional[Any] value: Any | None
externalValue: Optional[AnyUrl] externalValue: AnyUrl | None
__pydantic_config__ = {"extra": "allow"} # type: ignore[misc] __pydantic_config__ = {"extra": "allow"} # type: ignore[misc]
@ -226,33 +226,33 @@ class ParameterInType(Enum):
class Encoding(BaseModelWithConfig): class Encoding(BaseModelWithConfig):
contentType: Optional[str] = None contentType: str | None = None
headers: Optional[dict[str, Union["Header", Reference]]] = None headers: dict[str, Union["Header", Reference]] | None = None
style: Optional[str] = None style: str | None = None
explode: Optional[bool] = None explode: bool | None = None
allowReserved: Optional[bool] = None allowReserved: bool | None = None
class MediaType(BaseModelWithConfig): class MediaType(BaseModelWithConfig):
schema_: Optional[Union[Schema, Reference]] = Field(default=None, alias="schema") schema_: Schema | Reference | None = Field(default=None, alias="schema")
example: Optional[Any] = None example: Any | None = None
examples: Optional[dict[str, Union[Example, Reference]]] = None examples: dict[str, Example | Reference] | None = None
encoding: Optional[dict[str, Encoding]] = None encoding: dict[str, Encoding] | None = None
class ParameterBase(BaseModelWithConfig): class ParameterBase(BaseModelWithConfig):
description: Optional[str] = None description: str | None = None
required: Optional[bool] = None required: bool | None = None
deprecated: Optional[bool] = None deprecated: bool | None = None
# Serialization rules for simple scenarios # Serialization rules for simple scenarios
style: Optional[str] = None style: str | None = None
explode: Optional[bool] = None explode: bool | None = None
allowReserved: Optional[bool] = None allowReserved: bool | None = None
schema_: Optional[Union[Schema, Reference]] = Field(default=None, alias="schema") schema_: Schema | Reference | None = Field(default=None, alias="schema")
example: Optional[Any] = None example: Any | None = None
examples: Optional[dict[str, Union[Example, Reference]]] = None examples: dict[str, Example | Reference] | None = None
# Serialization rules for more complex scenarios # Serialization rules for more complex scenarios
content: Optional[dict[str, MediaType]] = None content: dict[str, MediaType] | None = None
class Parameter(ParameterBase): class Parameter(ParameterBase):
@ -265,57 +265,57 @@ class Header(ParameterBase):
class RequestBody(BaseModelWithConfig): class RequestBody(BaseModelWithConfig):
description: Optional[str] = None description: str | None = None
content: dict[str, MediaType] content: dict[str, MediaType]
required: Optional[bool] = None required: bool | None = None
class Link(BaseModelWithConfig): class Link(BaseModelWithConfig):
operationRef: Optional[str] = None operationRef: str | None = None
operationId: Optional[str] = None operationId: str | None = None
parameters: Optional[dict[str, Union[Any, str]]] = None parameters: dict[str, Any | str] | None = None
requestBody: Optional[Union[Any, str]] = None requestBody: Any | str | None = None
description: Optional[str] = None description: str | None = None
server: Optional[Server] = None server: Server | None = None
class Response(BaseModelWithConfig): class Response(BaseModelWithConfig):
description: str description: str
headers: Optional[dict[str, Union[Header, Reference]]] = None headers: dict[str, Header | Reference] | None = None
content: Optional[dict[str, MediaType]] = None content: dict[str, MediaType] | None = None
links: Optional[dict[str, Union[Link, Reference]]] = None links: dict[str, Link | Reference] | None = None
class Operation(BaseModelWithConfig): class Operation(BaseModelWithConfig):
tags: Optional[list[str]] = None tags: list[str] | None = None
summary: Optional[str] = None summary: str | None = None
description: Optional[str] = None description: str | None = None
externalDocs: Optional[ExternalDocumentation] = None externalDocs: ExternalDocumentation | None = None
operationId: Optional[str] = None operationId: str | None = None
parameters: Optional[list[Union[Parameter, Reference]]] = None parameters: list[Parameter | Reference] | None = None
requestBody: Optional[Union[RequestBody, Reference]] = None requestBody: RequestBody | Reference | None = None
# Using Any for Specification Extensions # Using Any for Specification Extensions
responses: Optional[dict[str, Union[Response, Any]]] = None responses: dict[str, Response | Any] | None = None
callbacks: Optional[dict[str, Union[dict[str, "PathItem"], Reference]]] = None callbacks: dict[str, dict[str, "PathItem"] | Reference] | None = None
deprecated: Optional[bool] = None deprecated: bool | None = None
security: Optional[list[dict[str, list[str]]]] = None security: list[dict[str, list[str]]] | None = None
servers: Optional[list[Server]] = None servers: list[Server] | None = None
class PathItem(BaseModelWithConfig): class PathItem(BaseModelWithConfig):
ref: Optional[str] = Field(default=None, alias="$ref") ref: str | None = Field(default=None, alias="$ref")
summary: Optional[str] = None summary: str | None = None
description: Optional[str] = None description: str | None = None
get: Optional[Operation] = None get: Operation | None = None
put: Optional[Operation] = None put: Operation | None = None
post: Optional[Operation] = None post: Operation | None = None
delete: Optional[Operation] = None delete: Operation | None = None
options: Optional[Operation] = None options: Operation | None = None
head: Optional[Operation] = None head: Operation | None = None
patch: Optional[Operation] = None patch: Operation | None = None
trace: Optional[Operation] = None trace: Operation | None = None
servers: Optional[list[Server]] = None servers: list[Server] | None = None
parameters: Optional[list[Union[Parameter, Reference]]] = None parameters: list[Parameter | Reference] | None = None
class SecuritySchemeType(Enum): class SecuritySchemeType(Enum):
@ -327,7 +327,7 @@ class SecuritySchemeType(Enum):
class SecurityBase(BaseModelWithConfig): class SecurityBase(BaseModelWithConfig):
type_: SecuritySchemeType = Field(alias="type") type_: SecuritySchemeType = Field(alias="type")
description: Optional[str] = None description: str | None = None
class APIKeyIn(Enum): class APIKeyIn(Enum):
@ -349,11 +349,11 @@ class HTTPBase(SecurityBase):
class HTTPBearer(HTTPBase): class HTTPBearer(HTTPBase):
scheme: Literal["bearer"] = "bearer" scheme: Literal["bearer"] = "bearer"
bearerFormat: Optional[str] = None bearerFormat: str | None = None
class OAuthFlow(BaseModelWithConfig): class OAuthFlow(BaseModelWithConfig):
refreshUrl: Optional[str] = None refreshUrl: str | None = None
scopes: dict[str, str] = {} scopes: dict[str, str] = {}
@ -375,10 +375,10 @@ class OAuthFlowAuthorizationCode(OAuthFlow):
class OAuthFlows(BaseModelWithConfig): class OAuthFlows(BaseModelWithConfig):
implicit: Optional[OAuthFlowImplicit] = None implicit: OAuthFlowImplicit | None = None
password: Optional[OAuthFlowPassword] = None password: OAuthFlowPassword | None = None
clientCredentials: Optional[OAuthFlowClientCredentials] = None clientCredentials: OAuthFlowClientCredentials | None = None
authorizationCode: Optional[OAuthFlowAuthorizationCode] = None authorizationCode: OAuthFlowAuthorizationCode | None = None
class OAuth2(SecurityBase): class OAuth2(SecurityBase):
@ -393,41 +393,41 @@ class OpenIdConnect(SecurityBase):
openIdConnectUrl: str openIdConnectUrl: str
SecurityScheme = Union[APIKey, HTTPBase, OAuth2, OpenIdConnect, HTTPBearer] SecurityScheme = APIKey | HTTPBase | OAuth2 | OpenIdConnect | HTTPBearer
class Components(BaseModelWithConfig): class Components(BaseModelWithConfig):
schemas: Optional[dict[str, Union[Schema, Reference]]] = None schemas: dict[str, Schema | Reference] | None = None
responses: Optional[dict[str, Union[Response, Reference]]] = None responses: dict[str, Response | Reference] | None = None
parameters: Optional[dict[str, Union[Parameter, Reference]]] = None parameters: dict[str, Parameter | Reference] | None = None
examples: Optional[dict[str, Union[Example, Reference]]] = None examples: dict[str, Example | Reference] | None = None
requestBodies: Optional[dict[str, Union[RequestBody, Reference]]] = None requestBodies: dict[str, RequestBody | Reference] | None = None
headers: Optional[dict[str, Union[Header, Reference]]] = None headers: dict[str, Header | Reference] | None = None
securitySchemes: Optional[dict[str, Union[SecurityScheme, Reference]]] = None securitySchemes: dict[str, SecurityScheme | Reference] | None = None
links: Optional[dict[str, Union[Link, Reference]]] = None links: dict[str, Link | Reference] | None = None
# Using Any for Specification Extensions # Using Any for Specification Extensions
callbacks: Optional[dict[str, Union[dict[str, PathItem], Reference, Any]]] = None callbacks: dict[str, dict[str, PathItem] | Reference | Any] | None = None
pathItems: Optional[dict[str, Union[PathItem, Reference]]] = None pathItems: dict[str, PathItem | Reference] | None = None
class Tag(BaseModelWithConfig): class Tag(BaseModelWithConfig):
name: str name: str
description: Optional[str] = None description: str | None = None
externalDocs: Optional[ExternalDocumentation] = None externalDocs: ExternalDocumentation | None = None
class OpenAPI(BaseModelWithConfig): class OpenAPI(BaseModelWithConfig):
openapi: str openapi: str
info: Info info: Info
jsonSchemaDialect: Optional[str] = None jsonSchemaDialect: str | None = None
servers: Optional[list[Server]] = None servers: list[Server] | None = None
# Using Any for Specification Extensions # Using Any for Specification Extensions
paths: Optional[dict[str, Union[PathItem, Any]]] = None paths: dict[str, PathItem | Any] | None = None
webhooks: Optional[dict[str, Union[PathItem, Reference]]] = None webhooks: dict[str, PathItem | Reference] | None = None
components: Optional[Components] = None components: Components | None = None
security: Optional[list[dict[str, list[str]]]] = None security: list[dict[str, list[str]]] | None = None
tags: Optional[list[Tag]] = None tags: list[Tag] | None = None
externalDocs: Optional[ExternalDocumentation] = None externalDocs: ExternalDocumentation | None = None
Schema.model_rebuild() Schema.model_rebuild()

31
fastapi/openapi/utils.py

@ -3,7 +3,7 @@ import http.client
import inspect import inspect
import warnings import warnings
from collections.abc import Sequence from collections.abc import Sequence
from typing import Any, Optional, Union, cast from typing import Any, Literal, cast
from fastapi import routing from fastapi import routing
from fastapi._compat import ( from fastapi._compat import (
@ -38,7 +38,6 @@ from fastapi.utils import (
from pydantic import BaseModel from pydantic import BaseModel
from starlette.responses import JSONResponse from starlette.responses import JSONResponse
from starlette.routing import BaseRoute from starlette.routing import BaseRoute
from typing_extensions import Literal
validation_error_definition = { validation_error_definition = {
"title": "ValidationError", "title": "ValidationError",
@ -180,13 +179,13 @@ def _get_openapi_operation_parameters(
def get_openapi_operation_request_body( def get_openapi_operation_request_body(
*, *,
body_field: Optional[ModelField], body_field: ModelField | None,
model_name_map: ModelNameMap, model_name_map: ModelNameMap,
field_mapping: dict[ field_mapping: dict[
tuple[ModelField, Literal["validation", "serialization"]], dict[str, Any] tuple[ModelField, Literal["validation", "serialization"]], dict[str, Any]
], ],
separate_input_output_schemas: bool = True, separate_input_output_schemas: bool = True,
) -> Optional[dict[str, Any]]: ) -> dict[str, Any] | None:
if not body_field: if not body_field:
return None return None
assert isinstance(body_field, ModelField) assert isinstance(body_field, ModelField)
@ -279,7 +278,7 @@ def get_openapi_path(
else: else:
current_response_class = route.response_class current_response_class = route.response_class
assert current_response_class, "A response class is needed to generate OpenAPI" assert current_response_class, "A response class is needed to generate OpenAPI"
route_response_media_type: Optional[str] = current_response_class.media_type route_response_media_type: str | None = current_response_class.media_type
if route.include_in_schema: if route.include_in_schema:
for method in route.methods: for method in route.methods:
operation = get_openapi_operation_metadata( operation = get_openapi_operation_metadata(
@ -393,7 +392,7 @@ def get_openapi_path(
"An additional response must be a dict" "An additional response must be a dict"
) )
field = route.response_fields.get(additional_status_code) field = route.response_fields.get(additional_status_code)
additional_field_schema: Optional[dict[str, Any]] = None additional_field_schema: dict[str, Any] | None = None
if field: if field:
additional_field_schema = get_schema_from_model_field( additional_field_schema = get_schema_from_model_field(
field=field, field=field,
@ -408,7 +407,7 @@ def get_openapi_path(
.setdefault("schema", {}) .setdefault("schema", {})
) )
deep_dict_update(additional_schema, additional_field_schema) deep_dict_update(additional_schema, additional_field_schema)
status_text: Optional[str] = status_code_ranges.get( status_text: str | None = status_code_ranges.get(
str(additional_status_code).upper() str(additional_status_code).upper()
) or http.client.responses.get(int(additional_status_code)) ) or http.client.responses.get(int(additional_status_code))
description = ( description = (
@ -482,17 +481,17 @@ def get_openapi(
title: str, title: str,
version: str, version: str,
openapi_version: str = "3.1.0", openapi_version: str = "3.1.0",
summary: Optional[str] = None, summary: str | None = None,
description: Optional[str] = None, description: str | None = None,
routes: Sequence[BaseRoute], routes: Sequence[BaseRoute],
webhooks: Optional[Sequence[BaseRoute]] = None, webhooks: Sequence[BaseRoute] | None = None,
tags: Optional[list[dict[str, Any]]] = None, tags: list[dict[str, Any]] | None = None,
servers: Optional[list[dict[str, Union[str, Any]]]] = None, servers: list[dict[str, str | Any]] | None = None,
terms_of_service: Optional[str] = None, terms_of_service: str | None = None,
contact: Optional[dict[str, Union[str, Any]]] = None, contact: dict[str, str | Any] | None = None,
license_info: Optional[dict[str, Union[str, Any]]] = None, license_info: dict[str, str | Any] | None = None,
separate_input_output_schemas: bool = True, separate_input_output_schemas: bool = True,
external_docs: Optional[dict[str, Any]] = None, external_docs: dict[str, Any] | None = None,
) -> dict[str, Any]: ) -> dict[str, Any]:
info: dict[str, Any] = {"title": title, "version": version} info: dict[str, Any] = {"title": title, "version": version}
if summary: if summary:

380
fastapi/param_functions.py

File diff suppressed because it is too large

446
fastapi/params.py

@ -1,14 +1,14 @@
import warnings import warnings
from collections.abc import Sequence from collections.abc import Callable, Sequence
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum from enum import Enum
from typing import Annotated, Any, Callable, Optional, Union from typing import Annotated, Any, Literal
from fastapi.exceptions import FastAPIDeprecationWarning from fastapi.exceptions import FastAPIDeprecationWarning
from fastapi.openapi.models import Example from fastapi.openapi.models import Example
from pydantic import AliasChoices, AliasPath from pydantic import AliasChoices, AliasPath
from pydantic.fields import FieldInfo from pydantic.fields import FieldInfo
from typing_extensions import Literal, deprecated from typing_extensions import deprecated
from ._compat import ( from ._compat import (
Undefined, Undefined,
@ -31,45 +31,45 @@ class Param(FieldInfo): # type: ignore[misc]
self, self,
default: Any = Undefined, default: Any = Undefined,
*, *,
default_factory: Union[Callable[[], Any], None] = _Unset, default_factory: Callable[[], Any] | None = _Unset,
annotation: Optional[Any] = None, annotation: Any | None = None,
alias: Optional[str] = None, alias: str | None = None,
alias_priority: Union[int, None] = _Unset, alias_priority: int | None = _Unset,
validation_alias: Union[str, AliasPath, AliasChoices, None] = None, validation_alias: str | AliasPath | AliasChoices | None = None,
serialization_alias: Union[str, None] = None, serialization_alias: str | None = None,
title: Optional[str] = None, title: str | None = None,
description: Optional[str] = None, description: str | None = None,
gt: Optional[float] = None, gt: float | None = None,
ge: Optional[float] = None, ge: float | None = None,
lt: Optional[float] = None, lt: float | None = None,
le: Optional[float] = None, le: float | None = None,
min_length: Optional[int] = None, min_length: int | None = None,
max_length: Optional[int] = None, max_length: int | None = None,
pattern: Optional[str] = None, pattern: str | None = None,
regex: Annotated[ regex: Annotated[
Optional[str], str | None,
deprecated( deprecated(
"Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
), ),
] = None, ] = None,
discriminator: Union[str, None] = None, discriminator: str | None = None,
strict: Union[bool, None] = _Unset, strict: bool | None = _Unset,
multiple_of: Union[float, None] = _Unset, multiple_of: float | None = _Unset,
allow_inf_nan: Union[bool, None] = _Unset, allow_inf_nan: bool | None = _Unset,
max_digits: Union[int, None] = _Unset, max_digits: int | None = _Unset,
decimal_places: Union[int, None] = _Unset, decimal_places: int | None = _Unset,
examples: Optional[list[Any]] = None, examples: list[Any] | None = None,
example: Annotated[ example: Annotated[
Optional[Any], Any | None,
deprecated( deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead." "although still supported. Use examples instead."
), ),
] = _Unset, ] = _Unset,
openapi_examples: Optional[dict[str, Example]] = None, openapi_examples: dict[str, Example] | None = None,
deprecated: Union[deprecated, str, bool, None] = None, deprecated: deprecated | str | bool | None = None,
include_in_schema: bool = True, include_in_schema: bool = True,
json_schema_extra: Union[dict[str, Any], None] = None, json_schema_extra: dict[str, Any] | None = None,
**extra: Any, **extra: Any,
): ):
if example is not _Unset: if example is not _Unset:
@ -142,45 +142,45 @@ class Path(Param): # type: ignore[misc]
self, self,
default: Any = ..., default: Any = ...,
*, *,
default_factory: Union[Callable[[], Any], None] = _Unset, default_factory: Callable[[], Any] | None = _Unset,
annotation: Optional[Any] = None, annotation: Any | None = None,
alias: Optional[str] = None, alias: str | None = None,
alias_priority: Union[int, None] = _Unset, alias_priority: int | None = _Unset,
validation_alias: Union[str, AliasPath, AliasChoices, None] = None, validation_alias: str | AliasPath | AliasChoices | None = None,
serialization_alias: Union[str, None] = None, serialization_alias: str | None = None,
title: Optional[str] = None, title: str | None = None,
description: Optional[str] = None, description: str | None = None,
gt: Optional[float] = None, gt: float | None = None,
ge: Optional[float] = None, ge: float | None = None,
lt: Optional[float] = None, lt: float | None = None,
le: Optional[float] = None, le: float | None = None,
min_length: Optional[int] = None, min_length: int | None = None,
max_length: Optional[int] = None, max_length: int | None = None,
pattern: Optional[str] = None, pattern: str | None = None,
regex: Annotated[ regex: Annotated[
Optional[str], str | None,
deprecated( deprecated(
"Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
), ),
] = None, ] = None,
discriminator: Union[str, None] = None, discriminator: str | None = None,
strict: Union[bool, None] = _Unset, strict: bool | None = _Unset,
multiple_of: Union[float, None] = _Unset, multiple_of: float | None = _Unset,
allow_inf_nan: Union[bool, None] = _Unset, allow_inf_nan: bool | None = _Unset,
max_digits: Union[int, None] = _Unset, max_digits: int | None = _Unset,
decimal_places: Union[int, None] = _Unset, decimal_places: int | None = _Unset,
examples: Optional[list[Any]] = None, examples: list[Any] | None = None,
example: Annotated[ example: Annotated[
Optional[Any], Any | None,
deprecated( deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead." "although still supported. Use examples instead."
), ),
] = _Unset, ] = _Unset,
openapi_examples: Optional[dict[str, Example]] = None, openapi_examples: dict[str, Example] | None = None,
deprecated: Union[deprecated, str, bool, None] = None, deprecated: deprecated | str | bool | None = None,
include_in_schema: bool = True, include_in_schema: bool = True,
json_schema_extra: Union[dict[str, Any], None] = None, json_schema_extra: dict[str, Any] | None = None,
**extra: Any, **extra: Any,
): ):
assert default is ..., "Path parameters cannot have a default value" assert default is ..., "Path parameters cannot have a default value"
@ -226,45 +226,45 @@ class Query(Param): # type: ignore[misc]
self, self,
default: Any = Undefined, default: Any = Undefined,
*, *,
default_factory: Union[Callable[[], Any], None] = _Unset, default_factory: Callable[[], Any] | None = _Unset,
annotation: Optional[Any] = None, annotation: Any | None = None,
alias: Optional[str] = None, alias: str | None = None,
alias_priority: Union[int, None] = _Unset, alias_priority: int | None = _Unset,
validation_alias: Union[str, AliasPath, AliasChoices, None] = None, validation_alias: str | AliasPath | AliasChoices | None = None,
serialization_alias: Union[str, None] = None, serialization_alias: str | None = None,
title: Optional[str] = None, title: str | None = None,
description: Optional[str] = None, description: str | None = None,
gt: Optional[float] = None, gt: float | None = None,
ge: Optional[float] = None, ge: float | None = None,
lt: Optional[float] = None, lt: float | None = None,
le: Optional[float] = None, le: float | None = None,
min_length: Optional[int] = None, min_length: int | None = None,
max_length: Optional[int] = None, max_length: int | None = None,
pattern: Optional[str] = None, pattern: str | None = None,
regex: Annotated[ regex: Annotated[
Optional[str], str | None,
deprecated( deprecated(
"Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
), ),
] = None, ] = None,
discriminator: Union[str, None] = None, discriminator: str | None = None,
strict: Union[bool, None] = _Unset, strict: bool | None = _Unset,
multiple_of: Union[float, None] = _Unset, multiple_of: float | None = _Unset,
allow_inf_nan: Union[bool, None] = _Unset, allow_inf_nan: bool | None = _Unset,
max_digits: Union[int, None] = _Unset, max_digits: int | None = _Unset,
decimal_places: Union[int, None] = _Unset, decimal_places: int | None = _Unset,
examples: Optional[list[Any]] = None, examples: list[Any] | None = None,
example: Annotated[ example: Annotated[
Optional[Any], Any | None,
deprecated( deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead." "although still supported. Use examples instead."
), ),
] = _Unset, ] = _Unset,
openapi_examples: Optional[dict[str, Example]] = None, openapi_examples: dict[str, Example] | None = None,
deprecated: Union[deprecated, str, bool, None] = None, deprecated: deprecated | str | bool | None = None,
include_in_schema: bool = True, include_in_schema: bool = True,
json_schema_extra: Union[dict[str, Any], None] = None, json_schema_extra: dict[str, Any] | None = None,
**extra: Any, **extra: Any,
): ):
super().__init__( super().__init__(
@ -308,46 +308,46 @@ class Header(Param): # type: ignore[misc]
self, self,
default: Any = Undefined, default: Any = Undefined,
*, *,
default_factory: Union[Callable[[], Any], None] = _Unset, default_factory: Callable[[], Any] | None = _Unset,
annotation: Optional[Any] = None, annotation: Any | None = None,
alias: Optional[str] = None, alias: str | None = None,
alias_priority: Union[int, None] = _Unset, alias_priority: int | None = _Unset,
validation_alias: Union[str, AliasPath, AliasChoices, None] = None, validation_alias: str | AliasPath | AliasChoices | None = None,
serialization_alias: Union[str, None] = None, serialization_alias: str | None = None,
convert_underscores: bool = True, convert_underscores: bool = True,
title: Optional[str] = None, title: str | None = None,
description: Optional[str] = None, description: str | None = None,
gt: Optional[float] = None, gt: float | None = None,
ge: Optional[float] = None, ge: float | None = None,
lt: Optional[float] = None, lt: float | None = None,
le: Optional[float] = None, le: float | None = None,
min_length: Optional[int] = None, min_length: int | None = None,
max_length: Optional[int] = None, max_length: int | None = None,
pattern: Optional[str] = None, pattern: str | None = None,
regex: Annotated[ regex: Annotated[
Optional[str], str | None,
deprecated( deprecated(
"Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
), ),
] = None, ] = None,
discriminator: Union[str, None] = None, discriminator: str | None = None,
strict: Union[bool, None] = _Unset, strict: bool | None = _Unset,
multiple_of: Union[float, None] = _Unset, multiple_of: float | None = _Unset,
allow_inf_nan: Union[bool, None] = _Unset, allow_inf_nan: bool | None = _Unset,
max_digits: Union[int, None] = _Unset, max_digits: int | None = _Unset,
decimal_places: Union[int, None] = _Unset, decimal_places: int | None = _Unset,
examples: Optional[list[Any]] = None, examples: list[Any] | None = None,
example: Annotated[ example: Annotated[
Optional[Any], Any | None,
deprecated( deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead." "although still supported. Use examples instead."
), ),
] = _Unset, ] = _Unset,
openapi_examples: Optional[dict[str, Example]] = None, openapi_examples: dict[str, Example] | None = None,
deprecated: Union[deprecated, str, bool, None] = None, deprecated: deprecated | str | bool | None = None,
include_in_schema: bool = True, include_in_schema: bool = True,
json_schema_extra: Union[dict[str, Any], None] = None, json_schema_extra: dict[str, Any] | None = None,
**extra: Any, **extra: Any,
): ):
self.convert_underscores = convert_underscores self.convert_underscores = convert_underscores
@ -392,45 +392,45 @@ class Cookie(Param): # type: ignore[misc]
self, self,
default: Any = Undefined, default: Any = Undefined,
*, *,
default_factory: Union[Callable[[], Any], None] = _Unset, default_factory: Callable[[], Any] | None = _Unset,
annotation: Optional[Any] = None, annotation: Any | None = None,
alias: Optional[str] = None, alias: str | None = None,
alias_priority: Union[int, None] = _Unset, alias_priority: int | None = _Unset,
validation_alias: Union[str, AliasPath, AliasChoices, None] = None, validation_alias: str | AliasPath | AliasChoices | None = None,
serialization_alias: Union[str, None] = None, serialization_alias: str | None = None,
title: Optional[str] = None, title: str | None = None,
description: Optional[str] = None, description: str | None = None,
gt: Optional[float] = None, gt: float | None = None,
ge: Optional[float] = None, ge: float | None = None,
lt: Optional[float] = None, lt: float | None = None,
le: Optional[float] = None, le: float | None = None,
min_length: Optional[int] = None, min_length: int | None = None,
max_length: Optional[int] = None, max_length: int | None = None,
pattern: Optional[str] = None, pattern: str | None = None,
regex: Annotated[ regex: Annotated[
Optional[str], str | None,
deprecated( deprecated(
"Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
), ),
] = None, ] = None,
discriminator: Union[str, None] = None, discriminator: str | None = None,
strict: Union[bool, None] = _Unset, strict: bool | None = _Unset,
multiple_of: Union[float, None] = _Unset, multiple_of: float | None = _Unset,
allow_inf_nan: Union[bool, None] = _Unset, allow_inf_nan: bool | None = _Unset,
max_digits: Union[int, None] = _Unset, max_digits: int | None = _Unset,
decimal_places: Union[int, None] = _Unset, decimal_places: int | None = _Unset,
examples: Optional[list[Any]] = None, examples: list[Any] | None = None,
example: Annotated[ example: Annotated[
Optional[Any], Any | None,
deprecated( deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead." "although still supported. Use examples instead."
), ),
] = _Unset, ] = _Unset,
openapi_examples: Optional[dict[str, Example]] = None, openapi_examples: dict[str, Example] | None = None,
deprecated: Union[deprecated, str, bool, None] = None, deprecated: deprecated | str | bool | None = None,
include_in_schema: bool = True, include_in_schema: bool = True,
json_schema_extra: Union[dict[str, Any], None] = None, json_schema_extra: dict[str, Any] | None = None,
**extra: Any, **extra: Any,
): ):
super().__init__( super().__init__(
@ -472,47 +472,47 @@ class Body(FieldInfo): # type: ignore[misc]
self, self,
default: Any = Undefined, default: Any = Undefined,
*, *,
default_factory: Union[Callable[[], Any], None] = _Unset, default_factory: Callable[[], Any] | None = _Unset,
annotation: Optional[Any] = None, annotation: Any | None = None,
embed: Union[bool, None] = None, embed: bool | None = None,
media_type: str = "application/json", media_type: str = "application/json",
alias: Optional[str] = None, alias: str | None = None,
alias_priority: Union[int, None] = _Unset, alias_priority: int | None = _Unset,
validation_alias: Union[str, AliasPath, AliasChoices, None] = None, validation_alias: str | AliasPath | AliasChoices | None = None,
serialization_alias: Union[str, None] = None, serialization_alias: str | None = None,
title: Optional[str] = None, title: str | None = None,
description: Optional[str] = None, description: str | None = None,
gt: Optional[float] = None, gt: float | None = None,
ge: Optional[float] = None, ge: float | None = None,
lt: Optional[float] = None, lt: float | None = None,
le: Optional[float] = None, le: float | None = None,
min_length: Optional[int] = None, min_length: int | None = None,
max_length: Optional[int] = None, max_length: int | None = None,
pattern: Optional[str] = None, pattern: str | None = None,
regex: Annotated[ regex: Annotated[
Optional[str], str | None,
deprecated( deprecated(
"Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
), ),
] = None, ] = None,
discriminator: Union[str, None] = None, discriminator: str | None = None,
strict: Union[bool, None] = _Unset, strict: bool | None = _Unset,
multiple_of: Union[float, None] = _Unset, multiple_of: float | None = _Unset,
allow_inf_nan: Union[bool, None] = _Unset, allow_inf_nan: bool | None = _Unset,
max_digits: Union[int, None] = _Unset, max_digits: int | None = _Unset,
decimal_places: Union[int, None] = _Unset, decimal_places: int | None = _Unset,
examples: Optional[list[Any]] = None, examples: list[Any] | None = None,
example: Annotated[ example: Annotated[
Optional[Any], Any | None,
deprecated( deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead." "although still supported. Use examples instead."
), ),
] = _Unset, ] = _Unset,
openapi_examples: Optional[dict[str, Example]] = None, openapi_examples: dict[str, Example] | None = None,
deprecated: Union[deprecated, str, bool, None] = None, deprecated: deprecated | str | bool | None = None,
include_in_schema: bool = True, include_in_schema: bool = True,
json_schema_extra: Union[dict[str, Any], None] = None, json_schema_extra: dict[str, Any] | None = None,
**extra: Any, **extra: Any,
): ):
self.embed = embed self.embed = embed
@ -584,46 +584,46 @@ class Form(Body): # type: ignore[misc]
self, self,
default: Any = Undefined, default: Any = Undefined,
*, *,
default_factory: Union[Callable[[], Any], None] = _Unset, default_factory: Callable[[], Any] | None = _Unset,
annotation: Optional[Any] = None, annotation: Any | None = None,
media_type: str = "application/x-www-form-urlencoded", media_type: str = "application/x-www-form-urlencoded",
alias: Optional[str] = None, alias: str | None = None,
alias_priority: Union[int, None] = _Unset, alias_priority: int | None = _Unset,
validation_alias: Union[str, AliasPath, AliasChoices, None] = None, validation_alias: str | AliasPath | AliasChoices | None = None,
serialization_alias: Union[str, None] = None, serialization_alias: str | None = None,
title: Optional[str] = None, title: str | None = None,
description: Optional[str] = None, description: str | None = None,
gt: Optional[float] = None, gt: float | None = None,
ge: Optional[float] = None, ge: float | None = None,
lt: Optional[float] = None, lt: float | None = None,
le: Optional[float] = None, le: float | None = None,
min_length: Optional[int] = None, min_length: int | None = None,
max_length: Optional[int] = None, max_length: int | None = None,
pattern: Optional[str] = None, pattern: str | None = None,
regex: Annotated[ regex: Annotated[
Optional[str], str | None,
deprecated( deprecated(
"Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
), ),
] = None, ] = None,
discriminator: Union[str, None] = None, discriminator: str | None = None,
strict: Union[bool, None] = _Unset, strict: bool | None = _Unset,
multiple_of: Union[float, None] = _Unset, multiple_of: float | None = _Unset,
allow_inf_nan: Union[bool, None] = _Unset, allow_inf_nan: bool | None = _Unset,
max_digits: Union[int, None] = _Unset, max_digits: int | None = _Unset,
decimal_places: Union[int, None] = _Unset, decimal_places: int | None = _Unset,
examples: Optional[list[Any]] = None, examples: list[Any] | None = None,
example: Annotated[ example: Annotated[
Optional[Any], Any | None,
deprecated( deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead." "although still supported. Use examples instead."
), ),
] = _Unset, ] = _Unset,
openapi_examples: Optional[dict[str, Example]] = None, openapi_examples: dict[str, Example] | None = None,
deprecated: Union[deprecated, str, bool, None] = None, deprecated: deprecated | str | bool | None = None,
include_in_schema: bool = True, include_in_schema: bool = True,
json_schema_extra: Union[dict[str, Any], None] = None, json_schema_extra: dict[str, Any] | None = None,
**extra: Any, **extra: Any,
): ):
super().__init__( super().__init__(
@ -666,46 +666,46 @@ class File(Form): # type: ignore[misc]
self, self,
default: Any = Undefined, default: Any = Undefined,
*, *,
default_factory: Union[Callable[[], Any], None] = _Unset, default_factory: Callable[[], Any] | None = _Unset,
annotation: Optional[Any] = None, annotation: Any | None = None,
media_type: str = "multipart/form-data", media_type: str = "multipart/form-data",
alias: Optional[str] = None, alias: str | None = None,
alias_priority: Union[int, None] = _Unset, alias_priority: int | None = _Unset,
validation_alias: Union[str, AliasPath, AliasChoices, None] = None, validation_alias: str | AliasPath | AliasChoices | None = None,
serialization_alias: Union[str, None] = None, serialization_alias: str | None = None,
title: Optional[str] = None, title: str | None = None,
description: Optional[str] = None, description: str | None = None,
gt: Optional[float] = None, gt: float | None = None,
ge: Optional[float] = None, ge: float | None = None,
lt: Optional[float] = None, lt: float | None = None,
le: Optional[float] = None, le: float | None = None,
min_length: Optional[int] = None, min_length: int | None = None,
max_length: Optional[int] = None, max_length: int | None = None,
pattern: Optional[str] = None, pattern: str | None = None,
regex: Annotated[ regex: Annotated[
Optional[str], str | None,
deprecated( deprecated(
"Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead." "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
), ),
] = None, ] = None,
discriminator: Union[str, None] = None, discriminator: str | None = None,
strict: Union[bool, None] = _Unset, strict: bool | None = _Unset,
multiple_of: Union[float, None] = _Unset, multiple_of: float | None = _Unset,
allow_inf_nan: Union[bool, None] = _Unset, allow_inf_nan: bool | None = _Unset,
max_digits: Union[int, None] = _Unset, max_digits: int | None = _Unset,
decimal_places: Union[int, None] = _Unset, decimal_places: int | None = _Unset,
examples: Optional[list[Any]] = None, examples: list[Any] | None = None,
example: Annotated[ example: Annotated[
Optional[Any], Any | None,
deprecated( deprecated(
"Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, " "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
"although still supported. Use examples instead." "although still supported. Use examples instead."
), ),
] = _Unset, ] = _Unset,
openapi_examples: Optional[dict[str, Example]] = None, openapi_examples: dict[str, Example] | None = None,
deprecated: Union[deprecated, str, bool, None] = None, deprecated: deprecated | str | bool | None = None,
include_in_schema: bool = True, include_in_schema: bool = True,
json_schema_extra: Union[dict[str, Any], None] = None, json_schema_extra: dict[str, Any] | None = None,
**extra: Any, **extra: Any,
): ):
super().__init__( super().__init__(
@ -745,11 +745,11 @@ class File(Form): # type: ignore[misc]
@dataclass(frozen=True) @dataclass(frozen=True)
class Depends: class Depends:
dependency: Optional[Callable[..., Any]] = None dependency: Callable[..., Any] | None = None
use_cache: bool = True use_cache: bool = True
scope: Union[Literal["function", "request"], None] = None scope: Literal["function", "request"] | None = None
@dataclass(frozen=True) @dataclass(frozen=True)
class Security(Depends): class Security(Depends):
scopes: Optional[Sequence[str]] = None scopes: Sequence[str] | None = None

412
fastapi/routing.py

File diff suppressed because it is too large

26
fastapi/security/api_key.py

@ -1,4 +1,4 @@
from typing import Annotated, Optional, Union from typing import Annotated
from annotated_doc import Doc from annotated_doc import Doc
from fastapi.openapi.models import APIKey, APIKeyIn from fastapi.openapi.models import APIKey, APIKeyIn
@ -13,8 +13,8 @@ class APIKeyBase(SecurityBase):
self, self,
location: APIKeyIn, location: APIKeyIn,
name: str, name: str,
description: Union[str, None], description: str | None,
scheme_name: Union[str, None], scheme_name: str | None,
auto_error: bool, auto_error: bool,
): ):
self.auto_error = auto_error self.auto_error = auto_error
@ -42,7 +42,7 @@ class APIKeyBase(SecurityBase):
headers={"WWW-Authenticate": "APIKey"}, headers={"WWW-Authenticate": "APIKey"},
) )
def check_api_key(self, api_key: Optional[str]) -> Optional[str]: def check_api_key(self, api_key: str | None) -> str | None:
if not api_key: if not api_key:
if self.auto_error: if self.auto_error:
raise self.make_not_authenticated_error() raise self.make_not_authenticated_error()
@ -90,7 +90,7 @@ class APIKeyQuery(APIKeyBase):
Doc("Query parameter name."), Doc("Query parameter name."),
], ],
scheme_name: Annotated[ scheme_name: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
Security scheme name. Security scheme name.
@ -100,7 +100,7 @@ class APIKeyQuery(APIKeyBase):
), ),
] = None, ] = None,
description: Annotated[ description: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
Security scheme description. Security scheme description.
@ -137,7 +137,7 @@ class APIKeyQuery(APIKeyBase):
auto_error=auto_error, auto_error=auto_error,
) )
async def __call__(self, request: Request) -> Optional[str]: async def __call__(self, request: Request) -> str | None:
api_key = request.query_params.get(self.model.name) api_key = request.query_params.get(self.model.name)
return self.check_api_key(api_key) return self.check_api_key(api_key)
@ -179,7 +179,7 @@ class APIKeyHeader(APIKeyBase):
*, *,
name: Annotated[str, Doc("Header name.")], name: Annotated[str, Doc("Header name.")],
scheme_name: Annotated[ scheme_name: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
Security scheme name. Security scheme name.
@ -189,7 +189,7 @@ class APIKeyHeader(APIKeyBase):
), ),
] = None, ] = None,
description: Annotated[ description: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
Security scheme description. Security scheme description.
@ -225,7 +225,7 @@ class APIKeyHeader(APIKeyBase):
auto_error=auto_error, auto_error=auto_error,
) )
async def __call__(self, request: Request) -> Optional[str]: async def __call__(self, request: Request) -> str | None:
api_key = request.headers.get(self.model.name) api_key = request.headers.get(self.model.name)
return self.check_api_key(api_key) return self.check_api_key(api_key)
@ -267,7 +267,7 @@ class APIKeyCookie(APIKeyBase):
*, *,
name: Annotated[str, Doc("Cookie name.")], name: Annotated[str, Doc("Cookie name.")],
scheme_name: Annotated[ scheme_name: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
Security scheme name. Security scheme name.
@ -277,7 +277,7 @@ class APIKeyCookie(APIKeyBase):
), ),
] = None, ] = None,
description: Annotated[ description: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
Security scheme description. Security scheme description.
@ -313,6 +313,6 @@ class APIKeyCookie(APIKeyBase):
auto_error=auto_error, auto_error=auto_error,
) )
async def __call__(self, request: Request) -> Optional[str]: async def __call__(self, request: Request) -> str | None:
api_key = request.cookies.get(self.model.name) api_key = request.cookies.get(self.model.name)
return self.check_api_key(api_key) return self.check_api_key(api_key)

36
fastapi/security/http.py

@ -1,6 +1,6 @@
import binascii import binascii
from base64 import b64decode from base64 import b64decode
from typing import Annotated, Optional from typing import Annotated
from annotated_doc import Doc from annotated_doc import Doc
from fastapi.exceptions import HTTPException from fastapi.exceptions import HTTPException
@ -71,8 +71,8 @@ class HTTPBase(SecurityBase):
self, self,
*, *,
scheme: str, scheme: str,
scheme_name: Optional[str] = None, scheme_name: str | None = None,
description: Optional[str] = None, description: str | None = None,
auto_error: bool = True, auto_error: bool = True,
): ):
self.model: HTTPBaseModel = HTTPBaseModel( self.model: HTTPBaseModel = HTTPBaseModel(
@ -91,9 +91,7 @@ class HTTPBase(SecurityBase):
headers=self.make_authenticate_headers(), headers=self.make_authenticate_headers(),
) )
async def __call__( async def __call__(self, request: Request) -> HTTPAuthorizationCredentials | None:
self, request: Request
) -> Optional[HTTPAuthorizationCredentials]:
authorization = request.headers.get("Authorization") authorization = request.headers.get("Authorization")
scheme, credentials = get_authorization_scheme_param(authorization) scheme, credentials = get_authorization_scheme_param(authorization)
if not (authorization and scheme and credentials): if not (authorization and scheme and credentials):
@ -143,7 +141,7 @@ class HTTPBasic(HTTPBase):
self, self,
*, *,
scheme_name: Annotated[ scheme_name: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
Security scheme name. Security scheme name.
@ -153,7 +151,7 @@ class HTTPBasic(HTTPBase):
), ),
] = None, ] = None,
realm: Annotated[ realm: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
HTTP Basic authentication realm. HTTP Basic authentication realm.
@ -161,7 +159,7 @@ class HTTPBasic(HTTPBase):
), ),
] = None, ] = None,
description: Annotated[ description: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
Security scheme description. Security scheme description.
@ -203,7 +201,7 @@ class HTTPBasic(HTTPBase):
async def __call__( # type: ignore async def __call__( # type: ignore
self, request: Request self, request: Request
) -> Optional[HTTPBasicCredentials]: ) -> HTTPBasicCredentials | None:
authorization = request.headers.get("Authorization") authorization = request.headers.get("Authorization")
scheme, param = get_authorization_scheme_param(authorization) scheme, param = get_authorization_scheme_param(authorization)
if not authorization or scheme.lower() != "basic": if not authorization or scheme.lower() != "basic":
@ -256,9 +254,9 @@ class HTTPBearer(HTTPBase):
def __init__( def __init__(
self, self,
*, *,
bearerFormat: Annotated[Optional[str], Doc("Bearer token format.")] = None, bearerFormat: Annotated[str | None, Doc("Bearer token format.")] = None,
scheme_name: Annotated[ scheme_name: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
Security scheme name. Security scheme name.
@ -268,7 +266,7 @@ class HTTPBearer(HTTPBase):
), ),
] = None, ] = None,
description: Annotated[ description: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
Security scheme description. Security scheme description.
@ -302,9 +300,7 @@ class HTTPBearer(HTTPBase):
self.scheme_name = scheme_name or self.__class__.__name__ self.scheme_name = scheme_name or self.__class__.__name__
self.auto_error = auto_error self.auto_error = auto_error
async def __call__( async def __call__(self, request: Request) -> HTTPAuthorizationCredentials | None:
self, request: Request
) -> Optional[HTTPAuthorizationCredentials]:
authorization = request.headers.get("Authorization") authorization = request.headers.get("Authorization")
scheme, credentials = get_authorization_scheme_param(authorization) scheme, credentials = get_authorization_scheme_param(authorization)
if not (authorization and scheme and credentials): if not (authorization and scheme and credentials):
@ -362,7 +358,7 @@ class HTTPDigest(HTTPBase):
self, self,
*, *,
scheme_name: Annotated[ scheme_name: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
Security scheme name. Security scheme name.
@ -372,7 +368,7 @@ class HTTPDigest(HTTPBase):
), ),
] = None, ] = None,
description: Annotated[ description: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
Security scheme description. Security scheme description.
@ -405,9 +401,7 @@ class HTTPDigest(HTTPBase):
self.scheme_name = scheme_name or self.__class__.__name__ self.scheme_name = scheme_name or self.__class__.__name__
self.auto_error = auto_error self.auto_error = auto_error
async def __call__( async def __call__(self, request: Request) -> HTTPAuthorizationCredentials | None:
self, request: Request
) -> Optional[HTTPAuthorizationCredentials]:
authorization = request.headers.get("Authorization") authorization = request.headers.get("Authorization")
scheme, credentials = get_authorization_scheme_param(authorization) scheme, credentials = get_authorization_scheme_param(authorization)
if not (authorization and scheme and credentials): if not (authorization and scheme and credentials):

42
fastapi/security/oauth2.py

@ -1,4 +1,4 @@
from typing import Annotated, Any, Optional, Union, cast from typing import Annotated, Any, cast
from annotated_doc import Doc from annotated_doc import Doc
from fastapi.exceptions import HTTPException from fastapi.exceptions import HTTPException
@ -60,7 +60,7 @@ class OAuth2PasswordRequestForm:
self, self,
*, *,
grant_type: Annotated[ grant_type: Annotated[
Union[str, None], str | None,
Form(pattern="^password$"), Form(pattern="^password$"),
Doc( Doc(
""" """
@ -128,7 +128,7 @@ class OAuth2PasswordRequestForm:
), ),
] = "", ] = "",
client_id: Annotated[ client_id: Annotated[
Union[str, None], str | None,
Form(), Form(),
Doc( Doc(
""" """
@ -139,7 +139,7 @@ class OAuth2PasswordRequestForm:
), ),
] = None, ] = None,
client_secret: Annotated[ client_secret: Annotated[
Union[str, None], str | None,
Form(json_schema_extra={"format": "password"}), Form(json_schema_extra={"format": "password"}),
Doc( Doc(
""" """
@ -294,7 +294,7 @@ class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm):
), ),
] = "", ] = "",
client_id: Annotated[ client_id: Annotated[
Union[str, None], str | None,
Form(), Form(),
Doc( Doc(
""" """
@ -305,7 +305,7 @@ class OAuth2PasswordRequestFormStrict(OAuth2PasswordRequestForm):
), ),
] = None, ] = None,
client_secret: Annotated[ client_secret: Annotated[
Union[str, None], str | None,
Form(), Form(),
Doc( Doc(
""" """
@ -344,7 +344,7 @@ class OAuth2(SecurityBase):
self, self,
*, *,
flows: Annotated[ flows: Annotated[
Union[OAuthFlowsModel, dict[str, dict[str, Any]]], OAuthFlowsModel | dict[str, dict[str, Any]],
Doc( Doc(
""" """
The dictionary of OAuth2 flows. The dictionary of OAuth2 flows.
@ -352,7 +352,7 @@ class OAuth2(SecurityBase):
), ),
] = OAuthFlowsModel(), ] = OAuthFlowsModel(),
scheme_name: Annotated[ scheme_name: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
Security scheme name. Security scheme name.
@ -362,7 +362,7 @@ class OAuth2(SecurityBase):
), ),
] = None, ] = None,
description: Annotated[ description: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
Security scheme description. Security scheme description.
@ -420,7 +420,7 @@ class OAuth2(SecurityBase):
headers={"WWW-Authenticate": "Bearer"}, headers={"WWW-Authenticate": "Bearer"},
) )
async def __call__(self, request: Request) -> Optional[str]: async def __call__(self, request: Request) -> str | None:
authorization = request.headers.get("Authorization") authorization = request.headers.get("Authorization")
if not authorization: if not authorization:
if self.auto_error: if self.auto_error:
@ -454,7 +454,7 @@ class OAuth2PasswordBearer(OAuth2):
), ),
], ],
scheme_name: Annotated[ scheme_name: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
Security scheme name. Security scheme name.
@ -464,7 +464,7 @@ class OAuth2PasswordBearer(OAuth2):
), ),
] = None, ] = None,
scopes: Annotated[ scopes: Annotated[
Optional[dict[str, str]], dict[str, str] | None,
Doc( Doc(
""" """
The OAuth2 scopes that would be required by the *path operations* that The OAuth2 scopes that would be required by the *path operations* that
@ -476,7 +476,7 @@ class OAuth2PasswordBearer(OAuth2):
), ),
] = None, ] = None,
description: Annotated[ description: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
Security scheme description. Security scheme description.
@ -506,7 +506,7 @@ class OAuth2PasswordBearer(OAuth2):
), ),
] = True, ] = True,
refreshUrl: Annotated[ refreshUrl: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
The URL to refresh the token and obtain a new one. The URL to refresh the token and obtain a new one.
@ -533,7 +533,7 @@ class OAuth2PasswordBearer(OAuth2):
auto_error=auto_error, auto_error=auto_error,
) )
async def __call__(self, request: Request) -> Optional[str]: async def __call__(self, request: Request) -> str | None:
authorization = request.headers.get("Authorization") authorization = request.headers.get("Authorization")
scheme, param = get_authorization_scheme_param(authorization) scheme, param = get_authorization_scheme_param(authorization)
if not authorization or scheme.lower() != "bearer": if not authorization or scheme.lower() != "bearer":
@ -562,7 +562,7 @@ class OAuth2AuthorizationCodeBearer(OAuth2):
), ),
], ],
refreshUrl: Annotated[ refreshUrl: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
The URL to refresh the token and obtain a new one. The URL to refresh the token and obtain a new one.
@ -570,7 +570,7 @@ class OAuth2AuthorizationCodeBearer(OAuth2):
), ),
] = None, ] = None,
scheme_name: Annotated[ scheme_name: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
Security scheme name. Security scheme name.
@ -580,7 +580,7 @@ class OAuth2AuthorizationCodeBearer(OAuth2):
), ),
] = None, ] = None,
scopes: Annotated[ scopes: Annotated[
Optional[dict[str, str]], dict[str, str] | None,
Doc( Doc(
""" """
The OAuth2 scopes that would be required by the *path operations* that The OAuth2 scopes that would be required by the *path operations* that
@ -589,7 +589,7 @@ class OAuth2AuthorizationCodeBearer(OAuth2):
), ),
] = None, ] = None,
description: Annotated[ description: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
Security scheme description. Security scheme description.
@ -639,7 +639,7 @@ class OAuth2AuthorizationCodeBearer(OAuth2):
auto_error=auto_error, auto_error=auto_error,
) )
async def __call__(self, request: Request) -> Optional[str]: async def __call__(self, request: Request) -> str | None:
authorization = request.headers.get("Authorization") authorization = request.headers.get("Authorization")
scheme, param = get_authorization_scheme_param(authorization) scheme, param = get_authorization_scheme_param(authorization)
if not authorization or scheme.lower() != "bearer": if not authorization or scheme.lower() != "bearer":
@ -666,7 +666,7 @@ class SecurityScopes:
def __init__( def __init__(
self, self,
scopes: Annotated[ scopes: Annotated[
Optional[list[str]], list[str] | None,
Doc( Doc(
""" """
This will be filled by FastAPI. This will be filled by FastAPI.

8
fastapi/security/open_id_connect_url.py

@ -1,4 +1,4 @@
from typing import Annotated, Optional from typing import Annotated
from annotated_doc import Doc from annotated_doc import Doc
from fastapi.openapi.models import OpenIdConnect as OpenIdConnectModel from fastapi.openapi.models import OpenIdConnect as OpenIdConnectModel
@ -31,7 +31,7 @@ class OpenIdConnect(SecurityBase):
), ),
], ],
scheme_name: Annotated[ scheme_name: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
Security scheme name. Security scheme name.
@ -41,7 +41,7 @@ class OpenIdConnect(SecurityBase):
), ),
] = None, ] = None,
description: Annotated[ description: Annotated[
Optional[str], str | None,
Doc( Doc(
""" """
Security scheme description. Security scheme description.
@ -84,7 +84,7 @@ class OpenIdConnect(SecurityBase):
headers={"WWW-Authenticate": "Bearer"}, headers={"WWW-Authenticate": "Bearer"},
) )
async def __call__(self, request: Request) -> Optional[str]: async def __call__(self, request: Request) -> str | None:
authorization = request.headers.get("Authorization") authorization = request.headers.get("Authorization")
if not authorization: if not authorization:
if self.auto_error: if self.auto_error:

5
fastapi/security/utils.py

@ -1,8 +1,5 @@
from typing import Optional
def get_authorization_scheme_param( def get_authorization_scheme_param(
authorization_header_value: Optional[str], authorization_header_value: str | None,
) -> tuple[str, str]: ) -> tuple[str, str]:
if not authorization_header_value: if not authorization_header_value:
return "", "" return "", ""

7
fastapi/types.py

@ -1,11 +1,12 @@
import types import types
from collections.abc import Callable
from enum import Enum from enum import Enum
from typing import Any, Callable, Optional, TypeVar, Union from typing import Any, TypeVar, Union
from pydantic import BaseModel from pydantic import BaseModel
from pydantic.main import IncEx as IncEx from pydantic.main import IncEx as IncEx
DecoratedCallable = TypeVar("DecoratedCallable", bound=Callable[..., Any]) 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[type[BaseModel] | type[Enum], str]
DependencyCacheKey = tuple[Optional[Callable[..., Any]], tuple[str, ...], str] DependencyCacheKey = tuple[Callable[..., Any] | None, tuple[str, ...], str]

18
fastapi/utils.py

@ -3,8 +3,7 @@ import warnings
from typing import ( from typing import (
TYPE_CHECKING, TYPE_CHECKING,
Any, Any,
Optional, Literal,
Union,
) )
import fastapi import fastapi
@ -17,7 +16,6 @@ from fastapi._compat import (
from fastapi.datastructures import DefaultPlaceholder, DefaultType from fastapi.datastructures import DefaultPlaceholder, DefaultType
from fastapi.exceptions import FastAPIDeprecationWarning, PydanticV1NotSupportedError from fastapi.exceptions import FastAPIDeprecationWarning, PydanticV1NotSupportedError
from pydantic.fields import FieldInfo from pydantic.fields import FieldInfo
from typing_extensions import Literal
from ._compat import v2 from ._compat import v2
@ -25,7 +23,7 @@ if TYPE_CHECKING: # pragma: nocover
from .routing import APIRoute from .routing import APIRoute
def is_body_allowed_for_status_code(status_code: Union[int, str, None]) -> bool: def is_body_allowed_for_status_code(status_code: int | str | None) -> bool:
if status_code is None: if status_code is None:
return True return True
# Ref: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#patterned-fields-1 # Ref: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#patterned-fields-1
@ -60,9 +58,9 @@ _invalid_args_message = (
def create_model_field( def create_model_field(
name: str, name: str,
type_: Any, type_: Any,
default: Optional[Any] = Undefined, default: Any | None = Undefined,
field_info: Optional[FieldInfo] = None, field_info: FieldInfo | None = None,
alias: Optional[str] = None, alias: str | None = None,
mode: Literal["validation", "serialization"] = "validation", mode: Literal["validation", "serialization"] = "validation",
) -> ModelField: ) -> ModelField:
if annotation_is_pydantic_v1(type_): if annotation_is_pydantic_v1(type_):
@ -121,9 +119,9 @@ def deep_dict_update(main_dict: dict[Any, Any], update_dict: dict[Any, Any]) ->
def get_value_or_default( def get_value_or_default(
first_item: Union[DefaultPlaceholder, DefaultType], first_item: DefaultPlaceholder | DefaultType,
*extra_items: Union[DefaultPlaceholder, DefaultType], *extra_items: DefaultPlaceholder | DefaultType,
) -> Union[DefaultPlaceholder, DefaultType]: ) -> DefaultPlaceholder | DefaultType:
""" """
Pass items or `DefaultPlaceholder`s by descending priority. Pass items or `DefaultPlaceholder`s by descending priority.

Loading…
Cancel
Save