From 1c4fc96c917a1097149b6e1ff8e36a62f8d994b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Wed, 17 Dec 2025 13:25:59 -0800 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Upgrade=20internal=20synta?= =?UTF-8?q?x=20to=20Python=203.9+=20=F0=9F=8E=89=20(#14564)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_src/header_params/tutorial003_an_py39.py | 4 +- fastapi/_compat/main.py | 30 ++- fastapi/_compat/may_v1.py | 25 +-- fastapi/_compat/model_field.py | 9 +- fastapi/_compat/shared.py | 35 ++-- fastapi/_compat/v1.py | 41 ++-- fastapi/_compat/v2.py | 78 ++++---- fastapi/applications.py | 156 ++++++++------- fastapi/background.py | 4 +- fastapi/concurrency.py | 6 +- fastapi/datastructures.py | 14 +- fastapi/dependencies/models.py | 22 +-- fastapi/dependencies/utils.py | 89 ++++----- fastapi/encoders.py | 17 +- fastapi/exceptions.py | 10 +- fastapi/openapi/docs.py | 9 +- fastapi/openapi/models.py | 109 ++++++----- fastapi/openapi/utils.py | 83 ++++---- fastapi/param_functions.py | 47 ++--- fastapi/params.py | 53 ++--- fastapi/routing.py | 184 +++++++++--------- fastapi/security/api_key.py | 3 +- fastapi/security/http.py | 7 +- fastapi/security/oauth2.py | 15 +- fastapi/security/open_id_connect_url.py | 3 +- fastapi/security/utils.py | 4 +- fastapi/temp_pydantic_v1_params.py | 52 ++--- fastapi/types.py | 8 +- fastapi/utils.py | 19 +- pdm_build.py | 6 +- scripts/docs.py | 14 +- scripts/mkdocs_hooks.py | 12 +- scripts/notify_translations.py | 20 +- scripts/people.py | 3 +- tests/main.py | 8 +- tests/test_additional_properties.py | 4 +- ...tional_responses_custom_validationerror.py | 4 +- ...est_additional_responses_response_class.py | 4 +- tests/test_allow_inf_nan_in_enforcing.py | 3 +- tests/test_ambiguous_params.py | 3 +- tests/test_annotated.py | 3 +- tests/test_arbitrary_types.py | 7 +- tests/test_compat.py | 16 +- tests/test_compat_params_v1.py | 7 +- tests/test_custom_schema_fields.py | 3 +- tests/test_datastructures.py | 3 +- tests/test_dependency_after_yield_raise.py | 3 +- .../test_dependency_after_yield_streaming.py | 4 +- .../test_dependency_after_yield_websockets.py | 4 +- tests/test_dependency_class.py | 2 +- tests/test_dependency_contextmanager.py | 9 +- tests/test_dependency_contextvars.py | 5 +- tests/test_dependency_duplicates.py | 4 +- tests/test_dependency_paramless.py | 3 +- tests/test_dependency_partial.py | 4 +- tests/test_dependency_security_overrides.py | 6 +- tests/test_dependency_wrapped.py | 2 +- tests/test_dependency_yield_scope.py | 11 +- .../test_dependency_yield_scope_websockets.py | 13 +- tests/test_file_and_form_order_issue_9116.py | 7 +- tests/test_form_default.py | 3 +- tests/test_forms_single_model.py | 5 +- tests/test_forms_single_param.py | 3 +- tests/test_generate_unique_id_function.py | 47 +++-- tests/test_generic_parameterless_depends.py | 3 +- ...t_get_model_definitions_formfeed_escape.py | 7 +- tests/test_invalid_path_param.py | 8 +- tests/test_invalid_sequence_param.py | 8 +- tests/test_multi_body_errors.py | 3 +- tests/test_multi_query_errors.py | 4 +- tests/test_no_schema_split.py | 3 +- tests/test_openapi_schema_type.py | 4 +- ...t_openapi_separate_input_output_schemas.py | 8 +- tests/test_optional_file_list.py | 4 +- tests/test_params_repr.py | 4 +- tests/test_pydantic_v1_v2_01.py | 4 +- tests/test_pydantic_v1_v2_list.py | 18 +- tests/test_pydantic_v1_v2_mixed.py | 26 +-- tests/test_pydantic_v1_v2_multifile/main.py | 18 +- .../test_pydantic_v1_v2_multifile/modelsv1.py | 4 +- .../test_pydantic_v1_v2_multifile/modelsv2.py | 4 +- .../modelsv2b.py | 4 +- tests/test_pydantic_v1_v2_noneable.py | 6 +- ...ataclasses_uuid_stringified_annotations.py | 4 +- tests/test_regex_deprecated_body.py | 3 +- tests/test_regex_deprecated_params.py | 3 +- ...test_request_body_parameters_media_type.py | 4 +- .../test_body/test_list.py | 19 +- .../test_body/test_optional_list.py | 19 +- .../test_body/test_optional_str.py | 3 +- .../test_body/test_required_str.py | 11 +- tests/test_request_params/test_body/utils.py | 4 +- .../test_cookie/test_optional_str.py | 3 +- .../test_cookie/test_required_str.py | 3 +- .../test_file/test_list.py | 19 +- .../test_file/test_optional.py | 3 +- .../test_file/test_optional_list.py | 19 +- .../test_file/test_required.py | 3 +- tests/test_request_params/test_file/utils.py | 4 +- .../test_form/test_list.py | 19 +- .../test_form/test_optional_list.py | 19 +- .../test_form/test_optional_str.py | 3 +- .../test_form/test_required_str.py | 3 +- tests/test_request_params/test_form/utils.py | 4 +- .../test_header/test_list.py | 19 +- .../test_header/test_optional_list.py | 19 +- .../test_header/test_optional_str.py | 3 +- .../test_header/test_required_str.py | 3 +- .../test_path/test_required_str.py | 3 +- .../test_query/test_list.py | 19 +- .../test_query/test_optional_list.py | 19 +- .../test_query/test_optional_str.py | 3 +- .../test_query/test_required_str.py | 3 +- tests/test_response_by_alias.py | 8 +- tests/test_response_class_no_mediatype.py | 4 +- tests/test_response_code_no_body.py | 4 +- ...est_response_model_as_return_annotation.py | 8 +- tests/test_response_model_data_filter.py | 4 +- ...sponse_model_data_filter_no_inheritance.py | 4 +- tests/test_response_model_invalid.py | 6 +- tests/test_response_model_sub_types.py | 6 +- tests/test_router_events.py | 21 +- ...uthorization_code_bearer_scopes_openapi.py | 3 +- ...ation_code_bearer_scopes_openapi_simple.py | 3 +- tests/test_security_scopes.py | 7 +- tests/test_security_scopes_dont_propagate.py | 9 +- tests/test_security_scopes_sub_dependency.py | 8 +- tests/test_serialize_response.py | 6 +- tests/test_serialize_response_dataclass.py | 8 +- tests/test_serialize_response_model.py | 12 +- .../test_stringified_annotation_dependency.py | 3 +- tests/test_stringified_annotations_simple.py | 3 +- tests/test_tuples.py | 8 +- .../test_websockets/test_tutorial003.py | 7 +- tests/test_union_body_discriminator.py | 6 +- ...test_union_body_discriminator_annotated.py | 3 +- tests/test_union_forms.py | 3 +- tests/test_validate_response.py | 6 +- tests/test_validate_response_dataclass.py | 6 +- tests/test_validate_response_recursive/app.py | 8 +- tests/test_webhooks_security.py | 2 +- tests/test_ws_dependencies.py | 7 +- 142 files changed, 939 insertions(+), 1038 deletions(-) diff --git a/docs_src/header_params/tutorial003_an_py39.py b/docs_src/header_params/tutorial003_an_py39.py index c1dd49961..5aad89407 100644 --- a/docs_src/header_params/tutorial003_an_py39.py +++ b/docs_src/header_params/tutorial003_an_py39.py @@ -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} diff --git a/fastapi/_compat/main.py b/fastapi/_compat/main.py index e5275950e..2043a6678 100644 --- a/fastapi/_compat/main.py +++ b/fastapi/_compat/main.py @@ -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 diff --git a/fastapi/_compat/may_v1.py b/fastapi/_compat/may_v1.py index beea4d167..c77216228 100644 --- a/fastapi/_compat/may_v1.py +++ b/fastapi/_compat/may_v1.py @@ -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) ] diff --git a/fastapi/_compat/model_field.py b/fastapi/_compat/model_field.py index fa2008c5e..47d05cb94 100644 --- a/fastapi/_compat/model_field.py +++ b/fastapi/_compat/model_field.py @@ -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, diff --git a/fastapi/_compat/shared.py b/fastapi/_compat/shared.py index c4dd54dec..3a11e88ac 100644 --- a/fastapi/_compat/shared.py +++ b/fastapi/_compat/shared.py @@ -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 diff --git a/fastapi/_compat/v1.py b/fastapi/_compat/v1.py index e17ce8bea..b29a61f73 100644 --- a/fastapi/_compat/v1.py +++ b/fastapi/_compat/v1.py @@ -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] diff --git a/fastapi/_compat/v2.py b/fastapi/_compat/v2.py index a17d62556..c4fa49e40 100644 --- a/fastapi/_compat/v2.py +++ b/fastapi/_compat/v2.py @@ -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) diff --git a/fastapi/applications.py b/fastapi/applications.py index 02193312b..54175cb3b 100644 --- a/fastapi/applications.py +++ b/fastapi/applications.py @@ -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. diff --git a/fastapi/background.py b/fastapi/background.py index 6d4a30d44..20803ba67 100644 --- a/fastapi/background.py +++ b/fastapi/background.py @@ -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") diff --git a/fastapi/concurrency.py b/fastapi/concurrency.py index 3202c7078..76a5a2eb1 100644 --- a/fastapi/concurrency.py +++ b/fastapi/concurrency.py @@ -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 diff --git a/fastapi/datastructures.py b/fastapi/datastructures.py index 8ad9aa11a..b38a326de 100644 --- a/fastapi/datastructures.py +++ b/fastapi/datastructures.py @@ -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 diff --git a/fastapi/dependencies/models.py b/fastapi/dependencies/models.py index 6c4bf18b3..58392326d 100644 --- a/fastapi/dependencies/models.py +++ b/fastapi/dependencies/models.py @@ -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 diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index cc7e55b4b..3db006a91 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -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( diff --git a/fastapi/encoders.py b/fastapi/encoders.py index 793951089..cbeeee455 100644 --- a/fastapi/encoders.py +++ b/fastapi/encoders.py @@ -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) diff --git a/fastapi/exceptions.py b/fastapi/exceptions.py index a46e82350..8e0c55902 100644 --- a/fastapi/exceptions.py +++ b/fastapi/exceptions.py @@ -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): diff --git a/fastapi/openapi/docs.py b/fastapi/openapi/docs.py index 74b23a370..82380f85d 100644 --- a/fastapi/openapi/docs.py +++ b/fastapi/openapi/docs.py @@ -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. diff --git a/fastapi/openapi/models.py b/fastapi/openapi/models.py index 81d276aed..7aa80f5cb 100644 --- a/fastapi/openapi/models.py +++ b/fastapi/openapi/models.py @@ -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 diff --git a/fastapi/openapi/utils.py b/fastapi/openapi/utils.py index 9fe2044f2..a99d4188e 100644 --- a/fastapi/openapi/utils.py +++ b/fastapi/openapi/utils.py @@ -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( diff --git a/fastapi/param_functions.py b/fastapi/param_functions.py index e32f75593..844542594 100644 --- a/fastapi/param_functions.py +++ b/fastapi/param_functions.py @@ -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. diff --git a/fastapi/params.py b/fastapi/params.py index b6d0f08e3..4990d0e70 100644 --- a/fastapi/params.py +++ b/fastapi/params.py @@ -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__( diff --git a/fastapi/routing.py b/fastapi/routing.py index 9be2b44bc..fa6904a6b 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -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 diff --git a/fastapi/security/api_key.py b/fastapi/security/api_key.py index 81c7be10d..18dfb8e61 100644 --- a/fastapi/security/api_key.py +++ b/fastapi/security/api_key.py @@ -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): diff --git a/fastapi/security/http.py b/fastapi/security/http.py index 0d1bbba3a..b4c3bc6d8 100644 --- a/fastapi/security/http.py +++ b/fastapi/security/http.py @@ -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"} diff --git a/fastapi/security/oauth2.py b/fastapi/security/oauth2.py index b41b0f877..fc49ba190 100644 --- a/fastapi/security/oauth2.py +++ b/fastapi/security/oauth2.py @@ -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. diff --git a/fastapi/security/open_id_connect_url.py b/fastapi/security/open_id_connect_url.py index e574a56a8..f4d953351 100644 --- a/fastapi/security/open_id_connect_url.py +++ b/fastapi/security/open_id_connect_url.py @@ -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): diff --git a/fastapi/security/utils.py b/fastapi/security/utils.py index fa7a450b7..002e68b44 100644 --- a/fastapi/security/utils.py +++ b/fastapi/security/utils.py @@ -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(" ") diff --git a/fastapi/temp_pydantic_v1_params.py b/fastapi/temp_pydantic_v1_params.py index e41d71230..b6c804dc4 100644 --- a/fastapi/temp_pydantic_v1_params.py +++ b/fastapi/temp_pydantic_v1_params.py @@ -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__( diff --git a/fastapi/types.py b/fastapi/types.py index 3f4e81a7c..d3e980cb4 100644 --- a/fastapi/types.py +++ b/fastapi/types.py @@ -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] diff --git a/fastapi/utils.py b/fastapi/utils.py index b3b89ed2b..5b93b9af8 100644 --- a/fastapi/utils.py +++ b/fastapi/utils.py @@ -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 diff --git a/pdm_build.py b/pdm_build.py index c83222b33..a0eb88eeb 100644 --- a/pdm_build.py +++ b/pdm_build.py @@ -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 diff --git a/scripts/docs.py b/scripts/docs.py index b35bb3627..bf7d9de39 100644 --- a/scripts/docs.py +++ b/scripts/docs.py @@ -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. """ diff --git a/scripts/mkdocs_hooks.py b/scripts/mkdocs_hooks.py index 09cfa99e3..4b781270a 100644 --- a/scripts/mkdocs_hooks.py +++ b/scripts/mkdocs_hooks.py @@ -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 diff --git a/scripts/notify_translations.py b/scripts/notify_translations.py index c300624db..2ca740a60 100644 --- a/scripts/notify_translations.py +++ b/scripts/notify_translations.py @@ -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 diff --git a/scripts/people.py b/scripts/people.py index 65e009944..207ab4649 100644 --- a/scripts/people.py +++ b/scripts/people.py @@ -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 diff --git a/tests/main.py b/tests/main.py index 2f1d61711..7edb16c61 100644 --- a/tests/main.py +++ b/tests/main.py @@ -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 diff --git a/tests/test_additional_properties.py b/tests/test_additional_properties.py index be14d10ed..262236640 100644 --- a/tests/test_additional_properties.py +++ b/tests/test_additional_properties.py @@ -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") diff --git a/tests/test_additional_responses_custom_validationerror.py b/tests/test_additional_responses_custom_validationerror.py index 9fec5c96d..8724e5ecb 100644 --- a/tests/test_additional_responses_custom_validationerror.py +++ b/tests/test_additional_responses_custom_validationerror.py @@ -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( diff --git a/tests/test_additional_responses_response_class.py b/tests/test_additional_responses_response_class.py index 68753561c..fecc3ee16 100644 --- a/tests/test_additional_responses_response_class.py +++ b/tests/test_additional_responses_response_class.py @@ -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( diff --git a/tests/test_allow_inf_nan_in_enforcing.py b/tests/test_allow_inf_nan_in_enforcing.py index 9e855fdf8..083a024af 100644 --- a/tests/test_allow_inf_nan_in_enforcing.py +++ b/tests/test_allow_inf_nan_in_enforcing.py @@ -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() diff --git a/tests/test_ambiguous_params.py b/tests/test_ambiguous_params.py index 8a31442eb..5e48782f8 100644 --- a/tests/test_ambiguous_params.py +++ b/tests/test_ambiguous_params.py @@ -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() diff --git a/tests/test_annotated.py b/tests/test_annotated.py index 473d33e52..a9e7c78c9 100644 --- a/tests/test_annotated.py +++ b/tests/test_annotated.py @@ -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() diff --git a/tests/test_arbitrary_types.py b/tests/test_arbitrary_types.py index e5fa95ef2..8b8417bae 100644 --- a/tests/test_arbitrary_types.py +++ b/tests/test_arbitrary_types.py @@ -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), ] diff --git a/tests/test_compat.py b/tests/test_compat.py index 26537c5ab..3f9d84d89 100644 --- a/tests/test_compat.py +++ b/tests/test_compat.py @@ -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]) diff --git a/tests/test_compat_params_v1.py b/tests/test_compat_params_v1.py index 7064761cb..8829a0a61 100644 --- a/tests/test_compat_params_v1.py +++ b/tests/test_compat_params_v1.py @@ -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 { diff --git a/tests/test_custom_schema_fields.py b/tests/test_custom_schema_fields.py index d890291b1..bc3e1c5ec 100644 --- a/tests/test_custom_schema_fields.py +++ b/tests/test_custom_schema_fields.py @@ -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 diff --git a/tests/test_datastructures.py b/tests/test_datastructures.py index 7e57d525c..c175147bc 100644 --- a/tests/test_datastructures.py +++ b/tests/test_datastructures.py @@ -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"") app = FastAPI() - testing_file_store: List[UploadFile] = [] + testing_file_store: list[UploadFile] = [] @app.post("/uploadfile/") def create_upload_file(file: UploadFile): diff --git a/tests/test_dependency_after_yield_raise.py b/tests/test_dependency_after_yield_raise.py index b560dc36f..b56140277 100644 --- a/tests/test_dependency_after_yield_raise.py +++ b/tests/test_dependency_after_yield_raise.py @@ -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): diff --git a/tests/test_dependency_after_yield_streaming.py b/tests/test_dependency_after_yield_streaming.py index 7e1c8822b..cbadff8f8 100644 --- a/tests/test_dependency_after_yield_streaming.py +++ b/tests/test_dependency_after_yield_streaming.py @@ -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: diff --git a/tests/test_dependency_after_yield_websockets.py b/tests/test_dependency_after_yield_websockets.py index 7c323c338..0fdf697b6 100644 --- a/tests/test_dependency_after_yield_websockets.py +++ b/tests/test_dependency_after_yield_websockets.py @@ -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: diff --git a/tests/test_dependency_class.py b/tests/test_dependency_class.py index 75241b467..95ff3e9d9 100644 --- a/tests/test_dependency_class.py +++ b/tests/test_dependency_class.py @@ -1,4 +1,4 @@ -from typing import AsyncGenerator, Generator +from collections.abc import AsyncGenerator, Generator import pytest from fastapi import Depends, FastAPI diff --git a/tests/test_dependency_contextmanager.py b/tests/test_dependency_contextmanager.py index 02c10458c..5a8993474 100644 --- a/tests/test_dependency_contextmanager.py +++ b/tests/test_dependency_contextmanager.py @@ -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"] diff --git a/tests/test_dependency_contextvars.py b/tests/test_dependency_contextvars.py index 076802df8..0c2e5594b 100644 --- a/tests/test_dependency_contextvars.py +++ b/tests/test_dependency_contextvars.py @@ -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 ) diff --git a/tests/test_dependency_duplicates.py b/tests/test_dependency_duplicates.py index 8e8d07c2d..7c6717e2a 100644 --- a/tests/test_dependency_duplicates.py +++ b/tests/test_dependency_duplicates.py @@ -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] diff --git a/tests/test_dependency_paramless.py b/tests/test_dependency_paramless.py index 9c3cc3878..1774196fe 100644 --- a/tests/test_dependency_paramless.py +++ b/tests/test_dependency_paramless.py @@ -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() diff --git a/tests/test_dependency_partial.py b/tests/test_dependency_partial.py index 61a76236f..05a3cffa5 100644 --- a/tests/test_dependency_partial.py +++ b/tests/test_dependency_partial.py @@ -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() diff --git a/tests/test_dependency_security_overrides.py b/tests/test_dependency_security_overrides.py index b89d82db4..14b65c777 100644 --- a/tests/test_dependency_security_overrides.py +++ b/tests/test_dependency_security_overrides.py @@ -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} diff --git a/tests/test_dependency_wrapped.py b/tests/test_dependency_wrapped.py index 08356712d..a4044112a 100644 --- a/tests/test_dependency_wrapped.py +++ b/tests/test_dependency_wrapped.py @@ -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 diff --git a/tests/test_dependency_yield_scope.py b/tests/test_dependency_yield_scope.py index d87164fe8..f3fc3cc94 100644 --- a/tests/test_dependency_yield_scope.py +++ b/tests/test_dependency_yield_scope.py @@ -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() diff --git a/tests/test_dependency_yield_scope_websockets.py b/tests/test_dependency_yield_scope_websockets.py index 52a30ae7a..dbe35e576 100644 --- a/tests/test_dependency_yield_scope_websockets.py +++ b/tests/test_dependency_yield_scope_websockets.py @@ -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() diff --git a/tests/test_file_and_form_order_issue_9116.py b/tests/test_file_and_form_order_issue_9116.py index cb9a31d31..75290b60c 100644 --- a/tests/test_file_and_form_order_issue_9116.py +++ b/tests/test_file_and_form_order_issue_9116.py @@ -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} diff --git a/tests/test_form_default.py b/tests/test_form_default.py index 2a12049d1..0b3eb8f2e 100644 --- a/tests/test_form_default.py +++ b/tests/test_form_default.py @@ -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() diff --git a/tests/test_forms_single_model.py b/tests/test_forms_single_model.py index 1db63f021..b149b7653 100644 --- a/tests/test_forms_single_model.py +++ b/tests/test_forms_single_model.py @@ -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") diff --git a/tests/test_forms_single_param.py b/tests/test_forms_single_param.py index 3bb951441..67f054b34 100644 --- a/tests/test_forms_single_param.py +++ b/tests/test_forms_single_param.py @@ -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() diff --git a/tests/test_generate_unique_id_function.py b/tests/test_generate_unique_id_function.py index 5aeec6636..62ebfbc96 100644 --- a/tests/test_generate_unique_id_function.py +++ b/tests/test_generate_unique_id_function.py @@ -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 diff --git a/tests/test_generic_parameterless_depends.py b/tests/test_generic_parameterless_depends.py index 5aa35320c..93b72ad24 100644 --- a/tests/test_generic_parameterless_depends.py +++ b/tests/test_generic_parameterless_depends.py @@ -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() diff --git a/tests/test_get_model_definitions_formfeed_escape.py b/tests/test_get_model_definitions_formfeed_escape.py index 6601585ef..215d06a07 100644 --- a/tests/test_get_model_definitions_formfeed_escape.py +++ b/tests/test_get_model_definitions_formfeed_escape.py @@ -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__, diff --git a/tests/test_invalid_path_param.py b/tests/test_invalid_path_param.py index d5fa53c1f..35c00363d 100644 --- a/tests/test_invalid_path_param.py +++ b/tests/test_invalid_path_param.py @@ -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 diff --git a/tests/test_invalid_sequence_param.py b/tests/test_invalid_sequence_param.py index 475786adb..2b8fd059e 100644 --- a/tests/test_invalid_sequence_param.py +++ b/tests/test_invalid_sequence_param.py @@ -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 diff --git a/tests/test_multi_body_errors.py b/tests/test_multi_body_errors.py index 33304827a..6ea405fe7 100644 --- a/tests/test_multi_body_errors.py +++ b/tests/test_multi_body_errors.py @@ -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} diff --git a/tests/test_multi_query_errors.py b/tests/test_multi_query_errors.py index 8162d986c..7387a81dd 100644 --- a/tests/test_multi_query_errors.py +++ b/tests/test_multi_query_errors.py @@ -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} diff --git a/tests/test_no_schema_split.py b/tests/test_no_schema_split.py index b0b5958c1..6169867ba 100644 --- a/tests/test_no_schema_split.py +++ b/tests/test_no_schema_split.py @@ -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): diff --git a/tests/test_openapi_schema_type.py b/tests/test_openapi_schema_type.py index a45ea20c8..98d978745 100644 --- a/tests/test_openapi_schema_type.py +++ b/tests/test_openapi_schema_type.py @@ -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) diff --git a/tests/test_openapi_separate_input_output_schemas.py b/tests/test_openapi_separate_input_output_schemas.py index c9a05418b..bfe5e9a71 100644 --- a/tests/test_openapi_separate_input_output_schemas.py +++ b/tests/test_openapi_separate_input_output_schemas.py @@ -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", diff --git a/tests/test_optional_file_list.py b/tests/test_optional_file_list.py index 0228900cf..686025864 100644 --- a/tests/test_optional_file_list.py +++ b/tests/test_optional_file_list.py @@ -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]} diff --git a/tests/test_params_repr.py b/tests/test_params_repr.py index baa172497..19c2e8d69 100644 --- a/tests/test_params_repr.py +++ b/tests/test_params_repr.py @@ -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(): diff --git a/tests/test_pydantic_v1_v2_01.py b/tests/test_pydantic_v1_v2_01.py index 769e5fab6..ebf86b99f 100644 --- a/tests/test_pydantic_v1_v2_01.py +++ b/tests/test_pydantic_v1_v2_01.py @@ -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() diff --git a/tests/test_pydantic_v1_v2_list.py b/tests/test_pydantic_v1_v2_list.py index 64f3dd344..8879b010d 100644 --- a/tests/test_pydantic_v1_v2_list.py +++ b/tests/test_pydantic_v1_v2_list.py @@ -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}) diff --git a/tests/test_pydantic_v1_v2_mixed.py b/tests/test_pydantic_v1_v2_mixed.py index 54d408827..e66583cd5 100644 --- a/tests/test_pydantic_v1_v2_mixed.py +++ b/tests/test_pydantic_v1_v2_mixed.py @@ -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( diff --git a/tests/test_pydantic_v1_v2_multifile/main.py b/tests/test_pydantic_v1_v2_multifile/main.py index 8985cb7b4..9397368ab 100644 --- a/tests/test_pydantic_v1_v2_multifile/main.py +++ b/tests/test_pydantic_v1_v2_multifile/main.py @@ -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] diff --git a/tests/test_pydantic_v1_v2_multifile/modelsv1.py b/tests/test_pydantic_v1_v2_multifile/modelsv1.py index 889291a1a..0cc8de455 100644 --- a/tests/test_pydantic_v1_v2_multifile/modelsv1.py +++ b/tests/test_pydantic_v1_v2_multifile/modelsv1.py @@ -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): diff --git a/tests/test_pydantic_v1_v2_multifile/modelsv2.py b/tests/test_pydantic_v1_v2_multifile/modelsv2.py index 2c8c6ea35..d80b77e10 100644 --- a/tests/test_pydantic_v1_v2_multifile/modelsv2.py +++ b/tests/test_pydantic_v1_v2_multifile/modelsv2.py @@ -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): diff --git a/tests/test_pydantic_v1_v2_multifile/modelsv2b.py b/tests/test_pydantic_v1_v2_multifile/modelsv2b.py index dc0c06c66..e992bea2e 100644 --- a/tests/test_pydantic_v1_v2_multifile/modelsv2b.py +++ b/tests/test_pydantic_v1_v2_multifile/modelsv2b.py @@ -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): diff --git a/tests/test_pydantic_v1_v2_noneable.py b/tests/test_pydantic_v1_v2_noneable.py index d2d6f6635..3e8646990 100644 --- a/tests/test_pydantic_v1_v2_noneable.py +++ b/tests/test_pydantic_v1_v2_noneable.py @@ -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() diff --git a/tests/test_pydanticv2_dataclasses_uuid_stringified_annotations.py b/tests/test_pydanticv2_dataclasses_uuid_stringified_annotations.py index c9f94563b..b72b0518a 100644 --- a/tests/test_pydanticv2_dataclasses_uuid_stringified_annotations.py +++ b/tests/test_pydanticv2_dataclasses_uuid_stringified_annotations.py @@ -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 diff --git a/tests/test_regex_deprecated_body.py b/tests/test_regex_deprecated_body.py index 74654ff3c..cfbff19c8 100644 --- a/tests/test_regex_deprecated_body.py +++ b/tests/test_regex_deprecated_body.py @@ -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 diff --git a/tests/test_regex_deprecated_params.py b/tests/test_regex_deprecated_params.py index 2ce64c686..7d9988f9f 100644 --- a/tests/test_regex_deprecated_params.py +++ b/tests/test_regex_deprecated_params.py @@ -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 diff --git a/tests/test_request_body_parameters_media_type.py b/tests/test_request_body_parameters_media_type.py index 8c72fee54..d1bff9ddf 100644 --- a/tests/test_request_body_parameters_media_type.py +++ b/tests/test_request_body_parameters_media_type.py @@ -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 diff --git a/tests/test_request_params/test_body/test_list.py b/tests/test_request_params/test_body/test_list.py index 18a2a2f30..b2503a1c4 100644 --- a/tests/test_request_params/test_body/test_list.py +++ b/tests/test_request_params/test_body/test_list.py @@ -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( diff --git a/tests/test_request_params/test_body/test_optional_list.py b/tests/test_request_params/test_body/test_optional_list.py index e2ecdc7f4..ede635f84 100644 --- a/tests/test_request_params/test_body/test_optional_list.py +++ b/tests/test_request_params/test_body/test_optional_list.py @@ -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" ) diff --git a/tests/test_request_params/test_body/test_optional_str.py b/tests/test_request_params/test_body/test_optional_str.py index a49f5a367..9e0c7217c 100644 --- a/tests/test_request_params/test_body/test_optional_str.py +++ b/tests/test_request_params/test_body/test_optional_str.py @@ -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 diff --git a/tests/test_request_params/test_body/test_required_str.py b/tests/test_request_params/test_body/test_required_str.py index 18c660cad..aa47a4ede 100644 --- a/tests/test_request_params/test_body/test_required_str.py +++ b/tests/test_request_params/test_body/test_required_str.py @@ -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) diff --git a/tests/test_request_params/test_body/utils.py b/tests/test_request_params/test_body/utils.py index 5151a82d3..bf07394f9 100644 --- a/tests/test_request_params/test_body/utils.py +++ b/tests/test_request_params/test_body/utils.py @@ -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] diff --git a/tests/test_request_params/test_cookie/test_optional_str.py b/tests/test_request_params/test_cookie/test_optional_str.py index 1eec45689..f2d02dae5 100644 --- a/tests/test_request_params/test_cookie/test_optional_str.py +++ b/tests/test_request_params/test_cookie/test_optional_str.py @@ -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 diff --git a/tests/test_request_params/test_cookie/test_required_str.py b/tests/test_request_params/test_cookie/test_required_str.py index 6d0fa2ef2..3255857d4 100644 --- a/tests/test_request_params/test_cookie/test_required_str.py +++ b/tests/test_request_params/test_cookie/test_required_str.py @@ -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 diff --git a/tests/test_request_params/test_file/test_list.py b/tests/test_request_params/test_file/test_list.py index 94a33967f..52b032e44 100644 --- a/tests/test_request_params/test_file/test_list.py +++ b/tests/test_request_params/test_file/test_list.py @@ -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]} diff --git a/tests/test_request_params/test_file/test_optional.py b/tests/test_request_params/test_file/test_optional.py index 2c1ca9530..b7177e071 100644 --- a/tests/test_request_params/test_file/test_optional.py +++ b/tests/test_request_params/test_file/test_optional.py @@ -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 diff --git a/tests/test_request_params/test_file/test_optional_list.py b/tests/test_request_params/test_file/test_optional_list.py index 20e36501f..af9fe552c 100644 --- a/tests/test_request_params/test_file/test_optional_list.py +++ b/tests/test_request_params/test_file/test_optional_list.py @@ -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, ): diff --git a/tests/test_request_params/test_file/test_required.py b/tests/test_request_params/test_file/test_required.py index f041ac1cc..2979a1040 100644 --- a/tests/test_request_params/test_file/test_required.py +++ b/tests/test_request_params/test_file/test_required.py @@ -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 diff --git a/tests/test_request_params/test_file/utils.py b/tests/test_request_params/test_file/utils.py index e33f64385..e2a97ccd9 100644 --- a/tests/test_request_params/test_file/utils.py +++ b/tests/test_request_params/test_file/utils.py @@ -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] diff --git a/tests/test_request_params/test_form/test_list.py b/tests/test_request_params/test_form/test_list.py index 69a1b6a38..9f45aa755 100644 --- a/tests/test_request_params/test_form/test_list.py +++ b/tests/test_request_params/test_form/test_list.py @@ -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( diff --git a/tests/test_request_params/test_form/test_optional_list.py b/tests/test_request_params/test_form/test_optional_list.py index 1f779a7ed..0af6d3477 100644 --- a/tests/test_request_params/test_form/test_optional_list.py +++ b/tests/test_request_params/test_form/test_optional_list.py @@ -1,11 +1,10 @@ -from typing import List, Optional +from typing import Annotated, Optional import pytest from dirty_equals import IsDict 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 @@ -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]], Form()] = None, + p: Annotated[Optional[list[str]], Form()] = None, ): return {"p": p} class FormModelOptionalListStr(BaseModel): - p: Optional[List[str]] = None + p: Optional[list[str]] = None @app.post("/model-optional-list-str", operation_id="model_optional_list_str") @@ -95,13 +94,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]], Form(alias="p_alias")] = None, + p: Annotated[Optional[list[str]], Form(alias="p_alias")] = None, ): return {"p": p} class FormModelOptionalListAlias(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") @@ -193,13 +192,13 @@ def test_optional_list_alias_by_alias(path: str): "/optional-list-validation-alias", operation_id="optional_list_validation_alias" ) def read_optional_list_validation_alias( - p: Annotated[Optional[List[str]], Form(validation_alias="p_val_alias")] = None, + p: Annotated[Optional[list[str]], Form(validation_alias="p_val_alias")] = None, ): return {"p": p} class FormModelOptionalListValidationAlias(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( @@ -300,14 +299,14 @@ def test_optional_list_validation_alias_by_validation_alias(path: str): ) def read_optional_list_alias_and_validation_alias( p: Annotated[ - Optional[List[str]], Form(alias="p_alias", validation_alias="p_val_alias") + Optional[list[str]], Form(alias="p_alias", validation_alias="p_val_alias") ] = None, ): return {"p": p} class FormModelOptionalListAliasAndValidationAlias(BaseModel): - p: Optional[List[str]] = Field( + p: Optional[list[str]] = Field( None, alias="p_alias", validation_alias="p_val_alias" ) diff --git a/tests/test_request_params/test_form/test_optional_str.py b/tests/test_request_params/test_form/test_optional_str.py index 969865945..92329216e 100644 --- a/tests/test_request_params/test_form/test_optional_str.py +++ b/tests/test_request_params/test_form/test_optional_str.py @@ -1,11 +1,10 @@ -from typing import Optional +from typing import Annotated, Optional import pytest from dirty_equals import IsDict 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 diff --git a/tests/test_request_params/test_form/test_required_str.py b/tests/test_request_params/test_form/test_required_str.py index c901e7b44..1e3303804 100644 --- a/tests/test_request_params/test_form/test_required_str.py +++ b/tests/test_request_params/test_form/test_required_str.py @@ -1,9 +1,10 @@ +from typing import Annotated + import pytest from dirty_equals import IsDict, IsOneOf 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 diff --git a/tests/test_request_params/test_form/utils.py b/tests/test_request_params/test_form/utils.py index d200650df..913217311 100644 --- a/tests/test_request_params/test_form/utils.py +++ b/tests/test_request_params/test_form/utils.py @@ -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/x-www-form-urlencoded"]["schema"] return body_schema.get("$ref", "").split("/")[-1] diff --git a/tests/test_request_params/test_header/test_list.py b/tests/test_request_params/test_header/test_list.py index 4a5f4bb64..62f9f36ff 100644 --- a/tests/test_request_params/test_header/test_list.py +++ b/tests/test_request_params/test_header/test_list.py @@ -1,11 +1,10 @@ -from typing import List +from typing import Annotated import pytest from dirty_equals import AnyThing, IsDict, IsOneOf, IsPartialDict from fastapi import FastAPI, Header from fastapi.testclient import TestClient from pydantic import BaseModel, Field -from typing_extensions import Annotated from tests.utils import needs_pydanticv2 @@ -16,12 +15,12 @@ app = FastAPI() @app.get("/required-list-str") -async def read_required_list_str(p: Annotated[List[str], Header()]): +async def read_required_list_str(p: Annotated[list[str], Header()]): return {"p": p} class HeaderModelRequiredListStr(BaseModel): - p: List[str] + p: list[str] @app.get("/model-required-list-str") @@ -96,12 +95,12 @@ def test_required_list_str(path: str): @app.get("/required-list-alias") -async def read_required_list_alias(p: Annotated[List[str], Header(alias="p_alias")]): +async def read_required_list_alias(p: Annotated[list[str], Header(alias="p_alias")]): return {"p": p} class HeaderModelRequiredListAlias(BaseModel): - p: List[str] = Field(alias="p_alias") + p: list[str] = Field(alias="p_alias") @app.get("/model-required-list-alias") @@ -219,13 +218,13 @@ def test_required_list_alias_by_alias(path: str): @app.get("/required-list-validation-alias") def read_required_list_validation_alias( - p: Annotated[List[str], Header(validation_alias="p_val_alias")], + p: Annotated[list[str], Header(validation_alias="p_val_alias")], ): return {"p": p} class HeaderModelRequiredListValidationAlias(BaseModel): - p: List[str] = Field(validation_alias="p_val_alias") + p: list[str] = Field(validation_alias="p_val_alias") @app.get("/model-required-list-validation-alias") @@ -328,13 +327,13 @@ def test_required_list_validation_alias_by_validation_alias(path: str): @app.get("/required-list-alias-and-validation-alias") def read_required_list_alias_and_validation_alias( - p: Annotated[List[str], Header(alias="p_alias", validation_alias="p_val_alias")], + p: Annotated[list[str], Header(alias="p_alias", validation_alias="p_val_alias")], ): return {"p": p} class HeaderModelRequiredListAliasAndValidationAlias(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.get("/model-required-list-alias-and-validation-alias") diff --git a/tests/test_request_params/test_header/test_optional_list.py b/tests/test_request_params/test_header/test_optional_list.py index e81025c02..88243f05a 100644 --- a/tests/test_request_params/test_header/test_optional_list.py +++ b/tests/test_request_params/test_header/test_optional_list.py @@ -1,11 +1,10 @@ -from typing import List, Optional +from typing import Annotated, Optional import pytest from dirty_equals import IsDict from fastapi import FastAPI, Header from fastapi.testclient import TestClient from pydantic import BaseModel, Field -from typing_extensions import Annotated from tests.utils import needs_pydanticv2 @@ -17,13 +16,13 @@ app = FastAPI() @app.get("/optional-list-str") async def read_optional_list_str( - p: Annotated[Optional[List[str]], Header()] = None, + p: Annotated[Optional[list[str]], Header()] = None, ): return {"p": p} class HeaderModelOptionalListStr(BaseModel): - p: Optional[List[str]] = None + p: Optional[list[str]] = None @app.get("/model-optional-list-str") @@ -93,13 +92,13 @@ def test_optional_list_str(path: str): @app.get("/optional-list-alias") async def read_optional_list_alias( - p: Annotated[Optional[List[str]], Header(alias="p_alias")] = None, + p: Annotated[Optional[list[str]], Header(alias="p_alias")] = None, ): return {"p": p} class HeaderModelOptionalListAlias(BaseModel): - p: Optional[List[str]] = Field(None, alias="p_alias") + p: Optional[list[str]] = Field(None, alias="p_alias") @app.get("/model-optional-list-alias") @@ -187,13 +186,13 @@ def test_optional_list_alias_by_alias(path: str): @app.get("/optional-list-validation-alias") def read_optional_list_validation_alias( - p: Annotated[Optional[List[str]], Header(validation_alias="p_val_alias")] = None, + p: Annotated[Optional[list[str]], Header(validation_alias="p_val_alias")] = None, ): return {"p": p} class HeaderModelOptionalListValidationAlias(BaseModel): - p: Optional[List[str]] = Field(None, validation_alias="p_val_alias") + p: Optional[list[str]] = Field(None, validation_alias="p_val_alias") @app.get("/model-optional-list-validation-alias") @@ -273,14 +272,14 @@ def test_optional_list_validation_alias_by_validation_alias(path: str): @app.get("/optional-list-alias-and-validation-alias") def read_optional_list_alias_and_validation_alias( p: Annotated[ - Optional[List[str]], Header(alias="p_alias", validation_alias="p_val_alias") + Optional[list[str]], Header(alias="p_alias", validation_alias="p_val_alias") ] = None, ): return {"p": p} class HeaderModelOptionalListAliasAndValidationAlias(BaseModel): - p: Optional[List[str]] = Field( + p: Optional[list[str]] = Field( None, alias="p_alias", validation_alias="p_val_alias" ) diff --git a/tests/test_request_params/test_header/test_optional_str.py b/tests/test_request_params/test_header/test_optional_str.py index 5ae9f2670..e40b1669e 100644 --- a/tests/test_request_params/test_header/test_optional_str.py +++ b/tests/test_request_params/test_header/test_optional_str.py @@ -1,11 +1,10 @@ -from typing import Optional +from typing import Annotated, Optional import pytest from dirty_equals import IsDict from fastapi import FastAPI, Header from fastapi.testclient import TestClient from pydantic import BaseModel, Field -from typing_extensions import Annotated from tests.utils import needs_pydanticv2 diff --git a/tests/test_request_params/test_header/test_required_str.py b/tests/test_request_params/test_header/test_required_str.py index d57c66919..23554d3e4 100644 --- a/tests/test_request_params/test_header/test_required_str.py +++ b/tests/test_request_params/test_header/test_required_str.py @@ -1,9 +1,10 @@ +from typing import Annotated + import pytest from dirty_equals import AnyThing, IsDict, IsOneOf, IsPartialDict from fastapi import FastAPI, Header from fastapi.testclient import TestClient from pydantic import BaseModel, Field -from typing_extensions import Annotated from tests.utils import needs_pydanticv2 diff --git a/tests/test_request_params/test_path/test_required_str.py b/tests/test_request_params/test_path/test_required_str.py index 641719967..ecd4eb61c 100644 --- a/tests/test_request_params/test_path/test_required_str.py +++ b/tests/test_request_params/test_path/test_required_str.py @@ -1,7 +1,8 @@ +from typing import Annotated + import pytest from fastapi import FastAPI, Path from fastapi.testclient import TestClient -from typing_extensions import Annotated from tests.utils import needs_pydanticv2 diff --git a/tests/test_request_params/test_query/test_list.py b/tests/test_request_params/test_query/test_list.py index 71cbca51f..6a3000fbf 100644 --- a/tests/test_request_params/test_query/test_list.py +++ b/tests/test_request_params/test_query/test_list.py @@ -1,11 +1,10 @@ -from typing import List +from typing import Annotated import pytest from dirty_equals import IsDict, IsOneOf from fastapi import FastAPI, Query from fastapi.testclient import TestClient from pydantic import BaseModel, Field -from typing_extensions import Annotated from tests.utils import needs_pydanticv2 @@ -16,12 +15,12 @@ app = FastAPI() @app.get("/required-list-str") -async def read_required_list_str(p: Annotated[List[str], Query()]): +async def read_required_list_str(p: Annotated[list[str], Query()]): return {"p": p} class QueryModelRequiredListStr(BaseModel): - p: List[str] + p: list[str] @app.get("/model-required-list-str") @@ -96,12 +95,12 @@ def test_required_list_str(path: str): @app.get("/required-list-alias") -async def read_required_list_alias(p: Annotated[List[str], Query(alias="p_alias")]): +async def read_required_list_alias(p: Annotated[list[str], Query(alias="p_alias")]): return {"p": p} class QueryModelRequiredListAlias(BaseModel): - p: List[str] = Field(alias="p_alias") + p: list[str] = Field(alias="p_alias") @app.get("/model-required-list-alias") @@ -219,13 +218,13 @@ def test_required_list_alias_by_alias(path: str): @app.get("/required-list-validation-alias") def read_required_list_validation_alias( - p: Annotated[List[str], Query(validation_alias="p_val_alias")], + p: Annotated[list[str], Query(validation_alias="p_val_alias")], ): return {"p": p} class QueryModelRequiredListValidationAlias(BaseModel): - p: List[str] = Field(validation_alias="p_val_alias") + p: list[str] = Field(validation_alias="p_val_alias") @app.get("/model-required-list-validation-alias") @@ -326,13 +325,13 @@ def test_required_list_validation_alias_by_validation_alias(path: str): @app.get("/required-list-alias-and-validation-alias") def read_required_list_alias_and_validation_alias( - p: Annotated[List[str], Query(alias="p_alias", validation_alias="p_val_alias")], + p: Annotated[list[str], Query(alias="p_alias", validation_alias="p_val_alias")], ): return {"p": p} class QueryModelRequiredListAliasAndValidationAlias(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.get("/model-required-list-alias-and-validation-alias") diff --git a/tests/test_request_params/test_query/test_optional_list.py b/tests/test_request_params/test_query/test_optional_list.py index 560921336..f4b8ec6a8 100644 --- a/tests/test_request_params/test_query/test_optional_list.py +++ b/tests/test_request_params/test_query/test_optional_list.py @@ -1,11 +1,10 @@ -from typing import List, Optional +from typing import Annotated, Optional import pytest from dirty_equals import IsDict from fastapi import FastAPI, Query from fastapi.testclient import TestClient from pydantic import BaseModel, Field -from typing_extensions import Annotated from tests.utils import needs_pydanticv2 @@ -17,13 +16,13 @@ app = FastAPI() @app.get("/optional-list-str") async def read_optional_list_str( - p: Annotated[Optional[List[str]], Query()] = None, + p: Annotated[Optional[list[str]], Query()] = None, ): return {"p": p} class QueryModelOptionalListStr(BaseModel): - p: Optional[List[str]] = None + p: Optional[list[str]] = None @app.get("/model-optional-list-str") @@ -93,13 +92,13 @@ def test_optional_list_str(path: str): @app.get("/optional-list-alias") async def read_optional_list_alias( - p: Annotated[Optional[List[str]], Query(alias="p_alias")] = None, + p: Annotated[Optional[list[str]], Query(alias="p_alias")] = None, ): return {"p": p} class QueryModelOptionalListAlias(BaseModel): - p: Optional[List[str]] = Field(None, alias="p_alias") + p: Optional[list[str]] = Field(None, alias="p_alias") @app.get("/model-optional-list-alias") @@ -187,13 +186,13 @@ def test_optional_list_alias_by_alias(path: str): @app.get("/optional-list-validation-alias") def read_optional_list_validation_alias( - p: Annotated[Optional[List[str]], Query(validation_alias="p_val_alias")] = None, + p: Annotated[Optional[list[str]], Query(validation_alias="p_val_alias")] = None, ): return {"p": p} class QueryModelOptionalListValidationAlias(BaseModel): - p: Optional[List[str]] = Field(None, validation_alias="p_val_alias") + p: Optional[list[str]] = Field(None, validation_alias="p_val_alias") @app.get("/model-optional-list-validation-alias") @@ -271,14 +270,14 @@ def test_optional_list_validation_alias_by_validation_alias(path: str): @app.get("/optional-list-alias-and-validation-alias") def read_optional_list_alias_and_validation_alias( p: Annotated[ - Optional[List[str]], Query(alias="p_alias", validation_alias="p_val_alias") + Optional[list[str]], Query(alias="p_alias", validation_alias="p_val_alias") ] = None, ): return {"p": p} class QueryModelOptionalListAliasAndValidationAlias(BaseModel): - p: Optional[List[str]] = Field( + p: Optional[list[str]] = Field( None, alias="p_alias", validation_alias="p_val_alias" ) diff --git a/tests/test_request_params/test_query/test_optional_str.py b/tests/test_request_params/test_query/test_optional_str.py index 25e4ea42e..c7d20e37d 100644 --- a/tests/test_request_params/test_query/test_optional_str.py +++ b/tests/test_request_params/test_query/test_optional_str.py @@ -1,11 +1,10 @@ -from typing import Optional +from typing import Annotated, Optional import pytest from dirty_equals import IsDict from fastapi import FastAPI, Query from fastapi.testclient import TestClient from pydantic import BaseModel, Field -from typing_extensions import Annotated from tests.utils import needs_pydanticv2 diff --git a/tests/test_request_params/test_query/test_required_str.py b/tests/test_request_params/test_query/test_required_str.py index aa0731e2c..ce30f3b1f 100644 --- a/tests/test_request_params/test_query/test_required_str.py +++ b/tests/test_request_params/test_query/test_required_str.py @@ -1,9 +1,10 @@ +from typing import Annotated + import pytest from dirty_equals import IsDict, IsOneOf from fastapi import FastAPI, Query from fastapi.testclient import TestClient from pydantic import BaseModel, Field -from typing_extensions import Annotated from tests.utils import needs_pydanticv2 diff --git a/tests/test_response_by_alias.py b/tests/test_response_by_alias.py index e162cd39b..5b241c76b 100644 --- a/tests/test_response_by_alias.py +++ b/tests/test_response_by_alias.py @@ -1,5 +1,3 @@ -from typing import List - from fastapi import FastAPI from fastapi._compat import PYDANTIC_V2 from fastapi.testclient import TestClient @@ -45,7 +43,7 @@ def read_model(): return Model(alias="Foo") -@app.get("/list", response_model=List[Model], response_model_by_alias=False) +@app.get("/list", response_model=list[Model], response_model_by_alias=False) def read_list(): return [{"alias": "Foo"}, {"alias": "Bar"}] @@ -60,7 +58,7 @@ def by_alias_model(): return Model(alias="Foo") -@app.get("/by-alias/list", response_model=List[Model]) +@app.get("/by-alias/list", response_model=list[Model]) def by_alias_list(): return [{"alias": "Foo"}, {"alias": "Bar"}] @@ -75,7 +73,7 @@ def no_alias_model(): return ModelNoAlias(name="Foo") -@app.get("/no-alias/list", response_model=List[ModelNoAlias]) +@app.get("/no-alias/list", response_model=list[ModelNoAlias]) def no_alias_list(): return [{"name": "Foo"}, {"name": "Bar"}] diff --git a/tests/test_response_class_no_mediatype.py b/tests/test_response_class_no_mediatype.py index 706929ac3..4dc164bf9 100644 --- a/tests/test_response_class_no_mediatype.py +++ b/tests/test_response_class_no_mediatype.py @@ -1,5 +1,3 @@ -import typing - from fastapi import FastAPI, Response 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( diff --git a/tests/test_response_code_no_body.py b/tests/test_response_code_no_body.py index 3ca8708f1..70456a746 100644 --- a/tests/test_response_code_no_body.py +++ b/tests/test_response_code_no_body.py @@ -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( diff --git a/tests/test_response_model_as_return_annotation.py b/tests/test_response_model_as_return_annotation.py index 1745c69b6..44e882a76 100644 --- a/tests/test_response_model_as_return_annotation.py +++ b/tests/test_response_model_as_return_annotation.py @@ -1,4 +1,4 @@ -from typing import List, Union +from typing import Union import pytest from fastapi import FastAPI @@ -191,7 +191,7 @@ def response_model_filtering_model_annotation_submodel_return_submodel() -> DBUs return DBUser(name="John", surname="Doe", password_hash="secret") -@app.get("/response_model_list_of_model-no_annotation", response_model=List[User]) +@app.get("/response_model_list_of_model-no_annotation", response_model=list[User]) def response_model_list_of_model_no_annotation(): return [ DBUser(name="John", surname="Doe", password_hash="secret"), @@ -200,7 +200,7 @@ def response_model_list_of_model_no_annotation(): @app.get("/no_response_model-annotation_list_of_model") -def no_response_model_annotation_list_of_model() -> List[User]: +def no_response_model_annotation_list_of_model() -> list[User]: return [ DBUser(name="John", surname="Doe", password_hash="secret"), DBUser(name="Jane", surname="Does", password_hash="secret2"), @@ -208,7 +208,7 @@ def no_response_model_annotation_list_of_model() -> List[User]: @app.get("/no_response_model-annotation_forward_ref_list_of_model") -def no_response_model_annotation_forward_ref_list_of_model() -> "List[User]": +def no_response_model_annotation_forward_ref_list_of_model() -> "list[User]": return [ DBUser(name="John", surname="Doe", password_hash="secret"), DBUser(name="Jane", surname="Does", password_hash="secret2"), diff --git a/tests/test_response_model_data_filter.py b/tests/test_response_model_data_filter.py index a3e0f95f0..358697d6d 100644 --- a/tests/test_response_model_data_filter.py +++ b/tests/test_response_model_data_filter.py @@ -1,5 +1,3 @@ -from typing import List - from fastapi import FastAPI from fastapi.testclient import TestClient from pydantic import BaseModel @@ -44,7 +42,7 @@ async def read_pet(pet_id: int): return pet -@app.get("/pets/", response_model=List[PetOut]) +@app.get("/pets/", response_model=list[PetOut]) async def read_pets(): user = UserDB( email="johndoe@example.com", diff --git a/tests/test_response_model_data_filter_no_inheritance.py b/tests/test_response_model_data_filter_no_inheritance.py index 64003a841..c0c2f3a9d 100644 --- a/tests/test_response_model_data_filter_no_inheritance.py +++ b/tests/test_response_model_data_filter_no_inheritance.py @@ -1,5 +1,3 @@ -from typing import List - from fastapi import FastAPI from fastapi.testclient import TestClient from pydantic import BaseModel @@ -46,7 +44,7 @@ async def read_pet(pet_id: int): return pet -@app.get("/pets/", response_model=List[PetOut]) +@app.get("/pets/", response_model=list[PetOut]) async def read_pets(): user = UserDB( email="johndoe@example.com", diff --git a/tests/test_response_model_invalid.py b/tests/test_response_model_invalid.py index 88b55a436..f884b5c74 100644 --- a/tests/test_response_model_invalid.py +++ b/tests/test_response_model_invalid.py @@ -1,5 +1,3 @@ -from typing import List - import pytest from fastapi import FastAPI from fastapi.exceptions import FastAPIError @@ -22,7 +20,7 @@ def test_invalid_response_model_sub_type_raises(): with pytest.raises(FastAPIError): app = FastAPI() - @app.get("/", response_model=List[NonPydanticModel]) + @app.get("/", response_model=list[NonPydanticModel]) def read_root(): pass # pragma: nocover @@ -40,6 +38,6 @@ def test_invalid_response_model_sub_type_in_responses_raises(): with pytest.raises(FastAPIError): app = FastAPI() - @app.get("/", responses={"500": {"model": List[NonPydanticModel]}}) + @app.get("/", responses={"500": {"model": list[NonPydanticModel]}}) def read_root(): pass # pragma: nocover diff --git a/tests/test_response_model_sub_types.py b/tests/test_response_model_sub_types.py index 660bcee1b..8036bcb07 100644 --- a/tests/test_response_model_sub_types.py +++ b/tests/test_response_model_sub_types.py @@ -1,5 +1,3 @@ -from typing import List - from fastapi import FastAPI from fastapi.testclient import TestClient from pydantic import BaseModel @@ -17,7 +15,7 @@ def valid1(): pass -@app.get("/valid2", responses={"500": {"model": List[int]}}) +@app.get("/valid2", responses={"500": {"model": list[int]}}) def valid2(): pass @@ -27,7 +25,7 @@ def valid3(): pass -@app.get("/valid4", responses={"500": {"model": List[Model]}}) +@app.get("/valid4", responses={"500": {"model": list[Model]}}) def valid4(): pass diff --git a/tests/test_router_events.py b/tests/test_router_events.py index dd7ff3314..9df299cda 100644 --- a/tests/test_router_events.py +++ b/tests/test_router_events.py @@ -1,5 +1,6 @@ +from collections.abc import AsyncGenerator from contextlib import asynccontextmanager -from typing import AsyncGenerator, Dict, Union +from typing import Union import pytest from fastapi import APIRouter, FastAPI, Request @@ -28,7 +29,7 @@ def test_router_events(state: State) -> None: app = FastAPI() @app.get("/") - def main() -> Dict[str, str]: + def main() -> dict[str, str]: return {"message": "Hello World"} @app.on_event("startup") @@ -96,7 +97,7 @@ def test_app_lifespan_state(state: State) -> None: app = FastAPI(lifespan=lifespan) @app.get("/") - def main() -> Dict[str, str]: + def main() -> dict[str, str]: return {"message": "Hello World"} assert state.app_startup is False @@ -113,19 +114,19 @@ def test_app_lifespan_state(state: State) -> None: def test_router_nested_lifespan_state(state: State) -> None: @asynccontextmanager - async def lifespan(app: FastAPI) -> AsyncGenerator[Dict[str, bool], None]: + async def lifespan(app: FastAPI) -> AsyncGenerator[dict[str, bool], None]: state.app_startup = True yield {"app": True} state.app_shutdown = True @asynccontextmanager - async def router_lifespan(app: FastAPI) -> AsyncGenerator[Dict[str, bool], None]: + async def router_lifespan(app: FastAPI) -> AsyncGenerator[dict[str, bool], None]: state.router_startup = True yield {"router": True} state.router_shutdown = True @asynccontextmanager - async def subrouter_lifespan(app: FastAPI) -> AsyncGenerator[Dict[str, bool], None]: + async def subrouter_lifespan(app: FastAPI) -> AsyncGenerator[dict[str, bool], None]: state.sub_router_startup = True yield {"sub_router": True} state.sub_router_shutdown = True @@ -139,7 +140,7 @@ def test_router_nested_lifespan_state(state: State) -> None: app.include_router(router) @app.get("/") - def main(request: Request) -> Dict[str, str]: + def main(request: Request) -> dict[str, str]: assert request.state.app assert request.state.router assert request.state.sub_router @@ -175,7 +176,7 @@ def test_router_nested_lifespan_state_overriding_by_parent() -> None: @asynccontextmanager async def lifespan( app: FastAPI, - ) -> AsyncGenerator[Dict[str, Union[str, bool]], None]: + ) -> AsyncGenerator[dict[str, Union[str, bool]], None]: yield { "app_specific": True, "overridden": "app", @@ -184,7 +185,7 @@ def test_router_nested_lifespan_state_overriding_by_parent() -> None: @asynccontextmanager async def router_lifespan( app: FastAPI, - ) -> AsyncGenerator[Dict[str, Union[str, bool]], None]: + ) -> AsyncGenerator[dict[str, Union[str, bool]], None]: yield { "router_specific": True, "overridden": "router", # should override parent @@ -225,7 +226,7 @@ def test_merged_mixed_state_lifespans() -> None: yield @asynccontextmanager - async def router_lifespan(app: FastAPI) -> AsyncGenerator[Dict[str, bool], None]: + async def router_lifespan(app: FastAPI) -> AsyncGenerator[dict[str, bool], None]: yield {"router": True} @asynccontextmanager diff --git a/tests/test_security_oauth2_authorization_code_bearer_scopes_openapi.py b/tests/test_security_oauth2_authorization_code_bearer_scopes_openapi.py index d41f1dc1f..583007c8b 100644 --- a/tests/test_security_oauth2_authorization_code_bearer_scopes_openapi.py +++ b/tests/test_security_oauth2_authorization_code_bearer_scopes_openapi.py @@ -1,12 +1,11 @@ # Ref: https://github.com/fastapi/fastapi/issues/14454 -from typing import Optional +from typing import Annotated, Optional from fastapi import APIRouter, Depends, FastAPI, Security from fastapi.security import OAuth2AuthorizationCodeBearer from fastapi.testclient import TestClient from inline_snapshot import snapshot -from typing_extensions import Annotated oauth2_scheme = OAuth2AuthorizationCodeBearer( authorizationUrl="authorize", diff --git a/tests/test_security_oauth2_authorization_code_bearer_scopes_openapi_simple.py b/tests/test_security_oauth2_authorization_code_bearer_scopes_openapi_simple.py index ff866d4fc..1c21369d3 100644 --- a/tests/test_security_oauth2_authorization_code_bearer_scopes_openapi_simple.py +++ b/tests/test_security_oauth2_authorization_code_bearer_scopes_openapi_simple.py @@ -1,10 +1,11 @@ # Ref: https://github.com/fastapi/fastapi/issues/14454 +from typing import Annotated + from fastapi import Depends, FastAPI, Security from fastapi.security import OAuth2AuthorizationCodeBearer from fastapi.testclient import TestClient from inline_snapshot import snapshot -from typing_extensions import Annotated oauth2_scheme = OAuth2AuthorizationCodeBearer( authorizationUrl="api/oauth/authorize", diff --git a/tests/test_security_scopes.py b/tests/test_security_scopes.py index 248fd2bcc..fccb026fe 100644 --- a/tests/test_security_scopes.py +++ b/tests/test_security_scopes.py @@ -1,9 +1,8 @@ -from typing import Dict +from typing import Annotated import pytest from fastapi import Depends, FastAPI, Security from fastapi.testclient import TestClient -from typing_extensions import Annotated @pytest.fixture(name="call_counter") @@ -12,7 +11,7 @@ def call_counter_fixture(): @pytest.fixture(name="app") -def app_fixture(call_counter: Dict[str, int]): +def app_fixture(call_counter: dict[str, int]): def get_db(): call_counter["count"] += 1 return f"db_{call_counter['count']}" @@ -38,7 +37,7 @@ def client_fixture(app: FastAPI): def test_security_scopes_dependency_called_once( - client: TestClient, call_counter: Dict[str, int] + client: TestClient, call_counter: dict[str, int] ): response = client.get("/") diff --git a/tests/test_security_scopes_dont_propagate.py b/tests/test_security_scopes_dont_propagate.py index 2bbcc749d..c306ed059 100644 --- a/tests/test_security_scopes_dont_propagate.py +++ b/tests/test_security_scopes_dont_propagate.py @@ -1,11 +1,10 @@ # Ref: https://github.com/tiangolo/fastapi/issues/5623 -from typing import Any, Dict, List +from typing import Annotated, Any from fastapi import FastAPI, Security from fastapi.security import SecurityScopes from fastapi.testclient import TestClient -from typing_extensions import Annotated async def security1(scopes: SecurityScopes): @@ -17,8 +16,8 @@ async def security2(scopes: SecurityScopes): async def dep3( - dep1: Annotated[List[str], Security(security1, scopes=["scope1"])], - dep2: Annotated[List[str], Security(security2, scopes=["scope2"])], + dep1: Annotated[list[str], Security(security1, scopes=["scope1"])], + dep2: Annotated[list[str], Security(security2, scopes=["scope2"])], ): return {"dep1": dep1, "dep2": dep2} @@ -28,7 +27,7 @@ app = FastAPI() @app.get("/scopes") def get_scopes( - dep3: Annotated[Dict[str, Any], Security(dep3, scopes=["scope3"])], + dep3: Annotated[dict[str, Any], Security(dep3, scopes=["scope3"])], ): return dep3 diff --git a/tests/test_security_scopes_sub_dependency.py b/tests/test_security_scopes_sub_dependency.py index 9cc668d8e..2c64d5f3d 100644 --- a/tests/test_security_scopes_sub_dependency.py +++ b/tests/test_security_scopes_sub_dependency.py @@ -1,12 +1,12 @@ # Ref: https://github.com/fastapi/fastapi/discussions/6024#discussioncomment-8541913 -from typing import Dict + +from typing import Annotated import pytest from fastapi import Depends, FastAPI, Security from fastapi.security import SecurityScopes from fastapi.testclient import TestClient -from typing_extensions import Annotated @pytest.fixture(name="call_counts") @@ -20,7 +20,7 @@ def call_counts_fixture(): @pytest.fixture(name="app") -def app_fixture(call_counts: Dict[str, int]): +def app_fixture(call_counts: dict[str, int]): def get_db_session(): call_counts["get_db_session"] += 1 return f"db_session_{call_counts['get_db_session']}" @@ -75,7 +75,7 @@ def client_fixture(app: FastAPI): def test_security_scopes_sub_dependency_caching( - client: TestClient, call_counts: Dict[str, int] + client: TestClient, call_counts: dict[str, int] ): response = client.get("/") diff --git a/tests/test_serialize_response.py b/tests/test_serialize_response.py index d823e5e04..14f88dd93 100644 --- a/tests/test_serialize_response.py +++ b/tests/test_serialize_response.py @@ -1,4 +1,4 @@ -from typing import List, Optional +from typing import Optional from fastapi import FastAPI from fastapi.testclient import TestClient @@ -10,7 +10,7 @@ app = FastAPI() class Item(BaseModel): name: str price: Optional[float] = None - owner_ids: Optional[List[int]] = None + owner_ids: Optional[list[int]] = None @app.get("/items/valid", response_model=Item) @@ -23,7 +23,7 @@ def get_coerce(): return {"name": "coerce", "price": "1.0"} -@app.get("/items/validlist", response_model=List[Item]) +@app.get("/items/validlist", response_model=list[Item]) def get_validlist(): return [ {"name": "foo"}, diff --git a/tests/test_serialize_response_dataclass.py b/tests/test_serialize_response_dataclass.py index 1e3bf3b28..ee695368b 100644 --- a/tests/test_serialize_response_dataclass.py +++ b/tests/test_serialize_response_dataclass.py @@ -1,6 +1,6 @@ from dataclasses import dataclass from datetime import datetime -from typing import List, Optional +from typing import Optional from fastapi import FastAPI from fastapi.testclient import TestClient @@ -13,7 +13,7 @@ class Item: name: str date: datetime price: Optional[float] = None - owner_ids: Optional[List[int]] = None + owner_ids: Optional[list[int]] = None @app.get("/items/valid", response_model=Item) @@ -33,7 +33,7 @@ def get_coerce(): return {"name": "coerce", "date": datetime(2021, 7, 26).isoformat(), "price": "1.0"} -@app.get("/items/validlist", response_model=List[Item]) +@app.get("/items/validlist", response_model=list[Item]) def get_validlist(): return [ {"name": "foo", "date": datetime(2021, 7, 26)}, @@ -47,7 +47,7 @@ def get_validlist(): ] -@app.get("/items/objectlist", response_model=List[Item]) +@app.get("/items/objectlist", response_model=list[Item]) def get_objectlist(): return [ Item(name="foo", date=datetime(2021, 7, 26)), diff --git a/tests/test_serialize_response_model.py b/tests/test_serialize_response_model.py index 3bb46b2e9..79c90c9c2 100644 --- a/tests/test_serialize_response_model.py +++ b/tests/test_serialize_response_model.py @@ -1,4 +1,4 @@ -from typing import Dict, List, Optional +from typing import Optional from fastapi import FastAPI from pydantic import BaseModel, Field @@ -10,7 +10,7 @@ app = FastAPI() class Item(BaseModel): name: str = Field(alias="aliased_name") price: Optional[float] = None - owner_ids: Optional[List[int]] = None + owner_ids: Optional[list[int]] = None @app.get("/items/valid", response_model=Item) @@ -23,7 +23,7 @@ def get_coerce(): return Item(aliased_name="coerce", price="1.0") -@app.get("/items/validlist", response_model=List[Item]) +@app.get("/items/validlist", response_model=list[Item]) def get_validlist(): return [ Item(aliased_name="foo"), @@ -32,7 +32,7 @@ def get_validlist(): ] -@app.get("/items/validdict", response_model=Dict[str, Item]) +@app.get("/items/validdict", response_model=dict[str, Item]) def get_validdict(): return { "k1": Item(aliased_name="foo"), @@ -59,7 +59,7 @@ def get_coerce_exclude_unset(): @app.get( "/items/validlist-exclude-unset", - response_model=List[Item], + response_model=list[Item], response_model_exclude_unset=True, ) def get_validlist_exclude_unset(): @@ -72,7 +72,7 @@ def get_validlist_exclude_unset(): @app.get( "/items/validdict-exclude-unset", - response_model=Dict[str, Item], + response_model=dict[str, Item], response_model_exclude_unset=True, ) def get_validdict_exclude_unset(): diff --git a/tests/test_stringified_annotation_dependency.py b/tests/test_stringified_annotation_dependency.py index 89bb884b5..ce8807495 100644 --- a/tests/test_stringified_annotation_dependency.py +++ b/tests/test_stringified_annotation_dependency.py @@ -1,12 +1,11 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Annotated import pytest from fastapi import Depends, FastAPI from fastapi.testclient import TestClient from inline_snapshot import snapshot -from typing_extensions import Annotated if TYPE_CHECKING: # pragma: no cover from collections.abc import AsyncGenerator diff --git a/tests/test_stringified_annotations_simple.py b/tests/test_stringified_annotations_simple.py index 9bd6d09d6..6e037fb21 100644 --- a/tests/test_stringified_annotations_simple.py +++ b/tests/test_stringified_annotations_simple.py @@ -1,8 +1,9 @@ from __future__ import annotations +from typing import Annotated + from fastapi import Depends, FastAPI, Request from fastapi.testclient import TestClient -from typing_extensions import Annotated from .utils import needs_py310 diff --git a/tests/test_tuples.py b/tests/test_tuples.py index ca33d2580..fbc69a614 100644 --- a/tests/test_tuples.py +++ b/tests/test_tuples.py @@ -1,5 +1,3 @@ -from typing import List, Tuple - from dirty_equals import IsDict from fastapi import FastAPI, Form from fastapi.testclient import TestClient @@ -9,7 +7,7 @@ app = FastAPI() class ItemGroup(BaseModel): - items: List[Tuple[str, str]] + items: list[tuple[str, str]] class Coordinate(BaseModel): @@ -23,12 +21,12 @@ def post_model_with_tuple(item_group: ItemGroup): @app.post("/tuple-of-models/") -def post_tuple_of_models(square: Tuple[Coordinate, Coordinate]): +def post_tuple_of_models(square: tuple[Coordinate, Coordinate]): return square @app.post("/tuple-form/") -def hello(values: Tuple[int, int] = Form()): +def hello(values: tuple[int, int] = Form()): return values diff --git a/tests/test_tutorial/test_websockets/test_tutorial003.py b/tests/test_tutorial/test_websockets/test_tutorial003.py index f303990f0..0be1fc81d 100644 --- a/tests/test_tutorial/test_websockets/test_tutorial003.py +++ b/tests/test_tutorial/test_websockets/test_tutorial003.py @@ -35,9 +35,10 @@ def test_get(client: TestClient, html: str): def test_websocket_handle_disconnection(client: TestClient): - with client.websocket_connect("/ws/1234") as connection, client.websocket_connect( - "/ws/5678" - ) as connection_two: + with ( + client.websocket_connect("/ws/1234") as connection, + client.websocket_connect("/ws/5678") as connection_two, + ): connection.send_text("Hello from 1234") data1 = connection.receive_text() assert data1 == "You wrote: Hello from 1234" diff --git a/tests/test_union_body_discriminator.py b/tests/test_union_body_discriminator.py index 6af9e1d22..bf41a7291 100644 --- a/tests/test_union_body_discriminator.py +++ b/tests/test_union_body_discriminator.py @@ -1,11 +1,11 @@ -from typing import Any, Dict, Union +from typing import Annotated, Any, Union from dirty_equals import IsDict from fastapi import FastAPI from fastapi.testclient import TestClient from inline_snapshot import snapshot from pydantic import BaseModel, Field -from typing_extensions import Annotated, Literal +from typing_extensions import Literal from .utils import needs_pydanticv2 @@ -32,7 +32,7 @@ def test_discriminator_pydantic_v2() -> None: @app.post("/items/") def save_union_body_discriminator( item: Item, q: Annotated[str, Field(description="Query string")] - ) -> Dict[str, Any]: + ) -> dict[str, Any]: return {"item": item} client = TestClient(app) diff --git a/tests/test_union_body_discriminator_annotated.py b/tests/test_union_body_discriminator_annotated.py index 14145e6f6..f8108c8df 100644 --- a/tests/test_union_body_discriminator_annotated.py +++ b/tests/test_union_body_discriminator_annotated.py @@ -1,13 +1,12 @@ # Ref: https://github.com/fastapi/fastapi/discussions/14495 -from typing import Union +from typing import Annotated, Union import pytest from fastapi import FastAPI from fastapi.testclient import TestClient from inline_snapshot import snapshot from pydantic import BaseModel -from typing_extensions import Annotated from .utils import needs_pydanticv2 diff --git a/tests/test_union_forms.py b/tests/test_union_forms.py index cbe98ea82..018949f0c 100644 --- a/tests/test_union_forms.py +++ b/tests/test_union_forms.py @@ -1,9 +1,8 @@ -from typing import Union +from typing import Annotated, Union from fastapi import FastAPI, Form from fastapi.testclient import TestClient from pydantic import BaseModel -from typing_extensions import Annotated app = FastAPI() diff --git a/tests/test_validate_response.py b/tests/test_validate_response.py index cd97007a4..938d41956 100644 --- a/tests/test_validate_response.py +++ b/tests/test_validate_response.py @@ -1,4 +1,4 @@ -from typing import List, Optional, Union +from typing import Optional, Union import pytest from fastapi import FastAPI @@ -12,7 +12,7 @@ app = FastAPI() class Item(BaseModel): name: str price: Optional[float] = None - owner_ids: Optional[List[int]] = None + owner_ids: Optional[list[int]] = None @app.get("/items/invalid", response_model=Item) @@ -38,7 +38,7 @@ def get_innerinvalid(): return {"name": "double invalid", "price": "foo", "owner_ids": ["foo", "bar"]} -@app.get("/items/invalidlist", response_model=List[Item]) +@app.get("/items/invalidlist", response_model=list[Item]) def get_invalidlist(): return [ {"name": "foo"}, diff --git a/tests/test_validate_response_dataclass.py b/tests/test_validate_response_dataclass.py index 0415988a0..67282bcde 100644 --- a/tests/test_validate_response_dataclass.py +++ b/tests/test_validate_response_dataclass.py @@ -1,4 +1,4 @@ -from typing import List, Optional +from typing import Optional import pytest from fastapi import FastAPI @@ -13,7 +13,7 @@ app = FastAPI() class Item: name: str price: Optional[float] = None - owner_ids: Optional[List[int]] = None + owner_ids: Optional[list[int]] = None @app.get("/items/invalid", response_model=Item) @@ -26,7 +26,7 @@ def get_innerinvalid(): return {"name": "double invalid", "price": "foo", "owner_ids": ["foo", "bar"]} -@app.get("/items/invalidlist", response_model=List[Item]) +@app.get("/items/invalidlist", response_model=list[Item]) def get_invalidlist(): return [ {"name": "foo"}, diff --git a/tests/test_validate_response_recursive/app.py b/tests/test_validate_response_recursive/app.py index d23d27980..8f76572ba 100644 --- a/tests/test_validate_response_recursive/app.py +++ b/tests/test_validate_response_recursive/app.py @@ -1,5 +1,3 @@ -from typing import List - from fastapi import FastAPI from fastapi._compat import PYDANTIC_V2 from pydantic import BaseModel @@ -8,17 +6,17 @@ app = FastAPI() class RecursiveItem(BaseModel): - sub_items: List["RecursiveItem"] = [] + sub_items: list["RecursiveItem"] = [] name: str class RecursiveSubitemInSubmodel(BaseModel): - sub_items2: List["RecursiveItemViaSubmodel"] = [] + sub_items2: list["RecursiveItemViaSubmodel"] = [] name: str class RecursiveItemViaSubmodel(BaseModel): - sub_items1: List[RecursiveSubitemInSubmodel] = [] + sub_items1: list[RecursiveSubitemInSubmodel] = [] name: str diff --git a/tests/test_webhooks_security.py b/tests/test_webhooks_security.py index 21a694cb5..982ae1e21 100644 --- a/tests/test_webhooks_security.py +++ b/tests/test_webhooks_security.py @@ -1,10 +1,10 @@ from datetime import datetime +from typing import Annotated from fastapi import FastAPI, Security from fastapi.security import HTTPBearer from fastapi.testclient import TestClient from pydantic import BaseModel -from typing_extensions import Annotated app = FastAPI() diff --git a/tests/test_ws_dependencies.py b/tests/test_ws_dependencies.py index ccb1c4b7d..51b982c00 100644 --- a/tests/test_ws_dependencies.py +++ b/tests/test_ws_dependencies.py @@ -1,16 +1,15 @@ import json -from typing import List +from typing import Annotated from fastapi import APIRouter, Depends, FastAPI, WebSocket from fastapi.testclient import TestClient -from typing_extensions import Annotated -def dependency_list() -> List[str]: +def dependency_list() -> list[str]: return [] -DepList = Annotated[List[str], Depends(dependency_list)] +DepList = Annotated[list[str], Depends(dependency_list)] def create_dependency(name: str):