♻️ Upgrade internal syntax to Python 3.9+ 🎉 (#14564)

This commit is contained in:
Sebastián Ramírez 2025-12-17 13:25:59 -08:00 committed by GitHub
parent 7f9709d75e
commit 1c4fc96c91
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
142 changed files with 939 additions and 1038 deletions

View File

@ -1,4 +1,4 @@
from typing import Annotated, List, Union
from typing import Annotated, Union
from fastapi import FastAPI, Header
@ -6,5 +6,5 @@ app = FastAPI()
@app.get("/items/")
async def read_items(x_token: Annotated[Union[List[str], None], Header()] = None):
async def read_items(x_token: Annotated[Union[list[str], None], Header()] = None):
return {"X-Token values": x_token}

View File

@ -1,12 +1,8 @@
import sys
from collections.abc import Sequence
from functools import lru_cache
from typing import (
Any,
Dict,
List,
Sequence,
Tuple,
Type,
)
from fastapi._compat import may_v1
@ -50,7 +46,7 @@ else:
@lru_cache
def get_cached_model_fields(model: Type[BaseModel]) -> List[ModelField]:
def get_cached_model_fields(model: type[BaseModel]) -> list[ModelField]:
if lenient_issubclass(model, may_v1.BaseModel):
from fastapi._compat import v1
@ -119,7 +115,7 @@ def copy_field_info(*, field_info: FieldInfo, annotation: Any) -> FieldInfo:
def create_body_model(
*, fields: Sequence[ModelField], model_name: str
) -> Type[BaseModel]:
) -> type[BaseModel]:
if fields and isinstance(fields[0], may_v1.ModelField):
from fastapi._compat import v1
@ -221,7 +217,7 @@ def serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:
return v2.serialize_sequence_value(field=field, value=value) # type: ignore[arg-type]
def _model_rebuild(model: Type[BaseModel]) -> None:
def _model_rebuild(model: type[BaseModel]) -> None:
if lenient_issubclass(model, may_v1.BaseModel):
from fastapi._compat import v1
@ -232,7 +228,7 @@ def _model_rebuild(model: Type[BaseModel]) -> None:
v2._model_rebuild(model)
def get_compat_model_name_map(fields: List[ModelField]) -> ModelNameMap:
def get_compat_model_name_map(fields: list[ModelField]) -> ModelNameMap:
v1_model_fields = [
field for field in fields if isinstance(field, may_v1.ModelField)
]
@ -266,15 +262,15 @@ def get_compat_model_name_map(fields: List[ModelField]) -> ModelNameMap:
def get_definitions(
*,
fields: List[ModelField],
fields: list[ModelField],
model_name_map: ModelNameMap,
separate_input_output_schemas: bool = True,
) -> Tuple[
Dict[
Tuple[ModelField, Literal["validation", "serialization"]],
) -> tuple[
dict[
tuple[ModelField, Literal["validation", "serialization"]],
may_v1.JsonSchemaValue,
],
Dict[str, Dict[str, Any]],
dict[str, dict[str, Any]],
]:
if sys.version_info < (3, 14):
v1_fields = [field for field in fields if isinstance(field, may_v1.ModelField)]
@ -315,12 +311,12 @@ def get_schema_from_model_field(
*,
field: ModelField,
model_name_map: ModelNameMap,
field_mapping: Dict[
Tuple[ModelField, Literal["validation", "serialization"]],
field_mapping: dict[
tuple[ModelField, Literal["validation", "serialization"]],
may_v1.JsonSchemaValue,
],
separate_input_output_schemas: bool = True,
) -> Dict[str, Any]:
) -> dict[str, Any]:
if isinstance(field, may_v1.ModelField):
from fastapi._compat import v1

View File

@ -1,5 +1,6 @@
import sys
from typing import Any, Dict, List, Literal, Sequence, Tuple, Type, Union
from collections.abc import Sequence
from typing import Any, Literal, Union
from fastapi.types import ModelNameMap
@ -60,14 +61,14 @@ if sys.version_info >= (3, 14):
def get_definitions(
*,
fields: List[ModelField],
fields: list[ModelField],
model_name_map: ModelNameMap,
separate_input_output_schemas: bool = True,
) -> Tuple[
Dict[
Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
) -> tuple[
dict[
tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
],
Dict[str, Dict[str, Any]],
dict[str, dict[str, Any]],
]:
return {}, {} # pragma: no cover
@ -94,11 +95,11 @@ else:
from .v1 import get_definitions as get_definitions
RequestErrorModel: Type[BaseModel] = create_model("Request")
RequestErrorModel: type[BaseModel] = create_model("Request")
def _normalize_errors(errors: Sequence[Any]) -> List[Dict[str, Any]]:
use_errors: List[Any] = []
def _normalize_errors(errors: Sequence[Any]) -> list[dict[str, Any]]:
use_errors: list[Any] = []
for error in errors:
if isinstance(error, ErrorWrapper):
new_errors = ValidationError( # type: ignore[call-arg]
@ -113,9 +114,9 @@ def _normalize_errors(errors: Sequence[Any]) -> List[Dict[str, Any]]:
def _regenerate_error_with_loc(
*, errors: Sequence[Any], loc_prefix: Tuple[Union[str, int], ...]
) -> List[Dict[str, Any]]:
updated_loc_errors: List[Any] = [
*, errors: Sequence[Any], loc_prefix: tuple[Union[str, int], ...]
) -> list[dict[str, Any]]:
updated_loc_errors: list[Any] = [
{**err, "loc": loc_prefix + err.get("loc", ())}
for err in _normalize_errors(errors)
]

View File

@ -1,8 +1,5 @@
from typing import (
Any,
Dict,
List,
Tuple,
Union,
)
@ -34,10 +31,10 @@ class ModelField(Protocol):
def validate(
self,
value: Any,
values: Dict[str, Any] = {}, # noqa: B006
values: dict[str, Any] = {}, # noqa: B006
*,
loc: Tuple[Union[int, str], ...] = (),
) -> Tuple[Any, Union[List[Dict[str, Any]], None]]: ...
loc: tuple[Union[int, str], ...] = (),
) -> tuple[Any, Union[list[dict[str, Any]], None]]: ...
def serialize(
self,

View File

@ -2,17 +2,11 @@ import sys
import types
import typing
from collections import deque
from collections.abc import Mapping, Sequence
from dataclasses import is_dataclass
from typing import (
Annotated,
Any,
Deque,
FrozenSet,
List,
Mapping,
Sequence,
Set,
Tuple,
Type,
Union,
)
@ -21,7 +15,7 @@ from fastapi.types import UnionType
from pydantic import BaseModel
from pydantic.version import VERSION as PYDANTIC_VERSION
from starlette.datastructures import UploadFile
from typing_extensions import Annotated, get_args, get_origin
from typing_extensions import get_args, get_origin
# Copy from Pydantic v2, compatible with v1
if sys.version_info < (3, 10):
@ -39,26 +33,21 @@ PYDANTIC_V2 = PYDANTIC_VERSION_MINOR_TUPLE[0] == 2
sequence_annotation_to_type = {
Sequence: list,
List: list,
list: list,
Tuple: tuple,
tuple: tuple,
Set: set,
set: set,
FrozenSet: frozenset,
frozenset: frozenset,
Deque: deque,
deque: deque,
}
sequence_types = tuple(sequence_annotation_to_type.keys())
Url: Type[Any]
Url: type[Any]
# Copy of Pydantic v2, compatible with v1
def lenient_issubclass(
cls: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], ...], None]
cls: Any, class_or_tuple: Union[type[Any], tuple[type[Any], ...], None]
) -> bool:
try:
return isinstance(cls, type) and issubclass(cls, class_or_tuple) # type: ignore[arg-type]
@ -68,13 +57,13 @@ def lenient_issubclass(
raise # pragma: no cover
def _annotation_is_sequence(annotation: Union[Type[Any], None]) -> bool:
def _annotation_is_sequence(annotation: Union[type[Any], None]) -> bool:
if lenient_issubclass(annotation, (str, bytes)):
return False
return lenient_issubclass(annotation, sequence_types) # type: ignore[arg-type]
return lenient_issubclass(annotation, sequence_types)
def field_annotation_is_sequence(annotation: Union[Type[Any], None]) -> bool:
def field_annotation_is_sequence(annotation: Union[type[Any], None]) -> bool:
origin = get_origin(annotation)
if origin is Union or origin is UnionType:
for arg in get_args(annotation):
@ -87,10 +76,10 @@ def field_annotation_is_sequence(annotation: Union[Type[Any], None]) -> bool:
def value_is_sequence(value: Any) -> bool:
return isinstance(value, sequence_types) and not isinstance(value, (str, bytes)) # type: ignore[arg-type]
return isinstance(value, sequence_types) and not isinstance(value, (str, bytes))
def _annotation_is_complex(annotation: Union[Type[Any], None]) -> bool:
def _annotation_is_complex(annotation: Union[type[Any], None]) -> bool:
return (
lenient_issubclass(
annotation, (BaseModel, may_v1.BaseModel, Mapping, UploadFile)
@ -100,7 +89,7 @@ def _annotation_is_complex(annotation: Union[Type[Any], None]) -> bool:
)
def field_annotation_is_complex(annotation: Union[Type[Any], None]) -> bool:
def field_annotation_is_complex(annotation: Union[type[Any], None]) -> bool:
origin = get_origin(annotation)
if origin is Union or origin is UnionType:
return any(field_annotation_is_complex(arg) for arg in get_args(annotation))
@ -121,7 +110,7 @@ def field_annotation_is_scalar(annotation: Any) -> bool:
return annotation is Ellipsis or not field_annotation_is_complex(annotation)
def field_annotation_is_scalar_sequence(annotation: Union[Type[Any], None]) -> bool:
def field_annotation_is_scalar_sequence(annotation: Union[type[Any], None]) -> bool:
origin = get_origin(annotation)
if origin is Union or origin is UnionType:
at_least_one_scalar_sequence = False

View File

@ -1,15 +1,10 @@
from collections.abc import Sequence
from copy import copy
from dataclasses import dataclass, is_dataclass
from enum import Enum
from typing import (
Any,
Callable,
Dict,
List,
Sequence,
Set,
Tuple,
Type,
Union,
)
@ -124,7 +119,7 @@ else:
GetJsonSchemaHandler = Any
JsonSchemaValue = Dict[str, Any]
JsonSchemaValue = dict[str, Any]
CoreSchema = Any
Url = AnyUrl
@ -154,7 +149,7 @@ class PydanticSchemaGenerationError(Exception):
pass
RequestErrorModel: Type[BaseModel] = create_model("Request")
RequestErrorModel: type[BaseModel] = create_model("Request")
def with_info_plain_validator_function(
@ -169,10 +164,10 @@ def with_info_plain_validator_function(
def get_model_definitions(
*,
flat_models: Set[Union[Type[BaseModel], Type[Enum]]],
model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str],
) -> Dict[str, Any]:
definitions: Dict[str, Dict[str, Any]] = {}
flat_models: set[Union[type[BaseModel], type[Enum]]],
model_name_map: dict[Union[type[BaseModel], type[Enum]], str],
) -> dict[str, Any]:
definitions: dict[str, dict[str, Any]] = {}
for model in flat_models:
m_schema, m_definitions, m_nested_models = model_process_schema(
model, model_name_map=model_name_map, ref_prefix=REF_PREFIX
@ -219,7 +214,7 @@ def is_pv1_scalar_sequence_field(field: ModelField) -> bool:
return False
def _model_rebuild(model: Type[BaseModel]) -> None:
def _model_rebuild(model: type[BaseModel]) -> None:
model.update_forward_refs()
@ -237,11 +232,11 @@ def get_schema_from_model_field(
*,
field: ModelField,
model_name_map: ModelNameMap,
field_mapping: Dict[
Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
field_mapping: dict[
tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
],
separate_input_output_schemas: bool = True,
) -> Dict[str, Any]:
) -> dict[str, Any]:
return field_schema( # type: ignore[no-any-return]
field, model_name_map=model_name_map, ref_prefix=REF_PREFIX
)[0]
@ -254,12 +249,12 @@ def get_schema_from_model_field(
def get_definitions(
*,
fields: List[ModelField],
fields: list[ModelField],
model_name_map: ModelNameMap,
separate_input_output_schemas: bool = True,
) -> Tuple[
Dict[Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue],
Dict[str, Dict[str, Any]],
) -> tuple[
dict[tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue],
dict[str, dict[str, Any]],
]:
models = get_flat_models_from_fields(fields, known_models=set())
return {}, get_model_definitions(flat_models=models, model_name_map=model_name_map)
@ -293,7 +288,7 @@ def serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:
return sequence_shape_to_type[field.shape](value) # type: ignore[no-any-return]
def get_missing_field_error(loc: Tuple[str, ...]) -> Dict[str, Any]:
def get_missing_field_error(loc: tuple[str, ...]) -> dict[str, Any]:
missing_field_error = ErrorWrapper(MissingError(), loc=loc)
new_error = ValidationError([missing_field_error], RequestErrorModel)
return new_error.errors()[0] # type: ignore[return-value]
@ -301,12 +296,12 @@ def get_missing_field_error(loc: Tuple[str, ...]) -> Dict[str, Any]:
def create_body_model(
*, fields: Sequence[ModelField], model_name: str
) -> Type[BaseModel]:
) -> type[BaseModel]:
BodyModel = create_model(model_name)
for f in fields:
BodyModel.__fields__[f.name] = f # type: ignore[index]
return BodyModel
def get_model_fields(model: Type[BaseModel]) -> List[ModelField]:
def get_model_fields(model: type[BaseModel]) -> list[ModelField]:
return list(model.__fields__.values()) # type: ignore[attr-defined]

View File

@ -1,16 +1,12 @@
import re
import warnings
from collections.abc import Sequence
from copy import copy, deepcopy
from dataclasses import dataclass, is_dataclass
from enum import Enum
from typing import (
Annotated,
Any,
Dict,
List,
Sequence,
Set,
Tuple,
Type,
Union,
cast,
)
@ -33,7 +29,7 @@ from pydantic.json_schema import JsonSchemaValue as JsonSchemaValue
from pydantic_core import CoreSchema as CoreSchema
from pydantic_core import PydanticUndefined, PydanticUndefinedType
from pydantic_core import Url as Url
from typing_extensions import Annotated, Literal, get_args, get_origin
from typing_extensions import Literal, get_args, get_origin
try:
from pydantic_core.core_schema import (
@ -77,7 +73,7 @@ _Attrs = {
# TODO: remove when dropping support for Pydantic < v2.12.3
def asdict(field_info: FieldInfo) -> Dict[str, Any]:
def asdict(field_info: FieldInfo) -> dict[str, Any]:
attributes = {}
for attr in _Attrs:
value = getattr(field_info, attr, Undefined)
@ -169,10 +165,10 @@ class ModelField:
def validate(
self,
value: Any,
values: Dict[str, Any] = {}, # noqa: B006
values: dict[str, Any] = {}, # noqa: B006
*,
loc: Tuple[Union[int, str], ...] = (),
) -> Tuple[Any, Union[List[Dict[str, Any]], None]]:
loc: tuple[Union[int, str], ...] = (),
) -> tuple[Any, Union[list[dict[str, Any]], None]]:
try:
return (
self._type_adapter.validate_python(value, from_attributes=True),
@ -220,7 +216,7 @@ def get_annotation_from_field_info(
return annotation
def _model_rebuild(model: Type[BaseModel]) -> None:
def _model_rebuild(model: type[BaseModel]) -> None:
model.model_rebuild()
@ -245,11 +241,11 @@ def get_schema_from_model_field(
*,
field: ModelField,
model_name_map: ModelNameMap,
field_mapping: Dict[
Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
field_mapping: dict[
tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
],
separate_input_output_schemas: bool = True,
) -> Dict[str, Any]:
) -> dict[str, Any]:
override_mode: Union[Literal["validation"], None] = (
None
if (separate_input_output_schemas or _has_computed_fields(field))
@ -277,9 +273,9 @@ def get_definitions(
fields: Sequence[ModelField],
model_name_map: ModelNameMap,
separate_input_output_schemas: bool = True,
) -> Tuple[
Dict[Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue],
Dict[str, Dict[str, Any]],
) -> tuple[
dict[tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue],
dict[str, dict[str, Any]],
]:
schema_generator = GenerateJsonSchema(ref_template=REF_TEMPLATE)
validation_fields = [field for field in fields if field.mode == "validation"]
@ -324,7 +320,7 @@ def get_definitions(
for field in list(fields) + list(unique_flat_model_fields)
]
field_mapping, definitions = schema_generator.generate_definitions(inputs=inputs)
for item_def in cast(Dict[str, Dict[str, Any]], definitions).values():
for item_def in cast(dict[str, dict[str, Any]], definitions).values():
if "description" in item_def:
item_description = cast(str, item_def["description"]).split("\f")[0]
item_def["description"] = item_description
@ -338,9 +334,9 @@ def get_definitions(
def _replace_refs(
*,
schema: Dict[str, Any],
old_name_to_new_name_map: Dict[str, str],
) -> Dict[str, Any]:
schema: dict[str, Any],
old_name_to_new_name_map: dict[str, str],
) -> dict[str, Any]:
new_schema = deepcopy(schema)
for key, value in new_schema.items():
if key == "$ref":
@ -375,13 +371,13 @@ def _replace_refs(
def _remap_definitions_and_field_mappings(
*,
model_name_map: ModelNameMap,
definitions: Dict[str, Any],
field_mapping: Dict[
Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
definitions: dict[str, Any],
field_mapping: dict[
tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
],
) -> Tuple[
Dict[Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue],
Dict[str, Any],
) -> tuple[
dict[tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue],
dict[str, Any],
]:
old_name_to_new_name_map = {}
for field_key, schema in field_mapping.items():
@ -394,8 +390,8 @@ def _remap_definitions_and_field_mappings(
continue
old_name_to_new_name_map[old_name] = new_name
new_field_mapping: Dict[
Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
new_field_mapping: dict[
tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
] = {}
for field_key, schema in field_mapping.items():
new_schema = _replace_refs(
@ -461,10 +457,10 @@ def serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:
origin_type = get_origin(union_arg) or union_arg
break
assert issubclass(origin_type, shared.sequence_types) # type: ignore[arg-type]
return shared.sequence_annotation_to_type[origin_type](value) # type: ignore[no-any-return]
return shared.sequence_annotation_to_type[origin_type](value) # type: ignore[no-any-return,index]
def get_missing_field_error(loc: Tuple[str, ...]) -> Dict[str, Any]:
def get_missing_field_error(loc: tuple[str, ...]) -> dict[str, Any]:
error = ValidationError.from_exception_data(
"Field required", [{"type": "missing", "loc": loc, "input": {}}]
).errors(include_url=False)[0]
@ -474,14 +470,14 @@ def get_missing_field_error(loc: Tuple[str, ...]) -> Dict[str, Any]:
def create_body_model(
*, fields: Sequence[ModelField], model_name: str
) -> Type[BaseModel]:
) -> type[BaseModel]:
field_params = {f.name: (f.field_info.annotation, f.field_info) for f in fields}
BodyModel: Type[BaseModel] = create_model(model_name, **field_params) # type: ignore[call-overload]
BodyModel: type[BaseModel] = create_model(model_name, **field_params) # type: ignore[call-overload]
return BodyModel
def get_model_fields(model: Type[BaseModel]) -> List[ModelField]:
model_fields: List[ModelField] = []
def get_model_fields(model: type[BaseModel]) -> list[ModelField]:
model_fields: list[ModelField] = []
for name, field_info in model.model_fields.items():
type_ = field_info.annotation
if lenient_issubclass(type_, (BaseModel, dict)) or is_dataclass(type_):
@ -501,17 +497,17 @@ def get_model_fields(model: Type[BaseModel]) -> List[ModelField]:
# Duplicate of several schema functions from Pydantic v1 to make them compatible with
# Pydantic v2 and allow mixing the models
TypeModelOrEnum = Union[Type["BaseModel"], Type[Enum]]
TypeModelSet = Set[TypeModelOrEnum]
TypeModelOrEnum = Union[type["BaseModel"], type[Enum]]
TypeModelSet = set[TypeModelOrEnum]
def normalize_name(name: str) -> str:
return re.sub(r"[^a-zA-Z0-9.\-_]", "_", name)
def get_model_name_map(unique_models: TypeModelSet) -> Dict[TypeModelOrEnum, str]:
def get_model_name_map(unique_models: TypeModelSet) -> dict[TypeModelOrEnum, str]:
name_model_map = {}
conflicting_names: Set[str] = set()
conflicting_names: set[str] = set()
for model in unique_models:
model_name = normalize_name(model.__name__)
if model_name in conflicting_names:
@ -528,7 +524,7 @@ def get_model_name_map(unique_models: TypeModelSet) -> Dict[TypeModelOrEnum, str
def get_flat_models_from_model(
model: Type["BaseModel"], known_models: Union[TypeModelSet, None] = None
model: type["BaseModel"], known_models: Union[TypeModelSet, None] = None
) -> TypeModelSet:
known_models = known_models or set()
fields = get_model_fields(model)

View File

@ -1,14 +1,10 @@
from collections.abc import Awaitable, Coroutine, Sequence
from enum import Enum
from typing import (
Annotated,
Any,
Awaitable,
Callable,
Coroutine,
Dict,
List,
Optional,
Sequence,
Type,
TypeVar,
Union,
)
@ -44,7 +40,7 @@ from starlette.requests import Request
from starlette.responses import HTMLResponse, JSONResponse, Response
from starlette.routing import BaseRoute
from starlette.types import ASGIApp, ExceptionHandler, Lifespan, Receive, Scope, Send
from typing_extensions import Annotated, deprecated
from typing_extensions import deprecated
AppType = TypeVar("AppType", bound="FastAPI")
@ -81,7 +77,7 @@ class FastAPI(Starlette):
),
] = False,
routes: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
**Note**: you probably shouldn't use this parameter, it is inherited
@ -230,7 +226,7 @@ class FastAPI(Starlette):
),
] = "/openapi.json",
openapi_tags: Annotated[
Optional[List[Dict[str, Any]]],
Optional[list[dict[str, Any]]],
Doc(
"""
A list of tags used by OpenAPI, these are the same `tags` you can set
@ -290,7 +286,7 @@ class FastAPI(Starlette):
),
] = None,
servers: Annotated[
Optional[List[Dict[str, Union[str, Any]]]],
Optional[list[dict[str, Union[str, Any]]]],
Doc(
"""
A `list` of `dict`s with connectivity information to a target server.
@ -361,7 +357,7 @@ class FastAPI(Starlette):
),
] = None,
default_response_class: Annotated[
Type[Response],
type[Response],
Doc(
"""
The default response class to be used.
@ -467,7 +463,7 @@ class FastAPI(Starlette):
),
] = "/docs/oauth2-redirect",
swagger_ui_init_oauth: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
OAuth2 configuration for the Swagger UI, by default shown at `/docs`.
@ -493,8 +489,8 @@ class FastAPI(Starlette):
] = None,
exception_handlers: Annotated[
Optional[
Dict[
Union[int, Type[Exception]],
dict[
Union[int, type[Exception]],
Callable[[Request, Any], Coroutine[Any, Any, Response]],
]
],
@ -567,7 +563,7 @@ class FastAPI(Starlette):
),
] = None,
contact: Annotated[
Optional[Dict[str, Union[str, Any]]],
Optional[dict[str, Union[str, Any]]],
Doc(
"""
A dictionary with the contact information for the exposed API.
@ -600,7 +596,7 @@ class FastAPI(Starlette):
),
] = None,
license_info: Annotated[
Optional[Dict[str, Union[str, Any]]],
Optional[dict[str, Union[str, Any]]],
Doc(
"""
A dictionary with the license information for the exposed API.
@ -689,7 +685,7 @@ class FastAPI(Starlette):
),
] = True,
responses: Annotated[
Optional[Dict[Union[int, str], Dict[str, Any]]],
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses to be shown in OpenAPI.
@ -705,7 +701,7 @@ class FastAPI(Starlette):
),
] = None,
callbacks: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
OpenAPI callbacks that should apply to all *path operations*.
@ -762,7 +758,7 @@ class FastAPI(Starlette):
),
] = True,
swagger_ui_parameters: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
Parameters to configure Swagger UI, the autogenerated interactive API
@ -820,7 +816,7 @@ class FastAPI(Starlette):
),
] = True,
openapi_external_docs: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
This field allows you to provide additional external documentation links.
@ -906,7 +902,7 @@ class FastAPI(Starlette):
"""
),
] = "3.1.0"
self.openapi_schema: Optional[Dict[str, Any]] = None
self.openapi_schema: Optional[dict[str, Any]] = None
if self.openapi_url:
assert self.title, "A title must be provided for OpenAPI, e.g.: 'My API'"
assert self.version, "A version must be provided for OpenAPI, e.g.: '2.1.0'"
@ -949,7 +945,7 @@ class FastAPI(Starlette):
),
] = State()
self.dependency_overrides: Annotated[
Dict[Callable[..., Any], Callable[..., Any]],
dict[Callable[..., Any], Callable[..., Any]],
Doc(
"""
A dictionary with overrides for the dependencies.
@ -980,7 +976,7 @@ class FastAPI(Starlette):
responses=responses,
generate_unique_id_function=generate_unique_id_function,
)
self.exception_handlers: Dict[
self.exception_handlers: dict[
Any, Callable[[Request, Any], Union[Response, Awaitable[Response]]]
] = {} if exception_handlers is None else dict(exception_handlers)
self.exception_handlers.setdefault(HTTPException, http_exception_handler)
@ -993,7 +989,7 @@ class FastAPI(Starlette):
websocket_request_validation_exception_handler, # type: ignore
)
self.user_middleware: List[Middleware] = (
self.user_middleware: list[Middleware] = (
[] if middleware is None else list(middleware)
)
self.middleware_stack: Union[ASGIApp, None] = None
@ -1047,7 +1043,7 @@ class FastAPI(Starlette):
app = cls(app, *args, **kwargs)
return app
def openapi(self) -> Dict[str, Any]:
def openapi(self) -> dict[str, Any]:
"""
Generate the OpenAPI schema of the application. This is called by FastAPI
internally.
@ -1145,14 +1141,14 @@ class FastAPI(Starlette):
*,
response_model: Any = Default(None),
status_code: Optional[int] = None,
tags: Optional[List[Union[str, Enum]]] = None,
tags: Optional[list[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
response_description: str = "Successful Response",
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
responses: Optional[dict[Union[int, str], dict[str, Any]]] = None,
deprecated: Optional[bool] = None,
methods: Optional[List[str]] = None,
methods: Optional[list[str]] = None,
operation_id: Optional[str] = None,
response_model_include: Optional[IncEx] = None,
response_model_exclude: Optional[IncEx] = None,
@ -1161,11 +1157,11 @@ class FastAPI(Starlette):
response_model_exclude_defaults: bool = False,
response_model_exclude_none: bool = False,
include_in_schema: bool = True,
response_class: Union[Type[Response], DefaultPlaceholder] = Default(
response_class: Union[type[Response], DefaultPlaceholder] = Default(
JSONResponse
),
name: Optional[str] = None,
openapi_extra: Optional[Dict[str, Any]] = None,
openapi_extra: Optional[dict[str, Any]] = None,
generate_unique_id_function: Callable[[routing.APIRoute], str] = Default(
generate_unique_id
),
@ -1203,14 +1199,14 @@ class FastAPI(Starlette):
*,
response_model: Any = Default(None),
status_code: Optional[int] = None,
tags: Optional[List[Union[str, Enum]]] = None,
tags: Optional[list[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
response_description: str = "Successful Response",
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
responses: Optional[dict[Union[int, str], dict[str, Any]]] = None,
deprecated: Optional[bool] = None,
methods: Optional[List[str]] = None,
methods: Optional[list[str]] = None,
operation_id: Optional[str] = None,
response_model_include: Optional[IncEx] = None,
response_model_exclude: Optional[IncEx] = None,
@ -1219,9 +1215,9 @@ class FastAPI(Starlette):
response_model_exclude_defaults: bool = False,
response_model_exclude_none: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = Default(JSONResponse),
response_class: type[Response] = Default(JSONResponse),
name: Optional[str] = None,
openapi_extra: Optional[Dict[str, Any]] = None,
openapi_extra: Optional[dict[str, Any]] = None,
generate_unique_id_function: Callable[[routing.APIRoute], str] = Default(
generate_unique_id
),
@ -1343,7 +1339,7 @@ class FastAPI(Starlette):
*,
prefix: Annotated[str, Doc("An optional path prefix for the router.")] = "",
tags: Annotated[
Optional[List[Union[str, Enum]]],
Optional[list[Union[str, Enum]]],
Doc(
"""
A list of tags to be applied to all the *path operations* in this
@ -1385,7 +1381,7 @@ class FastAPI(Starlette):
),
] = None,
responses: Annotated[
Optional[Dict[Union[int, str], Dict[str, Any]]],
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses to be shown in OpenAPI.
@ -1452,7 +1448,7 @@ class FastAPI(Starlette):
),
] = True,
default_response_class: Annotated[
Type[Response],
type[Response],
Doc(
"""
Default response class to be used for the *path operations* in this
@ -1480,7 +1476,7 @@ class FastAPI(Starlette):
),
] = Default(JSONResponse),
callbacks: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
@ -1603,7 +1599,7 @@ class FastAPI(Starlette):
),
] = None,
tags: Annotated[
Optional[List[Union[str, Enum]]],
Optional[list[Union[str, Enum]]],
Doc(
"""
A list of tags to be applied to the *path operation*.
@ -1669,7 +1665,7 @@ class FastAPI(Starlette):
),
] = "Successful Response",
responses: Annotated[
Optional[Dict[Union[int, str], Dict[str, Any]]],
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses that could be returned by this *path operation*.
@ -1810,7 +1806,7 @@ class FastAPI(Starlette):
),
] = True,
response_class: Annotated[
Type[Response],
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
@ -1831,7 +1827,7 @@ class FastAPI(Starlette):
),
] = None,
callbacks: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
@ -1847,7 +1843,7 @@ class FastAPI(Starlette):
),
] = None,
openapi_extra: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
@ -1976,7 +1972,7 @@ class FastAPI(Starlette):
),
] = None,
tags: Annotated[
Optional[List[Union[str, Enum]]],
Optional[list[Union[str, Enum]]],
Doc(
"""
A list of tags to be applied to the *path operation*.
@ -2042,7 +2038,7 @@ class FastAPI(Starlette):
),
] = "Successful Response",
responses: Annotated[
Optional[Dict[Union[int, str], Dict[str, Any]]],
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses that could be returned by this *path operation*.
@ -2183,7 +2179,7 @@ class FastAPI(Starlette):
),
] = True,
response_class: Annotated[
Type[Response],
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
@ -2204,7 +2200,7 @@ class FastAPI(Starlette):
),
] = None,
callbacks: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
@ -2220,7 +2216,7 @@ class FastAPI(Starlette):
),
] = None,
openapi_extra: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
@ -2354,7 +2350,7 @@ class FastAPI(Starlette):
),
] = None,
tags: Annotated[
Optional[List[Union[str, Enum]]],
Optional[list[Union[str, Enum]]],
Doc(
"""
A list of tags to be applied to the *path operation*.
@ -2420,7 +2416,7 @@ class FastAPI(Starlette):
),
] = "Successful Response",
responses: Annotated[
Optional[Dict[Union[int, str], Dict[str, Any]]],
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses that could be returned by this *path operation*.
@ -2561,7 +2557,7 @@ class FastAPI(Starlette):
),
] = True,
response_class: Annotated[
Type[Response],
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
@ -2582,7 +2578,7 @@ class FastAPI(Starlette):
),
] = None,
callbacks: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
@ -2598,7 +2594,7 @@ class FastAPI(Starlette):
),
] = None,
openapi_extra: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
@ -2732,7 +2728,7 @@ class FastAPI(Starlette):
),
] = None,
tags: Annotated[
Optional[List[Union[str, Enum]]],
Optional[list[Union[str, Enum]]],
Doc(
"""
A list of tags to be applied to the *path operation*.
@ -2798,7 +2794,7 @@ class FastAPI(Starlette):
),
] = "Successful Response",
responses: Annotated[
Optional[Dict[Union[int, str], Dict[str, Any]]],
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses that could be returned by this *path operation*.
@ -2939,7 +2935,7 @@ class FastAPI(Starlette):
),
] = True,
response_class: Annotated[
Type[Response],
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
@ -2960,7 +2956,7 @@ class FastAPI(Starlette):
),
] = None,
callbacks: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
@ -2976,7 +2972,7 @@ class FastAPI(Starlette):
),
] = None,
openapi_extra: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
@ -3105,7 +3101,7 @@ class FastAPI(Starlette):
),
] = None,
tags: Annotated[
Optional[List[Union[str, Enum]]],
Optional[list[Union[str, Enum]]],
Doc(
"""
A list of tags to be applied to the *path operation*.
@ -3171,7 +3167,7 @@ class FastAPI(Starlette):
),
] = "Successful Response",
responses: Annotated[
Optional[Dict[Union[int, str], Dict[str, Any]]],
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses that could be returned by this *path operation*.
@ -3312,7 +3308,7 @@ class FastAPI(Starlette):
),
] = True,
response_class: Annotated[
Type[Response],
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
@ -3333,7 +3329,7 @@ class FastAPI(Starlette):
),
] = None,
callbacks: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
@ -3349,7 +3345,7 @@ class FastAPI(Starlette):
),
] = None,
openapi_extra: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
@ -3478,7 +3474,7 @@ class FastAPI(Starlette):
),
] = None,
tags: Annotated[
Optional[List[Union[str, Enum]]],
Optional[list[Union[str, Enum]]],
Doc(
"""
A list of tags to be applied to the *path operation*.
@ -3544,7 +3540,7 @@ class FastAPI(Starlette):
),
] = "Successful Response",
responses: Annotated[
Optional[Dict[Union[int, str], Dict[str, Any]]],
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses that could be returned by this *path operation*.
@ -3685,7 +3681,7 @@ class FastAPI(Starlette):
),
] = True,
response_class: Annotated[
Type[Response],
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
@ -3706,7 +3702,7 @@ class FastAPI(Starlette):
),
] = None,
callbacks: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
@ -3722,7 +3718,7 @@ class FastAPI(Starlette):
),
] = None,
openapi_extra: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
@ -3851,7 +3847,7 @@ class FastAPI(Starlette):
),
] = None,
tags: Annotated[
Optional[List[Union[str, Enum]]],
Optional[list[Union[str, Enum]]],
Doc(
"""
A list of tags to be applied to the *path operation*.
@ -3917,7 +3913,7 @@ class FastAPI(Starlette):
),
] = "Successful Response",
responses: Annotated[
Optional[Dict[Union[int, str], Dict[str, Any]]],
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses that could be returned by this *path operation*.
@ -4058,7 +4054,7 @@ class FastAPI(Starlette):
),
] = True,
response_class: Annotated[
Type[Response],
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
@ -4079,7 +4075,7 @@ class FastAPI(Starlette):
),
] = None,
callbacks: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
@ -4095,7 +4091,7 @@ class FastAPI(Starlette):
),
] = None,
openapi_extra: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
@ -4229,7 +4225,7 @@ class FastAPI(Starlette):
),
] = None,
tags: Annotated[
Optional[List[Union[str, Enum]]],
Optional[list[Union[str, Enum]]],
Doc(
"""
A list of tags to be applied to the *path operation*.
@ -4295,7 +4291,7 @@ class FastAPI(Starlette):
),
] = "Successful Response",
responses: Annotated[
Optional[Dict[Union[int, str], Dict[str, Any]]],
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses that could be returned by this *path operation*.
@ -4436,7 +4432,7 @@ class FastAPI(Starlette):
),
] = True,
response_class: Annotated[
Type[Response],
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
@ -4457,7 +4453,7 @@ class FastAPI(Starlette):
),
] = None,
callbacks: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
@ -4473,7 +4469,7 @@ class FastAPI(Starlette):
),
] = None,
openapi_extra: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
@ -4628,7 +4624,7 @@ class FastAPI(Starlette):
def exception_handler(
self,
exc_class_or_status_code: Annotated[
Union[int, Type[Exception]],
Union[int, type[Exception]],
Doc(
"""
The Exception class this would handle, or a status code.

View File

@ -1,8 +1,8 @@
from typing import Any, Callable
from typing import Annotated, Any, Callable
from annotated_doc import Doc
from starlette.background import BackgroundTasks as StarletteBackgroundTasks
from typing_extensions import Annotated, ParamSpec
from typing_extensions import ParamSpec
P = ParamSpec("P")

View File

@ -1,5 +1,7 @@
from collections.abc import AsyncGenerator
from contextlib import AbstractContextManager
from contextlib import asynccontextmanager as asynccontextmanager
from typing import AsyncGenerator, ContextManager, TypeVar
from typing import TypeVar
import anyio.to_thread
from anyio import CapacityLimiter
@ -14,7 +16,7 @@ _T = TypeVar("_T")
@asynccontextmanager
async def contextmanager_in_threadpool(
cm: ContextManager[_T],
cm: AbstractContextManager[_T],
) -> AsyncGenerator[_T, None]:
# blocking __exit__ from running waiting on a free thread
# can create race conditions/deadlocks if the context manager itself

View File

@ -1,11 +1,10 @@
from collections.abc import Iterable
from typing import (
Annotated,
Any,
BinaryIO,
Callable,
Dict,
Iterable,
Optional,
Type,
TypeVar,
cast,
)
@ -23,7 +22,6 @@ from starlette.datastructures import Headers as Headers # noqa: F401
from starlette.datastructures import QueryParams as QueryParams # noqa: F401
from starlette.datastructures import State as State # noqa: F401
from starlette.datastructures import UploadFile as StarletteUploadFile
from typing_extensions import Annotated
class UploadFile(StarletteUploadFile):
@ -138,11 +136,11 @@ class UploadFile(StarletteUploadFile):
return await super().close()
@classmethod
def __get_validators__(cls: Type["UploadFile"]) -> Iterable[Callable[..., Any]]:
def __get_validators__(cls: type["UploadFile"]) -> Iterable[Callable[..., Any]]:
yield cls.validate
@classmethod
def validate(cls: Type["UploadFile"], v: Any) -> Any:
def validate(cls: type["UploadFile"], v: Any) -> Any:
if not isinstance(v, StarletteUploadFile):
raise ValueError(f"Expected UploadFile, received: {type(v)}")
return v
@ -155,7 +153,7 @@ class UploadFile(StarletteUploadFile):
# TODO: remove when deprecating Pydantic v1
@classmethod
def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
def __modify_schema__(cls, field_schema: dict[str, Any]) -> None:
field_schema.update({"type": "string", "format": "binary"})
@classmethod
@ -166,7 +164,7 @@ class UploadFile(StarletteUploadFile):
@classmethod
def __get_pydantic_core_schema__(
cls, source: Type[Any], handler: Callable[[Any], CoreSchema]
cls, source: type[Any], handler: Callable[[Any], CoreSchema]
) -> CoreSchema:
from ._compat.v2 import with_info_plain_validator_function

View File

@ -2,7 +2,7 @@ import inspect
import sys
from dataclasses import dataclass, field
from functools import cached_property, partial
from typing import Any, Callable, List, Optional, Union
from typing import Any, Callable, Optional, Union
from fastapi._compat import ModelField
from fastapi.security.base import SecurityBase
@ -30,12 +30,12 @@ def _impartial(func: Callable[..., Any]) -> Callable[..., Any]:
@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)
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
@ -44,14 +44,14 @@ class Dependant:
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
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]:
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 []:
@ -98,7 +98,7 @@ class Dependant:
return unwrapped
@cached_property
def _security_dependencies(self) -> List["Dependant"]:
def _security_dependencies(self) -> list["Dependant"]:
security_deps = [dep for dep in self.dependencies if dep._is_security_scheme]
return security_deps

View File

@ -1,21 +1,16 @@
import dataclasses
import inspect
import sys
from collections.abc import Coroutine, Mapping, Sequence
from contextlib import AsyncExitStack, contextmanager
from copy import copy, deepcopy
from dataclasses import dataclass
from typing import (
Annotated,
Any,
Callable,
Coroutine,
Dict,
ForwardRef,
List,
Mapping,
Optional,
Sequence,
Tuple,
Type,
Union,
cast,
)
@ -75,7 +70,7 @@ from starlette.datastructures import (
from starlette.requests import HTTPConnection, Request
from starlette.responses import Response
from starlette.websockets import WebSocket
from typing_extensions import Annotated, Literal, get_args, get_origin
from typing_extensions import Literal, get_args, get_origin
from .. import temp_pydantic_v1_params
@ -125,7 +120,7 @@ def get_parameterless_sub_dependant(*, depends: params.Depends, path: str) -> De
assert callable(depends.dependency), (
"A parameter-less dependency must have a callable dependency"
)
own_oauth_scopes: List[str] = []
own_oauth_scopes: list[str] = []
if isinstance(depends, params.Security) and depends.scopes:
own_oauth_scopes.extend(depends.scopes)
return get_dependant(
@ -140,8 +135,8 @@ def get_flat_dependant(
dependant: Dependant,
*,
skip_repeats: bool = False,
visited: Optional[List[DependencyCacheKey]] = None,
parent_oauth_scopes: Optional[List[str]] = None,
visited: Optional[list[DependencyCacheKey]] = None,
parent_oauth_scopes: Optional[list[str]] = None,
) -> Dependant:
if visited is None:
visited = []
@ -190,7 +185,7 @@ def get_flat_dependant(
return flat_dependant
def _get_flat_fields_from_params(fields: List[ModelField]) -> List[ModelField]:
def _get_flat_fields_from_params(fields: list[ModelField]) -> list[ModelField]:
if not fields:
return fields
first_field = fields[0]
@ -200,7 +195,7 @@ def _get_flat_fields_from_params(fields: List[ModelField]) -> List[ModelField]:
return fields
def get_flat_params(dependant: Dependant) -> List[ModelField]:
def get_flat_params(dependant: Dependant) -> list[ModelField]:
flat_dependant = get_flat_dependant(dependant, skip_repeats=True)
path_params = _get_flat_fields_from_params(flat_dependant.path_params)
query_params = _get_flat_fields_from_params(flat_dependant.query_params)
@ -239,7 +234,7 @@ def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
return typed_signature
def get_typed_annotation(annotation: Any, globalns: Dict[str, Any]) -> Any:
def get_typed_annotation(annotation: Any, globalns: dict[str, Any]) -> Any:
if isinstance(annotation, str):
annotation = ForwardRef(annotation)
annotation = evaluate_forwardref(annotation, globalns, globalns)
@ -265,8 +260,8 @@ def get_dependant(
path: str,
call: Callable[..., Any],
name: Optional[str] = None,
own_oauth_scopes: Optional[List[str]] = None,
parent_oauth_scopes: Optional[List[str]] = None,
own_oauth_scopes: Optional[list[str]] = None,
parent_oauth_scopes: Optional[list[str]] = None,
use_cache: bool = True,
scope: Union[Literal["function", "request"], None] = None,
) -> Dependant:
@ -303,7 +298,7 @@ def get_dependant(
f'The dependency "{dependant.call.__name__}" has a scope of '
'"request", it cannot depend on dependencies with scope "function".'
)
sub_own_oauth_scopes: List[str] = []
sub_own_oauth_scopes: list[str] = []
if isinstance(param_details.depends, params.Security):
if param_details.depends.scopes:
sub_own_oauth_scopes = list(param_details.depends.scopes)
@ -572,7 +567,7 @@ def add_param_to_fields(*, field: ModelField, dependant: Dependant) -> None:
async def _solve_generator(
*, dependant: Dependant, stack: AsyncExitStack, sub_values: Dict[str, Any]
*, dependant: Dependant, stack: AsyncExitStack, sub_values: dict[str, Any]
) -> Any:
assert dependant.call
if dependant.is_async_gen_callable:
@ -584,22 +579,22 @@ async def _solve_generator(
@dataclass
class SolvedDependency:
values: Dict[str, Any]
errors: List[Any]
values: dict[str, Any]
errors: list[Any]
background_tasks: Optional[StarletteBackgroundTasks]
response: Response
dependency_cache: Dict[DependencyCacheKey, Any]
dependency_cache: dict[DependencyCacheKey, Any]
async def solve_dependencies(
*,
request: Union[Request, WebSocket],
dependant: Dependant,
body: Optional[Union[Dict[str, Any], FormData]] = None,
body: Optional[Union[dict[str, Any], FormData]] = None,
background_tasks: Optional[StarletteBackgroundTasks] = None,
response: Optional[Response] = None,
dependency_overrides_provider: Optional[Any] = None,
dependency_cache: Optional[Dict[DependencyCacheKey, Any]] = None,
dependency_cache: Optional[dict[DependencyCacheKey, Any]] = None,
# TODO: remove this parameter later, no longer used, not removing it yet as some
# people might be monkey patching this function (although that's not supported)
async_exit_stack: AsyncExitStack,
@ -613,8 +608,8 @@ async def solve_dependencies(
assert isinstance(function_astack, AsyncExitStack), (
"fastapi_function_astack not found in request scope"
)
values: Dict[str, Any] = {}
errors: List[Any] = []
values: dict[str, Any] = {}
errors: list[Any] = []
if response is None:
response = Response()
del response.headers["content-length"]
@ -732,8 +727,8 @@ async def solve_dependencies(
def _validate_value_with_model_field(
*, field: ModelField, value: Any, values: Dict[str, Any], loc: Tuple[str, ...]
) -> Tuple[Any, List[Any]]:
*, field: ModelField, value: Any, values: dict[str, Any], loc: tuple[str, ...]
) -> tuple[Any, list[Any]]:
if value is None:
if field.required:
return None, [get_missing_field_error(loc=loc)]
@ -776,9 +771,9 @@ def _get_multidict_value(
def request_params_to_args(
fields: Sequence[ModelField],
received_params: Union[Mapping[str, Any], QueryParams, Headers],
) -> Tuple[Dict[str, Any], List[Any]]:
values: Dict[str, Any] = {}
errors: List[Dict[str, Any]] = []
) -> tuple[dict[str, Any], list[Any]]:
values: dict[str, Any] = {}
errors: list[dict[str, Any]] = []
if not fields:
return values, errors
@ -796,7 +791,7 @@ def request_params_to_args(
first_field.field_info, "convert_underscores", True
)
params_to_process: Dict[str, Any] = {}
params_to_process: dict[str, Any] = {}
processed_keys = set()
@ -833,7 +828,7 @@ def request_params_to_args(
assert isinstance(field_info, (params.Param, temp_pydantic_v1_params.Param)), (
"Params must be subclasses of Param"
)
loc: Tuple[str, ...] = (field_info.in_.value,)
loc: tuple[str, ...] = (field_info.in_.value,)
v_, errors_ = _validate_value_with_model_field(
field=first_field, value=params_to_process, values=values, loc=loc
)
@ -875,7 +870,7 @@ def is_union_of_base_models(field_type: Any) -> bool:
return True
def _should_embed_body_fields(fields: List[ModelField]) -> bool:
def _should_embed_body_fields(fields: list[ModelField]) -> bool:
if not fields:
return False
# More than one dependency could have the same field, it would show up as multiple
@ -900,9 +895,9 @@ def _should_embed_body_fields(fields: List[ModelField]) -> bool:
async def _extract_form_body(
body_fields: List[ModelField],
body_fields: list[ModelField],
received_body: FormData,
) -> Dict[str, Any]:
) -> dict[str, Any]:
values = {}
for field in body_fields:
@ -920,8 +915,8 @@ async def _extract_form_body(
and value_is_sequence(value)
):
# For types
assert isinstance(value, sequence_types) # type: ignore[arg-type]
results: List[Union[bytes, str]] = []
assert isinstance(value, sequence_types)
results: list[Union[bytes, str]] = []
async def process_fn(
fn: Callable[[], Coroutine[Any, Any, Any]],
@ -947,18 +942,18 @@ async def _extract_form_body(
async def request_body_to_args(
body_fields: List[ModelField],
received_body: Optional[Union[Dict[str, Any], FormData]],
body_fields: list[ModelField],
received_body: Optional[Union[dict[str, Any], FormData]],
embed_body_fields: bool,
) -> Tuple[Dict[str, Any], List[Dict[str, Any]]]:
values: Dict[str, Any] = {}
errors: List[Dict[str, Any]] = []
) -> tuple[dict[str, Any], list[dict[str, Any]]]:
values: dict[str, Any] = {}
errors: list[dict[str, Any]] = []
assert body_fields, "request_body_to_args() should be called with fields"
single_not_embedded_field = len(body_fields) == 1 and not embed_body_fields
first_field = body_fields[0]
body_to_process = received_body
fields_to_extract: List[ModelField] = body_fields
fields_to_extract: list[ModelField] = body_fields
if (
single_not_embedded_field
@ -971,7 +966,7 @@ async def request_body_to_args(
body_to_process = await _extract_form_body(fields_to_extract, received_body)
if single_not_embedded_field:
loc: Tuple[str, ...] = ("body",)
loc: tuple[str, ...] = ("body",)
v_, errors_ = _validate_value_with_model_field(
field=first_field, value=body_to_process, values=values, loc=loc
)
@ -1019,19 +1014,19 @@ def get_body_field(
fields=flat_dependant.body_params, model_name=model_name
)
required = any(True for f in flat_dependant.body_params if f.required)
BodyFieldInfo_kwargs: Dict[str, Any] = {
BodyFieldInfo_kwargs: dict[str, Any] = {
"annotation": BodyModel,
"alias": "body",
}
if not required:
BodyFieldInfo_kwargs["default"] = None
if any(isinstance(f.field_info, params.File) for f in flat_dependant.body_params):
BodyFieldInfo: Type[params.Body] = params.File
BodyFieldInfo: type[params.Body] = params.File
elif any(
isinstance(f.field_info, temp_pydantic_v1_params.File)
for f in flat_dependant.body_params
):
BodyFieldInfo: Type[temp_pydantic_v1_params.Body] = temp_pydantic_v1_params.File # type: ignore[no-redef]
BodyFieldInfo: type[temp_pydantic_v1_params.Body] = temp_pydantic_v1_params.File # type: ignore[no-redef]
elif any(isinstance(f.field_info, params.Form) for f in flat_dependant.body_params):
BodyFieldInfo = params.Form
elif any(

View File

@ -14,7 +14,7 @@ from ipaddress import (
from pathlib import Path, PurePath
from re import Pattern
from types import GeneratorType
from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union
from typing import Annotated, Any, Callable, Optional, Union
from uuid import UUID
from annotated_doc import Doc
@ -24,7 +24,6 @@ from pydantic import BaseModel
from pydantic.color import Color
from pydantic.networks import AnyUrl, NameEmail
from pydantic.types import SecretBytes, SecretStr
from typing_extensions import Annotated
from ._compat import Url, _is_undefined, _model_dump
@ -61,7 +60,7 @@ def decimal_encoder(dec_value: Decimal) -> Union[int, float]:
return float(dec_value)
ENCODERS_BY_TYPE: Dict[Type[Any], Callable[[Any], Any]] = {
ENCODERS_BY_TYPE: dict[type[Any], Callable[[Any], Any]] = {
bytes: lambda o: o.decode(),
Color: str,
may_v1.Color: str,
@ -98,9 +97,9 @@ ENCODERS_BY_TYPE: Dict[Type[Any], Callable[[Any], Any]] = {
def generate_encoders_by_class_tuples(
type_encoder_map: Dict[Any, Callable[[Any], Any]],
) -> Dict[Callable[[Any], Any], Tuple[Any, ...]]:
encoders_by_class_tuples: Dict[Callable[[Any], Any], Tuple[Any, ...]] = defaultdict(
type_encoder_map: dict[Any, Callable[[Any], Any]],
) -> dict[Callable[[Any], Any], tuple[Any, ...]]:
encoders_by_class_tuples: dict[Callable[[Any], Any], tuple[Any, ...]] = defaultdict(
tuple
)
for type_, encoder in type_encoder_map.items():
@ -180,7 +179,7 @@ def jsonable_encoder(
),
] = False,
custom_encoder: Annotated[
Optional[Dict[Any, Callable[[Any], Any]]],
Optional[dict[Any, Callable[[Any], Any]]],
Doc(
"""
Pydantic's `custom_encoder` parameter, passed to Pydantic models to define
@ -227,7 +226,7 @@ def jsonable_encoder(
exclude = set(exclude)
if isinstance(obj, (BaseModel, may_v1.BaseModel)):
# TODO: remove when deprecating Pydantic v1
encoders: Dict[Any, Any] = {}
encoders: dict[Any, Any] = {}
if isinstance(obj, may_v1.BaseModel):
encoders = getattr(obj.__config__, "json_encoders", {}) # type: ignore[attr-defined]
if custom_encoder:
@ -336,7 +335,7 @@ def jsonable_encoder(
try:
data = dict(obj)
except Exception as e:
errors: List[Exception] = []
errors: list[Exception] = []
errors.append(e)
try:
data = vars(obj)

View File

@ -1,10 +1,10 @@
from typing import Any, Dict, Optional, Sequence, Type, TypedDict, Union
from collections.abc import Sequence
from typing import Annotated, Any, Optional, TypedDict, Union
from annotated_doc import Doc
from pydantic import BaseModel, create_model
from starlette.exceptions import HTTPException as StarletteHTTPException
from starlette.exceptions import WebSocketException as StarletteWebSocketException
from typing_extensions import Annotated
class EndpointContext(TypedDict, total=False):
@ -62,7 +62,7 @@ class HTTPException(StarletteHTTPException):
),
] = None,
headers: Annotated[
Optional[Dict[str, str]],
Optional[dict[str, str]],
Doc(
"""
Any headers to send to the client in the response.
@ -144,8 +144,8 @@ class WebSocketException(StarletteWebSocketException):
super().__init__(code=code, reason=reason)
RequestErrorModel: Type[BaseModel] = create_model("Request")
WebSocketErrorModel: Type[BaseModel] = create_model("WebSocket")
RequestErrorModel: type[BaseModel] = create_model("Request")
WebSocketErrorModel: type[BaseModel] = create_model("WebSocket")
class FastAPIError(RuntimeError):

View File

@ -1,13 +1,12 @@
import json
from typing import Any, Dict, Optional
from typing import Annotated, Any, Optional
from annotated_doc import Doc
from fastapi.encoders import jsonable_encoder
from starlette.responses import HTMLResponse
from typing_extensions import Annotated
swagger_ui_default_parameters: Annotated[
Dict[str, Any],
dict[str, Any],
Doc(
"""
Default configurations for Swagger UI.
@ -82,7 +81,7 @@ def get_swagger_ui_html(
),
] = None,
init_oauth: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
A dictionary with Swagger UI OAuth2 initialization configurations.
@ -90,7 +89,7 @@ def get_swagger_ui_html(
),
] = None,
swagger_ui_parameters: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
Configuration parameters for Swagger UI.

View File

@ -1,5 +1,6 @@
from collections.abc import Iterable
from enum import Enum
from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Type, Union
from typing import Annotated, Any, Callable, Optional, Union
from fastapi._compat import (
PYDANTIC_V2,
@ -11,7 +12,7 @@ from fastapi._compat import (
)
from fastapi.logger import logger
from pydantic import AnyUrl, BaseModel, Field
from typing_extensions import Annotated, Literal, TypedDict
from typing_extensions import Literal, TypedDict
from typing_extensions import deprecated as typing_deprecated
try:
@ -50,7 +51,7 @@ except ImportError: # pragma: no cover
@classmethod
def __get_pydantic_core_schema__(
cls, source: Type[Any], handler: Callable[[Any], CoreSchema]
cls, source: type[Any], handler: Callable[[Any], CoreSchema]
) -> CoreSchema:
return with_info_plain_validator_function(cls._validate)
@ -88,7 +89,7 @@ class Info(BaseModelWithConfig):
class ServerVariable(BaseModelWithConfig):
enum: Annotated[Optional[List[str]], Field(min_length=1)] = None
enum: Annotated[Optional[list[str]], Field(min_length=1)] = None
default: str
description: Optional[str] = None
@ -96,7 +97,7 @@ class ServerVariable(BaseModelWithConfig):
class Server(BaseModelWithConfig):
url: Union[AnyUrl, str]
description: Optional[str] = None
variables: Optional[Dict[str, ServerVariable]] = None
variables: Optional[dict[str, ServerVariable]] = None
class Reference(BaseModel):
@ -105,7 +106,7 @@ class Reference(BaseModel):
class Discriminator(BaseModel):
propertyName: str
mapping: Optional[Dict[str, str]] = None
mapping: Optional[dict[str, str]] = None
class XML(BaseModelWithConfig):
@ -137,34 +138,34 @@ class Schema(BaseModelWithConfig):
dynamicAnchor: Optional[str] = Field(default=None, alias="$dynamicAnchor")
ref: Optional[str] = Field(default=None, alias="$ref")
dynamicRef: Optional[str] = Field(default=None, alias="$dynamicRef")
defs: Optional[Dict[str, "SchemaOrBool"]] = Field(default=None, alias="$defs")
defs: Optional[dict[str, "SchemaOrBool"]] = Field(default=None, alias="$defs")
comment: Optional[str] = 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
# A Vocabulary for Applying Subschemas
allOf: Optional[List["SchemaOrBool"]] = None
anyOf: Optional[List["SchemaOrBool"]] = None
oneOf: Optional[List["SchemaOrBool"]] = None
allOf: Optional[list["SchemaOrBool"]] = None
anyOf: Optional[list["SchemaOrBool"]] = None
oneOf: Optional[list["SchemaOrBool"]] = None
not_: Optional["SchemaOrBool"] = Field(default=None, alias="not")
if_: Optional["SchemaOrBool"] = Field(default=None, alias="if")
then: Optional["SchemaOrBool"] = None
else_: Optional["SchemaOrBool"] = Field(default=None, alias="else")
dependentSchemas: Optional[Dict[str, "SchemaOrBool"]] = None
prefixItems: Optional[List["SchemaOrBool"]] = None
dependentSchemas: Optional[dict[str, "SchemaOrBool"]] = None
prefixItems: Optional[list["SchemaOrBool"]] = None
# TODO: uncomment and remove below when deprecating Pydantic v1
# It generates a list of schemas for tuples, before prefixItems was available
# items: Optional["SchemaOrBool"] = None
items: Optional[Union["SchemaOrBool", List["SchemaOrBool"]]] = None
items: Optional[Union["SchemaOrBool", list["SchemaOrBool"]]] = None
contains: Optional["SchemaOrBool"] = None
properties: Optional[Dict[str, "SchemaOrBool"]] = None
patternProperties: Optional[Dict[str, "SchemaOrBool"]] = None
properties: Optional[dict[str, "SchemaOrBool"]] = None
patternProperties: Optional[dict[str, "SchemaOrBool"]] = None
additionalProperties: Optional["SchemaOrBool"] = None
propertyNames: Optional["SchemaOrBool"] = None
unevaluatedItems: 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
# A Vocabulary for Structural Validation
type: Optional[Union[SchemaType, List[SchemaType]]] = None
enum: Optional[List[Any]] = None
type: Optional[Union[SchemaType, list[SchemaType]]] = None
enum: Optional[list[Any]] = None
const: Optional[Any] = None
multipleOf: Optional[float] = Field(default=None, gt=0)
maximum: Optional[float] = None
@ -181,8 +182,8 @@ class Schema(BaseModelWithConfig):
minContains: Optional[int] = Field(default=None, ge=0)
maxProperties: Optional[int] = Field(default=None, ge=0)
minProperties: Optional[int] = Field(default=None, ge=0)
required: Optional[List[str]] = None
dependentRequired: Optional[Dict[str, Set[str]]] = None
required: Optional[list[str]] = None
dependentRequired: Optional[dict[str, set[str]]] = None
# 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"
format: Optional[str] = None
@ -199,7 +200,7 @@ class Schema(BaseModelWithConfig):
deprecated: Optional[bool] = None
readOnly: Optional[bool] = None
writeOnly: Optional[bool] = None
examples: Optional[List[Any]] = None
examples: Optional[list[Any]] = None
# Ref: OpenAPI 3.1.0: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#schema-object
# Schema Object
discriminator: Optional[Discriminator] = None
@ -243,7 +244,7 @@ class ParameterInType(Enum):
class Encoding(BaseModelWithConfig):
contentType: Optional[str] = None
headers: Optional[Dict[str, Union["Header", Reference]]] = None
headers: Optional[dict[str, Union["Header", Reference]]] = None
style: Optional[str] = None
explode: Optional[bool] = None
allowReserved: Optional[bool] = None
@ -252,8 +253,8 @@ class Encoding(BaseModelWithConfig):
class MediaType(BaseModelWithConfig):
schema_: Optional[Union[Schema, Reference]] = Field(default=None, alias="schema")
example: Optional[Any] = None
examples: Optional[Dict[str, Union[Example, Reference]]] = None
encoding: Optional[Dict[str, Encoding]] = None
examples: Optional[dict[str, Union[Example, Reference]]] = None
encoding: Optional[dict[str, Encoding]] = None
class ParameterBase(BaseModelWithConfig):
@ -266,9 +267,9 @@ class ParameterBase(BaseModelWithConfig):
allowReserved: Optional[bool] = None
schema_: Optional[Union[Schema, Reference]] = Field(default=None, alias="schema")
example: Optional[Any] = None
examples: Optional[Dict[str, Union[Example, Reference]]] = None
examples: Optional[dict[str, Union[Example, Reference]]] = None
# Serialization rules for more complex scenarios
content: Optional[Dict[str, MediaType]] = None
content: Optional[dict[str, MediaType]] = None
class Parameter(ParameterBase):
@ -282,14 +283,14 @@ class Header(ParameterBase):
class RequestBody(BaseModelWithConfig):
description: Optional[str] = None
content: Dict[str, MediaType]
content: dict[str, MediaType]
required: Optional[bool] = None
class Link(BaseModelWithConfig):
operationRef: Optional[str] = None
operationId: Optional[str] = None
parameters: Optional[Dict[str, Union[Any, str]]] = None
parameters: Optional[dict[str, Union[Any, str]]] = None
requestBody: Optional[Union[Any, str]] = None
description: Optional[str] = None
server: Optional[Server] = None
@ -297,25 +298,25 @@ class Link(BaseModelWithConfig):
class Response(BaseModelWithConfig):
description: str
headers: Optional[Dict[str, Union[Header, Reference]]] = None
content: Optional[Dict[str, MediaType]] = None
links: Optional[Dict[str, Union[Link, Reference]]] = None
headers: Optional[dict[str, Union[Header, Reference]]] = None
content: Optional[dict[str, MediaType]] = None
links: Optional[dict[str, Union[Link, Reference]]] = None
class Operation(BaseModelWithConfig):
tags: Optional[List[str]] = None
tags: Optional[list[str]] = None
summary: Optional[str] = None
description: Optional[str] = None
externalDocs: Optional[ExternalDocumentation] = None
operationId: Optional[str] = None
parameters: Optional[List[Union[Parameter, Reference]]] = None
parameters: Optional[list[Union[Parameter, Reference]]] = None
requestBody: Optional[Union[RequestBody, Reference]] = None
# Using Any for Specification Extensions
responses: Optional[Dict[str, Union[Response, Any]]] = None
callbacks: Optional[Dict[str, Union[Dict[str, "PathItem"], Reference]]] = None
responses: Optional[dict[str, Union[Response, Any]]] = None
callbacks: Optional[dict[str, Union[dict[str, "PathItem"], Reference]]] = None
deprecated: Optional[bool] = None
security: Optional[List[Dict[str, List[str]]]] = None
servers: Optional[List[Server]] = None
security: Optional[list[dict[str, list[str]]]] = None
servers: Optional[list[Server]] = None
class PathItem(BaseModelWithConfig):
@ -330,8 +331,8 @@ class PathItem(BaseModelWithConfig):
head: Optional[Operation] = None
patch: Optional[Operation] = None
trace: Optional[Operation] = None
servers: Optional[List[Server]] = None
parameters: Optional[List[Union[Parameter, Reference]]] = None
servers: Optional[list[Server]] = None
parameters: Optional[list[Union[Parameter, Reference]]] = None
class SecuritySchemeType(Enum):
@ -370,7 +371,7 @@ class HTTPBearer(HTTPBase):
class OAuthFlow(BaseModelWithConfig):
refreshUrl: Optional[str] = None
scopes: Dict[str, str] = {}
scopes: dict[str, str] = {}
class OAuthFlowImplicit(OAuthFlow):
@ -413,17 +414,17 @@ SecurityScheme = Union[APIKey, HTTPBase, OAuth2, OpenIdConnect, HTTPBearer]
class Components(BaseModelWithConfig):
schemas: Optional[Dict[str, Union[Schema, Reference]]] = None
responses: Optional[Dict[str, Union[Response, Reference]]] = None
parameters: Optional[Dict[str, Union[Parameter, Reference]]] = None
examples: Optional[Dict[str, Union[Example, Reference]]] = None
requestBodies: Optional[Dict[str, Union[RequestBody, Reference]]] = None
headers: Optional[Dict[str, Union[Header, Reference]]] = None
securitySchemes: Optional[Dict[str, Union[SecurityScheme, Reference]]] = None
links: Optional[Dict[str, Union[Link, Reference]]] = None
schemas: Optional[dict[str, Union[Schema, Reference]]] = None
responses: Optional[dict[str, Union[Response, Reference]]] = None
parameters: Optional[dict[str, Union[Parameter, Reference]]] = None
examples: Optional[dict[str, Union[Example, Reference]]] = None
requestBodies: Optional[dict[str, Union[RequestBody, Reference]]] = None
headers: Optional[dict[str, Union[Header, Reference]]] = None
securitySchemes: Optional[dict[str, Union[SecurityScheme, Reference]]] = None
links: Optional[dict[str, Union[Link, Reference]]] = None
# Using Any for Specification Extensions
callbacks: Optional[Dict[str, Union[Dict[str, PathItem], Reference, Any]]] = None
pathItems: Optional[Dict[str, Union[PathItem, Reference]]] = None
callbacks: Optional[dict[str, Union[dict[str, PathItem], Reference, Any]]] = None
pathItems: Optional[dict[str, Union[PathItem, Reference]]] = None
class Tag(BaseModelWithConfig):
@ -436,13 +437,13 @@ class OpenAPI(BaseModelWithConfig):
openapi: str
info: Info
jsonSchemaDialect: Optional[str] = None
servers: Optional[List[Server]] = None
servers: Optional[list[Server]] = None
# Using Any for Specification Extensions
paths: Optional[Dict[str, Union[PathItem, Any]]] = None
webhooks: Optional[Dict[str, Union[PathItem, Reference]]] = None
paths: Optional[dict[str, Union[PathItem, Any]]] = None
webhooks: Optional[dict[str, Union[PathItem, Reference]]] = None
components: Optional[Components] = None
security: Optional[List[Dict[str, List[str]]]] = None
tags: Optional[List[Tag]] = None
security: Optional[list[dict[str, list[str]]]] = None
tags: Optional[list[Tag]] = None
externalDocs: Optional[ExternalDocumentation] = None

View File

@ -1,7 +1,8 @@
import http.client
import inspect
import warnings
from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Type, Union, cast
from collections.abc import Sequence
from typing import Any, Optional, Union, cast
from fastapi import routing
from fastapi._compat import (
@ -66,7 +67,7 @@ validation_error_response_definition = {
},
}
status_code_ranges: Dict[str, str] = {
status_code_ranges: dict[str, str] = {
"1XX": "Information",
"2XX": "Success",
"3XX": "Redirection",
@ -78,10 +79,10 @@ status_code_ranges: Dict[str, str] = {
def get_openapi_security_definitions(
flat_dependant: Dependant,
) -> Tuple[Dict[str, Any], List[Dict[str, Any]]]:
) -> tuple[dict[str, Any], list[dict[str, Any]]]:
security_definitions = {}
# Use a dict to merge scopes for same security scheme
operation_security_dict: Dict[str, List[str]] = {}
operation_security_dict: dict[str, list[str]] = {}
for security_dependency in flat_dependant._security_dependencies:
security_definition = jsonable_encoder(
security_dependency._security_scheme.model,
@ -106,11 +107,11 @@ def _get_openapi_operation_parameters(
*,
dependant: Dependant,
model_name_map: ModelNameMap,
field_mapping: Dict[
Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
field_mapping: dict[
tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
],
separate_input_output_schemas: bool = True,
) -> List[Dict[str, Any]]:
) -> list[dict[str, Any]]:
parameters = []
flat_dependant = get_flat_dependant(dependant, skip_repeats=True)
path_params = _get_flat_fields_from_params(flat_dependant.path_params)
@ -179,11 +180,11 @@ def get_openapi_operation_request_body(
*,
body_field: Optional[ModelField],
model_name_map: ModelNameMap,
field_mapping: Dict[
Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
field_mapping: dict[
tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
],
separate_input_output_schemas: bool = True,
) -> Optional[Dict[str, Any]]:
) -> Optional[dict[str, Any]]:
if not body_field:
return None
assert _is_model_field(body_field)
@ -196,10 +197,10 @@ def get_openapi_operation_request_body(
field_info = cast(Body, body_field.field_info)
request_media_type = field_info.media_type
required = body_field.required
request_body_oai: Dict[str, Any] = {}
request_body_oai: dict[str, Any] = {}
if required:
request_body_oai["required"] = required
request_media_content: Dict[str, Any] = {"schema": body_schema}
request_media_content: dict[str, Any] = {"schema": body_schema}
if field_info.openapi_examples:
request_media_content["examples"] = jsonable_encoder(
field_info.openapi_examples
@ -232,9 +233,9 @@ def generate_operation_summary(*, route: routing.APIRoute, method: str) -> str:
def get_openapi_operation_metadata(
*, route: routing.APIRoute, method: str, operation_ids: Set[str]
) -> Dict[str, Any]:
operation: Dict[str, Any] = {}
*, route: routing.APIRoute, method: str, operation_ids: set[str]
) -> dict[str, Any]:
operation: dict[str, Any] = {}
if route.tags:
operation["tags"] = route.tags
operation["summary"] = generate_operation_summary(route=route, method=method)
@ -260,19 +261,19 @@ def get_openapi_operation_metadata(
def get_openapi_path(
*,
route: routing.APIRoute,
operation_ids: Set[str],
operation_ids: set[str],
model_name_map: ModelNameMap,
field_mapping: Dict[
Tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
field_mapping: dict[
tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
],
separate_input_output_schemas: bool = True,
) -> Tuple[Dict[str, Any], Dict[str, Any], Dict[str, Any]]:
) -> tuple[dict[str, Any], dict[str, Any], dict[str, Any]]:
path = {}
security_schemes: Dict[str, Any] = {}
definitions: Dict[str, Any] = {}
security_schemes: dict[str, Any] = {}
definitions: dict[str, Any] = {}
assert route.methods is not None, "Methods must be a list"
if isinstance(route.response_class, DefaultPlaceholder):
current_response_class: Type[Response] = route.response_class.value
current_response_class: type[Response] = route.response_class.value
else:
current_response_class = route.response_class
assert current_response_class, "A response class is needed to generate OpenAPI"
@ -282,7 +283,7 @@ def get_openapi_path(
operation = get_openapi_operation_metadata(
route=route, method=method, operation_ids=operation_ids
)
parameters: List[Dict[str, Any]] = []
parameters: list[dict[str, Any]] = []
flat_dependant = get_flat_dependant(route.dependant, skip_repeats=True)
security_definitions, operation_security = get_openapi_security_definitions(
flat_dependant=flat_dependant
@ -390,7 +391,7 @@ def get_openapi_path(
"An additional response must be a dict"
)
field = route.response_fields.get(additional_status_code)
additional_field_schema: Optional[Dict[str, Any]] = None
additional_field_schema: Optional[dict[str, Any]] = None
if field:
additional_field_schema = get_schema_from_model_field(
field=field,
@ -445,11 +446,11 @@ def get_openapi_path(
def get_fields_from_routes(
routes: Sequence[BaseRoute],
) -> List[ModelField]:
body_fields_from_routes: List[ModelField] = []
responses_from_routes: List[ModelField] = []
request_fields_from_routes: List[ModelField] = []
callback_flat_models: List[ModelField] = []
) -> list[ModelField]:
body_fields_from_routes: list[ModelField] = []
responses_from_routes: list[ModelField] = []
request_fields_from_routes: list[ModelField] = []
callback_flat_models: list[ModelField] = []
for route in routes:
if getattr(route, "include_in_schema", None) and isinstance(
route, routing.APIRoute
@ -483,15 +484,15 @@ def get_openapi(
description: Optional[str] = None,
routes: Sequence[BaseRoute],
webhooks: Optional[Sequence[BaseRoute]] = None,
tags: Optional[List[Dict[str, Any]]] = None,
servers: Optional[List[Dict[str, Union[str, Any]]]] = None,
tags: Optional[list[dict[str, Any]]] = None,
servers: Optional[list[dict[str, Union[str, Any]]]] = None,
terms_of_service: Optional[str] = None,
contact: Optional[Dict[str, Union[str, Any]]] = None,
license_info: Optional[Dict[str, Union[str, Any]]] = None,
contact: Optional[dict[str, Union[str, Any]]] = None,
license_info: Optional[dict[str, Union[str, Any]]] = None,
separate_input_output_schemas: bool = True,
external_docs: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
info: Dict[str, Any] = {"title": title, "version": version}
external_docs: Optional[dict[str, Any]] = None,
) -> dict[str, Any]:
info: dict[str, Any] = {"title": title, "version": version}
if summary:
info["summary"] = summary
if description:
@ -502,13 +503,13 @@ def get_openapi(
info["contact"] = contact
if license_info:
info["license"] = license_info
output: Dict[str, Any] = {"openapi": openapi_version, "info": info}
output: dict[str, Any] = {"openapi": openapi_version, "info": info}
if servers:
output["servers"] = servers
components: Dict[str, Dict[str, Any]] = {}
paths: Dict[str, Dict[str, Any]] = {}
webhook_paths: Dict[str, Dict[str, Any]] = {}
operation_ids: Set[str] = set()
components: dict[str, dict[str, Any]] = {}
paths: dict[str, dict[str, Any]] = {}
webhook_paths: dict[str, dict[str, Any]] = {}
operation_ids: set[str] = set()
all_fields = get_fields_from_routes(list(routes or []) + list(webhooks or []))
model_name_map = get_compat_model_name_map(all_fields)
field_mapping, definitions = get_definitions(

View File

@ -1,10 +1,11 @@
from typing import Any, Callable, Dict, List, Optional, Sequence, Union
from collections.abc import Sequence
from typing import Annotated, Any, Callable, Optional, Union
from annotated_doc import Doc
from fastapi import params
from fastapi._compat import Undefined
from fastapi.openapi.models import Example
from typing_extensions import Annotated, Literal, deprecated
from typing_extensions import Literal, deprecated
_Unset: Any = Undefined
@ -209,7 +210,7 @@ def Path( # noqa: N802
),
] = _Unset,
examples: Annotated[
Optional[List[Any]],
Optional[list[Any]],
Doc(
"""
Example values for this field.
@ -224,7 +225,7 @@ def Path( # noqa: N802
),
] = _Unset,
openapi_examples: Annotated[
Optional[Dict[str, Example]],
Optional[dict[str, Example]],
Doc(
"""
OpenAPI-specific examples.
@ -262,7 +263,7 @@ def Path( # noqa: N802
),
] = True,
json_schema_extra: Annotated[
Union[Dict[str, Any], None],
Union[dict[str, Any], None],
Doc(
"""
Any additional JSON schema data.
@ -534,7 +535,7 @@ def Query( # noqa: N802
),
] = _Unset,
examples: Annotated[
Optional[List[Any]],
Optional[list[Any]],
Doc(
"""
Example values for this field.
@ -549,7 +550,7 @@ def Query( # noqa: N802
),
] = _Unset,
openapi_examples: Annotated[
Optional[Dict[str, Example]],
Optional[dict[str, Example]],
Doc(
"""
OpenAPI-specific examples.
@ -587,7 +588,7 @@ def Query( # noqa: N802
),
] = True,
json_schema_extra: Annotated[
Union[Dict[str, Any], None],
Union[dict[str, Any], None],
Doc(
"""
Any additional JSON schema data.
@ -849,7 +850,7 @@ def Header( # noqa: N802
),
] = _Unset,
examples: Annotated[
Optional[List[Any]],
Optional[list[Any]],
Doc(
"""
Example values for this field.
@ -864,7 +865,7 @@ def Header( # noqa: N802
),
] = _Unset,
openapi_examples: Annotated[
Optional[Dict[str, Example]],
Optional[dict[str, Example]],
Doc(
"""
OpenAPI-specific examples.
@ -902,7 +903,7 @@ def Header( # noqa: N802
),
] = True,
json_schema_extra: Annotated[
Union[Dict[str, Any], None],
Union[dict[str, Any], None],
Doc(
"""
Any additional JSON schema data.
@ -1154,7 +1155,7 @@ def Cookie( # noqa: N802
),
] = _Unset,
examples: Annotated[
Optional[List[Any]],
Optional[list[Any]],
Doc(
"""
Example values for this field.
@ -1169,7 +1170,7 @@ def Cookie( # noqa: N802
),
] = _Unset,
openapi_examples: Annotated[
Optional[Dict[str, Example]],
Optional[dict[str, Example]],
Doc(
"""
OpenAPI-specific examples.
@ -1207,7 +1208,7 @@ def Cookie( # noqa: N802
),
] = True,
json_schema_extra: Annotated[
Union[Dict[str, Any], None],
Union[dict[str, Any], None],
Doc(
"""
Any additional JSON schema data.
@ -1481,7 +1482,7 @@ def Body( # noqa: N802
),
] = _Unset,
examples: Annotated[
Optional[List[Any]],
Optional[list[Any]],
Doc(
"""
Example values for this field.
@ -1496,7 +1497,7 @@ def Body( # noqa: N802
),
] = _Unset,
openapi_examples: Annotated[
Optional[Dict[str, Example]],
Optional[dict[str, Example]],
Doc(
"""
OpenAPI-specific examples.
@ -1534,7 +1535,7 @@ def Body( # noqa: N802
),
] = True,
json_schema_extra: Annotated[
Union[Dict[str, Any], None],
Union[dict[str, Any], None],
Doc(
"""
Any additional JSON schema data.
@ -1796,7 +1797,7 @@ def Form( # noqa: N802
),
] = _Unset,
examples: Annotated[
Optional[List[Any]],
Optional[list[Any]],
Doc(
"""
Example values for this field.
@ -1811,7 +1812,7 @@ def Form( # noqa: N802
),
] = _Unset,
openapi_examples: Annotated[
Optional[Dict[str, Example]],
Optional[dict[str, Example]],
Doc(
"""
OpenAPI-specific examples.
@ -1849,7 +1850,7 @@ def Form( # noqa: N802
),
] = True,
json_schema_extra: Annotated[
Union[Dict[str, Any], None],
Union[dict[str, Any], None],
Doc(
"""
Any additional JSON schema data.
@ -2110,7 +2111,7 @@ def File( # noqa: N802
),
] = _Unset,
examples: Annotated[
Optional[List[Any]],
Optional[list[Any]],
Doc(
"""
Example values for this field.
@ -2125,7 +2126,7 @@ def File( # noqa: N802
),
] = _Unset,
openapi_examples: Annotated[
Optional[Dict[str, Example]],
Optional[dict[str, Example]],
Doc(
"""
OpenAPI-specific examples.
@ -2163,7 +2164,7 @@ def File( # noqa: N802
),
] = True,
json_schema_extra: Annotated[
Union[Dict[str, Any], None],
Union[dict[str, Any], None],
Doc(
"""
Any additional JSON schema data.

View File

@ -1,11 +1,12 @@
import warnings
from collections.abc import Sequence
from dataclasses import dataclass
from enum import Enum
from typing import Any, Callable, Dict, List, Optional, Sequence, Union
from typing import Annotated, Any, Callable, Optional, Union
from fastapi.openapi.models import Example
from pydantic.fields import FieldInfo
from typing_extensions import Annotated, Literal, deprecated
from typing_extensions import Literal, deprecated
from ._compat import (
PYDANTIC_V2,
@ -59,7 +60,7 @@ class Param(FieldInfo): # type: ignore[misc]
allow_inf_nan: Union[bool, None] = _Unset,
max_digits: Union[int, None] = _Unset,
decimal_places: Union[int, None] = _Unset,
examples: Optional[List[Any]] = None,
examples: Optional[list[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
@ -67,10 +68,10 @@ class Param(FieldInfo): # type: ignore[misc]
"although still supported. Use examples instead."
),
] = _Unset,
openapi_examples: Optional[Dict[str, Example]] = None,
openapi_examples: Optional[dict[str, Example]] = None,
deprecated: Union[deprecated, str, bool, None] = None,
include_in_schema: bool = True,
json_schema_extra: Union[Dict[str, Any], None] = None,
json_schema_extra: Union[dict[str, Any], None] = None,
**extra: Any,
):
if example is not _Unset:
@ -177,7 +178,7 @@ class Path(Param): # type: ignore[misc]
allow_inf_nan: Union[bool, None] = _Unset,
max_digits: Union[int, None] = _Unset,
decimal_places: Union[int, None] = _Unset,
examples: Optional[List[Any]] = None,
examples: Optional[list[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
@ -185,10 +186,10 @@ class Path(Param): # type: ignore[misc]
"although still supported. Use examples instead."
),
] = _Unset,
openapi_examples: Optional[Dict[str, Example]] = None,
openapi_examples: Optional[dict[str, Example]] = None,
deprecated: Union[deprecated, str, bool, None] = None,
include_in_schema: bool = True,
json_schema_extra: Union[Dict[str, Any], None] = None,
json_schema_extra: Union[dict[str, Any], None] = None,
**extra: Any,
):
assert default is ..., "Path parameters cannot have a default value"
@ -263,7 +264,7 @@ class Query(Param): # type: ignore[misc]
allow_inf_nan: Union[bool, None] = _Unset,
max_digits: Union[int, None] = _Unset,
decimal_places: Union[int, None] = _Unset,
examples: Optional[List[Any]] = None,
examples: Optional[list[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
@ -271,10 +272,10 @@ class Query(Param): # type: ignore[misc]
"although still supported. Use examples instead."
),
] = _Unset,
openapi_examples: Optional[Dict[str, Example]] = None,
openapi_examples: Optional[dict[str, Example]] = None,
deprecated: Union[deprecated, str, bool, None] = None,
include_in_schema: bool = True,
json_schema_extra: Union[Dict[str, Any], None] = None,
json_schema_extra: Union[dict[str, Any], None] = None,
**extra: Any,
):
super().__init__(
@ -348,7 +349,7 @@ class Header(Param): # type: ignore[misc]
allow_inf_nan: Union[bool, None] = _Unset,
max_digits: Union[int, None] = _Unset,
decimal_places: Union[int, None] = _Unset,
examples: Optional[List[Any]] = None,
examples: Optional[list[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
@ -356,10 +357,10 @@ class Header(Param): # type: ignore[misc]
"although still supported. Use examples instead."
),
] = _Unset,
openapi_examples: Optional[Dict[str, Example]] = None,
openapi_examples: Optional[dict[str, Example]] = None,
deprecated: Union[deprecated, str, bool, None] = None,
include_in_schema: bool = True,
json_schema_extra: Union[Dict[str, Any], None] = None,
json_schema_extra: Union[dict[str, Any], None] = None,
**extra: Any,
):
self.convert_underscores = convert_underscores
@ -433,7 +434,7 @@ class Cookie(Param): # type: ignore[misc]
allow_inf_nan: Union[bool, None] = _Unset,
max_digits: Union[int, None] = _Unset,
decimal_places: Union[int, None] = _Unset,
examples: Optional[List[Any]] = None,
examples: Optional[list[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
@ -441,10 +442,10 @@ class Cookie(Param): # type: ignore[misc]
"although still supported. Use examples instead."
),
] = _Unset,
openapi_examples: Optional[Dict[str, Example]] = None,
openapi_examples: Optional[dict[str, Example]] = None,
deprecated: Union[deprecated, str, bool, None] = None,
include_in_schema: bool = True,
json_schema_extra: Union[Dict[str, Any], None] = None,
json_schema_extra: Union[dict[str, Any], None] = None,
**extra: Any,
):
super().__init__(
@ -517,7 +518,7 @@ class Body(FieldInfo): # type: ignore[misc]
allow_inf_nan: Union[bool, None] = _Unset,
max_digits: Union[int, None] = _Unset,
decimal_places: Union[int, None] = _Unset,
examples: Optional[List[Any]] = None,
examples: Optional[list[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
@ -525,10 +526,10 @@ class Body(FieldInfo): # type: ignore[misc]
"although still supported. Use examples instead."
),
] = _Unset,
openapi_examples: Optional[Dict[str, Example]] = None,
openapi_examples: Optional[dict[str, Example]] = None,
deprecated: Union[deprecated, str, bool, None] = None,
include_in_schema: bool = True,
json_schema_extra: Union[Dict[str, Any], None] = None,
json_schema_extra: Union[dict[str, Any], None] = None,
**extra: Any,
):
self.embed = embed
@ -637,7 +638,7 @@ class Form(Body): # type: ignore[misc]
allow_inf_nan: Union[bool, None] = _Unset,
max_digits: Union[int, None] = _Unset,
decimal_places: Union[int, None] = _Unset,
examples: Optional[List[Any]] = None,
examples: Optional[list[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
@ -645,10 +646,10 @@ class Form(Body): # type: ignore[misc]
"although still supported. Use examples instead."
),
] = _Unset,
openapi_examples: Optional[Dict[str, Example]] = None,
openapi_examples: Optional[dict[str, Example]] = None,
deprecated: Union[deprecated, str, bool, None] = None,
include_in_schema: bool = True,
json_schema_extra: Union[Dict[str, Any], None] = None,
json_schema_extra: Union[dict[str, Any], None] = None,
**extra: Any,
):
super().__init__(
@ -721,7 +722,7 @@ class File(Form): # type: ignore[misc]
allow_inf_nan: Union[bool, None] = _Unset,
max_digits: Union[int, None] = _Unset,
decimal_places: Union[int, None] = _Unset,
examples: Optional[List[Any]] = None,
examples: Optional[list[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
@ -729,10 +730,10 @@ class File(Form): # type: ignore[misc]
"although still supported. Use examples instead."
),
] = _Unset,
openapi_examples: Optional[Dict[str, Example]] = None,
openapi_examples: Optional[dict[str, Example]] = None,
deprecated: Union[deprecated, str, bool, None] = None,
include_in_schema: bool = True,
json_schema_extra: Union[Dict[str, Any], None] = None,
json_schema_extra: Union[dict[str, Any], None] = None,
**extra: Any,
):
super().__init__(

View File

@ -3,23 +3,21 @@ import email.message
import functools
import inspect
import json
from collections.abc import (
AsyncIterator,
Awaitable,
Collection,
Coroutine,
Mapping,
Sequence,
)
from contextlib import AsyncExitStack, asynccontextmanager
from enum import Enum, IntEnum
from typing import (
Annotated,
Any,
AsyncIterator,
Awaitable,
Callable,
Collection,
Coroutine,
Dict,
List,
Mapping,
Optional,
Sequence,
Set,
Tuple,
Type,
Union,
)
@ -77,7 +75,7 @@ from starlette.routing import (
from starlette.routing import Mount as Mount # noqa
from starlette.types import AppType, ASGIApp, Lifespan, Receive, Scope, Send
from starlette.websockets import WebSocket
from typing_extensions import Annotated, deprecated
from typing_extensions import deprecated
# Copy of starlette.routing.request_response modified to include the
@ -214,7 +212,7 @@ def _merge_lifespan_context(
# Cache for endpoint context to avoid re-extracting on every request
_endpoint_context_cache: Dict[int, EndpointContext] = {}
_endpoint_context_cache: dict[int, EndpointContext] = {}
def _extract_endpoint_context(func: Any) -> EndpointContext:
@ -306,7 +304,7 @@ async def serialize_response(
async def run_endpoint_function(
*, dependant: Dependant, values: Dict[str, Any], is_coroutine: bool
*, dependant: Dependant, values: dict[str, Any], is_coroutine: bool
) -> Any:
# Only called by get_request_handler. Has been split into its own function to
# facilitate profiling endpoints, since inner functions are harder to profile.
@ -322,7 +320,7 @@ def get_request_handler(
dependant: Dependant,
body_field: Optional[ModelField] = None,
status_code: Optional[int] = None,
response_class: Union[Type[Response], DefaultPlaceholder] = Default(JSONResponse),
response_class: Union[type[Response], DefaultPlaceholder] = Default(JSONResponse),
response_field: Optional[ModelField] = None,
response_model_include: Optional[IncEx] = None,
response_model_exclude: Optional[IncEx] = None,
@ -339,7 +337,7 @@ def get_request_handler(
body_field.field_info, (params.Form, temp_pydantic_v1_params.Form)
)
if isinstance(response_class, DefaultPlaceholder):
actual_response_class: Type[Response] = response_class.value
actual_response_class: type[Response] = response_class.value
else:
actual_response_class = response_class
@ -412,7 +410,7 @@ def get_request_handler(
raise http_error from e
# Solve dependencies and run path operation function, auto-closing dependencies
errors: List[Any] = []
errors: list[Any] = []
async_exit_stack = request.scope.get("fastapi_inner_astack")
assert isinstance(async_exit_stack, AsyncExitStack), (
"fastapi_inner_astack not found in request scope"
@ -437,7 +435,7 @@ def get_request_handler(
raw_response.background = solved_result.background_tasks
response = raw_response
else:
response_args: Dict[str, Any] = {
response_args: dict[str, Any] = {
"background": solved_result.background_tasks
}
# If status_code was set, use it, otherwise use the default from the
@ -550,7 +548,7 @@ class APIWebSocketRoute(routing.WebSocketRoute):
)
)
def matches(self, scope: Scope) -> Tuple[Match, Scope]:
def matches(self, scope: Scope) -> tuple[Match, Scope]:
match, child_scope = super().matches(scope)
if match != Match.NONE:
child_scope["route"] = self
@ -565,15 +563,15 @@ class APIRoute(routing.Route):
*,
response_model: Any = Default(None),
status_code: Optional[int] = None,
tags: Optional[List[Union[str, Enum]]] = None,
tags: Optional[list[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
response_description: str = "Successful Response",
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
responses: Optional[dict[Union[int, str], dict[str, Any]]] = None,
deprecated: Optional[bool] = None,
name: Optional[str] = None,
methods: Optional[Union[Set[str], List[str]]] = None,
methods: Optional[Union[set[str], list[str]]] = None,
operation_id: Optional[str] = None,
response_model_include: Optional[IncEx] = None,
response_model_exclude: Optional[IncEx] = None,
@ -582,12 +580,12 @@ class APIRoute(routing.Route):
response_model_exclude_defaults: bool = False,
response_model_exclude_none: bool = False,
include_in_schema: bool = True,
response_class: Union[Type[Response], DefaultPlaceholder] = Default(
response_class: Union[type[Response], DefaultPlaceholder] = Default(
JSONResponse
),
dependency_overrides_provider: Optional[Any] = None,
callbacks: Optional[List[BaseRoute]] = None,
openapi_extra: Optional[Dict[str, Any]] = None,
callbacks: Optional[list[BaseRoute]] = None,
openapi_extra: Optional[dict[str, Any]] = None,
generate_unique_id_function: Union[
Callable[["APIRoute"], str], DefaultPlaceholder
] = Default(generate_unique_id),
@ -623,7 +621,7 @@ class APIRoute(routing.Route):
self.path_regex, self.path_format, self.param_convertors = compile_path(path)
if methods is None:
methods = ["GET"]
self.methods: Set[str] = {method.upper() for method in methods}
self.methods: set[str] = {method.upper() for method in methods}
if isinstance(generate_unique_id_function, DefaultPlaceholder):
current_generate_unique_id: Callable[[APIRoute], str] = (
generate_unique_id_function.value
@ -678,7 +676,7 @@ class APIRoute(routing.Route):
)
response_fields[additional_status_code] = response_field
if response_fields:
self.response_fields: Dict[Union[int, str], ModelField] = response_fields
self.response_fields: dict[Union[int, str], ModelField] = response_fields
else:
self.response_fields = {}
@ -719,7 +717,7 @@ class APIRoute(routing.Route):
embed_body_fields=self._embed_body_fields,
)
def matches(self, scope: Scope) -> Tuple[Match, Scope]:
def matches(self, scope: Scope) -> tuple[Match, Scope]:
match, child_scope = super().matches(scope)
if match != Match.NONE:
child_scope["route"] = self
@ -758,7 +756,7 @@ class APIRouter(routing.Router):
*,
prefix: Annotated[str, Doc("An optional path prefix for the router.")] = "",
tags: Annotated[
Optional[List[Union[str, Enum]]],
Optional[list[Union[str, Enum]]],
Doc(
"""
A list of tags to be applied to all the *path operations* in this
@ -784,7 +782,7 @@ class APIRouter(routing.Router):
),
] = None,
default_response_class: Annotated[
Type[Response],
type[Response],
Doc(
"""
The default response class to be used.
@ -795,7 +793,7 @@ class APIRouter(routing.Router):
),
] = Default(JSONResponse),
responses: Annotated[
Optional[Dict[Union[int, str], Dict[str, Any]]],
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses to be shown in OpenAPI.
@ -811,7 +809,7 @@ class APIRouter(routing.Router):
),
] = None,
callbacks: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
OpenAPI callbacks that should apply to all *path operations* in this
@ -825,7 +823,7 @@ class APIRouter(routing.Router):
),
] = None,
routes: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
**Note**: you probably shouldn't use this parameter, it is inherited
@ -876,7 +874,7 @@ class APIRouter(routing.Router):
),
] = None,
route_class: Annotated[
Type[APIRoute],
type[APIRoute],
Doc(
"""
Custom route (*path operation*) class to be used by this router.
@ -982,7 +980,7 @@ class APIRouter(routing.Router):
"A path prefix must not end with '/', as the routes will start with '/'"
)
self.prefix = prefix
self.tags: List[Union[str, Enum]] = tags or []
self.tags: list[Union[str, Enum]] = tags or []
self.dependencies = list(dependencies or [])
self.deprecated = deprecated
self.include_in_schema = include_in_schema
@ -1019,14 +1017,14 @@ class APIRouter(routing.Router):
*,
response_model: Any = Default(None),
status_code: Optional[int] = None,
tags: Optional[List[Union[str, Enum]]] = None,
tags: Optional[list[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
response_description: str = "Successful Response",
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
responses: Optional[dict[Union[int, str], dict[str, Any]]] = None,
deprecated: Optional[bool] = None,
methods: Optional[Union[Set[str], List[str]]] = None,
methods: Optional[Union[set[str], list[str]]] = None,
operation_id: Optional[str] = None,
response_model_include: Optional[IncEx] = None,
response_model_exclude: Optional[IncEx] = None,
@ -1035,13 +1033,13 @@ class APIRouter(routing.Router):
response_model_exclude_defaults: bool = False,
response_model_exclude_none: bool = False,
include_in_schema: bool = True,
response_class: Union[Type[Response], DefaultPlaceholder] = Default(
response_class: Union[type[Response], DefaultPlaceholder] = Default(
JSONResponse
),
name: Optional[str] = None,
route_class_override: Optional[Type[APIRoute]] = None,
callbacks: Optional[List[BaseRoute]] = None,
openapi_extra: Optional[Dict[str, Any]] = None,
route_class_override: Optional[type[APIRoute]] = None,
callbacks: Optional[list[BaseRoute]] = None,
openapi_extra: Optional[dict[str, Any]] = None,
generate_unique_id_function: Union[
Callable[[APIRoute], str], DefaultPlaceholder
] = Default(generate_unique_id),
@ -1100,14 +1098,14 @@ class APIRouter(routing.Router):
*,
response_model: Any = Default(None),
status_code: Optional[int] = None,
tags: Optional[List[Union[str, Enum]]] = None,
tags: Optional[list[Union[str, Enum]]] = None,
dependencies: Optional[Sequence[params.Depends]] = None,
summary: Optional[str] = None,
description: Optional[str] = None,
response_description: str = "Successful Response",
responses: Optional[Dict[Union[int, str], Dict[str, Any]]] = None,
responses: Optional[dict[Union[int, str], dict[str, Any]]] = None,
deprecated: Optional[bool] = None,
methods: Optional[List[str]] = None,
methods: Optional[list[str]] = None,
operation_id: Optional[str] = None,
response_model_include: Optional[IncEx] = None,
response_model_exclude: Optional[IncEx] = None,
@ -1116,10 +1114,10 @@ class APIRouter(routing.Router):
response_model_exclude_defaults: bool = False,
response_model_exclude_none: bool = False,
include_in_schema: bool = True,
response_class: Type[Response] = Default(JSONResponse),
response_class: type[Response] = Default(JSONResponse),
name: Optional[str] = None,
callbacks: Optional[List[BaseRoute]] = None,
openapi_extra: Optional[Dict[str, Any]] = None,
callbacks: Optional[list[BaseRoute]] = None,
openapi_extra: Optional[dict[str, Any]] = None,
generate_unique_id_function: Callable[[APIRoute], str] = Default(
generate_unique_id
),
@ -1259,7 +1257,7 @@ class APIRouter(routing.Router):
*,
prefix: Annotated[str, Doc("An optional path prefix for the router.")] = "",
tags: Annotated[
Optional[List[Union[str, Enum]]],
Optional[list[Union[str, Enum]]],
Doc(
"""
A list of tags to be applied to all the *path operations* in this
@ -1285,7 +1283,7 @@ class APIRouter(routing.Router):
),
] = None,
default_response_class: Annotated[
Type[Response],
type[Response],
Doc(
"""
The default response class to be used.
@ -1296,7 +1294,7 @@ class APIRouter(routing.Router):
),
] = Default(JSONResponse),
responses: Annotated[
Optional[Dict[Union[int, str], Dict[str, Any]]],
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses to be shown in OpenAPI.
@ -1312,7 +1310,7 @@ class APIRouter(routing.Router):
),
] = None,
callbacks: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
OpenAPI callbacks that should apply to all *path operations* in this
@ -1417,7 +1415,7 @@ class APIRouter(routing.Router):
current_tags.extend(tags)
if route.tags:
current_tags.extend(route.tags)
current_dependencies: List[params.Depends] = []
current_dependencies: list[params.Depends] = []
if dependencies:
current_dependencies.extend(dependencies)
if route.dependencies:
@ -1558,7 +1556,7 @@ class APIRouter(routing.Router):
),
] = None,
tags: Annotated[
Optional[List[Union[str, Enum]]],
Optional[list[Union[str, Enum]]],
Doc(
"""
A list of tags to be applied to the *path operation*.
@ -1624,7 +1622,7 @@ class APIRouter(routing.Router):
),
] = "Successful Response",
responses: Annotated[
Optional[Dict[Union[int, str], Dict[str, Any]]],
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses that could be returned by this *path operation*.
@ -1765,7 +1763,7 @@ class APIRouter(routing.Router):
),
] = True,
response_class: Annotated[
Type[Response],
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
@ -1786,7 +1784,7 @@ class APIRouter(routing.Router):
),
] = None,
callbacks: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
@ -1802,7 +1800,7 @@ class APIRouter(routing.Router):
),
] = None,
openapi_extra: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
@ -1935,7 +1933,7 @@ class APIRouter(routing.Router):
),
] = None,
tags: Annotated[
Optional[List[Union[str, Enum]]],
Optional[list[Union[str, Enum]]],
Doc(
"""
A list of tags to be applied to the *path operation*.
@ -2001,7 +1999,7 @@ class APIRouter(routing.Router):
),
] = "Successful Response",
responses: Annotated[
Optional[Dict[Union[int, str], Dict[str, Any]]],
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses that could be returned by this *path operation*.
@ -2142,7 +2140,7 @@ class APIRouter(routing.Router):
),
] = True,
response_class: Annotated[
Type[Response],
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
@ -2163,7 +2161,7 @@ class APIRouter(routing.Router):
),
] = None,
callbacks: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
@ -2179,7 +2177,7 @@ class APIRouter(routing.Router):
),
] = None,
openapi_extra: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
@ -2317,7 +2315,7 @@ class APIRouter(routing.Router):
),
] = None,
tags: Annotated[
Optional[List[Union[str, Enum]]],
Optional[list[Union[str, Enum]]],
Doc(
"""
A list of tags to be applied to the *path operation*.
@ -2383,7 +2381,7 @@ class APIRouter(routing.Router):
),
] = "Successful Response",
responses: Annotated[
Optional[Dict[Union[int, str], Dict[str, Any]]],
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses that could be returned by this *path operation*.
@ -2524,7 +2522,7 @@ class APIRouter(routing.Router):
),
] = True,
response_class: Annotated[
Type[Response],
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
@ -2545,7 +2543,7 @@ class APIRouter(routing.Router):
),
] = None,
callbacks: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
@ -2561,7 +2559,7 @@ class APIRouter(routing.Router):
),
] = None,
openapi_extra: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
@ -2699,7 +2697,7 @@ class APIRouter(routing.Router):
),
] = None,
tags: Annotated[
Optional[List[Union[str, Enum]]],
Optional[list[Union[str, Enum]]],
Doc(
"""
A list of tags to be applied to the *path operation*.
@ -2765,7 +2763,7 @@ class APIRouter(routing.Router):
),
] = "Successful Response",
responses: Annotated[
Optional[Dict[Union[int, str], Dict[str, Any]]],
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses that could be returned by this *path operation*.
@ -2906,7 +2904,7 @@ class APIRouter(routing.Router):
),
] = True,
response_class: Annotated[
Type[Response],
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
@ -2927,7 +2925,7 @@ class APIRouter(routing.Router):
),
] = None,
callbacks: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
@ -2943,7 +2941,7 @@ class APIRouter(routing.Router):
),
] = None,
openapi_extra: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
@ -3076,7 +3074,7 @@ class APIRouter(routing.Router):
),
] = None,
tags: Annotated[
Optional[List[Union[str, Enum]]],
Optional[list[Union[str, Enum]]],
Doc(
"""
A list of tags to be applied to the *path operation*.
@ -3142,7 +3140,7 @@ class APIRouter(routing.Router):
),
] = "Successful Response",
responses: Annotated[
Optional[Dict[Union[int, str], Dict[str, Any]]],
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses that could be returned by this *path operation*.
@ -3283,7 +3281,7 @@ class APIRouter(routing.Router):
),
] = True,
response_class: Annotated[
Type[Response],
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
@ -3304,7 +3302,7 @@ class APIRouter(routing.Router):
),
] = None,
callbacks: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
@ -3320,7 +3318,7 @@ class APIRouter(routing.Router):
),
] = None,
openapi_extra: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
@ -3453,7 +3451,7 @@ class APIRouter(routing.Router):
),
] = None,
tags: Annotated[
Optional[List[Union[str, Enum]]],
Optional[list[Union[str, Enum]]],
Doc(
"""
A list of tags to be applied to the *path operation*.
@ -3519,7 +3517,7 @@ class APIRouter(routing.Router):
),
] = "Successful Response",
responses: Annotated[
Optional[Dict[Union[int, str], Dict[str, Any]]],
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses that could be returned by this *path operation*.
@ -3660,7 +3658,7 @@ class APIRouter(routing.Router):
),
] = True,
response_class: Annotated[
Type[Response],
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
@ -3681,7 +3679,7 @@ class APIRouter(routing.Router):
),
] = None,
callbacks: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
@ -3697,7 +3695,7 @@ class APIRouter(routing.Router):
),
] = None,
openapi_extra: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
@ -3835,7 +3833,7 @@ class APIRouter(routing.Router):
),
] = None,
tags: Annotated[
Optional[List[Union[str, Enum]]],
Optional[list[Union[str, Enum]]],
Doc(
"""
A list of tags to be applied to the *path operation*.
@ -3901,7 +3899,7 @@ class APIRouter(routing.Router):
),
] = "Successful Response",
responses: Annotated[
Optional[Dict[Union[int, str], Dict[str, Any]]],
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses that could be returned by this *path operation*.
@ -4042,7 +4040,7 @@ class APIRouter(routing.Router):
),
] = True,
response_class: Annotated[
Type[Response],
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
@ -4063,7 +4061,7 @@ class APIRouter(routing.Router):
),
] = None,
callbacks: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
@ -4079,7 +4077,7 @@ class APIRouter(routing.Router):
),
] = None,
openapi_extra: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
@ -4217,7 +4215,7 @@ class APIRouter(routing.Router):
),
] = None,
tags: Annotated[
Optional[List[Union[str, Enum]]],
Optional[list[Union[str, Enum]]],
Doc(
"""
A list of tags to be applied to the *path operation*.
@ -4283,7 +4281,7 @@ class APIRouter(routing.Router):
),
] = "Successful Response",
responses: Annotated[
Optional[Dict[Union[int, str], Dict[str, Any]]],
Optional[dict[Union[int, str], dict[str, Any]]],
Doc(
"""
Additional responses that could be returned by this *path operation*.
@ -4424,7 +4422,7 @@ class APIRouter(routing.Router):
),
] = True,
response_class: Annotated[
Type[Response],
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
@ -4445,7 +4443,7 @@ class APIRouter(routing.Router):
),
] = None,
callbacks: Annotated[
Optional[List[BaseRoute]],
Optional[list[BaseRoute]],
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
@ -4461,7 +4459,7 @@ class APIRouter(routing.Router):
),
] = None,
openapi_extra: Annotated[
Optional[Dict[str, Any]],
Optional[dict[str, Any]],
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path

View File

@ -1,4 +1,4 @@
from typing import Optional, Union
from typing import Annotated, Optional, Union
from annotated_doc import Doc
from fastapi.openapi.models import APIKey, APIKeyIn
@ -6,7 +6,6 @@ from fastapi.security.base import SecurityBase
from starlette.exceptions import HTTPException
from starlette.requests import Request
from starlette.status import HTTP_401_UNAUTHORIZED
from typing_extensions import Annotated
class APIKeyBase(SecurityBase):

View File

@ -1,6 +1,6 @@
import binascii
from base64 import b64decode
from typing import Dict, Optional
from typing import Annotated, Optional
from annotated_doc import Doc
from fastapi.exceptions import HTTPException
@ -11,7 +11,6 @@ from fastapi.security.utils import get_authorization_scheme_param
from pydantic import BaseModel
from starlette.requests import Request
from starlette.status import HTTP_401_UNAUTHORIZED
from typing_extensions import Annotated
class HTTPBasicCredentials(BaseModel):
@ -82,7 +81,7 @@ class HTTPBase(SecurityBase):
self.scheme_name = scheme_name or self.__class__.__name__
self.auto_error = auto_error
def make_authenticate_headers(self) -> Dict[str, str]:
def make_authenticate_headers(self) -> dict[str, str]:
return {"WWW-Authenticate": f"{self.model.scheme.title()}"}
def make_not_authenticated_error(self) -> HTTPException:
@ -197,7 +196,7 @@ class HTTPBasic(HTTPBase):
self.realm = realm
self.auto_error = auto_error
def make_authenticate_headers(self) -> Dict[str, str]:
def make_authenticate_headers(self) -> dict[str, str]:
if self.realm:
return {"WWW-Authenticate": f'Basic realm="{self.realm}"'}
return {"WWW-Authenticate": "Basic"}

View File

@ -1,4 +1,4 @@
from typing import Any, Dict, List, Optional, Union, cast
from typing import Annotated, Any, Optional, Union, cast
from annotated_doc import Doc
from fastapi.exceptions import HTTPException
@ -10,9 +10,6 @@ from fastapi.security.utils import get_authorization_scheme_param
from starlette.requests import Request
from starlette.status import HTTP_401_UNAUTHORIZED
# TODO: import from typing when deprecating Python 3.9
from typing_extensions import Annotated
class OAuth2PasswordRequestForm:
"""
@ -323,7 +320,7 @@ class OAuth2(SecurityBase):
self,
*,
flows: Annotated[
Union[OAuthFlowsModel, Dict[str, Dict[str, Any]]],
Union[OAuthFlowsModel, dict[str, dict[str, Any]]],
Doc(
"""
The dictionary of OAuth2 flows.
@ -440,7 +437,7 @@ class OAuth2PasswordBearer(OAuth2):
),
] = None,
scopes: Annotated[
Optional[Dict[str, str]],
Optional[dict[str, str]],
Doc(
"""
The OAuth2 scopes that would be required by the *path operations* that
@ -553,7 +550,7 @@ class OAuth2AuthorizationCodeBearer(OAuth2):
),
] = None,
scopes: Annotated[
Optional[Dict[str, str]],
Optional[dict[str, str]],
Doc(
"""
The OAuth2 scopes that would be required by the *path operations* that
@ -639,7 +636,7 @@ class SecurityScopes:
def __init__(
self,
scopes: Annotated[
Optional[List[str]],
Optional[list[str]],
Doc(
"""
This will be filled by FastAPI.
@ -648,7 +645,7 @@ class SecurityScopes:
] = None,
):
self.scopes: Annotated[
List[str],
list[str],
Doc(
"""
The list of all the scopes required by dependencies.

View File

@ -1,4 +1,4 @@
from typing import Optional
from typing import Annotated, Optional
from annotated_doc import Doc
from fastapi.openapi.models import OpenIdConnect as OpenIdConnectModel
@ -6,7 +6,6 @@ from fastapi.security.base import SecurityBase
from starlette.exceptions import HTTPException
from starlette.requests import Request
from starlette.status import HTTP_401_UNAUTHORIZED
from typing_extensions import Annotated
class OpenIdConnect(SecurityBase):

View File

@ -1,9 +1,9 @@
from typing import Optional, Tuple
from typing import Optional
def get_authorization_scheme_param(
authorization_header_value: Optional[str],
) -> Tuple[str, str]:
) -> tuple[str, str]:
if not authorization_header_value:
return "", ""
scheme, _, param = authorization_header_value.partition(" ")

View File

@ -1,9 +1,9 @@
import warnings
from typing import Any, Callable, Dict, List, Optional, Union
from typing import Annotated, Any, Callable, Optional, Union
from fastapi.openapi.models import Example
from fastapi.params import ParamTypes
from typing_extensions import Annotated, deprecated
from typing_extensions import deprecated
from ._compat.may_v1 import FieldInfo, Undefined
from ._compat.shared import PYDANTIC_VERSION_MINOR_TUPLE
@ -47,7 +47,7 @@ class Param(FieldInfo): # type: ignore[misc]
allow_inf_nan: Union[bool, None] = _Unset,
max_digits: Union[int, None] = _Unset,
decimal_places: Union[int, None] = _Unset,
examples: Optional[List[Any]] = None,
examples: Optional[list[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
@ -55,10 +55,10 @@ class Param(FieldInfo): # type: ignore[misc]
"although still supported. Use examples instead."
),
] = _Unset,
openapi_examples: Optional[Dict[str, Example]] = None,
openapi_examples: Optional[dict[str, Example]] = None,
deprecated: Union[deprecated, str, bool, None] = None,
include_in_schema: bool = True,
json_schema_extra: Union[Dict[str, Any], None] = None,
json_schema_extra: Union[dict[str, Any], None] = None,
**extra: Any,
):
if example is not _Unset:
@ -148,7 +148,7 @@ class Path(Param): # type: ignore[misc]
allow_inf_nan: Union[bool, None] = _Unset,
max_digits: Union[int, None] = _Unset,
decimal_places: Union[int, None] = _Unset,
examples: Optional[List[Any]] = None,
examples: Optional[list[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
@ -156,10 +156,10 @@ class Path(Param): # type: ignore[misc]
"although still supported. Use examples instead."
),
] = _Unset,
openapi_examples: Optional[Dict[str, Example]] = None,
openapi_examples: Optional[dict[str, Example]] = None,
deprecated: Union[deprecated, str, bool, None] = None,
include_in_schema: bool = True,
json_schema_extra: Union[Dict[str, Any], None] = None,
json_schema_extra: Union[dict[str, Any], None] = None,
**extra: Any,
):
assert default is ..., "Path parameters cannot have a default value"
@ -234,7 +234,7 @@ class Query(Param): # type: ignore[misc]
allow_inf_nan: Union[bool, None] = _Unset,
max_digits: Union[int, None] = _Unset,
decimal_places: Union[int, None] = _Unset,
examples: Optional[List[Any]] = None,
examples: Optional[list[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
@ -242,10 +242,10 @@ class Query(Param): # type: ignore[misc]
"although still supported. Use examples instead."
),
] = _Unset,
openapi_examples: Optional[Dict[str, Example]] = None,
openapi_examples: Optional[dict[str, Example]] = None,
deprecated: Union[deprecated, str, bool, None] = None,
include_in_schema: bool = True,
json_schema_extra: Union[Dict[str, Any], None] = None,
json_schema_extra: Union[dict[str, Any], None] = None,
**extra: Any,
):
super().__init__(
@ -319,7 +319,7 @@ class Header(Param): # type: ignore[misc]
allow_inf_nan: Union[bool, None] = _Unset,
max_digits: Union[int, None] = _Unset,
decimal_places: Union[int, None] = _Unset,
examples: Optional[List[Any]] = None,
examples: Optional[list[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
@ -327,10 +327,10 @@ class Header(Param): # type: ignore[misc]
"although still supported. Use examples instead."
),
] = _Unset,
openapi_examples: Optional[Dict[str, Example]] = None,
openapi_examples: Optional[dict[str, Example]] = None,
deprecated: Union[deprecated, str, bool, None] = None,
include_in_schema: bool = True,
json_schema_extra: Union[Dict[str, Any], None] = None,
json_schema_extra: Union[dict[str, Any], None] = None,
**extra: Any,
):
self.convert_underscores = convert_underscores
@ -404,7 +404,7 @@ class Cookie(Param): # type: ignore[misc]
allow_inf_nan: Union[bool, None] = _Unset,
max_digits: Union[int, None] = _Unset,
decimal_places: Union[int, None] = _Unset,
examples: Optional[List[Any]] = None,
examples: Optional[list[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
@ -412,10 +412,10 @@ class Cookie(Param): # type: ignore[misc]
"although still supported. Use examples instead."
),
] = _Unset,
openapi_examples: Optional[Dict[str, Example]] = None,
openapi_examples: Optional[dict[str, Example]] = None,
deprecated: Union[deprecated, str, bool, None] = None,
include_in_schema: bool = True,
json_schema_extra: Union[Dict[str, Any], None] = None,
json_schema_extra: Union[dict[str, Any], None] = None,
**extra: Any,
):
super().__init__(
@ -488,7 +488,7 @@ class Body(FieldInfo): # type: ignore[misc]
allow_inf_nan: Union[bool, None] = _Unset,
max_digits: Union[int, None] = _Unset,
decimal_places: Union[int, None] = _Unset,
examples: Optional[List[Any]] = None,
examples: Optional[list[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
@ -496,10 +496,10 @@ class Body(FieldInfo): # type: ignore[misc]
"although still supported. Use examples instead."
),
] = _Unset,
openapi_examples: Optional[Dict[str, Example]] = None,
openapi_examples: Optional[dict[str, Example]] = None,
deprecated: Union[deprecated, str, bool, None] = None,
include_in_schema: bool = True,
json_schema_extra: Union[Dict[str, Any], None] = None,
json_schema_extra: Union[dict[str, Any], None] = None,
**extra: Any,
):
self.embed = embed
@ -591,7 +591,7 @@ class Form(Body): # type: ignore[misc]
allow_inf_nan: Union[bool, None] = _Unset,
max_digits: Union[int, None] = _Unset,
decimal_places: Union[int, None] = _Unset,
examples: Optional[List[Any]] = None,
examples: Optional[list[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
@ -599,10 +599,10 @@ class Form(Body): # type: ignore[misc]
"although still supported. Use examples instead."
),
] = _Unset,
openapi_examples: Optional[Dict[str, Example]] = None,
openapi_examples: Optional[dict[str, Example]] = None,
deprecated: Union[deprecated, str, bool, None] = None,
include_in_schema: bool = True,
json_schema_extra: Union[Dict[str, Any], None] = None,
json_schema_extra: Union[dict[str, Any], None] = None,
**extra: Any,
):
super().__init__(
@ -675,7 +675,7 @@ class File(Form): # type: ignore[misc]
allow_inf_nan: Union[bool, None] = _Unset,
max_digits: Union[int, None] = _Unset,
decimal_places: Union[int, None] = _Unset,
examples: Optional[List[Any]] = None,
examples: Optional[list[Any]] = None,
example: Annotated[
Optional[Any],
deprecated(
@ -683,10 +683,10 @@ class File(Form): # type: ignore[misc]
"although still supported. Use examples instead."
),
] = _Unset,
openapi_examples: Optional[Dict[str, Example]] = None,
openapi_examples: Optional[dict[str, Example]] = None,
deprecated: Union[deprecated, str, bool, None] = None,
include_in_schema: bool = True,
json_schema_extra: Union[Dict[str, Any], None] = None,
json_schema_extra: Union[dict[str, Any], None] = None,
**extra: Any,
):
super().__init__(

View File

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

View File

@ -1,14 +1,11 @@
import re
import warnings
from collections.abc import MutableMapping
from dataclasses import is_dataclass
from typing import (
TYPE_CHECKING,
Any,
Dict,
MutableMapping,
Optional,
Set,
Type,
Union,
cast,
)
@ -36,7 +33,7 @@ if TYPE_CHECKING: # pragma: nocover
from .routing import APIRoute
# Cache for `create_cloned_field`
_CLONED_TYPES_CACHE: MutableMapping[Type[BaseModel], Type[BaseModel]] = (
_CLONED_TYPES_CACHE: MutableMapping[type[BaseModel], type[BaseModel]] = (
WeakKeyDictionary()
)
@ -58,7 +55,7 @@ def is_body_allowed_for_status_code(status_code: Union[int, str, None]) -> bool:
return not (current_status_code < 200 or current_status_code in {204, 205, 304})
def get_path_param_names(path: str) -> Set[str]:
def get_path_param_names(path: str) -> set[str]:
return set(re.findall("{(.*?)}", path))
@ -76,10 +73,10 @@ _invalid_args_message = (
def create_model_field(
name: str,
type_: Any,
class_validators: Optional[Dict[str, Validator]] = None,
class_validators: Optional[dict[str, Validator]] = None,
default: Optional[Any] = Undefined,
required: Union[bool, UndefinedType] = Undefined,
model_config: Union[Type[BaseConfig], None] = None,
model_config: Union[type[BaseConfig], None] = None,
field_info: Optional[FieldInfo] = None,
alias: Optional[str] = None,
mode: Literal["validation", "serialization"] = "validation",
@ -141,7 +138,7 @@ def create_model_field(
def create_cloned_field(
field: ModelField,
*,
cloned_types: Optional[MutableMapping[Type[BaseModel], Type[BaseModel]]] = None,
cloned_types: Optional[MutableMapping[type[BaseModel], type[BaseModel]]] = None,
) -> ModelField:
if PYDANTIC_V2:
from ._compat import v2
@ -161,7 +158,7 @@ def create_cloned_field(
original_type = original_type.__pydantic_model__
use_type = original_type
if lenient_issubclass(original_type, v1.BaseModel):
original_type = cast(Type[v1.BaseModel], original_type)
original_type = cast(type[v1.BaseModel], original_type)
use_type = cloned_types.get(original_type)
if use_type is None:
use_type = v1.create_model(original_type.__name__, __base__=original_type)
@ -224,7 +221,7 @@ def generate_unique_id(route: "APIRoute") -> str:
return operation_id
def deep_dict_update(main_dict: Dict[Any, Any], update_dict: Dict[Any, Any]) -> None:
def deep_dict_update(main_dict: dict[Any, Any], update_dict: dict[Any, Any]) -> None:
for key, value in update_dict.items():
if (
key in main_dict

View File

@ -1,5 +1,5 @@
import os
from typing import Any, Dict
from typing import Any
from pdm.backend.hooks import Context
@ -9,12 +9,12 @@ TIANGOLO_BUILD_PACKAGE = os.getenv("TIANGOLO_BUILD_PACKAGE", "fastapi")
def pdm_build_initialize(context: Context) -> None:
metadata = context.config.metadata
# Get custom config for the current package, from the env var
config: Dict[str, Any] = context.config.data["tool"]["tiangolo"][
config: dict[str, Any] = context.config.data["tool"]["tiangolo"][
"_internal-slim-build"
]["packages"].get(TIANGOLO_BUILD_PACKAGE)
if not config:
return
project_config: Dict[str, Any] = config["project"]
project_config: dict[str, Any] = config["project"]
# Override main [project] configs with custom configs for this package
for key, value in project_config.items():
metadata[key] = value

View File

@ -8,7 +8,7 @@ from html.parser import HTMLParser
from http.server import HTTPServer, SimpleHTTPRequestHandler
from multiprocessing import Pool
from pathlib import Path
from typing import Any, Dict, List, Optional, Union
from typing import Any, Optional, Union
import mkdocs.utils
import typer
@ -82,11 +82,11 @@ def slugify(text: str) -> str:
)
def get_en_config() -> Dict[str, Any]:
def get_en_config() -> dict[str, Any]:
return mkdocs.utils.yaml_load(en_config_path.read_text(encoding="utf-8"))
def get_lang_paths() -> List[Path]:
def get_lang_paths() -> list[Path]:
return sorted(docs_path.iterdir())
@ -334,14 +334,14 @@ def live(
)
def get_updated_config_content() -> Dict[str, Any]:
def get_updated_config_content() -> dict[str, Any]:
config = get_en_config()
languages = [{"en": "/"}]
new_alternate: List[Dict[str, str]] = []
new_alternate: list[dict[str, str]] = []
# Language names sourced from https://quickref.me/iso-639-1
# Contributors may wish to update or change these, e.g. to fix capitalization.
language_names_path = Path(__file__).parent / "../docs/language_names.yml"
local_language_names: Dict[str, str] = mkdocs.utils.yaml_load(
local_language_names: dict[str, str] = mkdocs.utils.yaml_load(
language_names_path.read_text(encoding="utf-8")
)
for lang_path in get_lang_paths():
@ -530,7 +530,7 @@ def add_permalinks_page(path: Path, update_existing: bool = False):
@app.command()
def add_permalinks_pages(pages: List[Path], update_existing: bool = False) -> None:
def add_permalinks_pages(pages: list[Path], update_existing: bool = False) -> None:
"""
Add or update header permalinks in specific pages of En docs.
"""

View File

@ -1,6 +1,6 @@
from functools import lru_cache
from pathlib import Path
from typing import Any, List, Union
from typing import Any, Union
import material
from mkdocs.config.defaults import MkDocsConfig
@ -27,7 +27,7 @@ def get_missing_translation_content(docs_dir: str) -> str:
@lru_cache
def get_mkdocs_material_langs() -> List[str]:
def get_mkdocs_material_langs() -> list[str]:
material_path = Path(material.__file__).parent
material_langs_path = material_path / "templates" / "partials" / "languages"
langs = [file.stem for file in material_langs_path.glob("*.html")]
@ -65,7 +65,7 @@ def resolve_file(*, item: str, files: Files, config: MkDocsConfig) -> None:
)
def resolve_files(*, items: List[Any], files: Files, config: MkDocsConfig) -> None:
def resolve_files(*, items: list[Any], files: Files, config: MkDocsConfig) -> None:
for item in items:
if isinstance(item, str):
resolve_file(item=item, files=files, config=config)
@ -94,9 +94,9 @@ def on_files(files: Files, *, config: MkDocsConfig) -> Files:
def generate_renamed_section_items(
items: List[Union[Page, Section, Link]], *, config: MkDocsConfig
) -> List[Union[Page, Section, Link]]:
new_items: List[Union[Page, Section, Link]] = []
items: list[Union[Page, Section, Link]], *, config: MkDocsConfig
) -> list[Union[Page, Section, Link]]:
new_items: list[Union[Page, Section, Link]] = []
for item in items:
if isinstance(item, Section):
new_title = item.title

View File

@ -3,7 +3,7 @@ import random
import sys
import time
from pathlib import Path
from typing import Any, Dict, List, Union, cast
from typing import Any, Union, cast
import httpx
from github import Github
@ -120,7 +120,7 @@ class CommentsEdge(BaseModel):
class Comments(BaseModel):
edges: List[CommentsEdge]
edges: list[CommentsEdge]
class CommentsDiscussion(BaseModel):
@ -149,7 +149,7 @@ class AllDiscussionsLabelsEdge(BaseModel):
class AllDiscussionsDiscussionLabels(BaseModel):
edges: List[AllDiscussionsLabelsEdge]
edges: list[AllDiscussionsLabelsEdge]
class AllDiscussionsDiscussionNode(BaseModel):
@ -160,7 +160,7 @@ class AllDiscussionsDiscussionNode(BaseModel):
class AllDiscussionsDiscussions(BaseModel):
nodes: List[AllDiscussionsDiscussionNode]
nodes: list[AllDiscussionsDiscussionNode]
class AllDiscussionsRepository(BaseModel):
@ -205,7 +205,7 @@ def get_graphql_response(
discussion_id: Union[str, None] = None,
comment_id: Union[str, None] = None,
body: Union[str, None] = None,
) -> Dict[str, Any]:
) -> dict[str, Any]:
headers = {"Authorization": f"token {settings.github_token.get_secret_value()}"}
variables = {
"after": after,
@ -233,12 +233,12 @@ def get_graphql_response(
logging.error(data["errors"])
logging.error(response.text)
raise RuntimeError(response.text)
return cast(Dict[str, Any], data)
return cast(dict[str, Any], data)
def get_graphql_translation_discussions(
*, settings: Settings
) -> List[AllDiscussionsDiscussionNode]:
) -> list[AllDiscussionsDiscussionNode]:
data = get_graphql_response(
settings=settings,
query=all_discussions_query,
@ -250,7 +250,7 @@ def get_graphql_translation_discussions(
def get_graphql_translation_discussion_comments_edges(
*, settings: Settings, discussion_number: int, after: Union[str, None] = None
) -> List[CommentsEdge]:
) -> list[CommentsEdge]:
data = get_graphql_response(
settings=settings,
query=translation_discussion_query,
@ -264,7 +264,7 @@ def get_graphql_translation_discussion_comments_edges(
def get_graphql_translation_discussion_comments(
*, settings: Settings, discussion_number: int
) -> list[Comment]:
comment_nodes: List[Comment] = []
comment_nodes: list[Comment] = []
discussion_edges = get_graphql_translation_discussion_comments_edges(
settings=settings, discussion_number=discussion_number
)
@ -348,7 +348,7 @@ def main() -> None:
# Generate translation map, lang ID to discussion
discussions = get_graphql_translation_discussions(settings=settings)
lang_to_discussion_map: Dict[str, AllDiscussionsDiscussionNode] = {}
lang_to_discussion_map: dict[str, AllDiscussionsDiscussionNode] = {}
for discussion in discussions:
for edge in discussion.labels.edges:
label = edge.node.name

View File

@ -3,9 +3,10 @@ import secrets
import subprocess
import time
from collections import Counter
from collections.abc import Container
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Any, Container, Union
from typing import Any, Union
import httpx
import yaml

View File

@ -1,5 +1,5 @@
import http
from typing import FrozenSet, List, Optional
from typing import Optional
from fastapi import FastAPI, Path, Query
@ -195,15 +195,15 @@ def get_enum_status_code():
@app.get("/query/frozenset")
def get_query_type_frozenset(query: FrozenSet[int] = Query(...)):
def get_query_type_frozenset(query: frozenset[int] = Query(...)):
return ",".join(map(str, sorted(query)))
@app.get("/query/list")
def get_query_list(device_ids: List[int] = Query()) -> List[int]:
def get_query_list(device_ids: list[int] = Query()) -> list[int]:
return device_ids
@app.get("/query/list-default")
def get_query_list_default(device_ids: List[int] = Query(default=[])) -> List[int]:
def get_query_list_default(device_ids: list[int] = Query(default=[])) -> list[int]:
return device_ids

View File

@ -1,5 +1,3 @@
from typing import Dict
from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel
@ -8,7 +6,7 @@ app = FastAPI()
class Items(BaseModel):
items: Dict[str, int]
items: dict[str, int]
@app.post("/foo")

View File

@ -1,5 +1,3 @@
import typing
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from fastapi.testclient import TestClient
@ -18,7 +16,7 @@ class Error(BaseModel):
class JsonApiError(BaseModel):
errors: typing.List[Error]
errors: list[Error]
@app.get(

View File

@ -1,5 +1,3 @@
import typing
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from fastapi.testclient import TestClient
@ -18,7 +16,7 @@ class Error(BaseModel):
class JsonApiError(BaseModel):
errors: typing.List[Error]
errors: list[Error]
@app.get(

View File

@ -1,7 +1,8 @@
from typing import Annotated
import pytest
from fastapi import Body, FastAPI, Query
from fastapi.testclient import TestClient
from typing_extensions import Annotated
app = FastAPI()

View File

@ -1,9 +1,10 @@
from typing import Annotated
import pytest
from fastapi import Depends, FastAPI, Path
from fastapi.param_functions import Query
from fastapi.testclient import TestClient
from fastapi.utils import PYDANTIC_V2
from typing_extensions import Annotated
app = FastAPI()

View File

@ -1,8 +1,9 @@
from typing import Annotated
import pytest
from dirty_equals import IsDict
from fastapi import APIRouter, FastAPI, Query
from fastapi.testclient import TestClient
from typing_extensions import Annotated
app = FastAPI()

View File

@ -1,10 +1,9 @@
from typing import List
from typing import Annotated
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
from typing_extensions import Annotated
from .utils import needs_pydanticv2
@ -25,7 +24,7 @@ def get_client():
FakeNumpyArrayPydantic = Annotated[
FakeNumpyArray,
WithJsonSchema(TypeAdapter(List[float]).json_schema()),
WithJsonSchema(TypeAdapter(list[float]).json_schema()),
PlainSerializer(lambda v: v.data),
]
@ -66,7 +65,7 @@ def test_typeadapter():
FakeNumpyArrayPydantic = Annotated[
FakeNumpyArray,
WithJsonSchema(TypeAdapter(List[float]).json_schema()),
WithJsonSchema(TypeAdapter(list[float]).json_schema()),
PlainSerializer(lambda v: v.data),
]

View File

@ -1,4 +1,4 @@
from typing import Any, Dict, List, Union
from typing import Any, Union
from fastapi import FastAPI, UploadFile
from fastapi._compat import (
@ -61,7 +61,7 @@ def test_complex():
app = FastAPI()
@app.post("/")
def foo(foo: Union[str, List[int]]):
def foo(foo: Union[str, list[int]]):
return foo
client = TestClient(app)
@ -95,7 +95,7 @@ def test_propagates_pydantic2_model_config():
embedded_model: EmbeddedModel = EmbeddedModel()
@app.post("/")
def foo(req: Model) -> Dict[str, Union[str, None]]:
def foo(req: Model) -> dict[str, Union[str, None]]:
return {
"value": req.value or None,
"embedded_value": req.embedded_model.value or None,
@ -125,7 +125,7 @@ def test_is_bytes_sequence_annotation_union():
# TODO: in theory this would allow declaring types that could be lists of bytes
# to be read from files and other types, but I'm not even sure it's a good idea
# to support it as a first class "feature"
assert is_bytes_sequence_annotation(Union[List[str], List[bytes]])
assert is_bytes_sequence_annotation(Union[list[str], list[bytes]])
def test_is_uploadfile_sequence_annotation():
@ -133,7 +133,7 @@ def test_is_uploadfile_sequence_annotation():
# TODO: in theory this would allow declaring types that could be lists of UploadFile
# and other types, but I'm not even sure it's a good idea to support it as a first
# class "feature"
assert is_uploadfile_sequence_annotation(Union[List[str], List[UploadFile]])
assert is_uploadfile_sequence_annotation(Union[list[str], list[UploadFile]])
@needs_pydanticv2
@ -141,7 +141,7 @@ def test_serialize_sequence_value_with_optional_list():
"""Test that serialize_sequence_value handles optional lists correctly."""
from fastapi._compat import v2
field_info = FieldInfo(annotation=Union[List[str], None])
field_info = FieldInfo(annotation=Union[list[str], None])
field = v2.ModelField(name="items", field_info=field_info)
result = v2.serialize_sequence_value(field=field, value=["a", "b", "c"])
assert result == ["a", "b", "c"]
@ -166,7 +166,7 @@ def test_serialize_sequence_value_with_none_first_in_union():
"""Test that serialize_sequence_value handles Union[None, List[...]] correctly."""
from fastapi._compat import v2
field_info = FieldInfo(annotation=Union[None, List[str]])
field_info = FieldInfo(annotation=Union[None, list[str]])
field = v2.ModelField(name="items", field_info=field_info)
result = v2.serialize_sequence_value(field=field, value=["x", "y"])
assert result == ["x", "y"]
@ -179,7 +179,7 @@ def test_is_pv1_scalar_field():
# For coverage
class Model(v1.BaseModel):
foo: Union[str, Dict[str, Any]]
foo: Union[str, dict[str, Any]]
fields = v1.get_model_fields(Model)
assert not is_scalar_field(fields[0])

View File

@ -1,5 +1,5 @@
import sys
from typing import List, Optional
from typing import Optional
import pytest
@ -8,6 +8,8 @@ from tests.utils import pydantic_snapshot, skip_module_if_py_gte_314
if sys.version_info >= (3, 14):
skip_module_if_py_gte_314()
from typing import Annotated
from fastapi import FastAPI
from fastapi._compat.v1 import BaseModel
from fastapi.temp_pydantic_v1_params import (
@ -21,7 +23,6 @@ from fastapi.temp_pydantic_v1_params import (
)
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
from typing_extensions import Annotated
class Item(BaseModel):
@ -112,7 +113,7 @@ def upload_file(
@app.post("/upload-multiple/")
def upload_multiple_files(
files: Annotated[List[bytes], File()],
files: Annotated[list[bytes], File()],
note: Annotated[str, Form()] = "",
):
return {

View File

@ -1,10 +1,9 @@
from typing import Optional
from typing import Annotated, Optional
from fastapi import FastAPI
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel
from typing_extensions import Annotated
if PYDANTIC_V2:
from pydantic import WithJsonSchema

View File

@ -1,6 +1,5 @@
import io
from pathlib import Path
from typing import List
import pytest
from fastapi import FastAPI, UploadFile
@ -38,7 +37,7 @@ def test_upload_file_is_closed(tmp_path: Path):
path.write_bytes(b"<file content>")
app = FastAPI()
testing_file_store: List[UploadFile] = []
testing_file_store: list[UploadFile] = []
@app.post("/uploadfile/")
def create_upload_file(file: UploadFile):

View File

@ -1,9 +1,8 @@
from typing import Any
from typing import Annotated, Any
import pytest
from fastapi import Depends, FastAPI, HTTPException
from fastapi.testclient import TestClient
from typing_extensions import Annotated
class CustomError(Exception):

View File

@ -1,11 +1,11 @@
from collections.abc import Generator
from contextlib import contextmanager
from typing import Any, Generator
from typing import Annotated, Any
import pytest
from fastapi import Depends, FastAPI
from fastapi.responses import StreamingResponse
from fastapi.testclient import TestClient
from typing_extensions import Annotated
class Session:

View File

@ -1,10 +1,10 @@
from collections.abc import Generator
from contextlib import contextmanager
from typing import Any, Generator
from typing import Annotated, Any
import pytest
from fastapi import Depends, FastAPI, WebSocket
from fastapi.testclient import TestClient
from typing_extensions import Annotated
class Session:

View File

@ -1,4 +1,4 @@
from typing import AsyncGenerator, Generator
from collections.abc import AsyncGenerator, Generator
import pytest
from fastapi import Depends, FastAPI

View File

@ -1,5 +1,4 @@
import json
from typing import Dict
import pytest
from fastapi import BackgroundTasks, Depends, FastAPI
@ -37,19 +36,19 @@ class OtherDependencyError(Exception):
pass
async def asyncgen_state(state: Dict[str, str] = Depends(get_state)):
async def asyncgen_state(state: dict[str, str] = Depends(get_state)):
state["/async"] = "asyncgen started"
yield state["/async"]
state["/async"] = "asyncgen completed"
def generator_state(state: Dict[str, str] = Depends(get_state)):
def generator_state(state: dict[str, str] = Depends(get_state)):
state["/sync"] = "generator started"
yield state["/sync"]
state["/sync"] = "generator completed"
async def asyncgen_state_try(state: Dict[str, str] = Depends(get_state)):
async def asyncgen_state_try(state: dict[str, str] = Depends(get_state)):
state["/async_raise"] = "asyncgen raise started"
try:
yield state["/async_raise"]
@ -60,7 +59,7 @@ async def asyncgen_state_try(state: Dict[str, str] = Depends(get_state)):
state["/async_raise"] = "asyncgen raise finalized"
def generator_state_try(state: Dict[str, str] = Depends(get_state)):
def generator_state_try(state: dict[str, str] = Depends(get_state)):
state["/sync_raise"] = "generator raise started"
try:
yield state["/sync_raise"]

View File

@ -1,10 +1,11 @@
from collections.abc import Awaitable
from contextvars import ContextVar
from typing import Any, Awaitable, Callable, Dict, Optional
from typing import Any, Callable, Optional
from fastapi import Depends, FastAPI, Request, Response
from fastapi.testclient import TestClient
legacy_request_state_context_var: ContextVar[Optional[Dict[str, Any]]] = ContextVar(
legacy_request_state_context_var: ContextVar[Optional[dict[str, Any]]] = ContextVar(
"legacy_request_state_context_var", default=None
)

View File

@ -1,5 +1,3 @@
from typing import List
from dirty_equals import IsDict
from fastapi import Depends, FastAPI
from fastapi.testclient import TestClient
@ -40,7 +38,7 @@ async def no_duplicates(item: Item, item2: Item = Depends(dependency)):
@app.post("/with-duplicates-sub")
async def no_duplicates_sub(
item: Item, sub_items: List[Item] = Depends(sub_duplicate_dependency)
item: Item, sub_items: list[Item] = Depends(sub_duplicate_dependency)
):
return [item, sub_items]

View File

@ -1,4 +1,4 @@
from typing import Union
from typing import Annotated, Union
from fastapi import FastAPI, HTTPException, Security
from fastapi.security import (
@ -6,7 +6,6 @@ from fastapi.security import (
SecurityScopes,
)
from fastapi.testclient import TestClient
from typing_extensions import Annotated
app = FastAPI()

View File

@ -1,10 +1,10 @@
from collections.abc import AsyncGenerator, Generator
from functools import partial
from typing import AsyncGenerator, Generator
from typing import Annotated
import pytest
from fastapi import Depends, FastAPI
from fastapi.testclient import TestClient
from typing_extensions import Annotated
app = FastAPI()

View File

@ -1,5 +1,3 @@
from typing import List, Tuple
from fastapi import Depends, FastAPI, Security
from fastapi.security import SecurityScopes
from fastapi.testclient import TestClient
@ -25,8 +23,8 @@ def get_data_override():
@app.get("/user")
def read_user(
user_data: Tuple[str, List[str]] = Security(get_user, scopes=["foo", "bar"]),
data: List[int] = Depends(get_data),
user_data: tuple[str, list[str]] = Security(get_user, scopes=["foo", "bar"]),
data: list[int] = Depends(get_data),
):
return {"user": user_data[0], "scopes": user_data[1], "data": data}

View File

@ -1,7 +1,7 @@
import inspect
import sys
from collections.abc import AsyncGenerator, Generator
from functools import wraps
from typing import AsyncGenerator, Generator
import pytest
from fastapi import Depends, FastAPI

View File

@ -1,12 +1,11 @@
import json
from typing import Any, Tuple
from typing import Annotated, Any
import pytest
from fastapi import APIRouter, Depends, FastAPI, HTTPException
from fastapi.exceptions import FastAPIError
from fastapi.responses import StreamingResponse
from fastapi.testclient import TestClient
from typing_extensions import Annotated
class Session:
@ -43,7 +42,7 @@ def get_named_session(session: SessionRequestDep, session_b: SessionDefaultDep)
named_session.open = False
NamedSessionsDep = Annotated[Tuple[NamedSession, Session], Depends(get_named_session)]
NamedSessionsDep = Annotated[tuple[NamedSession, Session], Depends(get_named_session)]
def get_named_func_session(session: SessionFuncDep) -> Any:
@ -58,14 +57,14 @@ def get_named_regular_func_session(session: SessionFuncDep) -> Any:
BrokenSessionsDep = Annotated[
Tuple[NamedSession, Session], Depends(get_named_func_session)
tuple[NamedSession, Session], Depends(get_named_func_session)
]
NamedSessionsFuncDep = Annotated[
Tuple[NamedSession, Session], Depends(get_named_func_session, scope="function")
tuple[NamedSession, Session], Depends(get_named_func_session, scope="function")
]
RegularSessionsDep = Annotated[
Tuple[NamedSession, Session], Depends(get_named_regular_func_session)
tuple[NamedSession, Session], Depends(get_named_regular_func_session)
]
app = FastAPI()

View File

@ -1,13 +1,12 @@
from contextvars import ContextVar
from typing import Any, Dict, Tuple
from typing import Annotated, Any
import pytest
from fastapi import Depends, FastAPI, WebSocket
from fastapi.exceptions import FastAPIError
from fastapi.testclient import TestClient
from typing_extensions import Annotated
global_context: ContextVar[Dict[str, Any]] = ContextVar("global_context", default={}) # noqa: B039
global_context: ContextVar[dict[str, Any]] = ContextVar("global_context", default={}) # noqa: B039
class Session:
@ -43,7 +42,7 @@ def get_named_session(session: SessionRequestDep, session_b: SessionDefaultDep)
global_state["named_session_closed"] = True
NamedSessionsDep = Annotated[Tuple[NamedSession, Session], Depends(get_named_session)]
NamedSessionsDep = Annotated[tuple[NamedSession, Session], Depends(get_named_session)]
def get_named_func_session(session: SessionFuncDep) -> Any:
@ -60,14 +59,14 @@ def get_named_regular_func_session(session: SessionFuncDep) -> Any:
BrokenSessionsDep = Annotated[
Tuple[NamedSession, Session], Depends(get_named_func_session)
tuple[NamedSession, Session], Depends(get_named_func_session)
]
NamedSessionsFuncDep = Annotated[
Tuple[NamedSession, Session], Depends(get_named_func_session, scope="function")
tuple[NamedSession, Session], Depends(get_named_func_session, scope="function")
]
RegularSessionsDep = Annotated[
Tuple[NamedSession, Session], Depends(get_named_regular_func_session)
tuple[NamedSession, Session], Depends(get_named_regular_func_session)
]
app = FastAPI()

View File

@ -4,12 +4,11 @@ See https://github.com/tiangolo/fastapi/discussions/9116
"""
from pathlib import Path
from typing import List
from typing import Annotated
import pytest
from fastapi import FastAPI, File, Form
from fastapi.testclient import TestClient
from typing_extensions import Annotated
app = FastAPI()
@ -32,7 +31,7 @@ def file_after_form(
@app.post("/file_list_before_form")
def file_list_before_form(
files: Annotated[List[bytes], File()],
files: Annotated[list[bytes], File()],
city: Annotated[str, Form()],
):
return {"file_contents": files, "city": city}
@ -41,7 +40,7 @@ def file_list_before_form(
@app.post("/file_list_after_form")
def file_list_after_form(
city: Annotated[str, Form()],
files: Annotated[List[bytes], File()],
files: Annotated[list[bytes], File()],
):
return {"file_contents": files, "city": city}

View File

@ -1,8 +1,7 @@
from typing import Optional
from typing import Annotated, Optional
from fastapi import FastAPI, File, Form
from starlette.testclient import TestClient
from typing_extensions import Annotated
app = FastAPI()

View File

@ -1,11 +1,10 @@
from typing import List, Optional
from typing import Annotated, Optional
from dirty_equals import IsDict
from fastapi import FastAPI, Form
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
app = FastAPI()
@ -14,7 +13,7 @@ class FormModel(BaseModel):
username: str
lastname: str
age: Optional[int] = None
tags: List[str] = ["foo", "bar"]
tags: list[str] = ["foo", "bar"]
alias_with: str = Field(alias="with", default="nothing")

View File

@ -1,6 +1,7 @@
from typing import Annotated
from fastapi import FastAPI, Form
from fastapi.testclient import TestClient
from typing_extensions import Annotated
app = FastAPI()

View File

@ -1,5 +1,4 @@
import warnings
from typing import List
from fastapi import APIRouter, FastAPI
from fastapi.routing import APIRoute
@ -33,12 +32,12 @@ def test_top_level_generate_unique_id():
app = FastAPI(generate_unique_id_function=custom_generate_unique_id)
router = APIRouter()
@app.post("/", response_model=List[Item], responses={404: {"model": List[Message]}})
@app.post("/", response_model=list[Item], responses={404: {"model": list[Message]}})
def post_root(item1: Item, item2: Item):
return item1, item2 # pragma: nocover
@router.post(
"/router", response_model=List[Item], responses={404: {"model": List[Message]}}
"/router", response_model=list[Item], responses={404: {"model": list[Message]}}
)
def post_router(item1: Item, item2: Item):
return item1, item2 # pragma: nocover
@ -234,12 +233,12 @@ def test_router_overrides_generate_unique_id():
app = FastAPI(generate_unique_id_function=custom_generate_unique_id)
router = APIRouter(generate_unique_id_function=custom_generate_unique_id2)
@app.post("/", response_model=List[Item], responses={404: {"model": List[Message]}})
@app.post("/", response_model=list[Item], responses={404: {"model": list[Message]}})
def post_root(item1: Item, item2: Item):
return item1, item2 # pragma: nocover
@router.post(
"/router", response_model=List[Item], responses={404: {"model": List[Message]}}
"/router", response_model=list[Item], responses={404: {"model": list[Message]}}
)
def post_router(item1: Item, item2: Item):
return item1, item2 # pragma: nocover
@ -435,12 +434,12 @@ def test_router_include_overrides_generate_unique_id():
app = FastAPI(generate_unique_id_function=custom_generate_unique_id)
router = APIRouter(generate_unique_id_function=custom_generate_unique_id2)
@app.post("/", response_model=List[Item], responses={404: {"model": List[Message]}})
@app.post("/", response_model=list[Item], responses={404: {"model": list[Message]}})
def post_root(item1: Item, item2: Item):
return item1, item2 # pragma: nocover
@router.post(
"/router", response_model=List[Item], responses={404: {"model": List[Message]}}
"/router", response_model=list[Item], responses={404: {"model": list[Message]}}
)
def post_router(item1: Item, item2: Item):
return item1, item2 # pragma: nocover
@ -637,20 +636,20 @@ def test_subrouter_top_level_include_overrides_generate_unique_id():
router = APIRouter()
sub_router = APIRouter(generate_unique_id_function=custom_generate_unique_id2)
@app.post("/", response_model=List[Item], responses={404: {"model": List[Message]}})
@app.post("/", response_model=list[Item], responses={404: {"model": list[Message]}})
def post_root(item1: Item, item2: Item):
return item1, item2 # pragma: nocover
@router.post(
"/router", response_model=List[Item], responses={404: {"model": List[Message]}}
"/router", response_model=list[Item], responses={404: {"model": list[Message]}}
)
def post_router(item1: Item, item2: Item):
return item1, item2 # pragma: nocover
@sub_router.post(
"/subrouter",
response_model=List[Item],
responses={404: {"model": List[Message]}},
response_model=list[Item],
responses={404: {"model": list[Message]}},
)
def post_subrouter(item1: Item, item2: Item):
return item1, item2 # pragma: nocover
@ -910,14 +909,14 @@ def test_router_path_operation_overrides_generate_unique_id():
app = FastAPI(generate_unique_id_function=custom_generate_unique_id)
router = APIRouter(generate_unique_id_function=custom_generate_unique_id2)
@app.post("/", response_model=List[Item], responses={404: {"model": List[Message]}})
@app.post("/", response_model=list[Item], responses={404: {"model": list[Message]}})
def post_root(item1: Item, item2: Item):
return item1, item2 # pragma: nocover
@router.post(
"/router",
response_model=List[Item],
responses={404: {"model": List[Message]}},
response_model=list[Item],
responses={404: {"model": list[Message]}},
generate_unique_id_function=custom_generate_unique_id3,
)
def post_router(item1: Item, item2: Item):
@ -1116,8 +1115,8 @@ def test_app_path_operation_overrides_generate_unique_id():
@app.post(
"/",
response_model=List[Item],
responses={404: {"model": List[Message]}},
response_model=list[Item],
responses={404: {"model": list[Message]}},
generate_unique_id_function=custom_generate_unique_id3,
)
def post_root(item1: Item, item2: Item):
@ -1125,8 +1124,8 @@ def test_app_path_operation_overrides_generate_unique_id():
@router.post(
"/router",
response_model=List[Item],
responses={404: {"model": List[Message]}},
response_model=list[Item],
responses={404: {"model": list[Message]}},
)
def post_router(item1: Item, item2: Item):
return item1, item2 # pragma: nocover
@ -1324,8 +1323,8 @@ def test_callback_override_generate_unique_id():
@callback_router.post(
"/post-callback",
response_model=List[Item],
responses={404: {"model": List[Message]}},
response_model=list[Item],
responses={404: {"model": list[Message]}},
generate_unique_id_function=custom_generate_unique_id3,
)
def post_callback(item1: Item, item2: Item):
@ -1333,8 +1332,8 @@ def test_callback_override_generate_unique_id():
@app.post(
"/",
response_model=List[Item],
responses={404: {"model": List[Message]}},
response_model=list[Item],
responses={404: {"model": list[Message]}},
generate_unique_id_function=custom_generate_unique_id3,
callbacks=callback_router.routes,
)
@ -1343,8 +1342,8 @@ def test_callback_override_generate_unique_id():
@app.post(
"/tocallback",
response_model=List[Item],
responses={404: {"model": List[Message]}},
response_model=list[Item],
responses={404: {"model": list[Message]}},
)
def post_with_callback(item1: Item, item2: Item):
return item1, item2 # pragma: nocover

View File

@ -1,8 +1,7 @@
from typing import TypeVar
from typing import Annotated, TypeVar
from fastapi import Depends, FastAPI
from fastapi.testclient import TestClient
from typing_extensions import Annotated
app = FastAPI()

View File

@ -1,4 +1,5 @@
from typing import Any, Iterator, Set, Type
from collections.abc import Iterator
from typing import Any
import fastapi._compat
import fastapi.openapi.utils
@ -143,11 +144,11 @@ class SortedTypeSet(set):
Set of Types whose `__iter__()` method yields results sorted by the type names
"""
def __init__(self, seq: Set[Type[Any]], *, sort_reversed: bool):
def __init__(self, seq: set[type[Any]], *, sort_reversed: bool):
super().__init__(seq)
self.sort_reversed = sort_reversed
def __iter__(self) -> Iterator[Type[Any]]:
def __iter__(self) -> Iterator[type[Any]]:
members_sorted = sorted(
super().__iter__(),
key=lambda type_: type_.__name__,

View File

@ -1,5 +1,3 @@
from typing import Dict, List, Tuple
import pytest
from fastapi import FastAPI
from pydantic import BaseModel
@ -13,7 +11,7 @@ def test_invalid_sequence():
title: str
@app.get("/items/{id}")
def read_items(id: List[Item]):
def read_items(id: list[Item]):
pass # pragma: no cover
@ -25,7 +23,7 @@ def test_invalid_tuple():
title: str
@app.get("/items/{id}")
def read_items(id: Tuple[Item, Item]):
def read_items(id: tuple[Item, Item]):
pass # pragma: no cover
@ -37,7 +35,7 @@ def test_invalid_dict():
title: str
@app.get("/items/{id}")
def read_items(id: Dict[str, Item]):
def read_items(id: dict[str, Item]):
pass # pragma: no cover

View File

@ -1,4 +1,4 @@
from typing import Dict, List, Optional, Tuple
from typing import Optional
import pytest
from fastapi import FastAPI, Query
@ -13,7 +13,7 @@ def test_invalid_sequence():
title: str
@app.get("/items/")
def read_items(q: List[Item] = Query(default=None)):
def read_items(q: list[Item] = Query(default=None)):
pass # pragma: no cover
@ -25,7 +25,7 @@ def test_invalid_tuple():
title: str
@app.get("/items/")
def read_items(q: Tuple[Item, Item] = Query(default=None)):
def read_items(q: tuple[Item, Item] = Query(default=None)):
pass # pragma: no cover
@ -37,7 +37,7 @@ def test_invalid_dict():
title: str
@app.get("/items/")
def read_items(q: Dict[str, Item] = Query(default=None)):
def read_items(q: dict[str, Item] = Query(default=None)):
pass # pragma: no cover

View File

@ -1,5 +1,4 @@
from decimal import Decimal
from typing import List
from dirty_equals import IsDict, IsOneOf
from fastapi import FastAPI
@ -15,7 +14,7 @@ class Item(BaseModel):
@app.post("/items/")
def save_item_no_body(item: List[Item]):
def save_item_no_body(item: list[Item]):
return {"item": item}

View File

@ -1,5 +1,3 @@
from typing import List
from dirty_equals import IsDict
from fastapi import FastAPI, Query
from fastapi.testclient import TestClient
@ -8,7 +6,7 @@ app = FastAPI()
@app.get("/items/")
def read_items(q: List[int] = Query(default=None)):
def read_items(q: list[int] = Query(default=None)):
return {"q": q}

View File

@ -3,7 +3,6 @@
# Made an issue in:
# https://github.com/fastapi/fastapi/issues/14247
from enum import Enum
from typing import List
from fastapi import FastAPI
from fastapi.testclient import TestClient
@ -25,7 +24,7 @@ class MessageEvent(BaseModel):
class MessageOutput(BaseModel):
body: str = ""
events: List[MessageEvent] = []
events: list[MessageEvent] = []
class Message(BaseModel):

View File

@ -1,4 +1,4 @@
from typing import List, Optional, Union
from typing import Optional, Union
import pytest
from fastapi.openapi.models import Schema, SchemaType
@ -13,7 +13,7 @@ from fastapi.openapi.models import Schema, SchemaType
],
)
def test_allowed_schema_type(
type_value: Optional[Union[SchemaType, List[SchemaType]]],
type_value: Optional[Union[SchemaType, list[SchemaType]]],
) -> None:
"""Test that Schema accepts SchemaType, List[SchemaType] and None for type field."""
schema = Schema(type=type_value)

View File

@ -1,4 +1,4 @@
from typing import List, Optional
from typing import Optional
from fastapi import FastAPI
from fastapi.testclient import TestClient
@ -11,7 +11,7 @@ from .utils import PYDANTIC_V2, needs_pydanticv2
class SubItem(BaseModel):
subname: str
sub_description: Optional[str] = None
tags: List[str] = []
tags: list[str] = []
if PYDANTIC_V2:
model_config = {"json_schema_serialization_defaults_required": True}
@ -44,11 +44,11 @@ def get_app_client(separate_input_output_schemas: bool = True) -> TestClient:
return item
@app.post("/items-list/")
def create_item_list(item: List[Item]):
def create_item_list(item: list[Item]):
return item
@app.get("/items/")
def read_items() -> List[Item]:
def read_items() -> list[Item]:
return [
Item(
name="Portal Gun",

View File

@ -1,4 +1,4 @@
from typing import List, Optional
from typing import Optional
from fastapi import FastAPI, File
from fastapi.testclient import TestClient
@ -7,7 +7,7 @@ app = FastAPI()
@app.post("/files")
async def upload_files(files: Optional[List[bytes]] = File(None)):
async def upload_files(files: Optional[list[bytes]] = File(None)):
if files is None:
return {"files_count": 0}
return {"files_count": len(files), "sizes": [len(f) for f in files]}

View File

@ -1,9 +1,9 @@
from typing import Any, List
from typing import Any
from dirty_equals import IsOneOf
from fastapi.params import Body, Cookie, Header, Param, Path, Query
test_data: List[Any] = ["teststr", None, ..., 1, []]
test_data: list[Any] = ["teststr", None, ..., 1, []]
def get_user():

View File

@ -1,5 +1,5 @@
import sys
from typing import Any, List, Union
from typing import Any, Union
from tests.utils import pydantic_snapshot, skip_module_if_py_gte_314
@ -21,7 +21,7 @@ class Item(BaseModel):
size: int
description: Union[str, None] = None
sub: SubItem
multi: List[SubItem] = []
multi: list[SubItem] = []
app = FastAPI()

View File

@ -1,5 +1,5 @@
import sys
from typing import Any, List, Union
from typing import Any, Union
from tests.utils import pydantic_snapshot, skip_module_if_py_gte_314
@ -21,18 +21,18 @@ class Item(BaseModel):
size: int
description: Union[str, None] = None
sub: SubItem
multi: List[SubItem] = []
multi: list[SubItem] = []
app = FastAPI()
@app.post("/item")
def handle_item(data: Item) -> List[Item]:
def handle_item(data: Item) -> list[Item]:
return [data, data]
@app.post("/item-filter", response_model=List[Item])
@app.post("/item-filter", response_model=list[Item])
def handle_item_filter(data: Item) -> Any:
extended_data = data.dict()
extended_data.update({"secret_data": "classified", "internal_id": 12345})
@ -41,14 +41,14 @@ def handle_item_filter(data: Item) -> Any:
@app.post("/item-list")
def handle_item_list(data: List[Item]) -> Item:
def handle_item_list(data: list[Item]) -> Item:
if data:
return data[0]
return Item(title="", size=0, sub=SubItem(name=""))
@app.post("/item-list-filter", response_model=Item)
def handle_item_list_filter(data: List[Item]) -> Any:
def handle_item_list_filter(data: list[Item]) -> Any:
if data:
extended_data = data[0].dict()
extended_data.update({"secret_data": "classified", "internal_id": 12345})
@ -58,12 +58,12 @@ def handle_item_list_filter(data: List[Item]) -> Any:
@app.post("/item-list-to-list")
def handle_item_list_to_list(data: List[Item]) -> List[Item]:
def handle_item_list_to_list(data: list[Item]) -> list[Item]:
return data
@app.post("/item-list-to-list-filter", response_model=List[Item])
def handle_item_list_to_list_filter(data: List[Item]) -> Any:
@app.post("/item-list-to-list-filter", response_model=list[Item])
def handle_item_list_to_list_filter(data: list[Item]) -> Any:
if data:
extended_data = data[0].dict()
extended_data.update({"secret_data": "classified", "internal_id": 12345})

View File

@ -1,5 +1,5 @@
import sys
from typing import Any, List, Union
from typing import Any, Union
from tests.utils import pydantic_snapshot, skip_module_if_py_gte_314
@ -22,7 +22,7 @@ class Item(BaseModel):
size: int
description: Union[str, None] = None
sub: SubItem
multi: List[SubItem] = []
multi: list[SubItem] = []
class NewSubItem(NewBaseModel):
@ -34,7 +34,7 @@ class NewItem(NewBaseModel):
new_size: int
new_description: Union[str, None] = None
new_sub: NewSubItem
new_multi: List[NewSubItem] = []
new_multi: list[NewSubItem] = []
app = FastAPI()
@ -93,7 +93,7 @@ def handle_v2_item_to_v1_filter(data: NewItem) -> Any:
@app.post("/v1-to-v2/item-to-list")
def handle_v1_item_to_v2_list(data: Item) -> List[NewItem]:
def handle_v1_item_to_v2_list(data: Item) -> list[NewItem]:
converted = NewItem(
new_title=data.title,
new_size=data.size,
@ -105,7 +105,7 @@ def handle_v1_item_to_v2_list(data: Item) -> List[NewItem]:
@app.post("/v1-to-v2/list-to-list")
def handle_v1_list_to_v2_list(data: List[Item]) -> List[NewItem]:
def handle_v1_list_to_v2_list(data: list[Item]) -> list[NewItem]:
result = []
for item in data:
result.append(
@ -120,8 +120,8 @@ def handle_v1_list_to_v2_list(data: List[Item]) -> List[NewItem]:
return result
@app.post("/v1-to-v2/list-to-list-filter", response_model=List[NewItem])
def handle_v1_list_to_v2_list_filter(data: List[Item]) -> Any:
@app.post("/v1-to-v2/list-to-list-filter", response_model=list[NewItem])
def handle_v1_list_to_v2_list_filter(data: list[Item]) -> Any:
result = []
for item in data:
converted = {
@ -140,7 +140,7 @@ def handle_v1_list_to_v2_list_filter(data: List[Item]) -> Any:
@app.post("/v1-to-v2/list-to-item")
def handle_v1_list_to_v2_item(data: List[Item]) -> NewItem:
def handle_v1_list_to_v2_item(data: list[Item]) -> NewItem:
if data:
item = data[0]
return NewItem(
@ -154,7 +154,7 @@ def handle_v1_list_to_v2_item(data: List[Item]) -> NewItem:
@app.post("/v2-to-v1/item-to-list")
def handle_v2_item_to_v1_list(data: NewItem) -> List[Item]:
def handle_v2_item_to_v1_list(data: NewItem) -> list[Item]:
converted = Item(
title=data.new_title,
size=data.new_size,
@ -166,7 +166,7 @@ def handle_v2_item_to_v1_list(data: NewItem) -> List[Item]:
@app.post("/v2-to-v1/list-to-list")
def handle_v2_list_to_v1_list(data: List[NewItem]) -> List[Item]:
def handle_v2_list_to_v1_list(data: list[NewItem]) -> list[Item]:
result = []
for item in data:
result.append(
@ -181,8 +181,8 @@ def handle_v2_list_to_v1_list(data: List[NewItem]) -> List[Item]:
return result
@app.post("/v2-to-v1/list-to-list-filter", response_model=List[Item])
def handle_v2_list_to_v1_list_filter(data: List[NewItem]) -> Any:
@app.post("/v2-to-v1/list-to-list-filter", response_model=list[Item])
def handle_v2_list_to_v1_list_filter(data: list[NewItem]) -> Any:
result = []
for item in data:
converted = {
@ -201,7 +201,7 @@ def handle_v2_list_to_v1_list_filter(data: List[NewItem]) -> Any:
@app.post("/v2-to-v1/list-to-item")
def handle_v2_list_to_v1_item(data: List[NewItem]) -> Item:
def handle_v2_list_to_v1_item(data: list[NewItem]) -> Item:
if data:
item = data[0]
return Item(

View File

@ -1,5 +1,3 @@
from typing import List
from fastapi import FastAPI
from . import modelsv1, modelsv2, modelsv2b
@ -30,7 +28,7 @@ def handle_v2_item_to_v1(data: modelsv2.Item) -> modelsv1.Item:
@app.post("/v1-to-v2/item-to-list")
def handle_v1_item_to_v2_list(data: modelsv1.Item) -> List[modelsv2.Item]:
def handle_v1_item_to_v2_list(data: modelsv1.Item) -> list[modelsv2.Item]:
converted = modelsv2.Item(
new_title=data.title,
new_size=data.size,
@ -42,7 +40,7 @@ def handle_v1_item_to_v2_list(data: modelsv1.Item) -> List[modelsv2.Item]:
@app.post("/v1-to-v2/list-to-list")
def handle_v1_list_to_v2_list(data: List[modelsv1.Item]) -> List[modelsv2.Item]:
def handle_v1_list_to_v2_list(data: list[modelsv1.Item]) -> list[modelsv2.Item]:
result = []
for item in data:
result.append(
@ -58,7 +56,7 @@ def handle_v1_list_to_v2_list(data: List[modelsv1.Item]) -> List[modelsv2.Item]:
@app.post("/v1-to-v2/list-to-item")
def handle_v1_list_to_v2_item(data: List[modelsv1.Item]) -> modelsv2.Item:
def handle_v1_list_to_v2_item(data: list[modelsv1.Item]) -> modelsv2.Item:
if data:
item = data[0]
return modelsv2.Item(
@ -74,7 +72,7 @@ def handle_v1_list_to_v2_item(data: List[modelsv1.Item]) -> modelsv2.Item:
@app.post("/v2-to-v1/item-to-list")
def handle_v2_item_to_v1_list(data: modelsv2.Item) -> List[modelsv1.Item]:
def handle_v2_item_to_v1_list(data: modelsv2.Item) -> list[modelsv1.Item]:
converted = modelsv1.Item(
title=data.new_title,
size=data.new_size,
@ -86,7 +84,7 @@ def handle_v2_item_to_v1_list(data: modelsv2.Item) -> List[modelsv1.Item]:
@app.post("/v2-to-v1/list-to-list")
def handle_v2_list_to_v1_list(data: List[modelsv2.Item]) -> List[modelsv1.Item]:
def handle_v2_list_to_v1_list(data: list[modelsv2.Item]) -> list[modelsv1.Item]:
result = []
for item in data:
result.append(
@ -102,7 +100,7 @@ def handle_v2_list_to_v1_list(data: List[modelsv2.Item]) -> List[modelsv1.Item]:
@app.post("/v2-to-v1/list-to-item")
def handle_v2_list_to_v1_item(data: List[modelsv2.Item]) -> modelsv1.Item:
def handle_v2_list_to_v1_item(data: list[modelsv2.Item]) -> modelsv1.Item:
if data:
item = data[0]
return modelsv1.Item(
@ -130,8 +128,8 @@ def handle_v2_same_name_to_v1(
@app.post("/v2-to-v1/list-of-items-to-list-of-items")
def handle_v2_items_in_list_to_v1_item_in_list(
data1: List[modelsv2.ItemInList], data2: List[modelsv2b.ItemInList]
) -> List[modelsv1.ItemInList]:
data1: list[modelsv2.ItemInList], data2: list[modelsv2b.ItemInList]
) -> list[modelsv1.ItemInList]:
result = []
item1 = data1[0]
item2 = data2[0]

View File

@ -1,4 +1,4 @@
from typing import List, Union
from typing import Union
from fastapi._compat.v1 import BaseModel
@ -12,7 +12,7 @@ class Item(BaseModel):
size: int
description: Union[str, None] = None
sub: SubItem
multi: List[SubItem] = []
multi: list[SubItem] = []
class ItemInList(BaseModel):

View File

@ -1,4 +1,4 @@
from typing import List, Union
from typing import Union
from pydantic import BaseModel
@ -12,7 +12,7 @@ class Item(BaseModel):
new_size: int
new_description: Union[str, None] = None
new_sub: SubItem
new_multi: List[SubItem] = []
new_multi: list[SubItem] = []
class ItemInList(BaseModel):

View File

@ -1,4 +1,4 @@
from typing import List, Union
from typing import Union
from pydantic import BaseModel
@ -12,7 +12,7 @@ class Item(BaseModel):
dup_size: int
dup_description: Union[str, None] = None
dup_sub: SubItem
dup_multi: List[SubItem] = []
dup_multi: list[SubItem] = []
class ItemInList(BaseModel):

View File

@ -1,5 +1,5 @@
import sys
from typing import Any, List, Union
from typing import Any, Union
from tests.utils import pydantic_snapshot, skip_module_if_py_gte_314
@ -22,7 +22,7 @@ class Item(BaseModel):
size: int
description: Union[str, None] = None
sub: SubItem
multi: List[SubItem] = []
multi: list[SubItem] = []
class NewSubItem(NewBaseModel):
@ -34,7 +34,7 @@ class NewItem(NewBaseModel):
new_size: int
new_description: Union[str, None] = None
new_sub: NewSubItem
new_multi: List[NewSubItem] = []
new_multi: list[NewSubItem] = []
app = FastAPI()

View File

@ -2,7 +2,7 @@ from __future__ import annotations
import uuid
from dataclasses import dataclass, field
from typing import List, Union
from typing import Union
from dirty_equals import IsUUID
from fastapi import FastAPI
@ -15,7 +15,7 @@ class Item:
id: uuid.UUID
name: str
price: float
tags: List[str] = field(default_factory=list)
tags: list[str] = field(default_factory=list)
description: Union[str, None] = None
tax: Union[float, None] = None

View File

@ -1,8 +1,9 @@
from typing import Annotated
import pytest
from dirty_equals import IsDict
from fastapi import FastAPI, Form
from fastapi.testclient import TestClient
from typing_extensions import Annotated
from .utils import needs_py310

View File

@ -1,8 +1,9 @@
from typing import Annotated
import pytest
from dirty_equals import IsDict
from fastapi import FastAPI, Query
from fastapi.testclient import TestClient
from typing_extensions import Annotated
from .utils import needs_py310

View File

@ -1,5 +1,3 @@
import typing
from fastapi import Body, FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel
@ -28,7 +26,7 @@ async def create_product(data: Product = Body(media_type=media_type, embed=True)
@app.post("/shops")
async def create_shop(
data: Shop = Body(media_type=media_type),
included: typing.List[Product] = Body(default=[], media_type=media_type),
included: list[Product] = Body(default=[], media_type=media_type),
):
pass # pragma: no cover

View File

@ -1,11 +1,10 @@
from typing import List, Union
from typing import Annotated, Union
import pytest
from dirty_equals import IsDict, IsOneOf, IsPartialDict
from fastapi import Body, FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
from tests.utils import needs_pydanticv2
@ -18,12 +17,12 @@ app = FastAPI()
@app.post("/required-list-str", operation_id="required_list_str")
async def read_required_list_str(p: Annotated[List[str], Body(embed=True)]):
async def read_required_list_str(p: Annotated[list[str], Body(embed=True)]):
return {"p": p}
class BodyModelRequiredListStr(BaseModel):
p: List[str]
p: list[str]
@app.post("/model-required-list-str", operation_id="model_required_list_str")
@ -103,13 +102,13 @@ def test_required_list_str(path: str):
@app.post("/required-list-alias", operation_id="required_list_alias")
async def read_required_list_alias(
p: Annotated[List[str], Body(embed=True, alias="p_alias")],
p: Annotated[list[str], Body(embed=True, alias="p_alias")],
):
return {"p": p}
class BodyModelRequiredListAlias(BaseModel):
p: List[str] = Field(alias="p_alias")
p: list[str] = Field(alias="p_alias")
@app.post("/model-required-list-alias", operation_id="model_required_list_alias")
@ -228,13 +227,13 @@ def test_required_list_alias_by_alias(path: str):
"/required-list-validation-alias", operation_id="required_list_validation_alias"
)
def read_required_list_validation_alias(
p: Annotated[List[str], Body(embed=True, validation_alias="p_val_alias")],
p: Annotated[list[str], Body(embed=True, validation_alias="p_val_alias")],
):
return {"p": p}
class BodyModelRequiredListValidationAlias(BaseModel):
p: List[str] = Field(validation_alias="p_val_alias")
p: list[str] = Field(validation_alias="p_val_alias")
@app.post(
@ -345,14 +344,14 @@ def test_required_list_validation_alias_by_validation_alias(path: str):
)
def read_required_list_alias_and_validation_alias(
p: Annotated[
List[str], Body(embed=True, alias="p_alias", validation_alias="p_val_alias")
list[str], Body(embed=True, alias="p_alias", validation_alias="p_val_alias")
],
):
return {"p": p}
class BodyModelRequiredListAliasAndValidationAlias(BaseModel):
p: List[str] = Field(alias="p_alias", validation_alias="p_val_alias")
p: list[str] = Field(alias="p_alias", validation_alias="p_val_alias")
@app.post(

View File

@ -1,11 +1,10 @@
from typing import List, Optional
from typing import Annotated, Optional
import pytest
from dirty_equals import IsDict
from fastapi import Body, FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
from tests.utils import needs_pydanticv2
@ -19,13 +18,13 @@ app = FastAPI()
@app.post("/optional-list-str", operation_id="optional_list_str")
async def read_optional_list_str(
p: Annotated[Optional[List[str]], Body(embed=True)] = None,
p: Annotated[Optional[list[str]], Body(embed=True)] = None,
):
return {"p": p}
class BodyModelOptionalListStr(BaseModel):
p: Optional[List[str]] = None
p: Optional[list[str]] = None
@app.post("/model-optional-list-str", operation_id="model_optional_list_str")
@ -131,13 +130,13 @@ def test_optional_list_str(path: str):
@app.post("/optional-list-alias", operation_id="optional_list_alias")
async def read_optional_list_alias(
p: Annotated[Optional[List[str]], Body(embed=True, alias="p_alias")] = None,
p: Annotated[Optional[list[str]], Body(embed=True, alias="p_alias")] = None,
):
return {"p": p}
class BodyModelOptionalListAlias(BaseModel):
p: Optional[List[str]] = Field(None, alias="p_alias")
p: Optional[list[str]] = Field(None, alias="p_alias")
@app.post("/model-optional-list-alias", operation_id="model_optional_list_alias")
@ -264,14 +263,14 @@ def test_optional_list_alias_by_alias(path: str):
)
def read_optional_list_validation_alias(
p: Annotated[
Optional[List[str]], Body(embed=True, validation_alias="p_val_alias")
Optional[list[str]], Body(embed=True, validation_alias="p_val_alias")
] = None,
):
return {"p": p}
class BodyModelOptionalListValidationAlias(BaseModel):
p: Optional[List[str]] = Field(None, validation_alias="p_val_alias")
p: Optional[list[str]] = Field(None, validation_alias="p_val_alias")
@app.post(
@ -410,7 +409,7 @@ def test_optional_list_validation_alias_by_validation_alias(path: str):
)
def read_optional_list_alias_and_validation_alias(
p: Annotated[
Optional[List[str]],
Optional[list[str]],
Body(embed=True, alias="p_alias", validation_alias="p_val_alias"),
] = None,
):
@ -418,7 +417,7 @@ def read_optional_list_alias_and_validation_alias(
class BodyModelOptionalListAliasAndValidationAlias(BaseModel):
p: Optional[List[str]] = Field(
p: Optional[list[str]] = Field(
None, alias="p_alias", validation_alias="p_val_alias"
)

View File

@ -1,11 +1,10 @@
from typing import Optional
from typing import Annotated, Optional
import pytest
from dirty_equals import IsDict
from fastapi import Body, FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
from tests.utils import needs_pydanticv2

View File

@ -1,11 +1,10 @@
from typing import Any, Dict, Union
from typing import Annotated, Any, Union
import pytest
from dirty_equals import IsDict, IsOneOf
from fastapi import Body, FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
from tests.utils import needs_pydanticv2
@ -54,7 +53,7 @@ def test_required_str_schema(path: str):
"path",
["/required-str", "/model-required-str"],
)
def test_required_str_missing(path: str, json: Union[Dict[str, Any], None]):
def test_required_str_missing(path: str, json: Union[dict[str, Any], None]):
client = TestClient(app)
response = client.post(path, json=json)
assert response.status_code == 422
@ -140,7 +139,7 @@ def test_required_str_alias_schema(path: str):
"path",
["/required-alias", "/model-required-alias"],
)
def test_required_alias_missing(path: str, json: Union[Dict[str, Any], None]):
def test_required_alias_missing(path: str, json: Union[dict[str, Any], None]):
client = TestClient(app)
response = client.post(path, json=json)
assert response.status_code == 422
@ -266,7 +265,7 @@ def test_required_validation_alias_schema(path: str):
],
)
def test_required_validation_alias_missing(
path: str, json: Union[Dict[str, Any], None]
path: str, json: Union[dict[str, Any], None]
):
client = TestClient(app)
response = client.post(path, json=json)
@ -386,7 +385,7 @@ def test_required_alias_and_validation_alias_schema(path: str):
],
)
def test_required_alias_and_validation_alias_missing(
path: str, json: Union[Dict[str, Any], None]
path: str, json: Union[dict[str, Any], None]
):
client = TestClient(app)
response = client.post(path, json=json)

View File

@ -1,7 +1,7 @@
from typing import Any, Dict
from typing import Any
def get_body_model_name(openapi: Dict[str, Any], path: str) -> str:
def get_body_model_name(openapi: dict[str, Any], path: str) -> str:
body = openapi["paths"][path]["post"]["requestBody"]
body_schema = body["content"]["application/json"]["schema"]
return body_schema.get("$ref", "").split("/")[-1]

View File

@ -1,11 +1,10 @@
from typing import Optional
from typing import Annotated, Optional
import pytest
from dirty_equals import IsDict
from fastapi import Cookie, FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
from tests.utils import needs_pydanticv2

View File

@ -1,9 +1,10 @@
from typing import Annotated
import pytest
from dirty_equals import IsDict, IsOneOf
from fastapi import Cookie, FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
from tests.utils import needs_pydanticv2

View File

@ -1,10 +1,9 @@
from typing import List
from typing import Annotated
import pytest
from dirty_equals import IsDict
from fastapi import FastAPI, File, UploadFile
from fastapi.testclient import TestClient
from typing_extensions import Annotated
from tests.utils import needs_pydanticv2
@ -17,12 +16,12 @@ app = FastAPI()
@app.post("/list-bytes", operation_id="list_bytes")
async def read_list_bytes(p: Annotated[List[bytes], File()]):
async def read_list_bytes(p: Annotated[list[bytes], File()]):
return {"file_size": [len(file) for file in p]}
@app.post("/list-uploadfile", operation_id="list_uploadfile")
async def read_list_uploadfile(p: Annotated[List[UploadFile], File()]):
async def read_list_uploadfile(p: Annotated[list[UploadFile], File()]):
return {"file_size": [file.size for file in p]}
@ -122,13 +121,13 @@ def test_list(path: str):
@app.post("/list-bytes-alias", operation_id="list_bytes_alias")
async def read_list_bytes_alias(p: Annotated[List[bytes], File(alias="p_alias")]):
async def read_list_bytes_alias(p: Annotated[list[bytes], File(alias="p_alias")]):
return {"file_size": [len(file) for file in p]}
@app.post("/list-uploadfile-alias", operation_id="list_uploadfile_alias")
async def read_list_uploadfile_alias(
p: Annotated[List[UploadFile], File(alias="p_alias")],
p: Annotated[list[UploadFile], File(alias="p_alias")],
):
return {"file_size": [file.size for file in p]}
@ -266,7 +265,7 @@ def test_list_alias_by_alias(path: str):
@app.post("/list-bytes-validation-alias", operation_id="list_bytes_validation_alias")
def read_list_bytes_validation_alias(
p: Annotated[List[bytes], File(validation_alias="p_val_alias")],
p: Annotated[list[bytes], File(validation_alias="p_val_alias")],
):
return {"file_size": [len(file) for file in p]}
@ -276,7 +275,7 @@ def read_list_bytes_validation_alias(
operation_id="list_uploadfile_validation_alias",
)
def read_list_uploadfile_validation_alias(
p: Annotated[List[UploadFile], File(validation_alias="p_val_alias")],
p: Annotated[list[UploadFile], File(validation_alias="p_val_alias")],
):
return {"file_size": [file.size for file in p]}
@ -401,7 +400,7 @@ def test_list_validation_alias_by_validation_alias(path: str):
operation_id="list_bytes_alias_and_validation_alias",
)
def read_list_bytes_alias_and_validation_alias(
p: Annotated[List[bytes], File(alias="p_alias", validation_alias="p_val_alias")],
p: Annotated[list[bytes], File(alias="p_alias", validation_alias="p_val_alias")],
):
return {"file_size": [len(file) for file in p]}
@ -412,7 +411,7 @@ def read_list_bytes_alias_and_validation_alias(
)
def read_list_uploadfile_alias_and_validation_alias(
p: Annotated[
List[UploadFile], File(alias="p_alias", validation_alias="p_val_alias")
list[UploadFile], File(alias="p_alias", validation_alias="p_val_alias")
],
):
return {"file_size": [file.size for file in p]}

View File

@ -1,10 +1,9 @@
from typing import Optional
from typing import Annotated, Optional
import pytest
from dirty_equals import IsDict
from fastapi import FastAPI, File, UploadFile
from fastapi.testclient import TestClient
from typing_extensions import Annotated
from tests.utils import needs_pydanticv2

View File

@ -1,10 +1,9 @@
from typing import List, Optional
from typing import Annotated, Optional
import pytest
from dirty_equals import IsDict
from fastapi import FastAPI, File, UploadFile
from fastapi.testclient import TestClient
from typing_extensions import Annotated
from tests.utils import needs_pydanticv2
@ -17,13 +16,13 @@ app = FastAPI()
@app.post("/optional-list-bytes")
async def read_optional_list_bytes(p: Annotated[Optional[List[bytes]], File()] = None):
async def read_optional_list_bytes(p: Annotated[Optional[list[bytes]], File()] = None):
return {"file_size": [len(file) for file in p] if p else None}
@app.post("/optional-list-uploadfile")
async def read_optional_list_uploadfile(
p: Annotated[Optional[List[UploadFile]], File()] = None,
p: Annotated[Optional[list[UploadFile]], File()] = None,
):
return {"file_size": [file.size for file in p] if p else None}
@ -103,14 +102,14 @@ def test_optional_list(path: str):
@app.post("/optional-list-bytes-alias")
async def read_optional_list_bytes_alias(
p: Annotated[Optional[List[bytes]], File(alias="p_alias")] = None,
p: Annotated[Optional[list[bytes]], File(alias="p_alias")] = None,
):
return {"file_size": [len(file) for file in p] if p else None}
@app.post("/optional-list-uploadfile-alias")
async def read_optional_list_uploadfile_alias(
p: Annotated[Optional[List[UploadFile]], File(alias="p_alias")] = None,
p: Annotated[Optional[list[UploadFile]], File(alias="p_alias")] = None,
):
return {"file_size": [file.size for file in p] if p else None}
@ -204,7 +203,7 @@ def test_optional_list_alias_by_alias(path: str):
@app.post("/optional-list-bytes-validation-alias")
def read_optional_list_bytes_validation_alias(
p: Annotated[Optional[List[bytes]], File(validation_alias="p_val_alias")] = None,
p: Annotated[Optional[list[bytes]], File(validation_alias="p_val_alias")] = None,
):
return {"file_size": [len(file) for file in p] if p else None}
@ -212,7 +211,7 @@ def read_optional_list_bytes_validation_alias(
@app.post("/optional-list-uploadfile-validation-alias")
def read_optional_list_uploadfile_validation_alias(
p: Annotated[
Optional[List[UploadFile]], File(validation_alias="p_val_alias")
Optional[list[UploadFile]], File(validation_alias="p_val_alias")
] = None,
):
return {"file_size": [file.size for file in p] if p else None}
@ -314,7 +313,7 @@ def test_optional_validation_alias_by_validation_alias(path: str):
@app.post("/optional-list-bytes-alias-and-validation-alias")
def read_optional_list_bytes_alias_and_validation_alias(
p: Annotated[
Optional[List[bytes]], File(alias="p_alias", validation_alias="p_val_alias")
Optional[list[bytes]], File(alias="p_alias", validation_alias="p_val_alias")
] = None,
):
return {"file_size": [len(file) for file in p] if p else None}
@ -323,7 +322,7 @@ def read_optional_list_bytes_alias_and_validation_alias(
@app.post("/optional-list-uploadfile-alias-and-validation-alias")
def read_optional_list_uploadfile_alias_and_validation_alias(
p: Annotated[
Optional[List[UploadFile]],
Optional[list[UploadFile]],
File(alias="p_alias", validation_alias="p_val_alias"),
] = None,
):

View File

@ -1,8 +1,9 @@
from typing import Annotated
import pytest
from dirty_equals import IsDict
from fastapi import FastAPI, File, UploadFile
from fastapi.testclient import TestClient
from typing_extensions import Annotated
from tests.utils import needs_pydanticv2

View File

@ -1,7 +1,7 @@
from typing import Any, Dict
from typing import Any
def get_body_model_name(openapi: Dict[str, Any], path: str) -> str:
def get_body_model_name(openapi: dict[str, Any], path: str) -> str:
body = openapi["paths"][path]["post"]["requestBody"]
body_schema = body["content"]["multipart/form-data"]["schema"]
return body_schema.get("$ref", "").split("/")[-1]

View File

@ -1,11 +1,10 @@
from typing import List
from typing import Annotated
import pytest
from dirty_equals import IsDict, IsOneOf, IsPartialDict
from fastapi import FastAPI, Form
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
from typing_extensions import Annotated
from tests.utils import needs_pydanticv2
@ -18,12 +17,12 @@ app = FastAPI()
@app.post("/required-list-str", operation_id="required_list_str")
async def read_required_list_str(p: Annotated[List[str], Form()]):
async def read_required_list_str(p: Annotated[list[str], Form()]):
return {"p": p}
class FormModelRequiredListStr(BaseModel):
p: List[str]
p: list[str]
@app.post("/model-required-list-str", operation_id="model_required_list_str")
@ -101,12 +100,12 @@ def test_required_list_str(path: str):
@app.post("/required-list-alias", operation_id="required_list_alias")
async def read_required_list_alias(p: Annotated[List[str], Form(alias="p_alias")]):
async def read_required_list_alias(p: Annotated[list[str], Form(alias="p_alias")]):
return {"p": p}
class FormModelRequiredListAlias(BaseModel):
p: List[str] = Field(alias="p_alias")
p: list[str] = Field(alias="p_alias")
@app.post("/model-required-list-alias", operation_id="model_required_list_alias")
@ -229,13 +228,13 @@ def test_required_list_alias_by_alias(path: str):
"/required-list-validation-alias", operation_id="required_list_validation_alias"
)
def read_required_list_validation_alias(
p: Annotated[List[str], Form(validation_alias="p_val_alias")],
p: Annotated[list[str], Form(validation_alias="p_val_alias")],
):
return {"p": p}
class FormModelRequiredListValidationAlias(BaseModel):
p: List[str] = Field(validation_alias="p_val_alias")
p: list[str] = Field(validation_alias="p_val_alias")
@app.post(
@ -345,13 +344,13 @@ def test_required_list_validation_alias_by_validation_alias(path: str):
operation_id="required_list_alias_and_validation_alias",
)
def read_required_list_alias_and_validation_alias(
p: Annotated[List[str], Form(alias="p_alias", validation_alias="p_val_alias")],
p: Annotated[list[str], Form(alias="p_alias", validation_alias="p_val_alias")],
):
return {"p": p}
class FormModelRequiredListAliasAndValidationAlias(BaseModel):
p: List[str] = Field(alias="p_alias", validation_alias="p_val_alias")
p: list[str] = Field(alias="p_alias", validation_alias="p_val_alias")
@app.post(

Some files were not shown because too many files have changed in this diff Show More