]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
➖ Drop support for `pydantic.v1` (#14609)
authorSebastián Ramírez <tiangolo@gmail.com>
Sat, 27 Dec 2025 12:54:56 +0000 (04:54 -0800)
committerGitHub <noreply@github.com>
Sat, 27 Dec 2025 12:54:56 +0000 (13:54 +0100)
55 files changed:
docs_src/request_form_models/tutorial002_pv1_an_py39.py [deleted file]
docs_src/request_form_models/tutorial002_pv1_py39.py [deleted file]
fastapi/_compat/__init__.py
fastapi/_compat/main.py [deleted file]
fastapi/_compat/may_v1.py [deleted file]
fastapi/_compat/model_field.py [deleted file]
fastapi/_compat/shared.py
fastapi/_compat/v1.py [deleted file]
fastapi/_compat/v2.py
fastapi/datastructures.py
fastapi/dependencies/utils.py
fastapi/encoders.py
fastapi/exceptions.py
fastapi/openapi/models.py
fastapi/openapi/utils.py
fastapi/param_functions.py
fastapi/params.py
fastapi/routing.py
fastapi/temp_pydantic_v1_params.py [deleted file]
fastapi/utils.py
pyproject.toml
tests/test_compat.py
tests/test_compat_params_v1.py [deleted file]
tests/test_datetime_custom_encoder.py
tests/test_filter_pydantic_sub_model/__init__.py [deleted file]
tests/test_filter_pydantic_sub_model/app_pv1.py [deleted file]
tests/test_filter_pydantic_sub_model/test_filter_pydantic_sub_model_pv1.py [deleted file]
tests/test_get_model_definitions_formfeed_escape.py
tests/test_inherited_custom_class.py
tests/test_jsonable_encoder.py
tests/test_pydantic_v1_deprecation_warnings.py [deleted file]
tests/test_pydantic_v1_error.py [new file with mode: 0644]
tests/test_pydantic_v1_v2_01.py [deleted file]
tests/test_pydantic_v1_v2_list.py [deleted file]
tests/test_pydantic_v1_v2_mixed.py [deleted file]
tests/test_pydantic_v1_v2_multifile/__init__.py [deleted file]
tests/test_pydantic_v1_v2_multifile/main.py [deleted file]
tests/test_pydantic_v1_v2_multifile/modelsv1.py [deleted file]
tests/test_pydantic_v1_v2_multifile/modelsv2.py [deleted file]
tests/test_pydantic_v1_v2_multifile/modelsv2b.py [deleted file]
tests/test_pydantic_v1_v2_multifile/test_multifile.py [deleted file]
tests/test_pydantic_v1_v2_noneable.py [deleted file]
tests/test_read_with_orm_mode.py
tests/test_response_model_as_return_annotation.py
tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial007_pv1.py [deleted file]
tests/test_tutorial/test_pydantic_v1_in_v2/__init__.py [deleted file]
tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial001.py [deleted file]
tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial002.py [deleted file]
tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial003.py [deleted file]
tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial004.py [deleted file]
tests/test_tutorial/test_request_form_models/test_tutorial002_pv1.py [deleted file]
tests/test_tutorial/test_schema_extra_example/test_tutorial001_pv1.py [deleted file]
tests/test_tutorial/test_settings/test_app03.py
tests/test_tutorial/test_settings/test_tutorial001.py
tests/utils.py

diff --git a/docs_src/request_form_models/tutorial002_pv1_an_py39.py b/docs_src/request_form_models/tutorial002_pv1_an_py39.py
deleted file mode 100644 (file)
index 392e687..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Annotated
-
-from fastapi import FastAPI
-from fastapi.temp_pydantic_v1_params import Form
-from pydantic.v1 import BaseModel
-
-app = FastAPI()
-
-
-class FormData(BaseModel):
-    username: str
-    password: str
-
-    class Config:
-        extra = "forbid"
-
-
-@app.post("/login/")
-async def login(data: Annotated[FormData, Form()]):
-    return data
diff --git a/docs_src/request_form_models/tutorial002_pv1_py39.py b/docs_src/request_form_models/tutorial002_pv1_py39.py
deleted file mode 100644 (file)
index da160b3..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-from fastapi import FastAPI
-from fastapi.temp_pydantic_v1_params import Form
-from pydantic.v1 import BaseModel
-
-app = FastAPI()
-
-
-class FormData(BaseModel):
-    username: str
-    password: str
-
-    class Config:
-        extra = "forbid"
-
-
-@app.post("/login/")
-async def login(data: FormData = Form()):
-    return data
index fd1df8c6a780340389bcf7a6f9ef62d3570e6e38..3dfaf9b7121a086377cb29123ad7da8cbd1bc1a8 100644 (file)
@@ -1,43 +1,9 @@
-from .main import BaseConfig as BaseConfig
-from .main import PydanticSchemaGenerationError as PydanticSchemaGenerationError
-from .main import RequiredParam as RequiredParam
-from .main import Undefined as Undefined
-from .main import UndefinedType as UndefinedType
-from .main import Url as Url
-from .main import Validator as Validator
-from .main import _get_model_config as _get_model_config
-from .main import _is_error_wrapper as _is_error_wrapper
-from .main import _is_model_class as _is_model_class
-from .main import _is_model_field as _is_model_field
-from .main import _is_undefined as _is_undefined
-from .main import _model_dump as _model_dump
-from .main import copy_field_info as copy_field_info
-from .main import create_body_model as create_body_model
-from .main import evaluate_forwardref as evaluate_forwardref
-from .main import get_annotation_from_field_info as get_annotation_from_field_info
-from .main import get_cached_model_fields as get_cached_model_fields
-from .main import get_compat_model_name_map as get_compat_model_name_map
-from .main import get_definitions as get_definitions
-from .main import get_missing_field_error as get_missing_field_error
-from .main import get_schema_from_model_field as get_schema_from_model_field
-from .main import is_bytes_field as is_bytes_field
-from .main import is_bytes_sequence_field as is_bytes_sequence_field
-from .main import is_scalar_field as is_scalar_field
-from .main import is_scalar_sequence_field as is_scalar_sequence_field
-from .main import is_sequence_field as is_sequence_field
-from .main import serialize_sequence_value as serialize_sequence_value
-from .main import (
-    with_info_plain_validator_function as with_info_plain_validator_function,
-)
-from .may_v1 import CoreSchema as CoreSchema
-from .may_v1 import GetJsonSchemaHandler as GetJsonSchemaHandler
-from .may_v1 import JsonSchemaValue as JsonSchemaValue
-from .may_v1 import _normalize_errors as _normalize_errors
-from .model_field import ModelField as ModelField
 from .shared import PYDANTIC_V2 as PYDANTIC_V2
 from .shared import PYDANTIC_VERSION_MINOR_TUPLE as PYDANTIC_VERSION_MINOR_TUPLE
 from .shared import annotation_is_pydantic_v1 as annotation_is_pydantic_v1
 from .shared import field_annotation_is_scalar as field_annotation_is_scalar
+from .shared import is_pydantic_v1_model_class as is_pydantic_v1_model_class
+from .shared import is_pydantic_v1_model_instance as is_pydantic_v1_model_instance
 from .shared import (
     is_uploadfile_or_nonable_uploadfile_annotation as is_uploadfile_or_nonable_uploadfile_annotation,
 )
@@ -47,3 +13,29 @@ from .shared import (
 from .shared import lenient_issubclass as lenient_issubclass
 from .shared import sequence_types as sequence_types
 from .shared import value_is_sequence as value_is_sequence
+from .v2 import BaseConfig as BaseConfig
+from .v2 import ModelField as ModelField
+from .v2 import PydanticSchemaGenerationError as PydanticSchemaGenerationError
+from .v2 import RequiredParam as RequiredParam
+from .v2 import Undefined as Undefined
+from .v2 import UndefinedType as UndefinedType
+from .v2 import Url as Url
+from .v2 import Validator as Validator
+from .v2 import _regenerate_error_with_loc as _regenerate_error_with_loc
+from .v2 import copy_field_info as copy_field_info
+from .v2 import create_body_model as create_body_model
+from .v2 import evaluate_forwardref as evaluate_forwardref
+from .v2 import get_cached_model_fields as get_cached_model_fields
+from .v2 import get_compat_model_name_map as get_compat_model_name_map
+from .v2 import get_definitions as get_definitions
+from .v2 import get_missing_field_error as get_missing_field_error
+from .v2 import get_schema_from_model_field as get_schema_from_model_field
+from .v2 import is_bytes_field as is_bytes_field
+from .v2 import is_bytes_sequence_field as is_bytes_sequence_field
+from .v2 import is_scalar_field as is_scalar_field
+from .v2 import is_scalar_sequence_field as is_scalar_sequence_field
+from .v2 import is_sequence_field as is_sequence_field
+from .v2 import serialize_sequence_value as serialize_sequence_value
+from .v2 import (
+    with_info_plain_validator_function as with_info_plain_validator_function,
+)
diff --git a/fastapi/_compat/main.py b/fastapi/_compat/main.py
deleted file mode 100644 (file)
index 95053a2..0000000
+++ /dev/null
@@ -1,264 +0,0 @@
-import sys
-from collections.abc import Sequence
-from functools import lru_cache
-from typing import (
-    Any,
-)
-
-from fastapi._compat import may_v1
-from fastapi._compat.shared import lenient_issubclass
-from fastapi.types import ModelNameMap
-from pydantic import BaseModel
-from typing_extensions import Literal
-
-from . import v2
-from .model_field import ModelField
-from .v2 import BaseConfig as BaseConfig
-from .v2 import FieldInfo as FieldInfo
-from .v2 import PydanticSchemaGenerationError as PydanticSchemaGenerationError
-from .v2 import RequiredParam as RequiredParam
-from .v2 import Undefined as Undefined
-from .v2 import UndefinedType as UndefinedType
-from .v2 import Url as Url
-from .v2 import Validator as Validator
-from .v2 import evaluate_forwardref as evaluate_forwardref
-from .v2 import get_missing_field_error as get_missing_field_error
-from .v2 import (
-    with_info_plain_validator_function as with_info_plain_validator_function,
-)
-
-
-@lru_cache
-def get_cached_model_fields(model: type[BaseModel]) -> list[ModelField]:
-    if lenient_issubclass(model, may_v1.BaseModel):
-        from fastapi._compat import v1
-
-        return v1.get_model_fields(model)  # type: ignore[arg-type,return-value]
-    else:
-        from . import v2
-
-        return v2.get_model_fields(model)  # type: ignore[return-value]
-
-
-def _is_undefined(value: object) -> bool:
-    if isinstance(value, may_v1.UndefinedType):
-        return True
-
-    return isinstance(value, v2.UndefinedType)
-
-
-def _get_model_config(model: BaseModel) -> Any:
-    if isinstance(model, may_v1.BaseModel):
-        from fastapi._compat import v1
-
-        return v1._get_model_config(model)
-
-    return v2._get_model_config(model)
-
-
-def _model_dump(
-    model: BaseModel, mode: Literal["json", "python"] = "json", **kwargs: Any
-) -> Any:
-    if isinstance(model, may_v1.BaseModel):
-        from fastapi._compat import v1
-
-        return v1._model_dump(model, mode=mode, **kwargs)
-
-    return v2._model_dump(model, mode=mode, **kwargs)
-
-
-def _is_error_wrapper(exc: Exception) -> bool:
-    if isinstance(exc, may_v1.ErrorWrapper):
-        return True
-
-    return isinstance(exc, v2.ErrorWrapper)
-
-
-def copy_field_info(*, field_info: FieldInfo, annotation: Any) -> FieldInfo:
-    if isinstance(field_info, may_v1.FieldInfo):
-        from fastapi._compat import v1
-
-        return v1.copy_field_info(field_info=field_info, annotation=annotation)
-
-    return v2.copy_field_info(field_info=field_info, annotation=annotation)
-
-
-def create_body_model(
-    *, fields: Sequence[ModelField], model_name: str
-) -> type[BaseModel]:
-    if fields and isinstance(fields[0], may_v1.ModelField):
-        from fastapi._compat import v1
-
-        return v1.create_body_model(fields=fields, model_name=model_name)
-
-    return v2.create_body_model(fields=fields, model_name=model_name)  # type: ignore[arg-type]
-
-
-def get_annotation_from_field_info(
-    annotation: Any, field_info: FieldInfo, field_name: str
-) -> Any:
-    if isinstance(field_info, may_v1.FieldInfo):
-        from fastapi._compat import v1
-
-        return v1.get_annotation_from_field_info(
-            annotation=annotation, field_info=field_info, field_name=field_name
-        )
-
-    return v2.get_annotation_from_field_info(
-        annotation=annotation, field_info=field_info, field_name=field_name
-    )
-
-
-def is_bytes_field(field: ModelField) -> bool:
-    if isinstance(field, may_v1.ModelField):
-        from fastapi._compat import v1
-
-        return v1.is_bytes_field(field)
-
-    return v2.is_bytes_field(field)  # type: ignore[arg-type]
-
-
-def is_bytes_sequence_field(field: ModelField) -> bool:
-    if isinstance(field, may_v1.ModelField):
-        from fastapi._compat import v1
-
-        return v1.is_bytes_sequence_field(field)
-
-    return v2.is_bytes_sequence_field(field)  # type: ignore[arg-type]
-
-
-def is_scalar_field(field: ModelField) -> bool:
-    if isinstance(field, may_v1.ModelField):
-        from fastapi._compat import v1
-
-        return v1.is_scalar_field(field)
-
-    return v2.is_scalar_field(field)  # type: ignore[arg-type]
-
-
-def is_scalar_sequence_field(field: ModelField) -> bool:
-    return v2.is_scalar_sequence_field(field)  # type: ignore[arg-type]
-
-
-def is_sequence_field(field: ModelField) -> bool:
-    if isinstance(field, may_v1.ModelField):
-        from fastapi._compat import v1
-
-        return v1.is_sequence_field(field)
-
-    return v2.is_sequence_field(field)  # type: ignore[arg-type]
-
-
-def serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:
-    if isinstance(field, may_v1.ModelField):
-        from fastapi._compat import v1
-
-        return v1.serialize_sequence_value(field=field, value=value)
-
-    return v2.serialize_sequence_value(field=field, value=value)  # type: ignore[arg-type]
-
-
-def get_compat_model_name_map(fields: list[ModelField]) -> ModelNameMap:
-    v1_model_fields = [
-        field for field in fields if isinstance(field, may_v1.ModelField)
-    ]
-    if v1_model_fields:
-        from fastapi._compat import v1
-
-        v1_flat_models = v1.get_flat_models_from_fields(
-            v1_model_fields,  # type: ignore[arg-type]
-            known_models=set(),
-        )
-        all_flat_models = v1_flat_models
-    else:
-        all_flat_models = set()
-
-    v2_model_fields = [field for field in fields if isinstance(field, v2.ModelField)]
-    v2_flat_models = v2.get_flat_models_from_fields(v2_model_fields, known_models=set())
-    all_flat_models = all_flat_models.union(v2_flat_models)  # type: ignore[arg-type]
-
-    model_name_map = v2.get_model_name_map(all_flat_models)  # type: ignore[arg-type]
-    return model_name_map
-
-
-def get_definitions(
-    *,
-    fields: list[ModelField],
-    model_name_map: ModelNameMap,
-    separate_input_output_schemas: bool = True,
-) -> tuple[
-    dict[
-        tuple[ModelField, Literal["validation", "serialization"]],
-        may_v1.JsonSchemaValue,
-    ],
-    dict[str, dict[str, Any]],
-]:
-    if sys.version_info < (3, 14):
-        v1_fields = [field for field in fields if isinstance(field, may_v1.ModelField)]
-        v1_field_maps, v1_definitions = may_v1.get_definitions(
-            fields=v1_fields,  # type: ignore[arg-type]
-            model_name_map=model_name_map,
-            separate_input_output_schemas=separate_input_output_schemas,
-        )
-
-        v2_fields = [field for field in fields if isinstance(field, v2.ModelField)]
-        v2_field_maps, v2_definitions = v2.get_definitions(
-            fields=v2_fields,
-            model_name_map=model_name_map,
-            separate_input_output_schemas=separate_input_output_schemas,
-        )
-        all_definitions = {**v1_definitions, **v2_definitions}
-        all_field_maps = {**v1_field_maps, **v2_field_maps}  # type: ignore[misc]
-        return all_field_maps, all_definitions
-
-    # Pydantic v1 is not supported since Python 3.14
-    else:
-        v2_fields = [field for field in fields if isinstance(field, v2.ModelField)]
-        v2_field_maps, v2_definitions = v2.get_definitions(
-            fields=v2_fields,
-            model_name_map=model_name_map,
-            separate_input_output_schemas=separate_input_output_schemas,
-        )
-        return v2_field_maps, v2_definitions
-
-
-def get_schema_from_model_field(
-    *,
-    field: ModelField,
-    model_name_map: ModelNameMap,
-    field_mapping: dict[
-        tuple[ModelField, Literal["validation", "serialization"]],
-        may_v1.JsonSchemaValue,
-    ],
-    separate_input_output_schemas: bool = True,
-) -> dict[str, Any]:
-    if isinstance(field, may_v1.ModelField):
-        from fastapi._compat import v1
-
-        return v1.get_schema_from_model_field(
-            field=field,
-            model_name_map=model_name_map,
-            field_mapping=field_mapping,
-            separate_input_output_schemas=separate_input_output_schemas,
-        )
-
-    return v2.get_schema_from_model_field(
-        field=field,  # type: ignore[arg-type]
-        model_name_map=model_name_map,
-        field_mapping=field_mapping,  # type: ignore[arg-type]
-        separate_input_output_schemas=separate_input_output_schemas,
-    )
-
-
-def _is_model_field(value: Any) -> bool:
-    if isinstance(value, may_v1.ModelField):
-        return True
-
-    return isinstance(value, v2.ModelField)
-
-
-def _is_model_class(value: Any) -> bool:
-    if lenient_issubclass(value, may_v1.BaseModel):
-        return True
-
-    return lenient_issubclass(value, v2.BaseModel)  # type: ignore[attr-defined]
diff --git a/fastapi/_compat/may_v1.py b/fastapi/_compat/may_v1.py
deleted file mode 100644 (file)
index 3ac86aa..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-import sys
-from collections.abc import Sequence
-from typing import Any, Literal, Union
-
-from fastapi.types import ModelNameMap
-
-if sys.version_info >= (3, 14):
-
-    class AnyUrl:
-        pass
-
-    class BaseConfig:
-        pass
-
-    class BaseModel:
-        pass
-
-    class Color:
-        pass
-
-    class CoreSchema:
-        pass
-
-    class ErrorWrapper:
-        pass
-
-    class FieldInfo:
-        pass
-
-    class GetJsonSchemaHandler:
-        pass
-
-    class JsonSchemaValue:
-        pass
-
-    class ModelField:
-        pass
-
-    class NameEmail:
-        pass
-
-    class RequiredParam:
-        pass
-
-    class SecretBytes:
-        pass
-
-    class SecretStr:
-        pass
-
-    class Undefined:
-        pass
-
-    class UndefinedType:
-        pass
-
-    class Url:
-        pass
-
-    from .v2 import ValidationError, create_model
-
-    def get_definitions(
-        *,
-        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]],
-    ]:
-        return {}, {}  # pragma: no cover
-
-
-else:
-    from .v1 import AnyUrl as AnyUrl
-    from .v1 import BaseConfig as BaseConfig
-    from .v1 import BaseModel as BaseModel
-    from .v1 import Color as Color
-    from .v1 import CoreSchema as CoreSchema
-    from .v1 import ErrorWrapper as ErrorWrapper
-    from .v1 import FieldInfo as FieldInfo
-    from .v1 import GetJsonSchemaHandler as GetJsonSchemaHandler
-    from .v1 import JsonSchemaValue as JsonSchemaValue
-    from .v1 import ModelField as ModelField
-    from .v1 import NameEmail as NameEmail
-    from .v1 import RequiredParam as RequiredParam
-    from .v1 import SecretBytes as SecretBytes
-    from .v1 import SecretStr as SecretStr
-    from .v1 import Undefined as Undefined
-    from .v1 import UndefinedType as UndefinedType
-    from .v1 import Url as Url
-    from .v1 import ValidationError, create_model
-    from .v1 import get_definitions as get_definitions
-
-
-RequestErrorModel: type[BaseModel] = create_model("Request")
-
-
-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(
-                errors=[error], model=RequestErrorModel
-            ).errors()
-            use_errors.extend(new_errors)
-        elif isinstance(error, list):
-            use_errors.extend(_normalize_errors(error))
-        else:
-            use_errors.append(error)
-    return use_errors
-
-
-def _regenerate_error_with_loc(
-    *, 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)
-    ]
-
-    return updated_loc_errors
diff --git a/fastapi/_compat/model_field.py b/fastapi/_compat/model_field.py
deleted file mode 100644 (file)
index 47d05cb..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-from typing import (
-    Any,
-    Union,
-)
-
-from fastapi.types import IncEx
-from pydantic.fields import FieldInfo
-from typing_extensions import Literal, Protocol
-
-
-class ModelField(Protocol):
-    field_info: "FieldInfo"
-    name: str
-    mode: Literal["validation", "serialization"] = "validation"
-    _version: Literal["v1", "v2"] = "v1"
-
-    @property
-    def alias(self) -> str: ...
-
-    @property
-    def required(self) -> bool: ...
-
-    @property
-    def default(self) -> Any: ...
-
-    @property
-    def type_(self) -> Any: ...
-
-    def get_default(self) -> Any: ...
-
-    def validate(
-        self,
-        value: Any,
-        values: dict[str, Any] = {},  # noqa: B006
-        *,
-        loc: tuple[Union[int, str], ...] = (),
-    ) -> tuple[Any, Union[list[dict[str, Any]], None]]: ...
-
-    def serialize(
-        self,
-        value: Any,
-        *,
-        mode: Literal["json", "python"] = "json",
-        include: Union[IncEx, None] = None,
-        exclude: Union[IncEx, None] = None,
-        by_alias: bool = True,
-        exclude_unset: bool = False,
-        exclude_defaults: bool = False,
-        exclude_none: bool = False,
-    ) -> Any: ...
index 3a11e88ac931f7b17a0f8233d1f208b29dbd9b6a..419b58f7f2a3547a36111316bbac45443b2fcc5f 100644 (file)
@@ -1,6 +1,7 @@
 import sys
 import types
 import typing
+import warnings
 from collections import deque
 from collections.abc import Mapping, Sequence
 from dataclasses import is_dataclass
@@ -10,7 +11,6 @@ from typing import (
     Union,
 )
 
-from fastapi._compat import may_v1
 from fastapi.types import UnionType
 from pydantic import BaseModel
 from pydantic.version import VERSION as PYDANTIC_VERSION
@@ -81,9 +81,7 @@ def value_is_sequence(value: Any) -> bool:
 
 def _annotation_is_complex(annotation: Union[type[Any], None]) -> bool:
     return (
-        lenient_issubclass(
-            annotation, (BaseModel, may_v1.BaseModel, Mapping, UploadFile)
-        )
+        lenient_issubclass(annotation, (BaseModel, Mapping, UploadFile))
         or _annotation_is_sequence(annotation)
         or is_dataclass(annotation)
     )
@@ -179,13 +177,27 @@ def is_uploadfile_sequence_annotation(annotation: Any) -> bool:
     )
 
 
+def is_pydantic_v1_model_instance(obj: Any) -> bool:
+    with warnings.catch_warnings():
+        warnings.simplefilter("ignore", UserWarning)
+        from pydantic import v1
+    return isinstance(obj, v1.BaseModel)
+
+
+def is_pydantic_v1_model_class(cls: Any) -> bool:
+    with warnings.catch_warnings():
+        warnings.simplefilter("ignore", UserWarning)
+        from pydantic import v1
+    return lenient_issubclass(cls, v1.BaseModel)
+
+
 def annotation_is_pydantic_v1(annotation: Any) -> bool:
-    if lenient_issubclass(annotation, may_v1.BaseModel):
+    if is_pydantic_v1_model_class(annotation):
         return True
     origin = get_origin(annotation)
     if origin is Union or origin is UnionType:
         for arg in get_args(annotation):
-            if lenient_issubclass(arg, may_v1.BaseModel):
+            if is_pydantic_v1_model_class(arg):
                 return True
     if field_annotation_is_sequence(annotation):
         for sub_annotation in get_args(annotation):
diff --git a/fastapi/_compat/v1.py b/fastapi/_compat/v1.py
deleted file mode 100644 (file)
index b0a9dd3..0000000
+++ /dev/null
@@ -1,222 +0,0 @@
-from collections.abc import Sequence
-from copy import copy
-from dataclasses import dataclass, is_dataclass
-from enum import Enum
-from typing import (
-    Any,
-    Callable,
-    Union,
-)
-
-from fastapi._compat import shared
-from fastapi.openapi.constants import REF_PREFIX as REF_PREFIX
-from fastapi.types import ModelNameMap
-from pydantic.v1 import BaseConfig as BaseConfig
-from pydantic.v1 import BaseModel as BaseModel
-from pydantic.v1 import ValidationError as ValidationError
-from pydantic.v1 import create_model as create_model
-from pydantic.v1.class_validators import Validator as Validator
-from pydantic.v1.color import Color as Color
-from pydantic.v1.error_wrappers import ErrorWrapper as ErrorWrapper
-from pydantic.v1.fields import (
-    SHAPE_FROZENSET,
-    SHAPE_LIST,
-    SHAPE_SEQUENCE,
-    SHAPE_SET,
-    SHAPE_SINGLETON,
-    SHAPE_TUPLE,
-    SHAPE_TUPLE_ELLIPSIS,
-)
-from pydantic.v1.fields import FieldInfo as FieldInfo
-from pydantic.v1.fields import ModelField as ModelField
-from pydantic.v1.fields import Undefined as Undefined
-from pydantic.v1.fields import UndefinedType as UndefinedType
-from pydantic.v1.networks import AnyUrl as AnyUrl
-from pydantic.v1.networks import NameEmail as NameEmail
-from pydantic.v1.schema import TypeModelSet as TypeModelSet
-from pydantic.v1.schema import field_schema, model_process_schema
-from pydantic.v1.schema import (
-    get_annotation_from_field_info as get_annotation_from_field_info,
-)
-from pydantic.v1.schema import (
-    get_flat_models_from_field as get_flat_models_from_field,
-)
-from pydantic.v1.schema import (
-    get_flat_models_from_fields as get_flat_models_from_fields,
-)
-from pydantic.v1.schema import get_model_name_map as get_model_name_map
-from pydantic.v1.types import SecretBytes as SecretBytes
-from pydantic.v1.types import SecretStr as SecretStr
-from pydantic.v1.typing import evaluate_forwardref as evaluate_forwardref
-from pydantic.v1.utils import lenient_issubclass as lenient_issubclass
-from pydantic.version import VERSION as PYDANTIC_VERSION
-from typing_extensions import Literal
-
-PYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(".")[:2])
-PYDANTIC_V2 = PYDANTIC_VERSION_MINOR_TUPLE[0] == 2
-# Keeping old "Required" functionality from Pydantic V1, without
-# shadowing typing.Required.
-RequiredParam: Any = Ellipsis
-
-
-GetJsonSchemaHandler = Any
-JsonSchemaValue = dict[str, Any]
-CoreSchema = Any
-Url = AnyUrl
-
-sequence_shapes = {
-    SHAPE_LIST,
-    SHAPE_SET,
-    SHAPE_FROZENSET,
-    SHAPE_TUPLE,
-    SHAPE_SEQUENCE,
-    SHAPE_TUPLE_ELLIPSIS,
-}
-sequence_shape_to_type = {
-    SHAPE_LIST: list,
-    SHAPE_SET: set,
-    SHAPE_TUPLE: tuple,
-    SHAPE_SEQUENCE: list,
-    SHAPE_TUPLE_ELLIPSIS: list,
-}
-
-
-@dataclass
-class GenerateJsonSchema:
-    ref_template: str
-
-
-class PydanticSchemaGenerationError(Exception):
-    pass
-
-
-RequestErrorModel: type[BaseModel] = create_model("Request")
-
-
-def with_info_plain_validator_function(
-    function: Callable[..., Any],
-    *,
-    ref: Union[str, None] = None,
-    metadata: Any = None,
-    serialization: Any = None,
-) -> Any:
-    return {}
-
-
-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]] = {}
-    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
-        )
-        definitions.update(m_definitions)
-        model_name = model_name_map[model]
-        definitions[model_name] = m_schema
-    for m_schema in definitions.values():
-        if "description" in m_schema:
-            m_schema["description"] = m_schema["description"].split("\f")[0]
-    return definitions
-
-
-def is_pv1_scalar_field(field: ModelField) -> bool:
-    from fastapi import params
-
-    field_info = field.field_info
-    if not (
-        field.shape == SHAPE_SINGLETON
-        and not lenient_issubclass(field.type_, BaseModel)
-        and not lenient_issubclass(field.type_, dict)
-        and not shared.field_annotation_is_sequence(field.type_)
-        and not is_dataclass(field.type_)
-        and not isinstance(field_info, params.Body)
-    ):
-        return False
-    if field.sub_fields:
-        if not all(is_pv1_scalar_field(f) for f in field.sub_fields):
-            return False
-    return True
-
-
-def _model_dump(
-    model: BaseModel, mode: Literal["json", "python"] = "json", **kwargs: Any
-) -> Any:
-    return model.dict(**kwargs)
-
-
-def _get_model_config(model: BaseModel) -> Any:
-    return model.__config__
-
-
-def get_schema_from_model_field(
-    *,
-    field: ModelField,
-    model_name_map: ModelNameMap,
-    field_mapping: dict[
-        tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
-    ],
-    separate_input_output_schemas: bool = True,
-) -> dict[str, Any]:
-    return field_schema(
-        field,
-        model_name_map=model_name_map,  # type: ignore[arg-type]
-        ref_prefix=REF_PREFIX,
-    )[0]
-
-
-# def get_compat_model_name_map(fields: List[ModelField]) -> ModelNameMap:
-#     models = get_flat_models_from_fields(fields, known_models=set())
-#     return get_model_name_map(models)  # type: ignore[no-any-return]
-
-
-def get_definitions(
-    *,
-    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]],
-]:
-    models = get_flat_models_from_fields(fields, known_models=set())
-    return {}, get_model_definitions(flat_models=models, model_name_map=model_name_map)  # type: ignore[arg-type]
-
-
-def is_scalar_field(field: ModelField) -> bool:
-    return is_pv1_scalar_field(field)
-
-
-def is_sequence_field(field: ModelField) -> bool:
-    return field.shape in sequence_shapes or shared._annotation_is_sequence(field.type_)
-
-
-def is_bytes_field(field: ModelField) -> bool:
-    return lenient_issubclass(field.type_, bytes)
-
-
-def is_bytes_sequence_field(field: ModelField) -> bool:
-    return field.shape in sequence_shapes and lenient_issubclass(field.type_, bytes)
-
-
-def copy_field_info(*, field_info: FieldInfo, annotation: Any) -> FieldInfo:
-    return copy(field_info)
-
-
-def serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:
-    return sequence_shape_to_type[field.shape](value)  # type: ignore[no-any-return]
-
-
-def create_body_model(
-    *, fields: Sequence[ModelField], model_name: str
-) -> type[BaseModel]:
-    BodyModel = create_model(model_name)
-    for f in fields:
-        BodyModel.__fields__[f.name] = f
-    return BodyModel
-
-
-def get_model_fields(model: type[BaseModel]) -> list[ModelField]:
-    return list(model.__fields__.values())
index cbcb98e1a2eb29369f0d41a3518e0ec9126750e0..25b68145368c56e137d8488043d684aaeea4e775 100644 (file)
@@ -4,6 +4,7 @@ from collections.abc import Sequence
 from copy import copy, deepcopy
 from dataclasses import dataclass, is_dataclass
 from enum import Enum
+from functools import lru_cache
 from typing import (
     Annotated,
     Any,
@@ -11,7 +12,7 @@ from typing import (
     cast,
 )
 
-from fastapi._compat import may_v1, shared
+from fastapi._compat import shared
 from fastapi.openapi.constants import REF_TEMPLATE
 from fastapi.types import IncEx, ModelNameMap, UnionType
 from pydantic import BaseModel, ConfigDict, Field, TypeAdapter, create_model
@@ -175,7 +176,7 @@ class ModelField:
                 None,
             )
         except ValidationError as exc:
-            return None, may_v1._regenerate_error_with_loc(
+            return None, _regenerate_error_with_loc(
                 errors=exc.errors(include_url=False), loc_prefix=loc
             )
 
@@ -210,22 +211,6 @@ class ModelField:
         return id(self)
 
 
-def get_annotation_from_field_info(
-    annotation: Any, field_info: FieldInfo, field_name: str
-) -> Any:
-    return annotation
-
-
-def _model_dump(
-    model: BaseModel, mode: Literal["json", "python"] = "json", **kwargs: Any
-) -> Any:
-    return model.model_dump(mode=mode, **kwargs)
-
-
-def _get_model_config(model: BaseModel) -> Any:
-    return model.model_config
-
-
 def _has_computed_fields(field: ModelField) -> bool:
     computed_fields = field._type_adapter.core_schema.get("schema", {}).get(
         "computed_fields", []
@@ -490,6 +475,11 @@ def get_model_fields(model: type[BaseModel]) -> list[ModelField]:
     return model_fields
 
 
+@lru_cache
+def get_cached_model_fields(model: type[BaseModel]) -> list[ModelField]:
+    return get_model_fields(model)  # type: ignore[return-value]
+
+
 # Duplicate of several schema functions from Pydantic v1 to make them compatible with
 # Pydantic v2 and allow mixing the models
 
@@ -503,22 +493,23 @@ def normalize_name(name: str) -> str:
 
 def get_model_name_map(unique_models: TypeModelSet) -> dict[TypeModelOrEnum, str]:
     name_model_map = {}
-    conflicting_names: set[str] = set()
     for model in unique_models:
         model_name = normalize_name(model.__name__)
-        if model_name in conflicting_names:
-            model_name = get_long_model_name(model)
-            name_model_map[model_name] = model
-        elif model_name in name_model_map:
-            conflicting_names.add(model_name)
-            conflicting_model = name_model_map.pop(model_name)
-            name_model_map[get_long_model_name(conflicting_model)] = conflicting_model
-            name_model_map[get_long_model_name(model)] = model
-        else:
-            name_model_map[model_name] = model
+        name_model_map[model_name] = model
     return {v: k for k, v in name_model_map.items()}
 
 
+def get_compat_model_name_map(fields: list[ModelField]) -> ModelNameMap:
+    all_flat_models = set()
+
+    v2_model_fields = [field for field in fields if isinstance(field, ModelField)]
+    v2_flat_models = get_flat_models_from_fields(v2_model_fields, known_models=set())
+    all_flat_models = all_flat_models.union(v2_flat_models)  # type: ignore[arg-type]
+
+    model_name_map = get_model_name_map(all_flat_models)  # type: ignore[arg-type]
+    return model_name_map
+
+
 def get_flat_models_from_model(
     model: type["BaseModel"], known_models: Union[TypeModelSet, None] = None
 ) -> TypeModelSet:
@@ -567,5 +558,11 @@ def get_flat_models_from_fields(
     return known_models
 
 
-def get_long_model_name(model: TypeModelOrEnum) -> str:
-    return f"{model.__module__}__{model.__qualname__}".replace(".", "__")
+def _regenerate_error_with_loc(
+    *, 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 errors
+    ]
+
+    return updated_loc_errors
index 492cbfcccbc87da739918c72c7a5a43d312b016c..2bf5fdb262b667cd9b83b27ea936cabaf6f9b2ab 100644 (file)
@@ -1,3 +1,4 @@
+from collections.abc import Mapping
 from typing import (
     Annotated,
     Any,
@@ -9,11 +10,7 @@ from typing import (
 )
 
 from annotated_doc import Doc
-from fastapi._compat import (
-    CoreSchema,
-    GetJsonSchemaHandler,
-    JsonSchemaValue,
-)
+from pydantic import GetJsonSchemaHandler
 from starlette.datastructures import URL as URL  # noqa: F401
 from starlette.datastructures import Address as Address  # noqa: F401
 from starlette.datastructures import FormData as FormData  # noqa: F401
@@ -142,14 +139,14 @@ class UploadFile(StarletteUploadFile):
 
     @classmethod
     def __get_pydantic_json_schema__(
-        cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler
-    ) -> JsonSchemaValue:
+        cls, core_schema: Mapping[str, Any], handler: GetJsonSchemaHandler
+    ) -> dict[str, Any]:
         return {"type": "string", "format": "binary"}
 
     @classmethod
     def __get_pydantic_core_schema__(
-        cls, source: type[Any], handler: Callable[[Any], CoreSchema]
-    ) -> CoreSchema:
+        cls, source: type[Any], handler: Callable[[Any], Mapping[str, Any]]
+    ) -> Mapping[str, Any]:
         from ._compat.v2 import with_info_plain_validator_function
 
         return with_info_plain_validator_function(cls._validate)
index af2bed9ad996c451f996bf3fd8e9fdcba5b00014..45e1ff3ed15736fc138717cc524e0796209480c4 100644 (file)
@@ -1,7 +1,6 @@
 import dataclasses
 import inspect
 import sys
-import warnings
 from collections.abc import Coroutine, Mapping, Sequence
 from contextlib import AsyncExitStack, contextmanager
 from copy import copy, deepcopy
@@ -22,13 +21,11 @@ from fastapi._compat import (
     ModelField,
     RequiredParam,
     Undefined,
-    _is_error_wrapper,
-    _is_model_class,
+    _regenerate_error_with_loc,
     copy_field_info,
     create_body_model,
     evaluate_forwardref,
     field_annotation_is_scalar,
-    get_annotation_from_field_info,
     get_cached_model_fields,
     get_missing_field_error,
     is_bytes_field,
@@ -39,19 +36,17 @@ from fastapi._compat import (
     is_uploadfile_or_nonable_uploadfile_annotation,
     is_uploadfile_sequence_annotation,
     lenient_issubclass,
-    may_v1,
     sequence_types,
     serialize_sequence_value,
     value_is_sequence,
 )
-from fastapi._compat.shared import annotation_is_pydantic_v1
 from fastapi.background import BackgroundTasks
 from fastapi.concurrency import (
     asynccontextmanager,
     contextmanager_in_threadpool,
 )
 from fastapi.dependencies.models import Dependant
-from fastapi.exceptions import DependencyScopeError, FastAPIDeprecationWarning
+from fastapi.exceptions import DependencyScopeError
 from fastapi.logger import logger
 from fastapi.security.oauth2 import SecurityScopes
 from fastapi.types import DependencyCacheKey
@@ -72,8 +67,6 @@ from starlette.responses import Response
 from starlette.websockets import WebSocket
 from typing_extensions import Literal, get_args, get_origin
 
-from .. import temp_pydantic_v1_params
-
 multipart_not_installed_error = (
     'Form data requires "python-multipart" to be installed. \n'
     'You can install "python-multipart" with: \n\n'
@@ -189,7 +182,7 @@ def _get_flat_fields_from_params(fields: list[ModelField]) -> list[ModelField]:
     if not fields:
         return fields
     first_field = fields[0]
-    if len(fields) == 1 and _is_model_class(first_field.type_):
+    if len(fields) == 1 and lenient_issubclass(first_field.type_, BaseModel):
         fields_to_extract = get_cached_model_fields(first_field.type_)
         return fields_to_extract
     return fields
@@ -323,16 +316,7 @@ def get_dependant(
             )
             continue
         assert param_details.field is not None
-        if isinstance(param_details.field, may_v1.ModelField):
-            warnings.warn(
-                "pydantic.v1 is deprecated and will soon stop being supported by FastAPI."
-                f" Please update the param {param_name}: {param_details.type_annotation!r}.",
-                category=FastAPIDeprecationWarning,
-                stacklevel=5,
-            )
-        if isinstance(
-            param_details.field.field_info, (params.Body, temp_pydantic_v1_params.Body)
-        ):
+        if isinstance(param_details.field.field_info, params.Body):
             dependant.body_params.append(param_details.field)
         else:
             add_param_to_fields(field=param_details.field, dependant=dependant)
@@ -391,7 +375,7 @@ def analyze_param(
         fastapi_annotations = [
             arg
             for arg in annotated_args[1:]
-            if isinstance(arg, (FieldInfo, may_v1.FieldInfo, params.Depends))
+            if isinstance(arg, (FieldInfo, params.Depends))
         ]
         fastapi_specific_annotations = [
             arg
@@ -400,30 +384,27 @@ def analyze_param(
                 arg,
                 (
                     params.Param,
-                    temp_pydantic_v1_params.Param,
                     params.Body,
-                    temp_pydantic_v1_params.Body,
                     params.Depends,
                 ),
             )
         ]
         if fastapi_specific_annotations:
-            fastapi_annotation: Union[
-                FieldInfo, may_v1.FieldInfo, params.Depends, None
-            ] = fastapi_specific_annotations[-1]
+            fastapi_annotation: Union[FieldInfo, params.Depends, None] = (
+                fastapi_specific_annotations[-1]
+            )
         else:
             fastapi_annotation = None
         # Set default for Annotated FieldInfo
-        if isinstance(fastapi_annotation, (FieldInfo, may_v1.FieldInfo)):
+        if isinstance(fastapi_annotation, FieldInfo):
             # Copy `field_info` because we mutate `field_info.default` below.
             field_info = copy_field_info(
                 field_info=fastapi_annotation,  # type: ignore[arg-type]
                 annotation=use_annotation,
             )
-            assert field_info.default in {
-                Undefined,
-                may_v1.Undefined,
-            } or field_info.default in {RequiredParam, may_v1.RequiredParam}, (
+            assert (
+                field_info.default == Undefined or field_info.default == RequiredParam
+            ), (
                 f"`{field_info.__class__.__name__}` default value cannot be set in"
                 f" `Annotated` for {param_name!r}. Set the default value with `=` instead."
             )
@@ -447,7 +428,7 @@ def analyze_param(
         )
         depends = value
     # Get FieldInfo from default value
-    elif isinstance(value, (FieldInfo, may_v1.FieldInfo)):
+    elif isinstance(value, FieldInfo):
         assert field_info is None, (
             "Cannot specify FastAPI annotations in `Annotated` and default value"
             f" together for {param_name!r}"
@@ -491,14 +472,7 @@ def analyze_param(
         ) or is_uploadfile_sequence_annotation(type_annotation):
             field_info = params.File(annotation=use_annotation, default=default_value)
         elif not field_annotation_is_scalar(annotation=type_annotation):
-            if annotation_is_pydantic_v1(use_annotation):
-                field_info = temp_pydantic_v1_params.Body(  # type: ignore[assignment]
-                    annotation=use_annotation, default=default_value
-                )
-            else:
-                field_info = params.Body(
-                    annotation=use_annotation, default=default_value
-                )
+            field_info = params.Body(annotation=use_annotation, default=default_value)
         else:
             field_info = params.Query(annotation=use_annotation, default=default_value)
 
@@ -507,23 +481,17 @@ def analyze_param(
     if field_info is not None:
         # Handle field_info.in_
         if is_path_param:
-            assert isinstance(
-                field_info, (params.Path, temp_pydantic_v1_params.Path)
-            ), (
+            assert isinstance(field_info, params.Path), (
                 f"Cannot use `{field_info.__class__.__name__}` for path param"
                 f" {param_name!r}"
             )
         elif (
-            isinstance(field_info, (params.Param, temp_pydantic_v1_params.Param))
+            isinstance(field_info, params.Param)
             and getattr(field_info, "in_", None) is None
         ):
             field_info.in_ = params.ParamTypes.query
-        use_annotation_from_field_info = get_annotation_from_field_info(
-            use_annotation,
-            field_info,
-            param_name,
-        )
-        if isinstance(field_info, (params.Form, temp_pydantic_v1_params.Form)):
+        use_annotation_from_field_info = use_annotation
+        if isinstance(field_info, params.Form):
             ensure_multipart_is_installed()
         if not field_info.alias and getattr(field_info, "convert_underscores", None):
             alias = param_name.replace("_", "-")
@@ -535,20 +503,19 @@ def analyze_param(
             type_=use_annotation_from_field_info,
             default=field_info.default,
             alias=alias,
-            required=field_info.default
-            in (RequiredParam, may_v1.RequiredParam, Undefined),
+            required=field_info.default in (RequiredParam, Undefined),
             field_info=field_info,
         )
         if is_path_param:
             assert is_scalar_field(field=field), (
                 "Path params must be of one of the supported types"
             )
-        elif isinstance(field_info, (params.Query, temp_pydantic_v1_params.Query)):
+        elif isinstance(field_info, params.Query):
             assert (
                 is_scalar_field(field)
                 or is_scalar_sequence_field(field)
                 or (
-                    _is_model_class(field.type_)
+                    lenient_issubclass(field.type_, BaseModel)
                     # For Pydantic v1
                     and getattr(field, "shape", 1) == 1
                 )
@@ -742,10 +709,8 @@ def _validate_value_with_model_field(
         else:
             return deepcopy(field.default), []
     v_, errors_ = field.validate(value, values, loc=loc)
-    if _is_error_wrapper(errors_):  # type: ignore[arg-type]
-        return None, [errors_]
-    elif isinstance(errors_, list):
-        new_errors = may_v1._regenerate_error_with_loc(errors=errors_, loc_prefix=())
+    if isinstance(errors_, list):
+        new_errors = _regenerate_error_with_loc(errors=errors_, loc_prefix=())
         return None, new_errors
     else:
         return v_, []
@@ -762,7 +727,7 @@ def _get_multidict_value(
     if (
         value is None
         or (
-            isinstance(field.field_info, (params.Form, temp_pydantic_v1_params.Form))
+            isinstance(field.field_info, params.Form)
             and isinstance(value, str)  # For type checks
             and value == ""
         )
@@ -832,7 +797,7 @@ def request_params_to_args(
 
     if single_not_embedded_field:
         field_info = first_field.field_info
-        assert isinstance(field_info, (params.Param, temp_pydantic_v1_params.Param)), (
+        assert isinstance(field_info, params.Param), (
             "Params must be subclasses of Param"
         )
         loc: tuple[str, ...] = (field_info.in_.value,)
@@ -844,7 +809,7 @@ def request_params_to_args(
     for field in fields:
         value = _get_multidict_value(field, received_params)
         field_info = field.field_info
-        assert isinstance(field_info, (params.Param, temp_pydantic_v1_params.Param)), (
+        assert isinstance(field_info, params.Param), (
             "Params must be subclasses of Param"
         )
         loc = (field_info.in_.value, get_validation_alias(field))
@@ -871,7 +836,7 @@ def is_union_of_base_models(field_type: Any) -> bool:
     union_args = get_args(field_type)
 
     for arg in union_args:
-        if not _is_model_class(arg):
+        if not lenient_issubclass(arg, BaseModel):
             return False
 
     return True
@@ -893,8 +858,8 @@ def _should_embed_body_fields(fields: list[ModelField]) -> bool:
     # If it's a Form (or File) field, it has to be a BaseModel (or a union of BaseModels) to be top level
     # otherwise it has to be embedded, so that the key value pair can be extracted
     if (
-        isinstance(first_field.field_info, (params.Form, temp_pydantic_v1_params.Form))
-        and not _is_model_class(first_field.type_)
+        isinstance(first_field.field_info, params.Form)
+        and not lenient_issubclass(first_field.type_, BaseModel)
         and not is_union_of_base_models(first_field.type_)
     ):
         return True
@@ -911,14 +876,14 @@ async def _extract_form_body(
         value = _get_multidict_value(field, received_body)
         field_info = field.field_info
         if (
-            isinstance(field_info, (params.File, temp_pydantic_v1_params.File))
+            isinstance(field_info, params.File)
             and is_bytes_field(field)
             and isinstance(value, UploadFile)
         ):
             value = await value.read()
         elif (
             is_bytes_sequence_field(field)
-            and isinstance(field_info, (params.File, temp_pydantic_v1_params.File))
+            and isinstance(field_info, params.File)
             and value_is_sequence(value)
         ):
             # For types
@@ -964,7 +929,7 @@ async def request_body_to_args(
 
     if (
         single_not_embedded_field
-        and _is_model_class(first_field.type_)
+        and lenient_issubclass(first_field.type_, BaseModel)
         and isinstance(received_body, FormData)
     ):
         fields_to_extract = get_cached_model_fields(first_field.type_)
@@ -1029,28 +994,15 @@ def get_body_field(
         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
-    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]
     elif any(isinstance(f.field_info, params.Form) for f in flat_dependant.body_params):
         BodyFieldInfo = params.Form
-    elif any(
-        isinstance(f.field_info, temp_pydantic_v1_params.Form)
-        for f in flat_dependant.body_params
-    ):
-        BodyFieldInfo = temp_pydantic_v1_params.Form  # type: ignore[assignment]
     else:
-        if annotation_is_pydantic_v1(BodyModel):
-            BodyFieldInfo = temp_pydantic_v1_params.Body  # type: ignore[assignment]
-        else:
-            BodyFieldInfo = params.Body
+        BodyFieldInfo = params.Body
 
         body_param_media_types = [
             f.field_info.media_type
             for f in flat_dependant.body_params
-            if isinstance(f.field_info, (params.Body, temp_pydantic_v1_params.Body))
+            if isinstance(f.field_info, params.Body)
         ]
         if len(set(body_param_media_types)) == 1:
             BodyFieldInfo_kwargs["media_type"] = body_param_media_types[0]
index 549da32797c368a775eaeae1c5671efe1ae9f72a..e8610c983bac2c3674a6c439a2b31f63ebd583f5 100644 (file)
@@ -18,14 +18,18 @@ from typing import Annotated, Any, Callable, Optional, Union
 from uuid import UUID
 
 from annotated_doc import Doc
-from fastapi._compat import may_v1
+from fastapi.exceptions import PydanticV1NotSupportedError
 from fastapi.types import IncEx
 from pydantic import BaseModel
 from pydantic.color import Color
 from pydantic.networks import AnyUrl, NameEmail
 from pydantic.types import SecretBytes, SecretStr
+from pydantic_core import PydanticUndefinedType
 
-from ._compat import Url, _is_undefined, _model_dump
+from ._compat import (
+    Url,
+    is_pydantic_v1_model_instance,
+)
 
 
 # Taken from Pydantic v1 as is
@@ -63,7 +67,6 @@ def decimal_encoder(dec_value: Decimal) -> Union[int, float]:
 ENCODERS_BY_TYPE: dict[type[Any], Callable[[Any], Any]] = {
     bytes: lambda o: o.decode(),
     Color: str,
-    may_v1.Color: str,
     datetime.date: isoformat,
     datetime.datetime: isoformat,
     datetime.time: isoformat,
@@ -80,19 +83,14 @@ ENCODERS_BY_TYPE: dict[type[Any], Callable[[Any], Any]] = {
     IPv6Interface: str,
     IPv6Network: str,
     NameEmail: str,
-    may_v1.NameEmail: str,
     Path: str,
     Pattern: lambda o: o.pattern,
     SecretBytes: str,
-    may_v1.SecretBytes: str,
     SecretStr: str,
-    may_v1.SecretStr: str,
     set: list,
     UUID: str,
     Url: str,
-    may_v1.Url: str,
     AnyUrl: str,
-    may_v1.AnyUrl: str,
 }
 
 
@@ -224,15 +222,8 @@ def jsonable_encoder(
         include = set(include)
     if exclude is not None and not isinstance(exclude, (set, dict)):
         exclude = set(exclude)
-    if isinstance(obj, (BaseModel, may_v1.BaseModel)):
-        # TODO: remove when deprecating Pydantic v1
-        encoders: dict[Any, Any] = {}
-        if isinstance(obj, may_v1.BaseModel):
-            encoders = getattr(obj.__config__, "json_encoders", {})
-            if custom_encoder:
-                encoders = {**encoders, **custom_encoder}
-        obj_dict = _model_dump(
-            obj,  # type: ignore[arg-type]
+    if isinstance(obj, BaseModel):
+        obj_dict = obj.model_dump(
             mode="json",
             include=include,
             exclude=exclude,
@@ -241,14 +232,10 @@ def jsonable_encoder(
             exclude_none=exclude_none,
             exclude_defaults=exclude_defaults,
         )
-        if "__root__" in obj_dict:
-            obj_dict = obj_dict["__root__"]
         return jsonable_encoder(
             obj_dict,
             exclude_none=exclude_none,
             exclude_defaults=exclude_defaults,
-            # TODO: remove when deprecating Pydantic v1
-            custom_encoder=encoders,
             sqlalchemy_safe=sqlalchemy_safe,
         )
     if dataclasses.is_dataclass(obj):
@@ -271,7 +258,7 @@ def jsonable_encoder(
         return str(obj)
     if isinstance(obj, (str, int, float, type(None))):
         return obj
-    if _is_undefined(obj):
+    if isinstance(obj, PydanticUndefinedType):
         return None
     if isinstance(obj, dict):
         encoded_dict = {}
@@ -331,7 +318,11 @@ def jsonable_encoder(
     for encoder, classes_tuple in encoders_by_class_tuples.items():
         if isinstance(obj, classes_tuple):
             return encoder(obj)
-
+    if is_pydantic_v1_model_instance(obj):
+        raise PydanticV1NotSupportedError(
+            "pydantic.v1 models are no longer supported by FastAPI."
+            f" Please update the model {obj!r}."
+        )
     try:
         data = dict(obj)
     except Exception as e:
index 53e5052818eb0dde47d71f52bc0152c0d89f54a5..1a3abd80c228165fdb972c39662e8c8fdd1f84b1 100644 (file)
@@ -233,6 +233,12 @@ class ResponseValidationError(ValidationException):
         self.body = body
 
 
+class PydanticV1NotSupportedError(FastAPIError):
+    """
+    A pydantic.v1 model is used, which is no longer supported.
+    """
+
+
 class FastAPIDeprecationWarning(UserWarning):
     """
     A custom deprecation warning as DeprecationWarning is ignored
index 680f678325719ffdbca6b26200db718dadd8dd88..ac6a6d52c31d2e8f589ab4e01294c5a82b2e9c35 100644 (file)
@@ -1,15 +1,15 @@
-from collections.abc import Iterable
+from collections.abc import Iterable, Mapping
 from enum import Enum
 from typing import Annotated, Any, Callable, Optional, Union
 
-from fastapi._compat import (
-    CoreSchema,
+from fastapi._compat import with_info_plain_validator_function
+from fastapi.logger import logger
+from pydantic import (
+    AnyUrl,
+    BaseModel,
+    Field,
     GetJsonSchemaHandler,
-    JsonSchemaValue,
-    with_info_plain_validator_function,
 )
-from fastapi.logger import logger
-from pydantic import AnyUrl, BaseModel, Field
 from typing_extensions import Literal, TypedDict
 from typing_extensions import deprecated as typing_deprecated
 
@@ -43,14 +43,14 @@ except ImportError:  # pragma: no cover
 
         @classmethod
         def __get_pydantic_json_schema__(
-            cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler
-        ) -> JsonSchemaValue:
+            cls, core_schema: Mapping[str, Any], handler: GetJsonSchemaHandler
+        ) -> dict[str, Any]:
             return {"type": "string", "format": "email"}
 
         @classmethod
         def __get_pydantic_core_schema__(
-            cls, source: type[Any], handler: Callable[[Any], CoreSchema]
-        ) -> CoreSchema:
+            cls, source: type[Any], handler: Callable[[Any], Mapping[str, Any]]
+        ) -> Mapping[str, Any]:
             return with_info_plain_validator_function(cls._validate)
 
 
index 6180fcde6aa10de589d7397e22f0ac29de462d15..75ff261025213c858040f0ee626f2ded7bc808bd 100644 (file)
@@ -6,7 +6,6 @@ from typing import Any, Optional, Union, cast
 
 from fastapi import routing
 from fastapi._compat import (
-    JsonSchemaValue,
     ModelField,
     Undefined,
     get_compat_model_name_map,
@@ -39,8 +38,6 @@ from starlette.responses import JSONResponse
 from starlette.routing import BaseRoute
 from typing_extensions import Literal
 
-from .._compat import _is_model_field
-
 validation_error_definition = {
     "title": "ValidationError",
     "type": "object",
@@ -109,7 +106,7 @@ def _get_openapi_operation_parameters(
     dependant: Dependant,
     model_name_map: ModelNameMap,
     field_mapping: dict[
-        tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
+        tuple[ModelField, Literal["validation", "serialization"]], dict[str, Any]
     ],
     separate_input_output_schemas: bool = True,
 ) -> list[dict[str, Any]]:
@@ -182,13 +179,13 @@ def get_openapi_operation_request_body(
     body_field: Optional[ModelField],
     model_name_map: ModelNameMap,
     field_mapping: dict[
-        tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
+        tuple[ModelField, Literal["validation", "serialization"]], dict[str, Any]
     ],
     separate_input_output_schemas: bool = True,
 ) -> Optional[dict[str, Any]]:
     if not body_field:
         return None
-    assert _is_model_field(body_field)
+    assert isinstance(body_field, ModelField)
     body_schema = get_schema_from_model_field(
         field=body_field,
         model_name_map=model_name_map,
@@ -265,7 +262,7 @@ def get_openapi_path(
     operation_ids: set[str],
     model_name_map: ModelNameMap,
     field_mapping: dict[
-        tuple[ModelField, Literal["validation", "serialization"]], JsonSchemaValue
+        tuple[ModelField, Literal["validation", "serialization"]], dict[str, Any]
     ],
     separate_input_output_schemas: bool = True,
 ) -> tuple[dict[str, Any], dict[str, Any], dict[str, Any]]:
@@ -457,7 +454,7 @@ def get_fields_from_routes(
             route, routing.APIRoute
         ):
             if route.body_field:
-                assert _is_model_field(route.body_field), (
+                assert isinstance(route.body_field, ModelField), (
                     "A request body must be a Pydantic Field"
                 )
                 body_fields_from_routes.append(route.body_field)
index 844542594ba959e69a68a6f66cb4f4c78c18bcdb..0834fd741ae74705e6cd7c8ae684a26dd2d293f8 100644 (file)
@@ -5,6 +5,7 @@ from annotated_doc import Doc
 from fastapi import params
 from fastapi._compat import Undefined
 from fastapi.openapi.models import Example
+from pydantic import AliasChoices, AliasPath
 from typing_extensions import Literal, deprecated
 
 _Unset: Any = Undefined
@@ -54,10 +55,8 @@ def Path(  # noqa: N802
             """
         ),
     ] = _Unset,
-    # TODO: update when deprecating Pydantic v1, import these types
-    # validation_alias: str | AliasPath | AliasChoices | None
     validation_alias: Annotated[
-        Union[str, None],
+        Union[str, AliasPath, AliasChoices, None],
         Doc(
             """
             'Whitelist' validation step. The parameter field will be the single one
@@ -379,10 +378,8 @@ def Query(  # noqa: N802
             """
         ),
     ] = _Unset,
-    # TODO: update when deprecating Pydantic v1, import these types
-    # validation_alias: str | AliasPath | AliasChoices | None
     validation_alias: Annotated[
-        Union[str, None],
+        Union[str, AliasPath, AliasChoices, None],
         Doc(
             """
             'Whitelist' validation step. The parameter field will be the single one
@@ -683,10 +680,8 @@ def Header(  # noqa: N802
             """
         ),
     ] = _Unset,
-    # TODO: update when deprecating Pydantic v1, import these types
-    # validation_alias: str | AliasPath | AliasChoices | None
     validation_alias: Annotated[
-        Union[str, None],
+        Union[str, AliasPath, AliasChoices, None],
         Doc(
             """
             'Whitelist' validation step. The parameter field will be the single one
@@ -999,10 +994,8 @@ def Cookie(  # noqa: N802
             """
         ),
     ] = _Unset,
-    # TODO: update when deprecating Pydantic v1, import these types
-    # validation_alias: str | AliasPath | AliasChoices | None
     validation_alias: Annotated[
-        Union[str, None],
+        Union[str, AliasPath, AliasChoices, None],
         Doc(
             """
             'Whitelist' validation step. The parameter field will be the single one
@@ -1326,10 +1319,8 @@ def Body(  # noqa: N802
             """
         ),
     ] = _Unset,
-    # TODO: update when deprecating Pydantic v1, import these types
-    # validation_alias: str | AliasPath | AliasChoices | None
     validation_alias: Annotated[
-        Union[str, None],
+        Union[str, AliasPath, AliasChoices, None],
         Doc(
             """
             'Whitelist' validation step. The parameter field will be the single one
@@ -1641,10 +1632,8 @@ def Form(  # noqa: N802
             """
         ),
     ] = _Unset,
-    # TODO: update when deprecating Pydantic v1, import these types
-    # validation_alias: str | AliasPath | AliasChoices | None
     validation_alias: Annotated[
-        Union[str, None],
+        Union[str, AliasPath, AliasChoices, None],
         Doc(
             """
             'Whitelist' validation step. The parameter field will be the single one
@@ -1955,10 +1944,8 @@ def File(  # noqa: N802
             """
         ),
     ] = _Unset,
-    # TODO: update when deprecating Pydantic v1, import these types
-    # validation_alias: str | AliasPath | AliasChoices | None
     validation_alias: Annotated[
-        Union[str, None],
+        Union[str, AliasPath, AliasChoices, None],
         Doc(
             """
             'Whitelist' validation step. The parameter field will be the single one
index cc2934f44d8b1b7b6742c019df0533f7be021a97..72e797f8337059f6cc95569595aa52e7bcaa2396 100644 (file)
@@ -6,6 +6,7 @@ from typing import Annotated, Any, Callable, Optional, Union
 
 from fastapi.exceptions import FastAPIDeprecationWarning
 from fastapi.openapi.models import Example
+from pydantic import AliasChoices, AliasPath
 from pydantic.fields import FieldInfo
 from typing_extensions import Literal, deprecated
 
@@ -34,9 +35,7 @@ class Param(FieldInfo):  # type: ignore[misc]
         annotation: Optional[Any] = None,
         alias: Optional[str] = None,
         alias_priority: Union[int, None] = _Unset,
-        # TODO: update when deprecating Pydantic v1, import these types
-        # validation_alias: str | AliasPath | AliasChoices | None
-        validation_alias: Union[str, None] = None,
+        validation_alias: Union[str, AliasPath, AliasChoices, None] = None,
         serialization_alias: Union[str, None] = None,
         title: Optional[str] = None,
         description: Optional[str] = None,
@@ -147,9 +146,7 @@ class Path(Param):  # type: ignore[misc]
         annotation: Optional[Any] = None,
         alias: Optional[str] = None,
         alias_priority: Union[int, None] = _Unset,
-        # TODO: update when deprecating Pydantic v1, import these types
-        # validation_alias: str | AliasPath | AliasChoices | None
-        validation_alias: Union[str, None] = None,
+        validation_alias: Union[str, AliasPath, AliasChoices, None] = None,
         serialization_alias: Union[str, None] = None,
         title: Optional[str] = None,
         description: Optional[str] = None,
@@ -233,9 +230,7 @@ class Query(Param):  # type: ignore[misc]
         annotation: Optional[Any] = None,
         alias: Optional[str] = None,
         alias_priority: Union[int, None] = _Unset,
-        # TODO: update when deprecating Pydantic v1, import these types
-        # validation_alias: str | AliasPath | AliasChoices | None
-        validation_alias: Union[str, None] = None,
+        validation_alias: Union[str, AliasPath, AliasChoices, None] = None,
         serialization_alias: Union[str, None] = None,
         title: Optional[str] = None,
         description: Optional[str] = None,
@@ -317,9 +312,7 @@ class Header(Param):  # type: ignore[misc]
         annotation: Optional[Any] = None,
         alias: Optional[str] = None,
         alias_priority: Union[int, None] = _Unset,
-        # TODO: update when deprecating Pydantic v1, import these types
-        # validation_alias: str | AliasPath | AliasChoices | None
-        validation_alias: Union[str, None] = None,
+        validation_alias: Union[str, AliasPath, AliasChoices, None] = None,
         serialization_alias: Union[str, None] = None,
         convert_underscores: bool = True,
         title: Optional[str] = None,
@@ -403,9 +396,7 @@ class Cookie(Param):  # type: ignore[misc]
         annotation: Optional[Any] = None,
         alias: Optional[str] = None,
         alias_priority: Union[int, None] = _Unset,
-        # TODO: update when deprecating Pydantic v1, import these types
-        # validation_alias: str | AliasPath | AliasChoices | None
-        validation_alias: Union[str, None] = None,
+        validation_alias: Union[str, AliasPath, AliasChoices, None] = None,
         serialization_alias: Union[str, None] = None,
         title: Optional[str] = None,
         description: Optional[str] = None,
@@ -487,9 +478,7 @@ class Body(FieldInfo):  # type: ignore[misc]
         media_type: str = "application/json",
         alias: Optional[str] = None,
         alias_priority: Union[int, None] = _Unset,
-        # TODO: update when deprecating Pydantic v1, import these types
-        # validation_alias: str | AliasPath | AliasChoices | None
-        validation_alias: Union[str, None] = None,
+        validation_alias: Union[str, AliasPath, AliasChoices, None] = None,
         serialization_alias: Union[str, None] = None,
         title: Optional[str] = None,
         description: Optional[str] = None,
@@ -600,9 +589,7 @@ class Form(Body):  # type: ignore[misc]
         media_type: str = "application/x-www-form-urlencoded",
         alias: Optional[str] = None,
         alias_priority: Union[int, None] = _Unset,
-        # TODO: update when deprecating Pydantic v1, import these types
-        # validation_alias: str | AliasPath | AliasChoices | None
-        validation_alias: Union[str, None] = None,
+        validation_alias: Union[str, AliasPath, AliasChoices, None] = None,
         serialization_alias: Union[str, None] = None,
         title: Optional[str] = None,
         description: Optional[str] = None,
@@ -684,9 +671,7 @@ class File(Form):  # type: ignore[misc]
         media_type: str = "multipart/form-data",
         alias: Optional[str] = None,
         alias_priority: Union[int, None] = _Unset,
-        # TODO: update when deprecating Pydantic v1, import these types
-        # validation_alias: str | AliasPath | AliasChoices | None
-        validation_alias: Union[str, None] = None,
+        validation_alias: Union[str, AliasPath, AliasChoices, None] = None,
         serialization_alias: Union[str, None] = None,
         title: Optional[str] = None,
         description: Optional[str] = None,
index 3f78e93e84c7f8700e5260b27e536ecd3b3994dd..9ca2f46732c9eeef6430ca387dbacd581bc17d01 100644 (file)
@@ -2,7 +2,6 @@ import email.message
 import functools
 import inspect
 import json
-import warnings
 from collections.abc import (
     AsyncIterator,
     Awaitable,
@@ -22,16 +21,12 @@ from typing import (
 )
 
 from annotated_doc import Doc
-from fastapi import params, temp_pydantic_v1_params
+from fastapi import params
 from fastapi._compat import (
     ModelField,
     Undefined,
-    _get_model_config,
-    _model_dump,
-    _normalize_errors,
     annotation_is_pydantic_v1,
     lenient_issubclass,
-    may_v1,
 )
 from fastapi.datastructures import Default, DefaultPlaceholder
 from fastapi.dependencies.models import Dependant
@@ -47,8 +42,8 @@ from fastapi.dependencies.utils import (
 from fastapi.encoders import jsonable_encoder
 from fastapi.exceptions import (
     EndpointContext,
-    FastAPIDeprecationWarning,
     FastAPIError,
+    PydanticV1NotSupportedError,
     RequestValidationError,
     ResponseValidationError,
     WebSocketRequestValidationError,
@@ -148,51 +143,6 @@ def websocket_session(
     return app
 
 
-def _prepare_response_content(
-    res: Any,
-    *,
-    exclude_unset: bool,
-    exclude_defaults: bool = False,
-    exclude_none: bool = False,
-) -> Any:
-    if isinstance(res, may_v1.BaseModel):
-        read_with_orm_mode = getattr(_get_model_config(res), "read_with_orm_mode", None)  # type: ignore[arg-type]
-        if read_with_orm_mode:
-            # Let from_orm extract the data from this model instead of converting
-            # it now to a dict.
-            # Otherwise, there's no way to extract lazy data that requires attribute
-            # access instead of dict iteration, e.g. lazy relationships.
-            return res
-        return _model_dump(
-            res,  # type: ignore[arg-type]
-            by_alias=True,
-            exclude_unset=exclude_unset,
-            exclude_defaults=exclude_defaults,
-            exclude_none=exclude_none,
-        )
-    elif isinstance(res, list):
-        return [
-            _prepare_response_content(
-                item,
-                exclude_unset=exclude_unset,
-                exclude_defaults=exclude_defaults,
-                exclude_none=exclude_none,
-            )
-            for item in res
-        ]
-    elif isinstance(res, dict):
-        return {
-            k: _prepare_response_content(
-                v,
-                exclude_unset=exclude_unset,
-                exclude_defaults=exclude_defaults,
-                exclude_none=exclude_none,
-            )
-            for k, v in res.items()
-        }
-    return res
-
-
 def _merge_lifespan_context(
     original_context: Lifespan[Any], nested_context: Lifespan[Any]
 ) -> Lifespan[Any]:
@@ -252,14 +202,6 @@ async def serialize_response(
 ) -> Any:
     if field:
         errors = []
-        if not hasattr(field, "serialize"):
-            # pydantic v1
-            response_content = _prepare_response_content(
-                response_content,
-                exclude_unset=exclude_unset,
-                exclude_defaults=exclude_defaults,
-                exclude_none=exclude_none,
-            )
         if is_coroutine:
             value, errors_ = field.validate(response_content, {}, loc=("response",))
         else:
@@ -268,28 +210,15 @@ async def serialize_response(
             )
         if isinstance(errors_, list):
             errors.extend(errors_)
-        elif errors_:
-            errors.append(errors_)
         if errors:
             ctx = endpoint_ctx or EndpointContext()
             raise ResponseValidationError(
-                errors=_normalize_errors(errors),
+                errors=errors,
                 body=response_content,
                 endpoint_ctx=ctx,
             )
 
-        if hasattr(field, "serialize"):
-            return field.serialize(
-                value,
-                include=include,
-                exclude=exclude,
-                by_alias=by_alias,
-                exclude_unset=exclude_unset,
-                exclude_defaults=exclude_defaults,
-                exclude_none=exclude_none,
-            )
-
-        return jsonable_encoder(
+        return field.serialize(
             value,
             include=include,
             exclude=exclude,
@@ -298,6 +227,7 @@ async def serialize_response(
             exclude_defaults=exclude_defaults,
             exclude_none=exclude_none,
         )
+
     else:
         return jsonable_encoder(response_content)
 
@@ -332,9 +262,7 @@ def get_request_handler(
 ) -> Callable[[Request], Coroutine[Any, Any, Response]]:
     assert dependant.call is not None, "dependant.call must be a function"
     is_coroutine = dependant.is_coroutine_callable
-    is_body_form = body_field and isinstance(
-        body_field.field_info, (params.Form, temp_pydantic_v1_params.Form)
-    )
+    is_body_form = body_field and isinstance(body_field.field_info, params.Form)
     if isinstance(response_class, DefaultPlaceholder):
         actual_response_class: type[Response] = response_class.value
     else:
@@ -464,7 +392,7 @@ def get_request_handler(
                 response.headers.raw.extend(solved_result.response.headers.raw)
         if errors:
             validation_error = RequestValidationError(
-                _normalize_errors(errors), body=body, endpoint_ctx=endpoint_ctx
+                errors, body=body, endpoint_ctx=endpoint_ctx
             )
             raise validation_error
 
@@ -503,7 +431,7 @@ def get_websocket_app(
         )
         if solved_result.errors:
             raise WebSocketRequestValidationError(
-                _normalize_errors(solved_result.errors),
+                solved_result.errors,
                 endpoint_ctx=endpoint_ctx,
             )
         assert dependant.call is not None, "dependant.call must be a function"
@@ -638,11 +566,9 @@ class APIRoute(routing.Route):
             )
             response_name = "Response_" + self.unique_id
             if annotation_is_pydantic_v1(self.response_model):
-                warnings.warn(
-                    "pydantic.v1 is deprecated and will soon stop being supported by FastAPI."
-                    f" Please update the response model {self.response_model!r}.",
-                    category=FastAPIDeprecationWarning,
-                    stacklevel=4,
+                raise PydanticV1NotSupportedError(
+                    "pydantic.v1 models are no longer supported by FastAPI."
+                    f" Please update the response model {self.response_model!r}."
                 )
             self.response_field = create_model_field(
                 name=response_name,
@@ -678,11 +604,9 @@ class APIRoute(routing.Route):
                 )
                 response_name = f"Response_{additional_status_code}_{self.unique_id}"
                 if annotation_is_pydantic_v1(model):
-                    warnings.warn(
-                        "pydantic.v1 is deprecated and will soon stop being supported by FastAPI."
-                        f" In responses={{}}, please update {model}.",
-                        category=FastAPIDeprecationWarning,
-                        stacklevel=4,
+                    raise PydanticV1NotSupportedError(
+                        "pydantic.v1 models are no longer supported by FastAPI."
+                        f" In responses={{}}, please update {model}."
                     )
                 response_field = create_model_field(
                     name=response_name, type_=model, mode="serialization"
diff --git a/fastapi/temp_pydantic_v1_params.py b/fastapi/temp_pydantic_v1_params.py
deleted file mode 100644 (file)
index 62230e4..0000000
+++ /dev/null
@@ -1,718 +0,0 @@
-import warnings
-from typing import Annotated, Any, Callable, Optional, Union
-
-from fastapi.exceptions import FastAPIDeprecationWarning
-from fastapi.openapi.models import Example
-from fastapi.params import ParamTypes
-from typing_extensions import deprecated
-
-from ._compat.may_v1 import FieldInfo, Undefined
-
-_Unset: Any = Undefined
-
-
-class Param(FieldInfo):
-    in_: ParamTypes
-
-    def __init__(
-        self,
-        default: Any = Undefined,
-        *,
-        default_factory: Union[Callable[[], Any], None] = _Unset,
-        annotation: Optional[Any] = None,
-        alias: Optional[str] = None,
-        alias_priority: Union[int, None] = _Unset,
-        # TODO: update when deprecating Pydantic v1, import these types
-        # validation_alias: str | AliasPath | AliasChoices | None
-        validation_alias: Union[str, None] = None,
-        serialization_alias: Union[str, None] = None,
-        title: Optional[str] = None,
-        description: Optional[str] = None,
-        gt: Optional[float] = None,
-        ge: Optional[float] = None,
-        lt: Optional[float] = None,
-        le: Optional[float] = None,
-        min_length: Optional[int] = None,
-        max_length: Optional[int] = None,
-        pattern: Optional[str] = None,
-        regex: Annotated[
-            Optional[str],
-            deprecated(
-                "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
-            ),
-        ] = None,
-        discriminator: Union[str, None] = None,
-        strict: Union[bool, None] = _Unset,
-        multiple_of: Union[float, None] = _Unset,
-        allow_inf_nan: Union[bool, None] = _Unset,
-        max_digits: Union[int, None] = _Unset,
-        decimal_places: Union[int, None] = _Unset,
-        examples: Optional[list[Any]] = None,
-        example: Annotated[
-            Optional[Any],
-            deprecated(
-                "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
-                "although still supported. Use examples instead."
-            ),
-        ] = _Unset,
-        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,
-        **extra: Any,
-    ):
-        if example is not _Unset:
-            warnings.warn(
-                "`example` has been deprecated, please use `examples` instead",
-                category=FastAPIDeprecationWarning,
-                stacklevel=4,
-            )
-        self.example = example
-        self.include_in_schema = include_in_schema
-        self.openapi_examples = openapi_examples
-        kwargs = dict(
-            default=default,
-            default_factory=default_factory,
-            alias=alias,
-            title=title,
-            description=description,
-            gt=gt,
-            ge=ge,
-            lt=lt,
-            le=le,
-            min_length=min_length,
-            max_length=max_length,
-            discriminator=discriminator,
-            multiple_of=multiple_of,
-            allow_inf_nan=allow_inf_nan,
-            max_digits=max_digits,
-            decimal_places=decimal_places,
-            **extra,
-        )
-        if examples is not None:
-            kwargs["examples"] = examples
-        if regex is not None:
-            warnings.warn(
-                "`regex` has been deprecated, please use `pattern` instead",
-                category=FastAPIDeprecationWarning,
-                stacklevel=4,
-            )
-        current_json_schema_extra = json_schema_extra or extra
-        kwargs["deprecated"] = deprecated
-        kwargs["regex"] = pattern or regex
-        kwargs.update(**current_json_schema_extra)
-        use_kwargs = {k: v for k, v in kwargs.items() if v is not _Unset}
-
-        super().__init__(**use_kwargs)
-
-    def __repr__(self) -> str:
-        return f"{self.__class__.__name__}({self.default})"
-
-
-class Path(Param):
-    in_ = ParamTypes.path
-
-    def __init__(
-        self,
-        default: Any = ...,
-        *,
-        default_factory: Union[Callable[[], Any], None] = _Unset,
-        annotation: Optional[Any] = None,
-        alias: Optional[str] = None,
-        alias_priority: Union[int, None] = _Unset,
-        # TODO: update when deprecating Pydantic v1, import these types
-        # validation_alias: str | AliasPath | AliasChoices | None
-        validation_alias: Union[str, None] = None,
-        serialization_alias: Union[str, None] = None,
-        title: Optional[str] = None,
-        description: Optional[str] = None,
-        gt: Optional[float] = None,
-        ge: Optional[float] = None,
-        lt: Optional[float] = None,
-        le: Optional[float] = None,
-        min_length: Optional[int] = None,
-        max_length: Optional[int] = None,
-        pattern: Optional[str] = None,
-        regex: Annotated[
-            Optional[str],
-            deprecated(
-                "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
-            ),
-        ] = None,
-        discriminator: Union[str, None] = None,
-        strict: Union[bool, None] = _Unset,
-        multiple_of: Union[float, None] = _Unset,
-        allow_inf_nan: Union[bool, None] = _Unset,
-        max_digits: Union[int, None] = _Unset,
-        decimal_places: Union[int, None] = _Unset,
-        examples: Optional[list[Any]] = None,
-        example: Annotated[
-            Optional[Any],
-            deprecated(
-                "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
-                "although still supported. Use examples instead."
-            ),
-        ] = _Unset,
-        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,
-        **extra: Any,
-    ):
-        assert default is ..., "Path parameters cannot have a default value"
-        self.in_ = self.in_
-        super().__init__(
-            default=default,
-            default_factory=default_factory,
-            annotation=annotation,
-            alias=alias,
-            alias_priority=alias_priority,
-            validation_alias=validation_alias,
-            serialization_alias=serialization_alias,
-            title=title,
-            description=description,
-            gt=gt,
-            ge=ge,
-            lt=lt,
-            le=le,
-            min_length=min_length,
-            max_length=max_length,
-            pattern=pattern,
-            regex=regex,
-            discriminator=discriminator,
-            strict=strict,
-            multiple_of=multiple_of,
-            allow_inf_nan=allow_inf_nan,
-            max_digits=max_digits,
-            decimal_places=decimal_places,
-            deprecated=deprecated,
-            example=example,
-            examples=examples,
-            openapi_examples=openapi_examples,
-            include_in_schema=include_in_schema,
-            json_schema_extra=json_schema_extra,
-            **extra,
-        )
-
-
-class Query(Param):
-    in_ = ParamTypes.query
-
-    def __init__(
-        self,
-        default: Any = Undefined,
-        *,
-        default_factory: Union[Callable[[], Any], None] = _Unset,
-        annotation: Optional[Any] = None,
-        alias: Optional[str] = None,
-        alias_priority: Union[int, None] = _Unset,
-        # TODO: update when deprecating Pydantic v1, import these types
-        # validation_alias: str | AliasPath | AliasChoices | None
-        validation_alias: Union[str, None] = None,
-        serialization_alias: Union[str, None] = None,
-        title: Optional[str] = None,
-        description: Optional[str] = None,
-        gt: Optional[float] = None,
-        ge: Optional[float] = None,
-        lt: Optional[float] = None,
-        le: Optional[float] = None,
-        min_length: Optional[int] = None,
-        max_length: Optional[int] = None,
-        pattern: Optional[str] = None,
-        regex: Annotated[
-            Optional[str],
-            deprecated(
-                "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
-            ),
-        ] = None,
-        discriminator: Union[str, None] = None,
-        strict: Union[bool, None] = _Unset,
-        multiple_of: Union[float, None] = _Unset,
-        allow_inf_nan: Union[bool, None] = _Unset,
-        max_digits: Union[int, None] = _Unset,
-        decimal_places: Union[int, None] = _Unset,
-        examples: Optional[list[Any]] = None,
-        example: Annotated[
-            Optional[Any],
-            deprecated(
-                "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
-                "although still supported. Use examples instead."
-            ),
-        ] = _Unset,
-        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,
-        **extra: Any,
-    ):
-        super().__init__(
-            default=default,
-            default_factory=default_factory,
-            annotation=annotation,
-            alias=alias,
-            alias_priority=alias_priority,
-            validation_alias=validation_alias,
-            serialization_alias=serialization_alias,
-            title=title,
-            description=description,
-            gt=gt,
-            ge=ge,
-            lt=lt,
-            le=le,
-            min_length=min_length,
-            max_length=max_length,
-            pattern=pattern,
-            regex=regex,
-            discriminator=discriminator,
-            strict=strict,
-            multiple_of=multiple_of,
-            allow_inf_nan=allow_inf_nan,
-            max_digits=max_digits,
-            decimal_places=decimal_places,
-            deprecated=deprecated,
-            example=example,
-            examples=examples,
-            openapi_examples=openapi_examples,
-            include_in_schema=include_in_schema,
-            json_schema_extra=json_schema_extra,
-            **extra,
-        )
-
-
-class Header(Param):
-    in_ = ParamTypes.header
-
-    def __init__(
-        self,
-        default: Any = Undefined,
-        *,
-        default_factory: Union[Callable[[], Any], None] = _Unset,
-        annotation: Optional[Any] = None,
-        alias: Optional[str] = None,
-        alias_priority: Union[int, None] = _Unset,
-        # TODO: update when deprecating Pydantic v1, import these types
-        # validation_alias: str | AliasPath | AliasChoices | None
-        validation_alias: Union[str, None] = None,
-        serialization_alias: Union[str, None] = None,
-        convert_underscores: bool = True,
-        title: Optional[str] = None,
-        description: Optional[str] = None,
-        gt: Optional[float] = None,
-        ge: Optional[float] = None,
-        lt: Optional[float] = None,
-        le: Optional[float] = None,
-        min_length: Optional[int] = None,
-        max_length: Optional[int] = None,
-        pattern: Optional[str] = None,
-        regex: Annotated[
-            Optional[str],
-            deprecated(
-                "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
-            ),
-        ] = None,
-        discriminator: Union[str, None] = None,
-        strict: Union[bool, None] = _Unset,
-        multiple_of: Union[float, None] = _Unset,
-        allow_inf_nan: Union[bool, None] = _Unset,
-        max_digits: Union[int, None] = _Unset,
-        decimal_places: Union[int, None] = _Unset,
-        examples: Optional[list[Any]] = None,
-        example: Annotated[
-            Optional[Any],
-            deprecated(
-                "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
-                "although still supported. Use examples instead."
-            ),
-        ] = _Unset,
-        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,
-        **extra: Any,
-    ):
-        self.convert_underscores = convert_underscores
-        super().__init__(
-            default=default,
-            default_factory=default_factory,
-            annotation=annotation,
-            alias=alias,
-            alias_priority=alias_priority,
-            validation_alias=validation_alias,
-            serialization_alias=serialization_alias,
-            title=title,
-            description=description,
-            gt=gt,
-            ge=ge,
-            lt=lt,
-            le=le,
-            min_length=min_length,
-            max_length=max_length,
-            pattern=pattern,
-            regex=regex,
-            discriminator=discriminator,
-            strict=strict,
-            multiple_of=multiple_of,
-            allow_inf_nan=allow_inf_nan,
-            max_digits=max_digits,
-            decimal_places=decimal_places,
-            deprecated=deprecated,
-            example=example,
-            examples=examples,
-            openapi_examples=openapi_examples,
-            include_in_schema=include_in_schema,
-            json_schema_extra=json_schema_extra,
-            **extra,
-        )
-
-
-class Cookie(Param):
-    in_ = ParamTypes.cookie
-
-    def __init__(
-        self,
-        default: Any = Undefined,
-        *,
-        default_factory: Union[Callable[[], Any], None] = _Unset,
-        annotation: Optional[Any] = None,
-        alias: Optional[str] = None,
-        alias_priority: Union[int, None] = _Unset,
-        # TODO: update when deprecating Pydantic v1, import these types
-        # validation_alias: str | AliasPath | AliasChoices | None
-        validation_alias: Union[str, None] = None,
-        serialization_alias: Union[str, None] = None,
-        title: Optional[str] = None,
-        description: Optional[str] = None,
-        gt: Optional[float] = None,
-        ge: Optional[float] = None,
-        lt: Optional[float] = None,
-        le: Optional[float] = None,
-        min_length: Optional[int] = None,
-        max_length: Optional[int] = None,
-        pattern: Optional[str] = None,
-        regex: Annotated[
-            Optional[str],
-            deprecated(
-                "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
-            ),
-        ] = None,
-        discriminator: Union[str, None] = None,
-        strict: Union[bool, None] = _Unset,
-        multiple_of: Union[float, None] = _Unset,
-        allow_inf_nan: Union[bool, None] = _Unset,
-        max_digits: Union[int, None] = _Unset,
-        decimal_places: Union[int, None] = _Unset,
-        examples: Optional[list[Any]] = None,
-        example: Annotated[
-            Optional[Any],
-            deprecated(
-                "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
-                "although still supported. Use examples instead."
-            ),
-        ] = _Unset,
-        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,
-        **extra: Any,
-    ):
-        super().__init__(
-            default=default,
-            default_factory=default_factory,
-            annotation=annotation,
-            alias=alias,
-            alias_priority=alias_priority,
-            validation_alias=validation_alias,
-            serialization_alias=serialization_alias,
-            title=title,
-            description=description,
-            gt=gt,
-            ge=ge,
-            lt=lt,
-            le=le,
-            min_length=min_length,
-            max_length=max_length,
-            pattern=pattern,
-            regex=regex,
-            discriminator=discriminator,
-            strict=strict,
-            multiple_of=multiple_of,
-            allow_inf_nan=allow_inf_nan,
-            max_digits=max_digits,
-            decimal_places=decimal_places,
-            deprecated=deprecated,
-            example=example,
-            examples=examples,
-            openapi_examples=openapi_examples,
-            include_in_schema=include_in_schema,
-            json_schema_extra=json_schema_extra,
-            **extra,
-        )
-
-
-class Body(FieldInfo):
-    def __init__(
-        self,
-        default: Any = Undefined,
-        *,
-        default_factory: Union[Callable[[], Any], None] = _Unset,
-        annotation: Optional[Any] = None,
-        embed: Union[bool, None] = None,
-        media_type: str = "application/json",
-        alias: Optional[str] = None,
-        alias_priority: Union[int, None] = _Unset,
-        # TODO: update when deprecating Pydantic v1, import these types
-        # validation_alias: str | AliasPath | AliasChoices | None
-        validation_alias: Union[str, None] = None,
-        serialization_alias: Union[str, None] = None,
-        title: Optional[str] = None,
-        description: Optional[str] = None,
-        gt: Optional[float] = None,
-        ge: Optional[float] = None,
-        lt: Optional[float] = None,
-        le: Optional[float] = None,
-        min_length: Optional[int] = None,
-        max_length: Optional[int] = None,
-        pattern: Optional[str] = None,
-        regex: Annotated[
-            Optional[str],
-            deprecated(
-                "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
-            ),
-        ] = None,
-        discriminator: Union[str, None] = None,
-        strict: Union[bool, None] = _Unset,
-        multiple_of: Union[float, None] = _Unset,
-        allow_inf_nan: Union[bool, None] = _Unset,
-        max_digits: Union[int, None] = _Unset,
-        decimal_places: Union[int, None] = _Unset,
-        examples: Optional[list[Any]] = None,
-        example: Annotated[
-            Optional[Any],
-            deprecated(
-                "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
-                "although still supported. Use examples instead."
-            ),
-        ] = _Unset,
-        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,
-        **extra: Any,
-    ):
-        self.embed = embed
-        self.media_type = media_type
-        if example is not _Unset:
-            warnings.warn(
-                "`example` has been deprecated, please use `examples` instead",
-                category=FastAPIDeprecationWarning,
-                stacklevel=4,
-            )
-        self.example = example
-        self.include_in_schema = include_in_schema
-        self.openapi_examples = openapi_examples
-        kwargs = dict(
-            default=default,
-            default_factory=default_factory,
-            alias=alias,
-            title=title,
-            description=description,
-            gt=gt,
-            ge=ge,
-            lt=lt,
-            le=le,
-            min_length=min_length,
-            max_length=max_length,
-            discriminator=discriminator,
-            multiple_of=multiple_of,
-            allow_inf_nan=allow_inf_nan,
-            max_digits=max_digits,
-            decimal_places=decimal_places,
-            **extra,
-        )
-        if examples is not None:
-            kwargs["examples"] = examples
-        if regex is not None:
-            warnings.warn(
-                "`regex` has been deprecated, please use `pattern` instead",
-                category=FastAPIDeprecationWarning,
-                stacklevel=4,
-            )
-        current_json_schema_extra = json_schema_extra or extra
-        kwargs["deprecated"] = deprecated
-        kwargs["regex"] = pattern or regex
-        kwargs.update(**current_json_schema_extra)
-
-        use_kwargs = {k: v for k, v in kwargs.items() if v is not _Unset}
-
-        super().__init__(**use_kwargs)
-
-    def __repr__(self) -> str:
-        return f"{self.__class__.__name__}({self.default})"
-
-
-class Form(Body):
-    def __init__(
-        self,
-        default: Any = Undefined,
-        *,
-        default_factory: Union[Callable[[], Any], None] = _Unset,
-        annotation: Optional[Any] = None,
-        media_type: str = "application/x-www-form-urlencoded",
-        alias: Optional[str] = None,
-        alias_priority: Union[int, None] = _Unset,
-        # TODO: update when deprecating Pydantic v1, import these types
-        # validation_alias: str | AliasPath | AliasChoices | None
-        validation_alias: Union[str, None] = None,
-        serialization_alias: Union[str, None] = None,
-        title: Optional[str] = None,
-        description: Optional[str] = None,
-        gt: Optional[float] = None,
-        ge: Optional[float] = None,
-        lt: Optional[float] = None,
-        le: Optional[float] = None,
-        min_length: Optional[int] = None,
-        max_length: Optional[int] = None,
-        pattern: Optional[str] = None,
-        regex: Annotated[
-            Optional[str],
-            deprecated(
-                "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
-            ),
-        ] = None,
-        discriminator: Union[str, None] = None,
-        strict: Union[bool, None] = _Unset,
-        multiple_of: Union[float, None] = _Unset,
-        allow_inf_nan: Union[bool, None] = _Unset,
-        max_digits: Union[int, None] = _Unset,
-        decimal_places: Union[int, None] = _Unset,
-        examples: Optional[list[Any]] = None,
-        example: Annotated[
-            Optional[Any],
-            deprecated(
-                "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
-                "although still supported. Use examples instead."
-            ),
-        ] = _Unset,
-        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,
-        **extra: Any,
-    ):
-        super().__init__(
-            default=default,
-            default_factory=default_factory,
-            annotation=annotation,
-            media_type=media_type,
-            alias=alias,
-            alias_priority=alias_priority,
-            validation_alias=validation_alias,
-            serialization_alias=serialization_alias,
-            title=title,
-            description=description,
-            gt=gt,
-            ge=ge,
-            lt=lt,
-            le=le,
-            min_length=min_length,
-            max_length=max_length,
-            pattern=pattern,
-            regex=regex,
-            discriminator=discriminator,
-            strict=strict,
-            multiple_of=multiple_of,
-            allow_inf_nan=allow_inf_nan,
-            max_digits=max_digits,
-            decimal_places=decimal_places,
-            deprecated=deprecated,
-            example=example,
-            examples=examples,
-            openapi_examples=openapi_examples,
-            include_in_schema=include_in_schema,
-            json_schema_extra=json_schema_extra,
-            **extra,
-        )
-
-
-class File(Form):
-    def __init__(
-        self,
-        default: Any = Undefined,
-        *,
-        default_factory: Union[Callable[[], Any], None] = _Unset,
-        annotation: Optional[Any] = None,
-        media_type: str = "multipart/form-data",
-        alias: Optional[str] = None,
-        alias_priority: Union[int, None] = _Unset,
-        # TODO: update when deprecating Pydantic v1, import these types
-        # validation_alias: str | AliasPath | AliasChoices | None
-        validation_alias: Union[str, None] = None,
-        serialization_alias: Union[str, None] = None,
-        title: Optional[str] = None,
-        description: Optional[str] = None,
-        gt: Optional[float] = None,
-        ge: Optional[float] = None,
-        lt: Optional[float] = None,
-        le: Optional[float] = None,
-        min_length: Optional[int] = None,
-        max_length: Optional[int] = None,
-        pattern: Optional[str] = None,
-        regex: Annotated[
-            Optional[str],
-            deprecated(
-                "Deprecated in FastAPI 0.100.0 and Pydantic v2, use `pattern` instead."
-            ),
-        ] = None,
-        discriminator: Union[str, None] = None,
-        strict: Union[bool, None] = _Unset,
-        multiple_of: Union[float, None] = _Unset,
-        allow_inf_nan: Union[bool, None] = _Unset,
-        max_digits: Union[int, None] = _Unset,
-        decimal_places: Union[int, None] = _Unset,
-        examples: Optional[list[Any]] = None,
-        example: Annotated[
-            Optional[Any],
-            deprecated(
-                "Deprecated in OpenAPI 3.1.0 that now uses JSON Schema 2020-12, "
-                "although still supported. Use examples instead."
-            ),
-        ] = _Unset,
-        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,
-        **extra: Any,
-    ):
-        super().__init__(
-            default=default,
-            default_factory=default_factory,
-            annotation=annotation,
-            media_type=media_type,
-            alias=alias,
-            alias_priority=alias_priority,
-            validation_alias=validation_alias,
-            serialization_alias=serialization_alias,
-            title=title,
-            description=description,
-            gt=gt,
-            ge=ge,
-            lt=lt,
-            le=le,
-            min_length=min_length,
-            max_length=max_length,
-            pattern=pattern,
-            regex=regex,
-            discriminator=discriminator,
-            strict=strict,
-            multiple_of=multiple_of,
-            allow_inf_nan=allow_inf_nan,
-            max_digits=max_digits,
-            decimal_places=decimal_places,
-            deprecated=deprecated,
-            example=example,
-            examples=examples,
-            openapi_examples=openapi_examples,
-            include_in_schema=include_in_schema,
-            json_schema_extra=json_schema_extra,
-            **extra,
-        )
index 8ae50aa145a1d81529b423f06522429c0f75acf0..78fdcbb5b4e3264faa58e1f6a55892084f132b3e 100644 (file)
@@ -6,7 +6,6 @@ from typing import (
     Any,
     Optional,
     Union,
-    cast,
 )
 from weakref import WeakKeyDictionary
 
@@ -19,11 +18,9 @@ from fastapi._compat import (
     UndefinedType,
     Validator,
     annotation_is_pydantic_v1,
-    lenient_issubclass,
-    may_v1,
 )
 from fastapi.datastructures import DefaultPlaceholder, DefaultType
-from fastapi.exceptions import FastAPIDeprecationWarning
+from fastapi.exceptions import FastAPIDeprecationWarning, PydanticV1NotSupportedError
 from pydantic import BaseModel
 from pydantic.fields import FieldInfo
 from typing_extensions import Literal
@@ -83,52 +80,18 @@ def create_model_field(
     mode: Literal["validation", "serialization"] = "validation",
     version: Literal["1", "auto"] = "auto",
 ) -> ModelField:
-    class_validators = class_validators or {}
-
-    v1_model_config = may_v1.BaseConfig
-    v1_field_info = field_info or may_v1.FieldInfo()
-    v1_kwargs = {
-        "name": name,
-        "field_info": v1_field_info,
-        "type_": type_,
-        "class_validators": class_validators,
-        "default": default,
-        "required": required,
-        "model_config": v1_model_config,
-        "alias": alias,
-    }
-
-    if (
-        annotation_is_pydantic_v1(type_)
-        or isinstance(field_info, may_v1.FieldInfo)
-        or version == "1"
-    ):
-        from fastapi._compat import v1
-
-        try:
-            return v1.ModelField(**v1_kwargs)  # type: ignore[return-value]
-        except RuntimeError:
-            raise fastapi.exceptions.FastAPIError(
-                _invalid_args_message.format(type_=type_)
-            ) from None
-    else:
-        field_info = field_info or FieldInfo(
-            annotation=type_, default=default, alias=alias
+    if annotation_is_pydantic_v1(type_):
+        raise PydanticV1NotSupportedError(
+            "pydantic.v1 models are no longer supported by FastAPI."
+            f" Please update the response model {type_!r}."
         )
-        kwargs = {"mode": mode, "name": name, "field_info": field_info}
-        try:
-            return v2.ModelField(**kwargs)  # type: ignore[return-value,arg-type]
-        except PydanticSchemaGenerationError:
-            raise fastapi.exceptions.FastAPIError(
-                _invalid_args_message.format(type_=type_)
-            ) from None
-    # Pydantic v2 is not installed, but it's not a Pydantic v1 ModelField, it could be
-    # a Pydantic v1 type, like a constrained int
-    from fastapi._compat import v1
+    class_validators = class_validators or {}
 
+    field_info = field_info or FieldInfo(annotation=type_, default=default, alias=alias)
+    kwargs = {"mode": mode, "name": name, "field_info": field_info}
     try:
-        return v1.ModelField(**v1_kwargs)
-    except RuntimeError:
+        return v2.ModelField(**kwargs)  # type: ignore[return-value,arg-type]
+    except PydanticSchemaGenerationError:
         raise fastapi.exceptions.FastAPIError(
             _invalid_args_message.format(type_=type_)
         ) from None
@@ -139,57 +102,7 @@ def create_cloned_field(
     *,
     cloned_types: Optional[MutableMapping[type[BaseModel], type[BaseModel]]] = None,
 ) -> ModelField:
-    if isinstance(field, v2.ModelField):
-        return field
-
-    from fastapi._compat import v1
-
-    # cloned_types caches already cloned types to support recursive models and improve
-    # performance by avoiding unnecessary cloning
-    if cloned_types is None:
-        cloned_types = _CLONED_TYPES_CACHE
-
-    original_type = field.type_
-    use_type = original_type
-    if lenient_issubclass(original_type, v1.BaseModel):
-        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)
-            cloned_types[original_type] = use_type
-            for f in original_type.__fields__.values():
-                use_type.__fields__[f.name] = create_cloned_field(
-                    f,
-                    cloned_types=cloned_types,
-                )
-    new_field = create_model_field(name=field.name, type_=use_type, version="1")
-    new_field.has_alias = field.has_alias  # type: ignore[attr-defined]
-    new_field.alias = field.alias  # type: ignore[misc]
-    new_field.class_validators = field.class_validators  # type: ignore[attr-defined]
-    new_field.default = field.default  # type: ignore[misc]
-    new_field.default_factory = field.default_factory  # type: ignore[attr-defined]
-    new_field.required = field.required  # type: ignore[misc]
-    new_field.model_config = field.model_config  # type: ignore[attr-defined]
-    new_field.field_info = field.field_info
-    new_field.allow_none = field.allow_none  # type: ignore[attr-defined]
-    new_field.validate_always = field.validate_always  # type: ignore[attr-defined]
-    if field.sub_fields:  # type: ignore[attr-defined]
-        new_field.sub_fields = [  # type: ignore[attr-defined]
-            create_cloned_field(sub_field, cloned_types=cloned_types)
-            for sub_field in field.sub_fields  # type: ignore[attr-defined]
-        ]
-    if field.key_field:  # type: ignore[attr-defined]
-        new_field.key_field = create_cloned_field(  # type: ignore[attr-defined]
-            field.key_field,  # type: ignore[attr-defined]
-            cloned_types=cloned_types,
-        )
-    new_field.validators = field.validators  # type: ignore[attr-defined]
-    new_field.pre_validators = field.pre_validators  # type: ignore[attr-defined]
-    new_field.post_validators = field.post_validators  # type: ignore[attr-defined]
-    new_field.parse_json = field.parse_json  # type: ignore[attr-defined]
-    new_field.shape = field.shape  # type: ignore[attr-defined]
-    new_field.populate_validators()  # type: ignore[attr-defined]
-    return new_field
+    return field
 
 
 def generate_operation_id_for_path(
index 8f824af5d591145e993aac5c09097e8e9a24f457..9c2c35a9f51ae3f0143c2786c723627fea4a8684 100644 (file)
@@ -199,14 +199,22 @@ omit = [
     "docs_src/dependencies/tutorial008_an_py39.py",  # difficult to mock
     "docs_src/dependencies/tutorial013_an_py310.py",  # temporary code example?
     "docs_src/dependencies/tutorial014_an_py310.py",  # temporary code example?
-    # Pydantic V1
+    # Pydantic v1 migration, no longer tested
+    "docs_src/pydantic_v1_in_v2/tutorial001_an_py310.py",
+    "docs_src/pydantic_v1_in_v2/tutorial001_an_py39.py",
+    "docs_src/pydantic_v1_in_v2/tutorial002_an_py310.py",
+    "docs_src/pydantic_v1_in_v2/tutorial002_an_py39.py",
+    "docs_src/pydantic_v1_in_v2/tutorial003_an_py310.py",
+    "docs_src/pydantic_v1_in_v2/tutorial003_an_py39.py",
+    "docs_src/pydantic_v1_in_v2/tutorial004_an_py310.py",
+    "docs_src/pydantic_v1_in_v2/tutorial004_an_py39.py",
+    # TODO: remove when removing this file, after updating translations, Pydantic v1
     "docs_src/schema_extra_example/tutorial001_pv1_py310.py",
-    "docs_src/query_param_models/tutorial002_pv1_py310.py",
-    "docs_src/query_param_models/tutorial002_pv1_an_py310.py",
-    "docs_src/header_param_models/tutorial002_pv1_py310.py",
-    "docs_src/header_param_models/tutorial002_pv1_an_py310.py",
-    "docs_src/cookie_param_models/tutorial002_pv1_py310.py",
-    "docs_src/cookie_param_models/tutorial002_pv1_an_py310.py",
+    "docs_src/schema_extra_example/tutorial001_pv1_py39.py",
+    "docs_src/path_operation_advanced_configuration/tutorial007_pv1_py39.py",
+    "docs_src/settings/app03_py39/config_pv1.py",
+    "docs_src/settings/app03_an_py39/config_pv1.py",
+    "docs_src/settings/tutorial001_pv1_py39.py",
 ]
 
 [tool.coverage.report]
index 8d20710303b4d87b200a3adcd20470a4446d3f34..0b5600f8f59dace5a2f99ac18ffaab3fcaab1fef 100644 (file)
@@ -1,20 +1,16 @@
-from typing import Any, Union
+from typing import Union
 
 from fastapi import FastAPI, UploadFile
 from fastapi._compat import (
     Undefined,
-    _get_model_config,
-    get_cached_model_fields,
-    is_scalar_field,
     is_uploadfile_sequence_annotation,
-    may_v1,
 )
 from fastapi._compat.shared import is_bytes_sequence_annotation
 from fastapi.testclient import TestClient
 from pydantic import BaseModel, ConfigDict
 from pydantic.fields import FieldInfo
 
-from .utils import needs_py310, needs_py_lt_314
+from .utils import needs_py310
 
 
 def test_model_field_default_required():
@@ -26,35 +22,6 @@ def test_model_field_default_required():
     assert field.default is Undefined
 
 
-@needs_py_lt_314
-def test_v1_plain_validator_function():
-    from fastapi._compat import v1
-
-    # For coverage
-    def func(v):  # pragma: no cover
-        return v
-
-    result = v1.with_info_plain_validator_function(func)
-    assert result == {}
-
-
-def test_is_model_field():
-    # For coverage
-    from fastapi._compat import _is_model_field
-
-    assert not _is_model_field(str)
-
-
-def test_get_model_config():
-    # For coverage in Pydantic v2
-    class Foo(BaseModel):
-        model_config = ConfigDict(from_attributes=True)
-
-    foo = Foo()
-    config = _get_model_config(foo)
-    assert config == {"from_attributes": True}
-
-
 def test_complex():
     app = FastAPI()
 
@@ -165,33 +132,3 @@ def test_serialize_sequence_value_with_none_first_in_union():
     result = v2.serialize_sequence_value(field=field, value=["x", "y"])
     assert result == ["x", "y"]
     assert isinstance(result, list)
-
-
-@needs_py_lt_314
-def test_is_pv1_scalar_field():
-    from fastapi._compat import v1
-
-    # For coverage
-    class Model(v1.BaseModel):
-        foo: Union[str, dict[str, Any]]
-
-    fields = v1.get_model_fields(Model)
-    assert not is_scalar_field(fields[0])
-
-
-@needs_py_lt_314
-def test_get_model_fields_cached():
-    from fastapi._compat import v1
-
-    class Model(may_v1.BaseModel):
-        foo: str
-
-    non_cached_fields = v1.get_model_fields(Model)
-    non_cached_fields2 = v1.get_model_fields(Model)
-    cached_fields = get_cached_model_fields(Model)
-    cached_fields2 = get_cached_model_fields(Model)
-    for f1, f2 in zip(cached_fields, cached_fields2):
-        assert f1 is f2
-
-    assert non_cached_fields is not non_cached_fields2
-    assert cached_fields is cached_fields2
diff --git a/tests/test_compat_params_v1.py b/tests/test_compat_params_v1.py
deleted file mode 100644 (file)
index 704b3f7..0000000
+++ /dev/null
@@ -1,1060 +0,0 @@
-import sys
-import warnings
-from typing import Optional
-
-import pytest
-from fastapi.exceptions import FastAPIDeprecationWarning
-
-from tests.utils import 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 (
-    Body,
-    Cookie,
-    File,
-    Form,
-    Header,
-    Path,
-    Query,
-)
-from fastapi.testclient import TestClient
-from inline_snapshot import snapshot
-
-
-class Item(BaseModel):
-    name: str
-    price: float
-    description: Optional[str] = None
-
-
-app = FastAPI()
-
-with warnings.catch_warnings(record=True):
-    warnings.simplefilter("always")
-
-    @app.get("/items/{item_id}")
-    def get_item_with_path(
-        item_id: Annotated[int, Path(title="The ID of the item", ge=1, le=1000)],
-    ):
-        return {"item_id": item_id}
-
-    @app.get("/items/")
-    def get_items_with_query(
-        q: Annotated[
-            Optional[str],
-            Query(min_length=3, max_length=50, pattern="^[a-zA-Z0-9 ]+$"),
-        ] = None,
-        skip: Annotated[int, Query(ge=0)] = 0,
-        limit: Annotated[int, Query(ge=1, le=100, examples=[5])] = 10,
-    ):
-        return {"q": q, "skip": skip, "limit": limit}
-
-    @app.get("/users/")
-    def get_user_with_header(
-        x_custom: Annotated[Optional[str], Header()] = None,
-        x_token: Annotated[Optional[str], Header(convert_underscores=True)] = None,
-    ):
-        return {"x_custom": x_custom, "x_token": x_token}
-
-    @app.get("/cookies/")
-    def get_cookies(
-        session_id: Annotated[Optional[str], Cookie()] = None,
-        tracking_id: Annotated[Optional[str], Cookie(min_length=10)] = None,
-    ):
-        return {"session_id": session_id, "tracking_id": tracking_id}
-
-    @app.post("/items/")
-    def create_item(
-        item: Annotated[
-            Item,
-            Body(
-                examples=[{"name": "Foo", "price": 35.4, "description": "The Foo item"}]
-            ),
-        ],
-    ):
-        return {"item": item}
-
-    @app.post("/items-embed/")
-    def create_item_embed(
-        item: Annotated[Item, Body(embed=True)],
-    ):
-        return {"item": item}
-
-    @app.put("/items/{item_id}")
-    def update_item(
-        item_id: Annotated[int, Path(ge=1)],
-        item: Annotated[Item, Body()],
-        importance: Annotated[int, Body(gt=0, le=10)],
-    ):
-        return {"item": item, "importance": importance}
-
-    @app.post("/form-data/")
-    def submit_form(
-        username: Annotated[str, Form(min_length=3, max_length=50)],
-        password: Annotated[str, Form(min_length=8)],
-        email: Annotated[Optional[str], Form()] = None,
-    ):
-        return {"username": username, "password": password, "email": email}
-
-    @app.post("/upload/")
-    def upload_file(
-        file: Annotated[bytes, File()],
-        description: Annotated[Optional[str], Form()] = None,
-    ):
-        return {"file_size": len(file), "description": description}
-
-    @app.post("/upload-multiple/")
-    def upload_multiple_files(
-        files: Annotated[list[bytes], File()],
-        note: Annotated[str, Form()] = "",
-    ):
-        return {
-            "file_count": len(files),
-            "total_size": sum(len(f) for f in files),
-            "note": note,
-        }
-
-
-client = TestClient(app)
-
-
-# Path parameter tests
-def test_path_param_valid():
-    response = client.get("/items/50")
-    assert response.status_code == 200
-    assert response.json() == {"item_id": 50}
-
-
-def test_path_param_too_large():
-    response = client.get("/items/1001")
-    assert response.status_code == 422
-    error = response.json()["detail"][0]
-    assert error["loc"] == ["path", "item_id"]
-
-
-def test_path_param_too_small():
-    response = client.get("/items/0")
-    assert response.status_code == 422
-    error = response.json()["detail"][0]
-    assert error["loc"] == ["path", "item_id"]
-
-
-# Query parameter tests
-def test_query_params_valid():
-    response = client.get("/items/?q=test search&skip=5&limit=20")
-    assert response.status_code == 200
-    assert response.json() == {"q": "test search", "skip": 5, "limit": 20}
-
-
-def test_query_params_defaults():
-    response = client.get("/items/")
-    assert response.status_code == 200
-    assert response.json() == {"q": None, "skip": 0, "limit": 10}
-
-
-def test_query_param_too_short():
-    response = client.get("/items/?q=ab")
-    assert response.status_code == 422
-    error = response.json()["detail"][0]
-    assert error["loc"] == ["query", "q"]
-
-
-def test_query_param_invalid_pattern():
-    response = client.get("/items/?q=test@#$")
-    assert response.status_code == 422
-    error = response.json()["detail"][0]
-    assert error["loc"] == ["query", "q"]
-
-
-def test_query_param_limit_too_large():
-    response = client.get("/items/?limit=101")
-    assert response.status_code == 422
-    error = response.json()["detail"][0]
-    assert error["loc"] == ["query", "limit"]
-
-
-# Header parameter tests
-def test_header_params():
-    response = client.get(
-        "/users/",
-        headers={"X-Custom": "Plumbus", "X-Token": "secret-token"},
-    )
-    assert response.status_code == 200
-    assert response.json() == {
-        "x_custom": "Plumbus",
-        "x_token": "secret-token",
-    }
-
-
-def test_header_underscore_conversion():
-    response = client.get(
-        "/users/",
-        headers={"x-token": "secret-token-with-dash"},
-    )
-    assert response.status_code == 200
-    assert response.json()["x_token"] == "secret-token-with-dash"
-
-
-def test_header_params_none():
-    response = client.get("/users/")
-    assert response.status_code == 200
-    assert response.json() == {"x_custom": None, "x_token": None}
-
-
-# Cookie parameter tests
-def test_cookie_params():
-    with TestClient(app) as test_client:
-        test_client.cookies.set("session_id", "abc123")
-        test_client.cookies.set("tracking_id", "1234567890abcdef")
-        response = test_client.get("/cookies/")
-    assert response.status_code == 200
-    assert response.json() == {
-        "session_id": "abc123",
-        "tracking_id": "1234567890abcdef",
-    }
-
-
-def test_cookie_tracking_id_too_short():
-    with TestClient(app) as test_client:
-        test_client.cookies.set("tracking_id", "short")
-        response = test_client.get("/cookies/")
-    assert response.status_code == 422
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "loc": ["cookie", "tracking_id"],
-                    "msg": "ensure this value has at least 10 characters",
-                    "type": "value_error.any_str.min_length",
-                    "ctx": {"limit_value": 10},
-                }
-            ]
-        }
-    )
-
-
-def test_cookie_params_none():
-    response = client.get("/cookies/")
-    assert response.status_code == 200
-    assert response.json() == {"session_id": None, "tracking_id": None}
-
-
-# Body parameter tests
-def test_body_param():
-    response = client.post(
-        "/items/",
-        json={"name": "Test Item", "price": 29.99, "description": "A test item"},
-    )
-    assert response.status_code == 200
-    assert response.json() == {
-        "item": {
-            "name": "Test Item",
-            "price": 29.99,
-            "description": "A test item",
-        }
-    }
-
-
-def test_body_param_minimal():
-    response = client.post(
-        "/items/",
-        json={"name": "Minimal", "price": 9.99},
-    )
-    assert response.status_code == 200
-    assert response.json() == {
-        "item": {"name": "Minimal", "price": 9.99, "description": None}
-    }
-
-
-def test_body_param_missing_required():
-    response = client.post(
-        "/items/",
-        json={"name": "Incomplete"},
-    )
-    assert response.status_code == 422
-    error = response.json()["detail"][0]
-    assert error["loc"] == ["body", "price"]
-
-
-def test_body_embed():
-    response = client.post(
-        "/items-embed/",
-        json={"item": {"name": "Embedded", "price": 15.0}},
-    )
-    assert response.status_code == 200
-    assert response.json() == {
-        "item": {"name": "Embedded", "price": 15.0, "description": None}
-    }
-
-
-def test_body_embed_wrong_structure():
-    response = client.post(
-        "/items-embed/",
-        json={"name": "Not Embedded", "price": 15.0},
-    )
-    assert response.status_code == 422
-
-
-# Multiple body parameters test
-def test_multiple_body_params():
-    response = client.put(
-        "/items/5",
-        json={
-            "item": {"name": "Updated Item", "price": 49.99},
-            "importance": 8,
-        },
-    )
-    assert response.status_code == 200
-    assert response.json() == snapshot(
-        {
-            "item": {"name": "Updated Item", "price": 49.99, "description": None},
-            "importance": 8,
-        }
-    )
-
-
-def test_multiple_body_params_importance_too_large():
-    response = client.put(
-        "/items/5",
-        json={
-            "item": {"name": "Item", "price": 10.0},
-            "importance": 11,
-        },
-    )
-    assert response.status_code == 422
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "loc": ["body", "importance"],
-                    "msg": "ensure this value is less than or equal to 10",
-                    "type": "value_error.number.not_le",
-                    "ctx": {"limit_value": 10},
-                }
-            ]
-        }
-    )
-
-
-def test_multiple_body_params_importance_too_small():
-    response = client.put(
-        "/items/5",
-        json={
-            "item": {"name": "Item", "price": 10.0},
-            "importance": 0,
-        },
-    )
-    assert response.status_code == 422
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "loc": ["body", "importance"],
-                    "msg": "ensure this value is greater than 0",
-                    "type": "value_error.number.not_gt",
-                    "ctx": {"limit_value": 0},
-                }
-            ]
-        }
-    )
-
-
-# Form parameter tests
-def test_form_data_valid():
-    response = client.post(
-        "/form-data/",
-        data={
-            "username": "testuser",
-            "password": "password123",
-            "email": "test@example.com",
-        },
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "username": "testuser",
-        "password": "password123",
-        "email": "test@example.com",
-    }
-
-
-def test_form_data_optional_field():
-    response = client.post(
-        "/form-data/",
-        data={"username": "testuser", "password": "password123"},
-    )
-    assert response.status_code == 200
-    assert response.json() == {
-        "username": "testuser",
-        "password": "password123",
-        "email": None,
-    }
-
-
-def test_form_data_username_too_short():
-    response = client.post(
-        "/form-data/",
-        data={"username": "ab", "password": "password123"},
-    )
-    assert response.status_code == 422
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "loc": ["body", "username"],
-                    "msg": "ensure this value has at least 3 characters",
-                    "type": "value_error.any_str.min_length",
-                    "ctx": {"limit_value": 3},
-                }
-            ]
-        }
-    )
-
-
-def test_form_data_password_too_short():
-    response = client.post(
-        "/form-data/",
-        data={"username": "testuser", "password": "short"},
-    )
-    assert response.status_code == 422
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "loc": ["body", "password"],
-                    "msg": "ensure this value has at least 8 characters",
-                    "type": "value_error.any_str.min_length",
-                    "ctx": {"limit_value": 8},
-                }
-            ]
-        }
-    )
-
-
-# File upload tests
-def test_upload_file():
-    response = client.post(
-        "/upload/",
-        files={"file": ("test.txt", b"Hello, World!", "text/plain")},
-        data={"description": "A test file"},
-    )
-    assert response.status_code == 200
-    assert response.json() == {
-        "file_size": 13,
-        "description": "A test file",
-    }
-
-
-def test_upload_file_without_description():
-    response = client.post(
-        "/upload/",
-        files={"file": ("test.txt", b"Hello!", "text/plain")},
-    )
-    assert response.status_code == 200
-    assert response.json() == {
-        "file_size": 6,
-        "description": None,
-    }
-
-
-def test_upload_multiple_files():
-    response = client.post(
-        "/upload-multiple/",
-        files=[
-            ("files", ("file1.txt", b"Content 1", "text/plain")),
-            ("files", ("file2.txt", b"Content 2", "text/plain")),
-            ("files", ("file3.txt", b"Content 3", "text/plain")),
-        ],
-        data={"note": "Multiple files uploaded"},
-    )
-    assert response.status_code == 200
-    assert response.json() == {
-        "file_count": 3,
-        "total_size": 27,
-        "note": "Multiple files uploaded",
-    }
-
-
-def test_upload_multiple_files_empty_note():
-    response = client.post(
-        "/upload-multiple/",
-        files=[
-            ("files", ("file1.txt", b"Test", "text/plain")),
-        ],
-    )
-    assert response.status_code == 200
-    assert response.json()["file_count"] == 1
-    assert response.json()["note"] == ""
-
-
-# __repr__ tests
-def test_query_repr():
-    query_param = Query(default=None, min_length=3)
-    assert repr(query_param) == "Query(None)"
-
-
-def test_body_repr():
-    body_param = Body(default=None)
-    assert repr(body_param) == "Body(None)"
-
-
-# Deprecation warning tests for regex parameter
-def test_query_regex_deprecation_warning():
-    with pytest.warns(FastAPIDeprecationWarning, match="`regex` has been deprecated"):
-        Query(regex="^test$")
-
-
-def test_body_regex_deprecation_warning():
-    with pytest.warns(FastAPIDeprecationWarning, match="`regex` has been deprecated"):
-        Body(regex="^test$")
-
-
-# Deprecation warning tests for example parameter
-def test_query_example_deprecation_warning():
-    with pytest.warns(FastAPIDeprecationWarning, match="`example` has been deprecated"):
-        Query(example="test example")
-
-
-def test_body_example_deprecation_warning():
-    with pytest.warns(FastAPIDeprecationWarning, match="`example` has been deprecated"):
-        Body(example={"test": "example"})
-
-
-def test_openapi_schema():
-    response = client.get("/openapi.json")
-    assert response.status_code == 200, response.text
-    assert response.json() == snapshot(
-        {
-            "openapi": "3.1.0",
-            "info": {"title": "FastAPI", "version": "0.1.0"},
-            "paths": {
-                "/items/{item_id}": {
-                    "get": {
-                        "summary": "Get Item With Path",
-                        "operationId": "get_item_with_path_items__item_id__get",
-                        "parameters": [
-                            {
-                                "name": "item_id",
-                                "in": "path",
-                                "required": True,
-                                "schema": {
-                                    "title": "The ID of the item",
-                                    "minimum": 1,
-                                    "maximum": 1000,
-                                    "type": "integer",
-                                },
-                            }
-                        ],
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {"application/json": {"schema": {}}},
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    },
-                    "put": {
-                        "summary": "Update Item",
-                        "operationId": "update_item_items__item_id__put",
-                        "parameters": [
-                            {
-                                "name": "item_id",
-                                "in": "path",
-                                "required": True,
-                                "schema": {
-                                    "title": "Item Id",
-                                    "minimum": 1,
-                                    "type": "integer",
-                                },
-                            }
-                        ],
-                        "requestBody": {
-                            "required": True,
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "title": "Body",
-                                        "allOf": [
-                                            {
-                                                "$ref": "#/components/schemas/Body_update_item_items__item_id__put"
-                                            }
-                                        ],
-                                    }
-                                }
-                            },
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {"application/json": {"schema": {}}},
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    },
-                },
-                "/items/": {
-                    "get": {
-                        "summary": "Get Items With Query",
-                        "operationId": "get_items_with_query_items__get",
-                        "parameters": [
-                            {
-                                "name": "q",
-                                "in": "query",
-                                "required": False,
-                                "schema": {
-                                    "title": "Q",
-                                    "maxLength": 50,
-                                    "minLength": 3,
-                                    "pattern": "^[a-zA-Z0-9 ]+$",
-                                    "type": "string",
-                                },
-                            },
-                            {
-                                "name": "skip",
-                                "in": "query",
-                                "required": False,
-                                "schema": {
-                                    "title": "Skip",
-                                    "default": 0,
-                                    "minimum": 0,
-                                    "type": "integer",
-                                },
-                            },
-                            {
-                                "name": "limit",
-                                "in": "query",
-                                "required": False,
-                                "schema": {
-                                    "title": "Limit",
-                                    "default": 10,
-                                    "minimum": 1,
-                                    "maximum": 100,
-                                    "examples": [5],
-                                    "type": "integer",
-                                },
-                            },
-                        ],
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {"application/json": {"schema": {}}},
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    },
-                    "post": {
-                        "summary": "Create Item",
-                        "operationId": "create_item_items__post",
-                        "requestBody": {
-                            "required": True,
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "title": "Item",
-                                        "examples": [
-                                            {
-                                                "name": "Foo",
-                                                "price": 35.4,
-                                                "description": "The Foo item",
-                                            }
-                                        ],
-                                        "allOf": [
-                                            {"$ref": "#/components/schemas/Item"}
-                                        ],
-                                    }
-                                }
-                            },
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {"application/json": {"schema": {}}},
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    },
-                },
-                "/users/": {
-                    "get": {
-                        "summary": "Get User With Header",
-                        "operationId": "get_user_with_header_users__get",
-                        "parameters": [
-                            {
-                                "name": "x-custom",
-                                "in": "header",
-                                "required": False,
-                                "schema": {"title": "X-Custom", "type": "string"},
-                            },
-                            {
-                                "name": "x-token",
-                                "in": "header",
-                                "required": False,
-                                "schema": {"title": "X-Token", "type": "string"},
-                            },
-                        ],
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {"application/json": {"schema": {}}},
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/cookies/": {
-                    "get": {
-                        "summary": "Get Cookies",
-                        "operationId": "get_cookies_cookies__get",
-                        "parameters": [
-                            {
-                                "name": "session_id",
-                                "in": "cookie",
-                                "required": False,
-                                "schema": {"title": "Session Id", "type": "string"},
-                            },
-                            {
-                                "name": "tracking_id",
-                                "in": "cookie",
-                                "required": False,
-                                "schema": {
-                                    "title": "Tracking Id",
-                                    "minLength": 10,
-                                    "type": "string",
-                                },
-                            },
-                        ],
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {"application/json": {"schema": {}}},
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/items-embed/": {
-                    "post": {
-                        "summary": "Create Item Embed",
-                        "operationId": "create_item_embed_items_embed__post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "allOf": [
-                                            {
-                                                "$ref": "#/components/schemas/Body_create_item_embed_items_embed__post"
-                                            }
-                                        ],
-                                        "title": "Body",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {"application/json": {"schema": {}}},
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/form-data/": {
-                    "post": {
-                        "summary": "Submit Form",
-                        "operationId": "submit_form_form_data__post",
-                        "requestBody": {
-                            "content": {
-                                "application/x-www-form-urlencoded": {
-                                    "schema": {
-                                        "allOf": [
-                                            {
-                                                "$ref": "#/components/schemas/Body_submit_form_form_data__post"
-                                            }
-                                        ],
-                                        "title": "Body",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {"application/json": {"schema": {}}},
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/upload/": {
-                    "post": {
-                        "summary": "Upload File",
-                        "operationId": "upload_file_upload__post",
-                        "requestBody": {
-                            "content": {
-                                "multipart/form-data": {
-                                    "schema": {
-                                        "allOf": [
-                                            {
-                                                "$ref": "#/components/schemas/Body_upload_file_upload__post"
-                                            }
-                                        ],
-                                        "title": "Body",
-                                    },
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {"application/json": {"schema": {}}},
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/upload-multiple/": {
-                    "post": {
-                        "summary": "Upload Multiple Files",
-                        "operationId": "upload_multiple_files_upload_multiple__post",
-                        "requestBody": {
-                            "content": {
-                                "multipart/form-data": {
-                                    "schema": {
-                                        "allOf": [
-                                            {
-                                                "$ref": "#/components/schemas/Body_upload_multiple_files_upload_multiple__post"
-                                            }
-                                        ],
-                                        "title": "Body",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {"application/json": {"schema": {}}},
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-            },
-            "components": {
-                "schemas": {
-                    "Body_create_item_embed_items_embed__post": {
-                        "properties": {
-                            "item": {
-                                "allOf": [{"$ref": "#/components/schemas/Item"}],
-                                "title": "Item",
-                            }
-                        },
-                        "type": "object",
-                        "required": ["item"],
-                        "title": "Body_create_item_embed_items_embed__post",
-                    },
-                    "Body_submit_form_form_data__post": {
-                        "properties": {
-                            "username": {
-                                "type": "string",
-                                "maxLength": 50,
-                                "minLength": 3,
-                                "title": "Username",
-                            },
-                            "password": {
-                                "type": "string",
-                                "minLength": 8,
-                                "title": "Password",
-                            },
-                            "email": {"type": "string", "title": "Email"},
-                        },
-                        "type": "object",
-                        "required": ["username", "password"],
-                        "title": "Body_submit_form_form_data__post",
-                    },
-                    "Body_update_item_items__item_id__put": {
-                        "properties": {
-                            "item": {
-                                "allOf": [{"$ref": "#/components/schemas/Item"}],
-                                "title": "Item",
-                            },
-                            "importance": {
-                                "type": "integer",
-                                "maximum": 10.0,
-                                "exclusiveMinimum": 0.0,
-                                "title": "Importance",
-                            },
-                        },
-                        "type": "object",
-                        "required": ["item", "importance"],
-                        "title": "Body_update_item_items__item_id__put",
-                    },
-                    "Body_upload_file_upload__post": {
-                        "properties": {
-                            "file": {
-                                "type": "string",
-                                "format": "binary",
-                                "title": "File",
-                            },
-                            "description": {"type": "string", "title": "Description"},
-                        },
-                        "type": "object",
-                        "required": ["file"],
-                        "title": "Body_upload_file_upload__post",
-                    },
-                    "Body_upload_multiple_files_upload_multiple__post": {
-                        "properties": {
-                            "files": {
-                                "items": {"type": "string", "format": "binary"},
-                                "type": "array",
-                                "title": "Files",
-                            },
-                            "note": {"type": "string", "title": "Note", "default": ""},
-                        },
-                        "type": "object",
-                        "required": ["files"],
-                        "title": "Body_upload_multiple_files_upload_multiple__post",
-                    },
-                    "HTTPValidationError": {
-                        "properties": {
-                            "detail": {
-                                "items": {
-                                    "$ref": "#/components/schemas/ValidationError"
-                                },
-                                "type": "array",
-                                "title": "Detail",
-                            }
-                        },
-                        "type": "object",
-                        "title": "HTTPValidationError",
-                    },
-                    "Item": {
-                        "properties": {
-                            "name": {"type": "string", "title": "Name"},
-                            "price": {"type": "number", "title": "Price"},
-                            "description": {"type": "string", "title": "Description"},
-                        },
-                        "type": "object",
-                        "required": ["name", "price"],
-                        "title": "Item",
-                    },
-                    "ValidationError": {
-                        "properties": {
-                            "loc": {
-                                "items": {
-                                    "anyOf": [{"type": "string"}, {"type": "integer"}]
-                                },
-                                "type": "array",
-                                "title": "Location",
-                            },
-                            "msg": {"type": "string", "title": "Message"},
-                            "type": {"type": "string", "title": "Error Type"},
-                        },
-                        "type": "object",
-                        "required": ["loc", "msg", "type"],
-                        "title": "ValidationError",
-                    },
-                }
-            },
-        }
-    )
index 56b6780f04d907d03cba67d53aeec02f77394d75..f154ede02932121a6d3c6602d68a9bfbb616803c 100644 (file)
@@ -1,12 +1,9 @@
-import warnings
 from datetime import datetime, timezone
 
 from fastapi import FastAPI
 from fastapi.testclient import TestClient
 from pydantic import BaseModel
 
-from .utils import needs_pydanticv1
-
 
 def test_pydanticv2():
     from pydantic import field_serializer
@@ -29,34 +26,3 @@ def test_pydanticv2():
     with client:
         response = client.get("/model")
     assert response.json() == {"dt_field": "2019-01-01T08:00:00+00:00"}
-
-
-# TODO: remove when deprecating Pydantic v1
-@needs_pydanticv1
-def test_pydanticv1():
-    from pydantic import v1
-
-    class ModelWithDatetimeField(v1.BaseModel):
-        dt_field: datetime
-
-        class Config:
-            json_encoders = {
-                datetime: lambda dt: dt.replace(
-                    microsecond=0, tzinfo=timezone.utc
-                ).isoformat()
-            }
-
-    app = FastAPI()
-    model = ModelWithDatetimeField(dt_field=datetime(2019, 1, 1, 8))
-
-    with warnings.catch_warnings(record=True):
-        warnings.simplefilter("always")
-
-        @app.get("/model", response_model=ModelWithDatetimeField)
-        def get_model():
-            return model
-
-    client = TestClient(app)
-    with client:
-        response = client.get("/model")
-    assert response.json() == {"dt_field": "2019-01-01T08:00:00+00:00"}
diff --git a/tests/test_filter_pydantic_sub_model/__init__.py b/tests/test_filter_pydantic_sub_model/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/tests/test_filter_pydantic_sub_model/app_pv1.py b/tests/test_filter_pydantic_sub_model/app_pv1.py
deleted file mode 100644 (file)
index d6f2ce7..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-import warnings
-from typing import Optional
-
-from fastapi import Depends, FastAPI
-from pydantic.v1 import BaseModel, validator
-
-app = FastAPI()
-
-
-class ModelB(BaseModel):
-    username: str
-
-
-class ModelC(ModelB):
-    password: str
-
-
-class ModelA(BaseModel):
-    name: str
-    description: Optional[str] = None
-    model_b: ModelB
-    tags: dict[str, str] = {}
-
-    @validator("name")
-    def lower_username(cls, name: str, values):
-        if not name.endswith("A"):
-            raise ValueError("name must end in A")
-        return name
-
-
-async def get_model_c() -> ModelC:
-    return ModelC(username="test-user", password="test-password")
-
-
-with warnings.catch_warnings(record=True):
-    warnings.simplefilter("always")
-
-    @app.get("/model/{name}", response_model=ModelA)
-    async def get_model_a(name: str, model_c=Depends(get_model_c)):
-        return {
-            "name": name,
-            "description": "model-a-desc",
-            "model_b": model_c,
-            "tags": {"key1": "value1", "key2": "value2"},
-        }
diff --git a/tests/test_filter_pydantic_sub_model/test_filter_pydantic_sub_model_pv1.py b/tests/test_filter_pydantic_sub_model/test_filter_pydantic_sub_model_pv1.py
deleted file mode 100644 (file)
index b464b4f..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-import pytest
-from fastapi.exceptions import ResponseValidationError
-from fastapi.testclient import TestClient
-from inline_snapshot import snapshot
-
-from ..utils import needs_pydanticv1
-
-
-@pytest.fixture(name="client")
-def get_client():
-    from .app_pv1 import app
-
-    client = TestClient(app)
-    return client
-
-
-@needs_pydanticv1
-def test_filter_sub_model(client: TestClient):
-    response = client.get("/model/modelA")
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "name": "modelA",
-        "description": "model-a-desc",
-        "model_b": {"username": "test-user"},
-        "tags": {"key1": "value1", "key2": "value2"},
-    }
-
-
-@needs_pydanticv1
-def test_validator_is_cloned(client: TestClient):
-    with pytest.raises(ResponseValidationError) as err:
-        client.get("/model/modelX")
-    assert err.value.errors() == [
-        {
-            "loc": ("response", "name"),
-            "msg": "name must end in A",
-            "type": "value_error",
-        }
-    ]
-
-
-@needs_pydanticv1
-def test_openapi_schema(client: TestClient):
-    response = client.get("/openapi.json")
-    assert response.status_code == 200, response.text
-    assert response.json() == snapshot(
-        {
-            "openapi": "3.1.0",
-            "info": {"title": "FastAPI", "version": "0.1.0"},
-            "paths": {
-                "/model/{name}": {
-                    "get": {
-                        "summary": "Get Model A",
-                        "operationId": "get_model_a_model__name__get",
-                        "parameters": [
-                            {
-                                "required": True,
-                                "schema": {"title": "Name", "type": "string"},
-                                "name": "name",
-                                "in": "path",
-                            }
-                        ],
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/ModelA"
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                }
-            },
-            "components": {
-                "schemas": {
-                    "HTTPValidationError": {
-                        "title": "HTTPValidationError",
-                        "type": "object",
-                        "properties": {
-                            "detail": {
-                                "title": "Detail",
-                                "type": "array",
-                                "items": {
-                                    "$ref": "#/components/schemas/ValidationError"
-                                },
-                            }
-                        },
-                    },
-                    "ModelA": {
-                        "title": "ModelA",
-                        "required": ["name", "model_b"],
-                        "type": "object",
-                        "properties": {
-                            "name": {"title": "Name", "type": "string"},
-                            "description": {"title": "Description", "type": "string"},
-                            "model_b": {"$ref": "#/components/schemas/ModelB"},
-                            "tags": {
-                                "additionalProperties": {"type": "string"},
-                                "type": "object",
-                                "title": "Tags",
-                                "default": {},
-                            },
-                        },
-                    },
-                    "ModelB": {
-                        "title": "ModelB",
-                        "required": ["username"],
-                        "type": "object",
-                        "properties": {
-                            "username": {"title": "Username", "type": "string"}
-                        },
-                    },
-                    "ValidationError": {
-                        "title": "ValidationError",
-                        "required": ["loc", "msg", "type"],
-                        "type": "object",
-                        "properties": {
-                            "loc": {
-                                "title": "Location",
-                                "type": "array",
-                                "items": {
-                                    "anyOf": [{"type": "string"}, {"type": "integer"}]
-                                },
-                            },
-                            "msg": {"title": "Message", "type": "string"},
-                            "type": {"title": "Error Type", "type": "string"},
-                        },
-                    },
-                }
-            },
-        }
-    )
index dee5955544aaad8ec90cf2578b308fbe0c2edafb..eb7939b69a922fb9241d2476e187d6db46555c90 100644 (file)
@@ -1,25 +1,12 @@
-import warnings
-
 import pytest
 from fastapi import FastAPI
 from fastapi.testclient import TestClient
 from inline_snapshot import snapshot
 
-from .utils import needs_pydanticv1
-
 
-@pytest.fixture(
-    name="client",
-    params=[
-        pytest.param("pydantic-v1", marks=needs_pydanticv1),
-        "pydantic-v2",
-    ],
-)
-def client_fixture(request: pytest.FixtureRequest) -> TestClient:
-    if request.param == "pydantic-v1":
-        from pydantic.v1 import BaseModel
-    else:
-        from pydantic import BaseModel
+@pytest.fixture(name="client")
+def client_fixture() -> TestClient:
+    from pydantic import BaseModel
 
     class Address(BaseModel):
         """
@@ -38,28 +25,12 @@ def client_fixture(request: pytest.FixtureRequest) -> TestClient:
 
     app = FastAPI()
 
-    if request.param == "pydantic-v1":
-        with warnings.catch_warnings(record=True):
-            warnings.simplefilter("always")
-
-            @app.get("/facilities/{facility_id}")
-            def get_facility(facility_id: str) -> Facility:
-                return Facility(
-                    id=facility_id,
-                    address=Address(
-                        line_1="123 Main St", city="Anytown", state_province="CA"
-                    ),
-                )
-    else:
-
-        @app.get("/facilities/{facility_id}")
-        def get_facility(facility_id: str) -> Facility:
-            return Facility(
-                id=facility_id,
-                address=Address(
-                    line_1="123 Main St", city="Anytown", state_province="CA"
-                ),
-            )
+    @app.get("/facilities/{facility_id}")
+    def get_facility(facility_id: str) -> Facility:
+        return Facility(
+            id=facility_id,
+            address=Address(line_1="123 Main St", city="Anytown", state_province="CA"),
+        )
 
     client = TestClient(app)
     return client
index 7f29fe33eda756af5402e7fc6c489f1f0741304d..8cf8952f92447c90bf40f4b4c3536e68f878a03a 100644 (file)
@@ -5,8 +5,6 @@ from fastapi import FastAPI
 from fastapi.testclient import TestClient
 from pydantic import BaseModel
 
-from .utils import needs_pydanticv1
-
 
 class MyUuid:
     def __init__(self, uuid_string: str):
@@ -67,46 +65,3 @@ def test_pydanticv2():
     assert response_pydantic.json() == {
         "a_uuid": "b8799909-f914-42de-91bc-95c819218d01"
     }
-
-
-# TODO: remove when deprecating Pydantic v1
-@needs_pydanticv1
-def test_pydanticv1():
-    from pydantic import v1
-
-    app = FastAPI()
-
-    @app.get("/fast_uuid")
-    def return_fast_uuid():
-        asyncpg_uuid = MyUuid("a10ff360-3b1e-4984-a26f-d3ab460bdb51")
-        assert isinstance(asyncpg_uuid, uuid.UUID)
-        assert type(asyncpg_uuid) is not uuid.UUID
-        with pytest.raises(TypeError):
-            vars(asyncpg_uuid)
-        return {"fast_uuid": asyncpg_uuid}
-
-    class SomeCustomClass(v1.BaseModel):
-        class Config:
-            arbitrary_types_allowed = True
-            json_encoders = {uuid.UUID: str}
-
-        a_uuid: MyUuid
-
-    @app.get("/get_custom_class")
-    def return_some_user():
-        # Test that the fix also works for custom pydantic classes
-        return SomeCustomClass(a_uuid=MyUuid("b8799909-f914-42de-91bc-95c819218d01"))
-
-    client = TestClient(app)
-
-    with client:
-        response_simple = client.get("/fast_uuid")
-        response_pydantic = client.get("/get_custom_class")
-
-    assert response_simple.json() == {
-        "fast_uuid": "a10ff360-3b1e-4984-a26f-d3ab460bdb51"
-    }
-
-    assert response_pydantic.json() == {
-        "a_uuid": "b8799909-f914-42de-91bc-95c819218d01"
-    }
index 81bf94ece0aa73be877c8bd6937ac07d4d5bd409..4528dff4406daa275bfecacf43fc6b74193213e4 100644 (file)
@@ -1,3 +1,4 @@
+import warnings
 from collections import deque
 from dataclasses import dataclass
 from datetime import datetime, timezone
@@ -5,15 +6,14 @@ from decimal import Decimal
 from enum import Enum
 from math import isinf, isnan
 from pathlib import PurePath, PurePosixPath, PureWindowsPath
-from typing import Optional
+from typing import Optional, TypedDict
 
 import pytest
 from fastapi._compat import Undefined
 from fastapi.encoders import jsonable_encoder
+from fastapi.exceptions import PydanticV1NotSupportedError
 from pydantic import BaseModel, Field, ValidationError
 
-from .utils import needs_pydanticv1
-
 
 class Person:
     def __init__(self, name: str):
@@ -156,29 +156,17 @@ def test_encode_custom_json_encoders_model_pydanticv2():
     assert jsonable_encoder(subclass_model) == {"dt_field": "2019-01-01T08:00:00+00:00"}
 
 
-# TODO: remove when deprecating Pydantic v1
-@needs_pydanticv1
-def test_encode_custom_json_encoders_model_pydanticv1():
-    from pydantic import v1
-
-    class ModelWithCustomEncoder(v1.BaseModel):
-        dt_field: datetime
+def test_json_encoder_error_with_pydanticv1():
+    with warnings.catch_warnings():
+        warnings.simplefilter("ignore", UserWarning)
+        from pydantic import v1
 
-        class Config:
-            json_encoders = {
-                datetime: lambda dt: dt.replace(
-                    microsecond=0, tzinfo=timezone.utc
-                ).isoformat()
-            }
-
-    class ModelWithCustomEncoderSubclass(ModelWithCustomEncoder):
-        class Config:
-            pass
+    class ModelV1(v1.BaseModel):
+        name: str
 
-    model = ModelWithCustomEncoder(dt_field=datetime(2019, 1, 1, 8))
-    assert jsonable_encoder(model) == {"dt_field": "2019-01-01T08:00:00+00:00"}
-    subclass_model = ModelWithCustomEncoderSubclass(dt_field=datetime(2019, 1, 1, 8))
-    assert jsonable_encoder(subclass_model) == {"dt_field": "2019-01-01T08:00:00+00:00"}
+    data = ModelV1(name="test")
+    with pytest.raises(PydanticV1NotSupportedError):
+        jsonable_encoder(data)
 
 
 def test_encode_model_with_config():
@@ -214,25 +202,27 @@ def test_encode_model_with_default():
     }
 
 
-@needs_pydanticv1
 def test_custom_encoders():
-    from pydantic import v1
-
     class safe_datetime(datetime):
         pass
 
-    class MyModel(v1.BaseModel):
+    class MyDict(TypedDict):
         dt_field: safe_datetime
 
-    instance = MyModel(dt_field=safe_datetime.now())
+    instance = MyDict(dt_field=safe_datetime.now())
 
     encoded_instance = jsonable_encoder(
         instance, custom_encoder={safe_datetime: lambda o: o.strftime("%H:%M:%S")}
     )
-    assert encoded_instance["dt_field"] == instance.dt_field.strftime("%H:%M:%S")
+    assert encoded_instance["dt_field"] == instance["dt_field"].strftime("%H:%M:%S")
+
+    encoded_instance = jsonable_encoder(
+        instance, custom_encoder={datetime: lambda o: o.strftime("%H:%M:%S")}
+    )
+    assert encoded_instance["dt_field"] == instance["dt_field"].strftime("%H:%M:%S")
 
     encoded_instance2 = jsonable_encoder(instance)
-    assert encoded_instance2["dt_field"] == instance.dt_field.isoformat()
+    assert encoded_instance2["dt_field"] == instance["dt_field"].isoformat()
 
 
 def test_custom_enum_encoders():
@@ -287,17 +277,6 @@ def test_encode_pure_path():
     assert jsonable_encoder({"path": test_path}) == {"path": str(test_path)}
 
 
-@needs_pydanticv1
-def test_encode_root():
-    from pydantic import v1
-
-    class ModelWithRoot(v1.BaseModel):
-        __root__: str
-
-    model = ModelWithRoot(__root__="Foo")
-    assert jsonable_encoder(model) == "Foo"
-
-
 def test_decimal_encoder_float():
     data = {"value": Decimal(1.23)}
     assert jsonable_encoder(data) == {"value": 1.23}
diff --git a/tests/test_pydantic_v1_deprecation_warnings.py b/tests/test_pydantic_v1_deprecation_warnings.py
deleted file mode 100644 (file)
index 89ca6a8..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-import sys
-
-import pytest
-from fastapi.exceptions import FastAPIDeprecationWarning
-
-from tests.utils import skip_module_if_py_gte_314
-
-if sys.version_info >= (3, 14):
-    skip_module_if_py_gte_314()
-
-from fastapi import FastAPI
-from fastapi._compat.v1 import BaseModel
-from fastapi.testclient import TestClient
-
-
-def test_warns_pydantic_v1_model_in_endpoint_param() -> None:
-    class ParamModelV1(BaseModel):
-        name: str
-
-    app = FastAPI()
-
-    with pytest.warns(
-        FastAPIDeprecationWarning,
-        match=r"pydantic\.v1 is deprecated.*Please update the param data:",
-    ):
-
-        @app.post("/param")
-        def endpoint(data: ParamModelV1):
-            return data
-
-    client = TestClient(app)
-    response = client.post("/param", json={"name": "test"})
-    assert response.status_code == 200, response.text
-    assert response.json() == {"name": "test"}
-
-
-def test_warns_pydantic_v1_model_in_return_type() -> None:
-    class ReturnModelV1(BaseModel):
-        name: str
-
-    app = FastAPI()
-
-    with pytest.warns(
-        FastAPIDeprecationWarning,
-        match=r"pydantic\.v1 is deprecated.*Please update the response model",
-    ):
-
-        @app.get("/return")
-        def endpoint() -> ReturnModelV1:
-            return ReturnModelV1(name="test")
-
-    client = TestClient(app)
-    response = client.get("/return")
-    assert response.status_code == 200, response.text
-    assert response.json() == {"name": "test"}
-
-
-def test_warns_pydantic_v1_model_in_response_model() -> None:
-    class ResponseModelV1(BaseModel):
-        name: str
-
-    app = FastAPI()
-
-    with pytest.warns(
-        FastAPIDeprecationWarning,
-        match=r"pydantic\.v1 is deprecated.*Please update the response model",
-    ):
-
-        @app.get("/response-model", response_model=ResponseModelV1)
-        def endpoint():
-            return {"name": "test"}
-
-    client = TestClient(app)
-    response = client.get("/response-model")
-    assert response.status_code == 200, response.text
-    assert response.json() == {"name": "test"}
-
-
-def test_warns_pydantic_v1_model_in_additional_responses_model() -> None:
-    class ErrorModelV1(BaseModel):
-        detail: str
-
-    app = FastAPI()
-
-    with pytest.warns(
-        FastAPIDeprecationWarning,
-        match=r"pydantic\.v1 is deprecated.*In responses=\{\}, please update",
-    ):
-
-        @app.get(
-            "/responses", response_model=None, responses={400: {"model": ErrorModelV1}}
-        )
-        def endpoint():
-            return {"ok": True}
-
-    client = TestClient(app)
-    response = client.get("/responses")
-    assert response.status_code == 200, response.text
-    assert response.json() == {"ok": True}
diff --git a/tests/test_pydantic_v1_error.py b/tests/test_pydantic_v1_error.py
new file mode 100644 (file)
index 0000000..13229a3
--- /dev/null
@@ -0,0 +1,97 @@
+import sys
+import warnings
+from typing import Union
+
+import pytest
+
+from tests.utils import skip_module_if_py_gte_314
+
+if sys.version_info >= (3, 14):
+    skip_module_if_py_gte_314()
+
+from fastapi import FastAPI
+from fastapi.exceptions import PydanticV1NotSupportedError
+
+with warnings.catch_warnings():
+    warnings.simplefilter("ignore", UserWarning)
+    from pydantic.v1 import BaseModel
+
+
+def test_raises_pydantic_v1_model_in_endpoint_param() -> None:
+    class ParamModelV1(BaseModel):
+        name: str
+
+    app = FastAPI()
+
+    with pytest.raises(PydanticV1NotSupportedError):
+
+        @app.post("/param")
+        def endpoint(data: ParamModelV1):  # pragma: no cover
+            return data
+
+
+def test_raises_pydantic_v1_model_in_return_type() -> None:
+    class ReturnModelV1(BaseModel):
+        name: str
+
+    app = FastAPI()
+
+    with pytest.raises(PydanticV1NotSupportedError):
+
+        @app.get("/return")
+        def endpoint() -> ReturnModelV1:  # pragma: no cover
+            return ReturnModelV1(name="test")
+
+
+def test_raises_pydantic_v1_model_in_response_model() -> None:
+    class ResponseModelV1(BaseModel):
+        name: str
+
+    app = FastAPI()
+
+    with pytest.raises(PydanticV1NotSupportedError):
+
+        @app.get("/response-model", response_model=ResponseModelV1)
+        def endpoint():  # pragma: no cover
+            return {"name": "test"}
+
+
+def test_raises_pydantic_v1_model_in_additional_responses_model() -> None:
+    class ErrorModelV1(BaseModel):
+        detail: str
+
+    app = FastAPI()
+
+    with pytest.raises(PydanticV1NotSupportedError):
+
+        @app.get(
+            "/responses", response_model=None, responses={400: {"model": ErrorModelV1}}
+        )
+        def endpoint():  # pragma: no cover
+            return {"ok": True}
+
+
+def test_raises_pydantic_v1_model_in_union() -> None:
+    class ModelV1A(BaseModel):
+        name: str
+
+    app = FastAPI()
+
+    with pytest.raises(PydanticV1NotSupportedError):
+
+        @app.post("/union")
+        def endpoint(data: Union[dict, ModelV1A]):  # pragma: no cover
+            return data
+
+
+def test_raises_pydantic_v1_model_in_sequence() -> None:
+    class ModelV1A(BaseModel):
+        name: str
+
+    app = FastAPI()
+
+    with pytest.raises(PydanticV1NotSupportedError):
+
+        @app.post("/sequence")
+        def endpoint(data: list[ModelV1A]):  # pragma: no cover
+            return data
diff --git a/tests/test_pydantic_v1_v2_01.py b/tests/test_pydantic_v1_v2_01.py
deleted file mode 100644 (file)
index 4868e5d..0000000
+++ /dev/null
@@ -1,439 +0,0 @@
-import sys
-import warnings
-from typing import Any, Union
-
-from tests.utils import skip_module_if_py_gte_314
-
-if sys.version_info >= (3, 14):
-    skip_module_if_py_gte_314()
-
-from fastapi import FastAPI
-from fastapi._compat.v1 import BaseModel
-from fastapi.testclient import TestClient
-from inline_snapshot import snapshot
-
-
-class SubItem(BaseModel):
-    name: str
-
-
-class Item(BaseModel):
-    title: str
-    size: int
-    description: Union[str, None] = None
-    sub: SubItem
-    multi: list[SubItem] = []
-
-
-app = FastAPI()
-
-with warnings.catch_warnings(record=True):
-    warnings.simplefilter("always")
-
-    @app.post("/simple-model")
-    def handle_simple_model(data: SubItem) -> SubItem:
-        return data
-
-    @app.post("/simple-model-filter", response_model=SubItem)
-    def handle_simple_model_filter(data: SubItem) -> Any:
-        extended_data = data.dict()
-        extended_data.update({"secret_price": 42})
-        return extended_data
-
-    @app.post("/item")
-    def handle_item(data: Item) -> Item:
-        return data
-
-    @app.post("/item-filter", response_model=Item)
-    def handle_item_filter(data: Item) -> Any:
-        extended_data = data.dict()
-        extended_data.update({"secret_data": "classified", "internal_id": 12345})
-        extended_data["sub"].update({"internal_id": 67890})
-        return extended_data
-
-
-client = TestClient(app)
-
-
-def test_old_simple_model():
-    response = client.post(
-        "/simple-model",
-        json={"name": "Foo"},
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == {"name": "Foo"}
-
-
-def test_old_simple_model_validation_error():
-    response = client.post(
-        "/simple-model",
-        json={"wrong_name": "Foo"},
-    )
-    assert response.status_code == 422, response.text
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "loc": ["body", "name"],
-                    "msg": "field required",
-                    "type": "value_error.missing",
-                }
-            ]
-        }
-    )
-
-
-def test_old_simple_model_filter():
-    response = client.post(
-        "/simple-model-filter",
-        json={"name": "Foo"},
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == {"name": "Foo"}
-
-
-def test_item_model():
-    response = client.post(
-        "/item",
-        json={
-            "title": "Test Item",
-            "size": 100,
-            "description": "This is a test item",
-            "sub": {"name": "SubItem1"},
-            "multi": [{"name": "Multi1"}, {"name": "Multi2"}],
-        },
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "title": "Test Item",
-        "size": 100,
-        "description": "This is a test item",
-        "sub": {"name": "SubItem1"},
-        "multi": [{"name": "Multi1"}, {"name": "Multi2"}],
-    }
-
-
-def test_item_model_minimal():
-    response = client.post(
-        "/item",
-        json={"title": "Minimal Item", "size": 50, "sub": {"name": "SubMin"}},
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "title": "Minimal Item",
-        "size": 50,
-        "description": None,
-        "sub": {"name": "SubMin"},
-        "multi": [],
-    }
-
-
-def test_item_model_validation_errors():
-    response = client.post(
-        "/item",
-        json={"title": "Missing fields"},
-    )
-    assert response.status_code == 422, response.text
-    error_detail = response.json()["detail"]
-    assert len(error_detail) == 2
-    assert {
-        "loc": ["body", "size"],
-        "msg": "field required",
-        "type": "value_error.missing",
-    } in error_detail
-    assert {
-        "loc": ["body", "sub"],
-        "msg": "field required",
-        "type": "value_error.missing",
-    } in error_detail
-
-
-def test_item_model_nested_validation_error():
-    response = client.post(
-        "/item",
-        json={"title": "Test Item", "size": 100, "sub": {"wrong_field": "test"}},
-    )
-    assert response.status_code == 422, response.text
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "loc": ["body", "sub", "name"],
-                    "msg": "field required",
-                    "type": "value_error.missing",
-                }
-            ]
-        }
-    )
-
-
-def test_item_model_invalid_type():
-    response = client.post(
-        "/item",
-        json={"title": "Test Item", "size": "not_a_number", "sub": {"name": "SubItem"}},
-    )
-    assert response.status_code == 422, response.text
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "loc": ["body", "size"],
-                    "msg": "value is not a valid integer",
-                    "type": "type_error.integer",
-                }
-            ]
-        }
-    )
-
-
-def test_item_filter():
-    response = client.post(
-        "/item-filter",
-        json={
-            "title": "Filtered Item",
-            "size": 200,
-            "description": "Test filtering",
-            "sub": {"name": "SubFiltered"},
-            "multi": [],
-        },
-    )
-    assert response.status_code == 200, response.text
-    result = response.json()
-    assert result == {
-        "title": "Filtered Item",
-        "size": 200,
-        "description": "Test filtering",
-        "sub": {"name": "SubFiltered"},
-        "multi": [],
-    }
-    assert "secret_data" not in result
-    assert "internal_id" not in result
-
-
-def test_openapi_schema():
-    response = client.get("/openapi.json")
-    assert response.status_code == 200, response.text
-    assert response.json() == snapshot(
-        {
-            "openapi": "3.1.0",
-            "info": {"title": "FastAPI", "version": "0.1.0"},
-            "paths": {
-                "/simple-model": {
-                    "post": {
-                        "summary": "Handle Simple Model",
-                        "operationId": "handle_simple_model_simple_model_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "allOf": [
-                                            {"$ref": "#/components/schemas/SubItem"}
-                                        ],
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/SubItem"
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/simple-model-filter": {
-                    "post": {
-                        "summary": "Handle Simple Model Filter",
-                        "operationId": "handle_simple_model_filter_simple_model_filter_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "allOf": [
-                                            {"$ref": "#/components/schemas/SubItem"}
-                                        ],
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/SubItem"
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/item": {
-                    "post": {
-                        "summary": "Handle Item",
-                        "operationId": "handle_item_item_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "allOf": [
-                                            {"$ref": "#/components/schemas/Item"}
-                                        ],
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {"$ref": "#/components/schemas/Item"}
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/item-filter": {
-                    "post": {
-                        "summary": "Handle Item Filter",
-                        "operationId": "handle_item_filter_item_filter_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "allOf": [
-                                            {"$ref": "#/components/schemas/Item"}
-                                        ],
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {"$ref": "#/components/schemas/Item"}
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-            },
-            "components": {
-                "schemas": {
-                    "HTTPValidationError": {
-                        "properties": {
-                            "detail": {
-                                "items": {
-                                    "$ref": "#/components/schemas/ValidationError"
-                                },
-                                "type": "array",
-                                "title": "Detail",
-                            }
-                        },
-                        "type": "object",
-                        "title": "HTTPValidationError",
-                    },
-                    "Item": {
-                        "properties": {
-                            "title": {"type": "string", "title": "Title"},
-                            "size": {"type": "integer", "title": "Size"},
-                            "description": {"type": "string", "title": "Description"},
-                            "sub": {"$ref": "#/components/schemas/SubItem"},
-                            "multi": {
-                                "items": {"$ref": "#/components/schemas/SubItem"},
-                                "type": "array",
-                                "title": "Multi",
-                                "default": [],
-                            },
-                        },
-                        "type": "object",
-                        "required": ["title", "size", "sub"],
-                        "title": "Item",
-                    },
-                    "SubItem": {
-                        "properties": {"name": {"type": "string", "title": "Name"}},
-                        "type": "object",
-                        "required": ["name"],
-                        "title": "SubItem",
-                    },
-                    "ValidationError": {
-                        "properties": {
-                            "loc": {
-                                "items": {
-                                    "anyOf": [{"type": "string"}, {"type": "integer"}]
-                                },
-                                "type": "array",
-                                "title": "Location",
-                            },
-                            "msg": {"type": "string", "title": "Message"},
-                            "type": {"type": "string", "title": "Error Type"},
-                        },
-                        "type": "object",
-                        "required": ["loc", "msg", "type"],
-                        "title": "ValidationError",
-                    },
-                }
-            },
-        }
-    )
diff --git a/tests/test_pydantic_v1_v2_list.py b/tests/test_pydantic_v1_v2_list.py
deleted file mode 100644 (file)
index 108f231..0000000
+++ /dev/null
@@ -1,682 +0,0 @@
-import sys
-import warnings
-from typing import Any, Union
-
-from tests.utils import skip_module_if_py_gte_314
-
-if sys.version_info >= (3, 14):
-    skip_module_if_py_gte_314()
-
-from fastapi import FastAPI
-from fastapi._compat.v1 import BaseModel
-from fastapi.testclient import TestClient
-from inline_snapshot import snapshot
-
-
-class SubItem(BaseModel):
-    name: str
-
-
-class Item(BaseModel):
-    title: str
-    size: int
-    description: Union[str, None] = None
-    sub: SubItem
-    multi: list[SubItem] = []
-
-
-app = FastAPI()
-
-
-with warnings.catch_warnings(record=True):
-    warnings.simplefilter("always")
-
-    @app.post("/item")
-    def handle_item(data: Item) -> list[Item]:
-        return [data, data]
-
-    @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})
-        extended_data["sub"].update({"internal_id": 67890})
-        return [extended_data, extended_data]
-
-    @app.post("/item-list")
-    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:
-        if data:
-            extended_data = data[0].dict()
-            extended_data.update({"secret_data": "classified", "internal_id": 12345})
-            extended_data["sub"].update({"internal_id": 67890})
-            return extended_data
-        return Item(title="", size=0, sub=SubItem(name=""))
-
-    @app.post("/item-list-to-list")
-    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:
-        if data:
-            extended_data = data[0].dict()
-            extended_data.update({"secret_data": "classified", "internal_id": 12345})
-            extended_data["sub"].update({"internal_id": 67890})
-            return [extended_data, extended_data]
-        return []
-
-
-client = TestClient(app)
-
-
-def test_item_to_list():
-    response = client.post(
-        "/item",
-        json={
-            "title": "Test Item",
-            "size": 100,
-            "description": "This is a test item",
-            "sub": {"name": "SubItem1"},
-            "multi": [{"name": "Multi1"}, {"name": "Multi2"}],
-        },
-    )
-    assert response.status_code == 200, response.text
-    result = response.json()
-    assert isinstance(result, list)
-    assert len(result) == 2
-    for item in result:
-        assert item == {
-            "title": "Test Item",
-            "size": 100,
-            "description": "This is a test item",
-            "sub": {"name": "SubItem1"},
-            "multi": [{"name": "Multi1"}, {"name": "Multi2"}],
-        }
-
-
-def test_item_to_list_filter():
-    response = client.post(
-        "/item-filter",
-        json={
-            "title": "Filtered Item",
-            "size": 200,
-            "description": "Test filtering",
-            "sub": {"name": "SubFiltered"},
-            "multi": [],
-        },
-    )
-    assert response.status_code == 200, response.text
-    result = response.json()
-    assert isinstance(result, list)
-    assert len(result) == 2
-    for item in result:
-        assert item == {
-            "title": "Filtered Item",
-            "size": 200,
-            "description": "Test filtering",
-            "sub": {"name": "SubFiltered"},
-            "multi": [],
-        }
-        # Verify secret fields are filtered out
-        assert "secret_data" not in item
-        assert "internal_id" not in item
-        assert "internal_id" not in item["sub"]
-
-
-def test_list_to_item():
-    response = client.post(
-        "/item-list",
-        json=[
-            {"title": "First Item", "size": 50, "sub": {"name": "First Sub"}},
-            {"title": "Second Item", "size": 75, "sub": {"name": "Second Sub"}},
-        ],
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "title": "First Item",
-        "size": 50,
-        "description": None,
-        "sub": {"name": "First Sub"},
-        "multi": [],
-    }
-
-
-def test_list_to_item_empty():
-    response = client.post(
-        "/item-list",
-        json=[],
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "title": "",
-        "size": 0,
-        "description": None,
-        "sub": {"name": ""},
-        "multi": [],
-    }
-
-
-def test_list_to_item_filter():
-    response = client.post(
-        "/item-list-filter",
-        json=[
-            {
-                "title": "First Item",
-                "size": 100,
-                "sub": {"name": "First Sub"},
-                "multi": [{"name": "Multi1"}],
-            },
-            {"title": "Second Item", "size": 200, "sub": {"name": "Second Sub"}},
-        ],
-    )
-    assert response.status_code == 200, response.text
-    result = response.json()
-    assert result == {
-        "title": "First Item",
-        "size": 100,
-        "description": None,
-        "sub": {"name": "First Sub"},
-        "multi": [{"name": "Multi1"}],
-    }
-    # Verify secret fields are filtered out
-    assert "secret_data" not in result
-    assert "internal_id" not in result
-
-
-def test_list_to_item_filter_no_data():
-    response = client.post("/item-list-filter", json=[])
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "title": "",
-        "size": 0,
-        "description": None,
-        "sub": {"name": ""},
-        "multi": [],
-    }
-
-
-def test_list_to_list():
-    input_items = [
-        {"title": "Item 1", "size": 10, "sub": {"name": "Sub1"}},
-        {
-            "title": "Item 2",
-            "size": 20,
-            "description": "Second item",
-            "sub": {"name": "Sub2"},
-            "multi": [{"name": "M1"}, {"name": "M2"}],
-        },
-        {"title": "Item 3", "size": 30, "sub": {"name": "Sub3"}},
-    ]
-    response = client.post(
-        "/item-list-to-list",
-        json=input_items,
-    )
-    assert response.status_code == 200, response.text
-    result = response.json()
-    assert isinstance(result, list)
-    assert len(result) == 3
-    assert result[0] == {
-        "title": "Item 1",
-        "size": 10,
-        "description": None,
-        "sub": {"name": "Sub1"},
-        "multi": [],
-    }
-    assert result[1] == {
-        "title": "Item 2",
-        "size": 20,
-        "description": "Second item",
-        "sub": {"name": "Sub2"},
-        "multi": [{"name": "M1"}, {"name": "M2"}],
-    }
-    assert result[2] == {
-        "title": "Item 3",
-        "size": 30,
-        "description": None,
-        "sub": {"name": "Sub3"},
-        "multi": [],
-    }
-
-
-def test_list_to_list_filter():
-    response = client.post(
-        "/item-list-to-list-filter",
-        json=[{"title": "Item 1", "size": 100, "sub": {"name": "Sub1"}}],
-    )
-    assert response.status_code == 200, response.text
-    result = response.json()
-    assert isinstance(result, list)
-    assert len(result) == 2
-    for item in result:
-        assert item == {
-            "title": "Item 1",
-            "size": 100,
-            "description": None,
-            "sub": {"name": "Sub1"},
-            "multi": [],
-        }
-        # Verify secret fields are filtered out
-        assert "secret_data" not in item
-        assert "internal_id" not in item
-
-
-def test_list_to_list_filter_no_data():
-    response = client.post(
-        "/item-list-to-list-filter",
-        json=[],
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == []
-
-
-def test_list_validation_error():
-    response = client.post(
-        "/item-list",
-        json=[
-            {"title": "Valid Item", "size": 100, "sub": {"name": "Sub1"}},
-            {
-                "title": "Invalid Item"
-                # Missing required fields: size and sub
-            },
-        ],
-    )
-    assert response.status_code == 422, response.text
-    error_detail = response.json()["detail"]
-    assert len(error_detail) == 2
-    assert {
-        "loc": ["body", 1, "size"],
-        "msg": "field required",
-        "type": "value_error.missing",
-    } in error_detail
-    assert {
-        "loc": ["body", 1, "sub"],
-        "msg": "field required",
-        "type": "value_error.missing",
-    } in error_detail
-
-
-def test_list_nested_validation_error():
-    response = client.post(
-        "/item-list",
-        json=[
-            {"title": "Item with bad sub", "size": 100, "sub": {"wrong_field": "value"}}
-        ],
-    )
-    assert response.status_code == 422, response.text
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "loc": ["body", 0, "sub", "name"],
-                    "msg": "field required",
-                    "type": "value_error.missing",
-                }
-            ]
-        }
-    )
-
-
-def test_list_type_validation_error():
-    response = client.post(
-        "/item-list",
-        json=[{"title": "Item", "size": "not_a_number", "sub": {"name": "Sub"}}],
-    )
-    assert response.status_code == 422, response.text
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "loc": ["body", 0, "size"],
-                    "msg": "value is not a valid integer",
-                    "type": "type_error.integer",
-                }
-            ]
-        }
-    )
-
-
-def test_invalid_list_structure():
-    response = client.post(
-        "/item-list",
-        json={"title": "Not a list", "size": 100, "sub": {"name": "Sub"}},
-    )
-    assert response.status_code == 422, response.text
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "loc": ["body"],
-                    "msg": "value is not a valid list",
-                    "type": "type_error.list",
-                }
-            ]
-        }
-    )
-
-
-def test_openapi_schema():
-    response = client.get("/openapi.json")
-    assert response.status_code == 200, response.text
-    assert response.json() == snapshot(
-        {
-            "openapi": "3.1.0",
-            "info": {"title": "FastAPI", "version": "0.1.0"},
-            "paths": {
-                "/item": {
-                    "post": {
-                        "summary": "Handle Item",
-                        "operationId": "handle_item_item_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "allOf": [
-                                            {"$ref": "#/components/schemas/Item"}
-                                        ],
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "items": {
-                                                "$ref": "#/components/schemas/Item"
-                                            },
-                                            "type": "array",
-                                            "title": "Response Handle Item Item Post",
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/item-filter": {
-                    "post": {
-                        "summary": "Handle Item Filter",
-                        "operationId": "handle_item_filter_item_filter_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "allOf": [
-                                            {"$ref": "#/components/schemas/Item"}
-                                        ],
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "items": {
-                                                "$ref": "#/components/schemas/Item"
-                                            },
-                                            "type": "array",
-                                            "title": "Response Handle Item Filter Item Filter Post",
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/item-list": {
-                    "post": {
-                        "summary": "Handle Item List",
-                        "operationId": "handle_item_list_item_list_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "items": {"$ref": "#/components/schemas/Item"},
-                                        "type": "array",
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {"$ref": "#/components/schemas/Item"}
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/item-list-filter": {
-                    "post": {
-                        "summary": "Handle Item List Filter",
-                        "operationId": "handle_item_list_filter_item_list_filter_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "items": {"$ref": "#/components/schemas/Item"},
-                                        "type": "array",
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {"$ref": "#/components/schemas/Item"}
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/item-list-to-list": {
-                    "post": {
-                        "summary": "Handle Item List To List",
-                        "operationId": "handle_item_list_to_list_item_list_to_list_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "items": {"$ref": "#/components/schemas/Item"},
-                                        "type": "array",
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "items": {
-                                                "$ref": "#/components/schemas/Item"
-                                            },
-                                            "type": "array",
-                                            "title": "Response Handle Item List To List Item List To List Post",
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/item-list-to-list-filter": {
-                    "post": {
-                        "summary": "Handle Item List To List Filter",
-                        "operationId": "handle_item_list_to_list_filter_item_list_to_list_filter_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "items": {"$ref": "#/components/schemas/Item"},
-                                        "type": "array",
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "items": {
-                                                "$ref": "#/components/schemas/Item"
-                                            },
-                                            "type": "array",
-                                            "title": "Response Handle Item List To List Filter Item List To List Filter Post",
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-            },
-            "components": {
-                "schemas": {
-                    "HTTPValidationError": {
-                        "properties": {
-                            "detail": {
-                                "items": {
-                                    "$ref": "#/components/schemas/ValidationError"
-                                },
-                                "type": "array",
-                                "title": "Detail",
-                            }
-                        },
-                        "type": "object",
-                        "title": "HTTPValidationError",
-                    },
-                    "Item": {
-                        "properties": {
-                            "title": {"type": "string", "title": "Title"},
-                            "size": {"type": "integer", "title": "Size"},
-                            "description": {"type": "string", "title": "Description"},
-                            "sub": {"$ref": "#/components/schemas/SubItem"},
-                            "multi": {
-                                "items": {"$ref": "#/components/schemas/SubItem"},
-                                "type": "array",
-                                "title": "Multi",
-                                "default": [],
-                            },
-                        },
-                        "type": "object",
-                        "required": ["title", "size", "sub"],
-                        "title": "Item",
-                    },
-                    "SubItem": {
-                        "properties": {"name": {"type": "string", "title": "Name"}},
-                        "type": "object",
-                        "required": ["name"],
-                        "title": "SubItem",
-                    },
-                    "ValidationError": {
-                        "properties": {
-                            "loc": {
-                                "items": {
-                                    "anyOf": [{"type": "string"}, {"type": "integer"}]
-                                },
-                                "type": "array",
-                                "title": "Location",
-                            },
-                            "msg": {"type": "string", "title": "Message"},
-                            "type": {"type": "string", "title": "Error Type"},
-                        },
-                        "type": "object",
-                        "required": ["loc", "msg", "type"],
-                        "title": "ValidationError",
-                    },
-                }
-            },
-        }
-    )
diff --git a/tests/test_pydantic_v1_v2_mixed.py b/tests/test_pydantic_v1_v2_mixed.py
deleted file mode 100644 (file)
index 895835a..0000000
+++ /dev/null
@@ -1,1408 +0,0 @@
-import sys
-import warnings
-from typing import Any, Union
-
-from tests.utils import skip_module_if_py_gte_314
-
-if sys.version_info >= (3, 14):
-    skip_module_if_py_gte_314()
-
-from fastapi import FastAPI
-from fastapi._compat.v1 import BaseModel
-from fastapi.testclient import TestClient
-from inline_snapshot import snapshot
-from pydantic import BaseModel as NewBaseModel
-
-
-class SubItem(BaseModel):
-    name: str
-
-
-class Item(BaseModel):
-    title: str
-    size: int
-    description: Union[str, None] = None
-    sub: SubItem
-    multi: list[SubItem] = []
-
-
-class NewSubItem(NewBaseModel):
-    new_sub_name: str
-
-
-class NewItem(NewBaseModel):
-    new_title: str
-    new_size: int
-    new_description: Union[str, None] = None
-    new_sub: NewSubItem
-    new_multi: list[NewSubItem] = []
-
-
-app = FastAPI()
-
-with warnings.catch_warnings(record=True):
-    warnings.simplefilter("always")
-
-    @app.post("/v1-to-v2/item")
-    def handle_v1_item_to_v2(data: Item) -> NewItem:
-        return NewItem(
-            new_title=data.title,
-            new_size=data.size,
-            new_description=data.description,
-            new_sub=NewSubItem(new_sub_name=data.sub.name),
-            new_multi=[NewSubItem(new_sub_name=s.name) for s in data.multi],
-        )
-
-    @app.post("/v1-to-v2/item-filter", response_model=NewItem)
-    def handle_v1_item_to_v2_filter(data: Item) -> Any:
-        result = {
-            "new_title": data.title,
-            "new_size": data.size,
-            "new_description": data.description,
-            "new_sub": {
-                "new_sub_name": data.sub.name,
-                "new_sub_secret": "sub_hidden",
-            },
-            "new_multi": [
-                {"new_sub_name": s.name, "new_sub_secret": "sub_hidden"}
-                for s in data.multi
-            ],
-            "secret": "hidden_v1_to_v2",
-        }
-        return result
-
-    @app.post("/v2-to-v1/item")
-    def handle_v2_item_to_v1(data: NewItem) -> Item:
-        return Item(
-            title=data.new_title,
-            size=data.new_size,
-            description=data.new_description,
-            sub=SubItem(name=data.new_sub.new_sub_name),
-            multi=[SubItem(name=s.new_sub_name) for s in data.new_multi],
-        )
-
-    @app.post("/v2-to-v1/item-filter", response_model=Item)
-    def handle_v2_item_to_v1_filter(data: NewItem) -> Any:
-        result = {
-            "title": data.new_title,
-            "size": data.new_size,
-            "description": data.new_description,
-            "sub": {"name": data.new_sub.new_sub_name, "sub_secret": "sub_hidden"},
-            "multi": [
-                {"name": s.new_sub_name, "sub_secret": "sub_hidden"}
-                for s in data.new_multi
-            ],
-            "secret": "hidden_v2_to_v1",
-        }
-        return result
-
-    @app.post("/v1-to-v2/item-to-list")
-    def handle_v1_item_to_v2_list(data: Item) -> list[NewItem]:
-        converted = NewItem(
-            new_title=data.title,
-            new_size=data.size,
-            new_description=data.description,
-            new_sub=NewSubItem(new_sub_name=data.sub.name),
-            new_multi=[NewSubItem(new_sub_name=s.name) for s in data.multi],
-        )
-        return [converted, converted]
-
-    @app.post("/v1-to-v2/list-to-list")
-    def handle_v1_list_to_v2_list(data: list[Item]) -> list[NewItem]:
-        result = []
-        for item in data:
-            result.append(
-                NewItem(
-                    new_title=item.title,
-                    new_size=item.size,
-                    new_description=item.description,
-                    new_sub=NewSubItem(new_sub_name=item.sub.name),
-                    new_multi=[NewSubItem(new_sub_name=s.name) for s in item.multi],
-                )
-            )
-        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:
-        result = []
-        for item in data:
-            converted = {
-                "new_title": item.title,
-                "new_size": item.size,
-                "new_description": item.description,
-                "new_sub": {
-                    "new_sub_name": item.sub.name,
-                    "new_sub_secret": "sub_hidden",
-                },
-                "new_multi": [
-                    {"new_sub_name": s.name, "new_sub_secret": "sub_hidden"}
-                    for s in item.multi
-                ],
-                "secret": "hidden_v2_to_v1",
-            }
-            result.append(converted)
-        return result
-
-    @app.post("/v1-to-v2/list-to-item")
-    def handle_v1_list_to_v2_item(data: list[Item]) -> NewItem:
-        if data:
-            item = data[0]
-            return NewItem(
-                new_title=item.title,
-                new_size=item.size,
-                new_description=item.description,
-                new_sub=NewSubItem(new_sub_name=item.sub.name),
-                new_multi=[NewSubItem(new_sub_name=s.name) for s in item.multi],
-            )
-        return NewItem(new_title="", new_size=0, new_sub=NewSubItem(new_sub_name=""))
-
-    @app.post("/v2-to-v1/item-to-list")
-    def handle_v2_item_to_v1_list(data: NewItem) -> list[Item]:
-        converted = Item(
-            title=data.new_title,
-            size=data.new_size,
-            description=data.new_description,
-            sub=SubItem(name=data.new_sub.new_sub_name),
-            multi=[SubItem(name=s.new_sub_name) for s in data.new_multi],
-        )
-        return [converted, converted]
-
-    @app.post("/v2-to-v1/list-to-list")
-    def handle_v2_list_to_v1_list(data: list[NewItem]) -> list[Item]:
-        result = []
-        for item in data:
-            result.append(
-                Item(
-                    title=item.new_title,
-                    size=item.new_size,
-                    description=item.new_description,
-                    sub=SubItem(name=item.new_sub.new_sub_name),
-                    multi=[SubItem(name=s.new_sub_name) for s in item.new_multi],
-                )
-            )
-        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:
-        result = []
-        for item in data:
-            converted = {
-                "title": item.new_title,
-                "size": item.new_size,
-                "description": item.new_description,
-                "sub": {
-                    "name": item.new_sub.new_sub_name,
-                    "sub_secret": "sub_hidden",
-                },
-                "multi": [
-                    {"name": s.new_sub_name, "sub_secret": "sub_hidden"}
-                    for s in item.new_multi
-                ],
-                "secret": "hidden_v2_to_v1",
-            }
-            result.append(converted)
-        return result
-
-    @app.post("/v2-to-v1/list-to-item")
-    def handle_v2_list_to_v1_item(data: list[NewItem]) -> Item:
-        if data:
-            item = data[0]
-            return Item(
-                title=item.new_title,
-                size=item.new_size,
-                description=item.new_description,
-                sub=SubItem(name=item.new_sub.new_sub_name),
-                multi=[SubItem(name=s.new_sub_name) for s in item.new_multi],
-            )
-        return Item(title="", size=0, sub=SubItem(name=""))
-
-
-client = TestClient(app)
-
-
-def test_v1_to_v2_item():
-    response = client.post(
-        "/v1-to-v2/item",
-        json={
-            "title": "Old Item",
-            "size": 100,
-            "description": "V1 description",
-            "sub": {"name": "V1 Sub"},
-            "multi": [{"name": "M1"}, {"name": "M2"}],
-        },
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "new_title": "Old Item",
-        "new_size": 100,
-        "new_description": "V1 description",
-        "new_sub": {"new_sub_name": "V1 Sub"},
-        "new_multi": [{"new_sub_name": "M1"}, {"new_sub_name": "M2"}],
-    }
-
-
-def test_v1_to_v2_item_minimal():
-    response = client.post(
-        "/v1-to-v2/item",
-        json={"title": "Minimal", "size": 50, "sub": {"name": "MinSub"}},
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "new_title": "Minimal",
-        "new_size": 50,
-        "new_description": None,
-        "new_sub": {"new_sub_name": "MinSub"},
-        "new_multi": [],
-    }
-
-
-def test_v1_to_v2_item_filter():
-    response = client.post(
-        "/v1-to-v2/item-filter",
-        json={
-            "title": "Filtered Item",
-            "size": 50,
-            "sub": {"name": "Sub"},
-            "multi": [{"name": "Multi1"}],
-        },
-    )
-    assert response.status_code == 200, response.text
-    result = response.json()
-    assert result == snapshot(
-        {
-            "new_title": "Filtered Item",
-            "new_size": 50,
-            "new_description": None,
-            "new_sub": {"new_sub_name": "Sub"},
-            "new_multi": [{"new_sub_name": "Multi1"}],
-        }
-    )
-    # Verify secret fields are filtered out
-    assert "secret" not in result
-    assert "new_sub_secret" not in result["new_sub"]
-    assert "new_sub_secret" not in result["new_multi"][0]
-
-
-def test_v2_to_v1_item():
-    response = client.post(
-        "/v2-to-v1/item",
-        json={
-            "new_title": "New Item",
-            "new_size": 200,
-            "new_description": "V2 description",
-            "new_sub": {"new_sub_name": "V2 Sub"},
-            "new_multi": [{"new_sub_name": "N1"}, {"new_sub_name": "N2"}],
-        },
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "title": "New Item",
-        "size": 200,
-        "description": "V2 description",
-        "sub": {"name": "V2 Sub"},
-        "multi": [{"name": "N1"}, {"name": "N2"}],
-    }
-
-
-def test_v2_to_v1_item_minimal():
-    response = client.post(
-        "/v2-to-v1/item",
-        json={
-            "new_title": "MinimalNew",
-            "new_size": 75,
-            "new_sub": {"new_sub_name": "MinNewSub"},
-        },
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "title": "MinimalNew",
-        "size": 75,
-        "description": None,
-        "sub": {"name": "MinNewSub"},
-        "multi": [],
-    }
-
-
-def test_v2_to_v1_item_filter():
-    response = client.post(
-        "/v2-to-v1/item-filter",
-        json={
-            "new_title": "Filtered New",
-            "new_size": 75,
-            "new_sub": {"new_sub_name": "NewSub"},
-            "new_multi": [],
-        },
-    )
-    assert response.status_code == 200, response.text
-    result = response.json()
-    assert result == snapshot(
-        {
-            "title": "Filtered New",
-            "size": 75,
-            "description": None,
-            "sub": {"name": "NewSub"},
-            "multi": [],
-        }
-    )
-    # Verify secret fields are filtered out
-    assert "secret" not in result
-    assert "sub_secret" not in result["sub"]
-
-
-def test_v1_item_to_v2_list():
-    response = client.post(
-        "/v1-to-v2/item-to-list",
-        json={
-            "title": "Single to List",
-            "size": 150,
-            "description": "Convert to list",
-            "sub": {"name": "Sub1"},
-            "multi": [],
-        },
-    )
-    assert response.status_code == 200, response.text
-    result = response.json()
-    assert result == [
-        {
-            "new_title": "Single to List",
-            "new_size": 150,
-            "new_description": "Convert to list",
-            "new_sub": {"new_sub_name": "Sub1"},
-            "new_multi": [],
-        },
-        {
-            "new_title": "Single to List",
-            "new_size": 150,
-            "new_description": "Convert to list",
-            "new_sub": {"new_sub_name": "Sub1"},
-            "new_multi": [],
-        },
-    ]
-
-
-def test_v1_list_to_v2_list():
-    response = client.post(
-        "/v1-to-v2/list-to-list",
-        json=[
-            {"title": "Item1", "size": 10, "sub": {"name": "Sub1"}},
-            {
-                "title": "Item2",
-                "size": 20,
-                "description": "Second item",
-                "sub": {"name": "Sub2"},
-                "multi": [{"name": "M1"}, {"name": "M2"}],
-            },
-            {"title": "Item3", "size": 30, "sub": {"name": "Sub3"}},
-        ],
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == [
-        {
-            "new_title": "Item1",
-            "new_size": 10,
-            "new_description": None,
-            "new_sub": {"new_sub_name": "Sub1"},
-            "new_multi": [],
-        },
-        {
-            "new_title": "Item2",
-            "new_size": 20,
-            "new_description": "Second item",
-            "new_sub": {"new_sub_name": "Sub2"},
-            "new_multi": [{"new_sub_name": "M1"}, {"new_sub_name": "M2"}],
-        },
-        {
-            "new_title": "Item3",
-            "new_size": 30,
-            "new_description": None,
-            "new_sub": {"new_sub_name": "Sub3"},
-            "new_multi": [],
-        },
-    ]
-
-
-def test_v1_list_to_v2_list_filter():
-    response = client.post(
-        "/v1-to-v2/list-to-list-filter",
-        json=[{"title": "FilterMe", "size": 30, "sub": {"name": "SubF"}}],
-    )
-    assert response.status_code == 200, response.text
-    result = response.json()
-    assert result == snapshot(
-        [
-            {
-                "new_title": "FilterMe",
-                "new_size": 30,
-                "new_description": None,
-                "new_sub": {"new_sub_name": "SubF"},
-                "new_multi": [],
-            }
-        ]
-    )
-    # Verify secret fields are filtered out
-    assert "secret" not in result[0]
-    assert "new_sub_secret" not in result[0]["new_sub"]
-
-
-def test_v1_list_to_v2_item():
-    response = client.post(
-        "/v1-to-v2/list-to-item",
-        json=[
-            {"title": "First", "size": 100, "sub": {"name": "FirstSub"}},
-            {"title": "Second", "size": 200, "sub": {"name": "SecondSub"}},
-        ],
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "new_title": "First",
-        "new_size": 100,
-        "new_description": None,
-        "new_sub": {"new_sub_name": "FirstSub"},
-        "new_multi": [],
-    }
-
-
-def test_v1_list_to_v2_item_empty():
-    response = client.post("/v1-to-v2/list-to-item", json=[])
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "new_title": "",
-        "new_size": 0,
-        "new_description": None,
-        "new_sub": {"new_sub_name": ""},
-        "new_multi": [],
-    }
-
-
-def test_v2_item_to_v1_list():
-    response = client.post(
-        "/v2-to-v1/item-to-list",
-        json={
-            "new_title": "Single New",
-            "new_size": 250,
-            "new_description": "New to list",
-            "new_sub": {"new_sub_name": "NewSub"},
-            "new_multi": [],
-        },
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == [
-        {
-            "title": "Single New",
-            "size": 250,
-            "description": "New to list",
-            "sub": {"name": "NewSub"},
-            "multi": [],
-        },
-        {
-            "title": "Single New",
-            "size": 250,
-            "description": "New to list",
-            "sub": {"name": "NewSub"},
-            "multi": [],
-        },
-    ]
-
-
-def test_v2_list_to_v1_list():
-    response = client.post(
-        "/v2-to-v1/list-to-list",
-        json=[
-            {"new_title": "New1", "new_size": 15, "new_sub": {"new_sub_name": "NS1"}},
-            {
-                "new_title": "New2",
-                "new_size": 25,
-                "new_description": "Second new",
-                "new_sub": {"new_sub_name": "NS2"},
-                "new_multi": [{"new_sub_name": "NM1"}],
-            },
-        ],
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == [
-        {
-            "title": "New1",
-            "size": 15,
-            "description": None,
-            "sub": {"name": "NS1"},
-            "multi": [],
-        },
-        {
-            "title": "New2",
-            "size": 25,
-            "description": "Second new",
-            "sub": {"name": "NS2"},
-            "multi": [{"name": "NM1"}],
-        },
-    ]
-
-
-def test_v2_list_to_v1_list_filter():
-    response = client.post(
-        "/v2-to-v1/list-to-list-filter",
-        json=[
-            {
-                "new_title": "FilterNew",
-                "new_size": 35,
-                "new_sub": {"new_sub_name": "NSF"},
-            }
-        ],
-    )
-    assert response.status_code == 200, response.text
-    result = response.json()
-    assert result == snapshot(
-        [
-            {
-                "title": "FilterNew",
-                "size": 35,
-                "description": None,
-                "sub": {"name": "NSF"},
-                "multi": [],
-            }
-        ]
-    )
-    # Verify secret fields are filtered out
-    assert "secret" not in result[0]
-    assert "sub_secret" not in result[0]["sub"]
-
-
-def test_v2_list_to_v1_item():
-    response = client.post(
-        "/v2-to-v1/list-to-item",
-        json=[
-            {
-                "new_title": "FirstNew",
-                "new_size": 300,
-                "new_sub": {"new_sub_name": "FNS"},
-            },
-            {
-                "new_title": "SecondNew",
-                "new_size": 400,
-                "new_sub": {"new_sub_name": "SNS"},
-            },
-        ],
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "title": "FirstNew",
-        "size": 300,
-        "description": None,
-        "sub": {"name": "FNS"},
-        "multi": [],
-    }
-
-
-def test_v2_list_to_v1_item_empty():
-    response = client.post("/v2-to-v1/list-to-item", json=[])
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "title": "",
-        "size": 0,
-        "description": None,
-        "sub": {"name": ""},
-        "multi": [],
-    }
-
-
-def test_v1_to_v2_validation_error():
-    response = client.post("/v1-to-v2/item", json={"title": "Missing fields"})
-    assert response.status_code == 422, response.text
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "loc": ["body", "size"],
-                    "msg": "field required",
-                    "type": "value_error.missing",
-                },
-                {
-                    "loc": ["body", "sub"],
-                    "msg": "field required",
-                    "type": "value_error.missing",
-                },
-            ]
-        }
-    )
-
-
-def test_v1_to_v2_nested_validation_error():
-    response = client.post(
-        "/v1-to-v2/item",
-        json={"title": "Bad sub", "size": 100, "sub": {"wrong_field": "value"}},
-    )
-    assert response.status_code == 422, response.text
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "loc": ["body", "sub", "name"],
-                    "msg": "field required",
-                    "type": "value_error.missing",
-                }
-            ]
-        }
-    )
-
-
-def test_v1_to_v2_type_validation_error():
-    response = client.post(
-        "/v1-to-v2/item",
-        json={"title": "Bad type", "size": "not_a_number", "sub": {"name": "Sub"}},
-    )
-    assert response.status_code == 422, response.text
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "loc": ["body", "size"],
-                    "msg": "value is not a valid integer",
-                    "type": "type_error.integer",
-                }
-            ]
-        }
-    )
-
-
-def test_v2_to_v1_validation_error():
-    response = client.post(
-        "/v2-to-v1/item",
-        json={"new_title": "Missing fields"},
-    )
-    assert response.status_code == 422, response.text
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "type": "missing",
-                    "loc": ["body", "new_size"],
-                    "msg": "Field required",
-                    "input": {"new_title": "Missing fields"},
-                },
-                {
-                    "type": "missing",
-                    "loc": ["body", "new_sub"],
-                    "msg": "Field required",
-                    "input": {"new_title": "Missing fields"},
-                },
-            ]
-        }
-    )
-
-
-def test_v2_to_v1_nested_validation_error():
-    response = client.post(
-        "/v2-to-v1/item",
-        json={
-            "new_title": "Bad sub",
-            "new_size": 200,
-            "new_sub": {"wrong_field": "value"},
-        },
-    )
-    assert response.status_code == 422, response.text
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "type": "missing",
-                    "loc": ["body", "new_sub", "new_sub_name"],
-                    "msg": "Field required",
-                    "input": {"wrong_field": "value"},
-                }
-            ]
-        }
-    )
-
-
-def test_v1_list_validation_error():
-    response = client.post(
-        "/v1-to-v2/list-to-list",
-        json=[
-            {"title": "Valid", "size": 10, "sub": {"name": "S"}},
-            {"title": "Invalid"},
-        ],
-    )
-    assert response.status_code == 422, response.text
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "loc": ["body", 1, "size"],
-                    "msg": "field required",
-                    "type": "value_error.missing",
-                },
-                {
-                    "loc": ["body", 1, "sub"],
-                    "msg": "field required",
-                    "type": "value_error.missing",
-                },
-            ]
-        }
-    )
-
-
-def test_v2_list_validation_error():
-    response = client.post(
-        "/v2-to-v1/list-to-list",
-        json=[
-            {"new_title": "Valid", "new_size": 10, "new_sub": {"new_sub_name": "NS"}},
-            {"new_title": "Invalid"},
-        ],
-    )
-    assert response.status_code == 422, response.text
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "type": "missing",
-                    "loc": ["body", 1, "new_size"],
-                    "msg": "Field required",
-                    "input": {"new_title": "Invalid"},
-                },
-                {
-                    "type": "missing",
-                    "loc": ["body", 1, "new_sub"],
-                    "msg": "Field required",
-                    "input": {"new_title": "Invalid"},
-                },
-            ]
-        }
-    )
-
-
-def test_invalid_list_structure_v1():
-    response = client.post(
-        "/v1-to-v2/list-to-list",
-        json={"title": "Not a list", "size": 100, "sub": {"name": "Sub"}},
-    )
-    assert response.status_code == 422, response.text
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "loc": ["body"],
-                    "msg": "value is not a valid list",
-                    "type": "type_error.list",
-                }
-            ]
-        }
-    )
-
-
-def test_invalid_list_structure_v2():
-    response = client.post(
-        "/v2-to-v1/list-to-list",
-        json={
-            "new_title": "Not a list",
-            "new_size": 100,
-            "new_sub": {"new_sub_name": "Sub"},
-        },
-    )
-    assert response.status_code == 422, response.text
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "type": "list_type",
-                    "loc": ["body"],
-                    "msg": "Input should be a valid list",
-                    "input": {
-                        "new_title": "Not a list",
-                        "new_size": 100,
-                        "new_sub": {"new_sub_name": "Sub"},
-                    },
-                }
-            ]
-        }
-    )
-
-
-def test_openapi_schema():
-    response = client.get("/openapi.json")
-    assert response.status_code == 200, response.text
-    assert response.json() == snapshot(
-        {
-            "openapi": "3.1.0",
-            "info": {"title": "FastAPI", "version": "0.1.0"},
-            "paths": {
-                "/v1-to-v2/item": {
-                    "post": {
-                        "summary": "Handle V1 Item To V2",
-                        "operationId": "handle_v1_item_to_v2_v1_to_v2_item_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "allOf": [
-                                            {"$ref": "#/components/schemas/Item"}
-                                        ],
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/NewItem"
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v1-to-v2/item-filter": {
-                    "post": {
-                        "summary": "Handle V1 Item To V2 Filter",
-                        "operationId": "handle_v1_item_to_v2_filter_v1_to_v2_item_filter_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "allOf": [
-                                            {"$ref": "#/components/schemas/Item"}
-                                        ],
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/NewItem"
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v2-to-v1/item": {
-                    "post": {
-                        "summary": "Handle V2 Item To V1",
-                        "operationId": "handle_v2_item_to_v1_v2_to_v1_item_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {"$ref": "#/components/schemas/NewItem"}
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {"$ref": "#/components/schemas/Item"}
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v2-to-v1/item-filter": {
-                    "post": {
-                        "summary": "Handle V2 Item To V1 Filter",
-                        "operationId": "handle_v2_item_to_v1_filter_v2_to_v1_item_filter_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {"$ref": "#/components/schemas/NewItem"}
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {"$ref": "#/components/schemas/Item"}
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v1-to-v2/item-to-list": {
-                    "post": {
-                        "summary": "Handle V1 Item To V2 List",
-                        "operationId": "handle_v1_item_to_v2_list_v1_to_v2_item_to_list_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "allOf": [
-                                            {"$ref": "#/components/schemas/Item"}
-                                        ],
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "items": {
-                                                "$ref": "#/components/schemas/NewItem"
-                                            },
-                                            "type": "array",
-                                            "title": "Response Handle V1 Item To V2 List V1 To V2 Item To List Post",
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v1-to-v2/list-to-list": {
-                    "post": {
-                        "summary": "Handle V1 List To V2 List",
-                        "operationId": "handle_v1_list_to_v2_list_v1_to_v2_list_to_list_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "items": {"$ref": "#/components/schemas/Item"},
-                                        "type": "array",
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "items": {
-                                                "$ref": "#/components/schemas/NewItem"
-                                            },
-                                            "type": "array",
-                                            "title": "Response Handle V1 List To V2 List V1 To V2 List To List Post",
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v1-to-v2/list-to-list-filter": {
-                    "post": {
-                        "summary": "Handle V1 List To V2 List Filter",
-                        "operationId": "handle_v1_list_to_v2_list_filter_v1_to_v2_list_to_list_filter_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "items": {"$ref": "#/components/schemas/Item"},
-                                        "type": "array",
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "items": {
-                                                "$ref": "#/components/schemas/NewItem"
-                                            },
-                                            "type": "array",
-                                            "title": "Response Handle V1 List To V2 List Filter V1 To V2 List To List Filter Post",
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v1-to-v2/list-to-item": {
-                    "post": {
-                        "summary": "Handle V1 List To V2 Item",
-                        "operationId": "handle_v1_list_to_v2_item_v1_to_v2_list_to_item_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "items": {"$ref": "#/components/schemas/Item"},
-                                        "type": "array",
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/NewItem"
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v2-to-v1/item-to-list": {
-                    "post": {
-                        "summary": "Handle V2 Item To V1 List",
-                        "operationId": "handle_v2_item_to_v1_list_v2_to_v1_item_to_list_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {"$ref": "#/components/schemas/NewItem"}
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "items": {
-                                                "$ref": "#/components/schemas/Item"
-                                            },
-                                            "type": "array",
-                                            "title": "Response Handle V2 Item To V1 List V2 To V1 Item To List Post",
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v2-to-v1/list-to-list": {
-                    "post": {
-                        "summary": "Handle V2 List To V1 List",
-                        "operationId": "handle_v2_list_to_v1_list_v2_to_v1_list_to_list_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "items": {
-                                            "$ref": "#/components/schemas/NewItem"
-                                        },
-                                        "type": "array",
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "items": {
-                                                "$ref": "#/components/schemas/Item"
-                                            },
-                                            "type": "array",
-                                            "title": "Response Handle V2 List To V1 List V2 To V1 List To List Post",
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v2-to-v1/list-to-list-filter": {
-                    "post": {
-                        "summary": "Handle V2 List To V1 List Filter",
-                        "operationId": "handle_v2_list_to_v1_list_filter_v2_to_v1_list_to_list_filter_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "items": {
-                                            "$ref": "#/components/schemas/NewItem"
-                                        },
-                                        "type": "array",
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "items": {
-                                                "$ref": "#/components/schemas/Item"
-                                            },
-                                            "type": "array",
-                                            "title": "Response Handle V2 List To V1 List Filter V2 To V1 List To List Filter Post",
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v2-to-v1/list-to-item": {
-                    "post": {
-                        "summary": "Handle V2 List To V1 Item",
-                        "operationId": "handle_v2_list_to_v1_item_v2_to_v1_list_to_item_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "items": {
-                                            "$ref": "#/components/schemas/NewItem"
-                                        },
-                                        "type": "array",
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {"$ref": "#/components/schemas/Item"}
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-            },
-            "components": {
-                "schemas": {
-                    "HTTPValidationError": {
-                        "properties": {
-                            "detail": {
-                                "items": {
-                                    "$ref": "#/components/schemas/ValidationError"
-                                },
-                                "type": "array",
-                                "title": "Detail",
-                            }
-                        },
-                        "type": "object",
-                        "title": "HTTPValidationError",
-                    },
-                    "Item": {
-                        "properties": {
-                            "title": {"type": "string", "title": "Title"},
-                            "size": {"type": "integer", "title": "Size"},
-                            "description": {"type": "string", "title": "Description"},
-                            "sub": {"$ref": "#/components/schemas/SubItem"},
-                            "multi": {
-                                "items": {"$ref": "#/components/schemas/SubItem"},
-                                "type": "array",
-                                "title": "Multi",
-                                "default": [],
-                            },
-                        },
-                        "type": "object",
-                        "required": ["title", "size", "sub"],
-                        "title": "Item",
-                    },
-                    "NewItem": {
-                        "properties": {
-                            "new_title": {"type": "string", "title": "New Title"},
-                            "new_size": {"type": "integer", "title": "New Size"},
-                            "new_description": {
-                                "anyOf": [{"type": "string"}, {"type": "null"}],
-                                "title": "New Description",
-                            },
-                            "new_sub": {"$ref": "#/components/schemas/NewSubItem"},
-                            "new_multi": {
-                                "items": {"$ref": "#/components/schemas/NewSubItem"},
-                                "type": "array",
-                                "title": "New Multi",
-                                "default": [],
-                            },
-                        },
-                        "type": "object",
-                        "required": ["new_title", "new_size", "new_sub"],
-                        "title": "NewItem",
-                    },
-                    "NewSubItem": {
-                        "properties": {
-                            "new_sub_name": {"type": "string", "title": "New Sub Name"}
-                        },
-                        "type": "object",
-                        "required": ["new_sub_name"],
-                        "title": "NewSubItem",
-                    },
-                    "SubItem": {
-                        "properties": {"name": {"type": "string", "title": "Name"}},
-                        "type": "object",
-                        "required": ["name"],
-                        "title": "SubItem",
-                    },
-                    "ValidationError": {
-                        "properties": {
-                            "loc": {
-                                "items": {
-                                    "anyOf": [{"type": "string"}, {"type": "integer"}]
-                                },
-                                "type": "array",
-                                "title": "Location",
-                            },
-                            "msg": {"type": "string", "title": "Message"},
-                            "type": {"type": "string", "title": "Error Type"},
-                        },
-                        "type": "object",
-                        "required": ["loc", "msg", "type"],
-                        "title": "ValidationError",
-                    },
-                }
-            },
-        }
-    )
diff --git a/tests/test_pydantic_v1_v2_multifile/__init__.py b/tests/test_pydantic_v1_v2_multifile/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/tests/test_pydantic_v1_v2_multifile/main.py b/tests/test_pydantic_v1_v2_multifile/main.py
deleted file mode 100644 (file)
index 4180ec3..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-import warnings
-
-from fastapi import FastAPI
-
-from . import modelsv1, modelsv2, modelsv2b
-
-app = FastAPI()
-
-with warnings.catch_warnings(record=True):
-    warnings.simplefilter("always")
-
-    @app.post("/v1-to-v2/item")
-    def handle_v1_item_to_v2(data: modelsv1.Item) -> modelsv2.Item:
-        return modelsv2.Item(
-            new_title=data.title,
-            new_size=data.size,
-            new_description=data.description,
-            new_sub=modelsv2.SubItem(new_sub_name=data.sub.name),
-            new_multi=[modelsv2.SubItem(new_sub_name=s.name) for s in data.multi],
-        )
-
-    @app.post("/v2-to-v1/item")
-    def handle_v2_item_to_v1(data: modelsv2.Item) -> modelsv1.Item:
-        return modelsv1.Item(
-            title=data.new_title,
-            size=data.new_size,
-            description=data.new_description,
-            sub=modelsv1.SubItem(name=data.new_sub.new_sub_name),
-            multi=[modelsv1.SubItem(name=s.new_sub_name) for s in data.new_multi],
-        )
-
-    @app.post("/v1-to-v2/item-to-list")
-    def handle_v1_item_to_v2_list(data: modelsv1.Item) -> list[modelsv2.Item]:
-        converted = modelsv2.Item(
-            new_title=data.title,
-            new_size=data.size,
-            new_description=data.description,
-            new_sub=modelsv2.SubItem(new_sub_name=data.sub.name),
-            new_multi=[modelsv2.SubItem(new_sub_name=s.name) for s in data.multi],
-        )
-        return [converted, converted]
-
-    @app.post("/v1-to-v2/list-to-list")
-    def handle_v1_list_to_v2_list(data: list[modelsv1.Item]) -> list[modelsv2.Item]:
-        result = []
-        for item in data:
-            result.append(
-                modelsv2.Item(
-                    new_title=item.title,
-                    new_size=item.size,
-                    new_description=item.description,
-                    new_sub=modelsv2.SubItem(new_sub_name=item.sub.name),
-                    new_multi=[
-                        modelsv2.SubItem(new_sub_name=s.name) for s in item.multi
-                    ],
-                )
-            )
-        return result
-
-    @app.post("/v1-to-v2/list-to-item")
-    def handle_v1_list_to_v2_item(data: list[modelsv1.Item]) -> modelsv2.Item:
-        if data:
-            item = data[0]
-            return modelsv2.Item(
-                new_title=item.title,
-                new_size=item.size,
-                new_description=item.description,
-                new_sub=modelsv2.SubItem(new_sub_name=item.sub.name),
-                new_multi=[modelsv2.SubItem(new_sub_name=s.name) for s in item.multi],
-            )
-        return modelsv2.Item(
-            new_title="", new_size=0, new_sub=modelsv2.SubItem(new_sub_name="")
-        )
-
-    @app.post("/v2-to-v1/item-to-list")
-    def handle_v2_item_to_v1_list(data: modelsv2.Item) -> list[modelsv1.Item]:
-        converted = modelsv1.Item(
-            title=data.new_title,
-            size=data.new_size,
-            description=data.new_description,
-            sub=modelsv1.SubItem(name=data.new_sub.new_sub_name),
-            multi=[modelsv1.SubItem(name=s.new_sub_name) for s in data.new_multi],
-        )
-        return [converted, converted]
-
-    @app.post("/v2-to-v1/list-to-list")
-    def handle_v2_list_to_v1_list(data: list[modelsv2.Item]) -> list[modelsv1.Item]:
-        result = []
-        for item in data:
-            result.append(
-                modelsv1.Item(
-                    title=item.new_title,
-                    size=item.new_size,
-                    description=item.new_description,
-                    sub=modelsv1.SubItem(name=item.new_sub.new_sub_name),
-                    multi=[
-                        modelsv1.SubItem(name=s.new_sub_name) for s in item.new_multi
-                    ],
-                )
-            )
-        return result
-
-    @app.post("/v2-to-v1/list-to-item")
-    def handle_v2_list_to_v1_item(data: list[modelsv2.Item]) -> modelsv1.Item:
-        if data:
-            item = data[0]
-            return modelsv1.Item(
-                title=item.new_title,
-                size=item.new_size,
-                description=item.new_description,
-                sub=modelsv1.SubItem(name=item.new_sub.new_sub_name),
-                multi=[modelsv1.SubItem(name=s.new_sub_name) for s in item.new_multi],
-            )
-        return modelsv1.Item(title="", size=0, sub=modelsv1.SubItem(name=""))
-
-    @app.post("/v2-to-v1/same-name")
-    def handle_v2_same_name_to_v1(
-        item1: modelsv2.Item, item2: modelsv2b.Item
-    ) -> modelsv1.Item:
-        return modelsv1.Item(
-            title=item1.new_title,
-            size=item2.dup_size,
-            description=item1.new_description,
-            sub=modelsv1.SubItem(name=item1.new_sub.new_sub_name),
-            multi=[modelsv1.SubItem(name=s.dup_sub_name) for s in item2.dup_multi],
-        )
-
-    @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]:
-        item1 = data1[0]
-        item2 = data2[0]
-        return [
-            modelsv1.ItemInList(name1=item1.name2),
-            modelsv1.ItemInList(name1=item2.dup_name2),
-        ]
diff --git a/tests/test_pydantic_v1_v2_multifile/modelsv1.py b/tests/test_pydantic_v1_v2_multifile/modelsv1.py
deleted file mode 100644 (file)
index 0cc8de4..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Union
-
-from fastapi._compat.v1 import BaseModel
-
-
-class SubItem(BaseModel):
-    name: str
-
-
-class Item(BaseModel):
-    title: str
-    size: int
-    description: Union[str, None] = None
-    sub: SubItem
-    multi: list[SubItem] = []
-
-
-class ItemInList(BaseModel):
-    name1: str
diff --git a/tests/test_pydantic_v1_v2_multifile/modelsv2.py b/tests/test_pydantic_v1_v2_multifile/modelsv2.py
deleted file mode 100644 (file)
index d80b77e..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Union
-
-from pydantic import BaseModel
-
-
-class SubItem(BaseModel):
-    new_sub_name: str
-
-
-class Item(BaseModel):
-    new_title: str
-    new_size: int
-    new_description: Union[str, None] = None
-    new_sub: SubItem
-    new_multi: list[SubItem] = []
-
-
-class ItemInList(BaseModel):
-    name2: str
diff --git a/tests/test_pydantic_v1_v2_multifile/modelsv2b.py b/tests/test_pydantic_v1_v2_multifile/modelsv2b.py
deleted file mode 100644 (file)
index e992bea..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-from typing import Union
-
-from pydantic import BaseModel
-
-
-class SubItem(BaseModel):
-    dup_sub_name: str
-
-
-class Item(BaseModel):
-    dup_title: str
-    dup_size: int
-    dup_description: Union[str, None] = None
-    dup_sub: SubItem
-    dup_multi: list[SubItem] = []
-
-
-class ItemInList(BaseModel):
-    dup_name2: str
diff --git a/tests/test_pydantic_v1_v2_multifile/test_multifile.py b/tests/test_pydantic_v1_v2_multifile/test_multifile.py
deleted file mode 100644 (file)
index 32d9019..0000000
+++ /dev/null
@@ -1,951 +0,0 @@
-import sys
-
-from tests.utils import skip_module_if_py_gte_314
-
-if sys.version_info >= (3, 14):
-    skip_module_if_py_gte_314()
-
-from fastapi.testclient import TestClient
-from inline_snapshot import snapshot
-
-from .main import app
-
-client = TestClient(app)
-
-
-def test_v1_to_v2_item():
-    response = client.post(
-        "/v1-to-v2/item",
-        json={"title": "Test", "size": 10, "sub": {"name": "SubTest"}},
-    )
-    assert response.status_code == 200
-    assert response.json() == {
-        "new_title": "Test",
-        "new_size": 10,
-        "new_description": None,
-        "new_sub": {"new_sub_name": "SubTest"},
-        "new_multi": [],
-    }
-
-
-def test_v2_to_v1_item():
-    response = client.post(
-        "/v2-to-v1/item",
-        json={
-            "new_title": "NewTest",
-            "new_size": 20,
-            "new_sub": {"new_sub_name": "NewSubTest"},
-        },
-    )
-    assert response.status_code == 200
-    assert response.json() == {
-        "title": "NewTest",
-        "size": 20,
-        "description": None,
-        "sub": {"name": "NewSubTest"},
-        "multi": [],
-    }
-
-
-def test_v1_to_v2_item_to_list():
-    response = client.post(
-        "/v1-to-v2/item-to-list",
-        json={"title": "ListTest", "size": 30, "sub": {"name": "SubListTest"}},
-    )
-    assert response.status_code == 200
-    assert response.json() == [
-        {
-            "new_title": "ListTest",
-            "new_size": 30,
-            "new_description": None,
-            "new_sub": {"new_sub_name": "SubListTest"},
-            "new_multi": [],
-        },
-        {
-            "new_title": "ListTest",
-            "new_size": 30,
-            "new_description": None,
-            "new_sub": {"new_sub_name": "SubListTest"},
-            "new_multi": [],
-        },
-    ]
-
-
-def test_v1_to_v2_list_to_list():
-    response = client.post(
-        "/v1-to-v2/list-to-list",
-        json=[
-            {"title": "Item1", "size": 40, "sub": {"name": "Sub1"}},
-            {"title": "Item2", "size": 50, "sub": {"name": "Sub2"}},
-        ],
-    )
-    assert response.status_code == 200
-    assert response.json() == [
-        {
-            "new_title": "Item1",
-            "new_size": 40,
-            "new_description": None,
-            "new_sub": {"new_sub_name": "Sub1"},
-            "new_multi": [],
-        },
-        {
-            "new_title": "Item2",
-            "new_size": 50,
-            "new_description": None,
-            "new_sub": {"new_sub_name": "Sub2"},
-            "new_multi": [],
-        },
-    ]
-
-
-def test_v1_to_v2_list_to_item():
-    response = client.post(
-        "/v1-to-v2/list-to-item",
-        json=[
-            {"title": "FirstItem", "size": 60, "sub": {"name": "FirstSub"}},
-            {"title": "SecondItem", "size": 70, "sub": {"name": "SecondSub"}},
-        ],
-    )
-    assert response.status_code == 200
-    assert response.json() == {
-        "new_title": "FirstItem",
-        "new_size": 60,
-        "new_description": None,
-        "new_sub": {"new_sub_name": "FirstSub"},
-        "new_multi": [],
-    }
-
-
-def test_v2_to_v1_item_to_list():
-    response = client.post(
-        "/v2-to-v1/item-to-list",
-        json={
-            "new_title": "ListNew",
-            "new_size": 80,
-            "new_sub": {"new_sub_name": "SubListNew"},
-        },
-    )
-    assert response.status_code == 200
-    assert response.json() == [
-        {
-            "title": "ListNew",
-            "size": 80,
-            "description": None,
-            "sub": {"name": "SubListNew"},
-            "multi": [],
-        },
-        {
-            "title": "ListNew",
-            "size": 80,
-            "description": None,
-            "sub": {"name": "SubListNew"},
-            "multi": [],
-        },
-    ]
-
-
-def test_v2_to_v1_list_to_list():
-    response = client.post(
-        "/v2-to-v1/list-to-list",
-        json=[
-            {
-                "new_title": "New1",
-                "new_size": 90,
-                "new_sub": {"new_sub_name": "NewSub1"},
-            },
-            {
-                "new_title": "New2",
-                "new_size": 100,
-                "new_sub": {"new_sub_name": "NewSub2"},
-            },
-        ],
-    )
-    assert response.status_code == 200
-    assert response.json() == [
-        {
-            "title": "New1",
-            "size": 90,
-            "description": None,
-            "sub": {"name": "NewSub1"},
-            "multi": [],
-        },
-        {
-            "title": "New2",
-            "size": 100,
-            "description": None,
-            "sub": {"name": "NewSub2"},
-            "multi": [],
-        },
-    ]
-
-
-def test_v2_to_v1_list_to_item():
-    response = client.post(
-        "/v2-to-v1/list-to-item",
-        json=[
-            {
-                "new_title": "FirstNew",
-                "new_size": 110,
-                "new_sub": {"new_sub_name": "FirstNewSub"},
-            },
-            {
-                "new_title": "SecondNew",
-                "new_size": 120,
-                "new_sub": {"new_sub_name": "SecondNewSub"},
-            },
-        ],
-    )
-    assert response.status_code == 200
-    assert response.json() == {
-        "title": "FirstNew",
-        "size": 110,
-        "description": None,
-        "sub": {"name": "FirstNewSub"},
-        "multi": [],
-    }
-
-
-def test_v1_to_v2_list_to_item_empty():
-    response = client.post("/v1-to-v2/list-to-item", json=[])
-    assert response.status_code == 200
-    assert response.json() == {
-        "new_title": "",
-        "new_size": 0,
-        "new_description": None,
-        "new_sub": {"new_sub_name": ""},
-        "new_multi": [],
-    }
-
-
-def test_v2_to_v1_list_to_item_empty():
-    response = client.post("/v2-to-v1/list-to-item", json=[])
-    assert response.status_code == 200
-    assert response.json() == {
-        "title": "",
-        "size": 0,
-        "description": None,
-        "sub": {"name": ""},
-        "multi": [],
-    }
-
-
-def test_v2_same_name_to_v1():
-    response = client.post(
-        "/v2-to-v1/same-name",
-        json={
-            "item1": {
-                "new_title": "Title1",
-                "new_size": 100,
-                "new_description": "Description1",
-                "new_sub": {"new_sub_name": "Sub1"},
-                "new_multi": [{"new_sub_name": "Multi1"}],
-            },
-            "item2": {
-                "dup_title": "Title2",
-                "dup_size": 200,
-                "dup_description": "Description2",
-                "dup_sub": {"dup_sub_name": "Sub2"},
-                "dup_multi": [
-                    {"dup_sub_name": "Multi2a"},
-                    {"dup_sub_name": "Multi2b"},
-                ],
-            },
-        },
-    )
-    assert response.status_code == 200
-    assert response.json() == {
-        "title": "Title1",
-        "size": 200,
-        "description": "Description1",
-        "sub": {"name": "Sub1"},
-        "multi": [{"name": "Multi2a"}, {"name": "Multi2b"}],
-    }
-
-
-def test_v2_items_in_list_to_v1_item_in_list():
-    response = client.post(
-        "/v2-to-v1/list-of-items-to-list-of-items",
-        json={
-            "data1": [{"name2": "Item1"}, {"name2": "Item2"}],
-            "data2": [{"dup_name2": "Item3"}, {"dup_name2": "Item4"}],
-        },
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == [
-        {"name1": "Item1"},
-        {"name1": "Item3"},
-    ]
-
-
-def test_openapi_schema():
-    response = client.get("/openapi.json")
-    assert response.status_code == 200
-    assert response.json() == snapshot(
-        {
-            "openapi": "3.1.0",
-            "info": {"title": "FastAPI", "version": "0.1.0"},
-            "paths": {
-                "/v1-to-v2/item": {
-                    "post": {
-                        "summary": "Handle V1 Item To V2",
-                        "operationId": "handle_v1_item_to_v2_v1_to_v2_item_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "allOf": [
-                                            {
-                                                "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item"
-                                            }
-                                        ],
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item"
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v2-to-v1/item": {
-                    "post": {
-                        "summary": "Handle V2 Item To V1",
-                        "operationId": "handle_v2_item_to_v1_v2_to_v1_item_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item-Input"
-                                    },
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item"
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v1-to-v2/item-to-list": {
-                    "post": {
-                        "summary": "Handle V1 Item To V2 List",
-                        "operationId": "handle_v1_item_to_v2_list_v1_to_v2_item_to_list_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "allOf": [
-                                            {
-                                                "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item"
-                                            }
-                                        ],
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "items": {
-                                                "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item"
-                                            },
-                                            "type": "array",
-                                            "title": "Response Handle V1 Item To V2 List V1 To V2 Item To List Post",
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v1-to-v2/list-to-list": {
-                    "post": {
-                        "summary": "Handle V1 List To V2 List",
-                        "operationId": "handle_v1_list_to_v2_list_v1_to_v2_list_to_list_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "items": {
-                                            "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item"
-                                        },
-                                        "type": "array",
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "items": {
-                                                "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item"
-                                            },
-                                            "type": "array",
-                                            "title": "Response Handle V1 List To V2 List V1 To V2 List To List Post",
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v1-to-v2/list-to-item": {
-                    "post": {
-                        "summary": "Handle V1 List To V2 Item",
-                        "operationId": "handle_v1_list_to_v2_item_v1_to_v2_list_to_item_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "items": {
-                                            "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item"
-                                        },
-                                        "type": "array",
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item"
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v2-to-v1/item-to-list": {
-                    "post": {
-                        "summary": "Handle V2 Item To V1 List",
-                        "operationId": "handle_v2_item_to_v1_list_v2_to_v1_item_to_list_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item-Input"
-                                    },
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "items": {
-                                                "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item"
-                                            },
-                                            "type": "array",
-                                            "title": "Response Handle V2 Item To V1 List V2 To V1 Item To List Post",
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v2-to-v1/list-to-list": {
-                    "post": {
-                        "summary": "Handle V2 List To V1 List",
-                        "operationId": "handle_v2_list_to_v1_list_v2_to_v1_list_to_list_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "items": {
-                                            "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item-Input"
-                                        },
-                                        "type": "array",
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "items": {
-                                                "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item"
-                                            },
-                                            "type": "array",
-                                            "title": "Response Handle V2 List To V1 List V2 To V1 List To List Post",
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v2-to-v1/list-to-item": {
-                    "post": {
-                        "summary": "Handle V2 List To V1 Item",
-                        "operationId": "handle_v2_list_to_v1_item_v2_to_v1_list_to_item_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "items": {
-                                            "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item-Input"
-                                        },
-                                        "type": "array",
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item"
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v2-to-v1/same-name": {
-                    "post": {
-                        "summary": "Handle V2 Same Name To V1",
-                        "operationId": "handle_v2_same_name_to_v1_v2_to_v1_same_name_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "$ref": "#/components/schemas/Body_handle_v2_same_name_to_v1_v2_to_v1_same_name_post"
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item"
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v2-to-v1/list-of-items-to-list-of-items": {
-                    "post": {
-                        "summary": "Handle V2 Items In List To V1 Item In List",
-                        "operationId": "handle_v2_items_in_list_to_v1_item_in_list_v2_to_v1_list_of_items_to_list_of_items_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "$ref": "#/components/schemas/Body_handle_v2_items_in_list_to_v1_item_in_list_v2_to_v1_list_of_items_to_list_of_items_post"
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "items": {
-                                                "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__ItemInList"
-                                            },
-                                            "type": "array",
-                                            "title": "Response Handle V2 Items In List To V1 Item In List V2 To V1 List Of Items To List Of Items Post",
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-            },
-            "components": {
-                "schemas": {
-                    "Body_handle_v2_items_in_list_to_v1_item_in_list_v2_to_v1_list_of_items_to_list_of_items_post": {
-                        "properties": {
-                            "data1": {
-                                "items": {
-                                    "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__ItemInList"
-                                },
-                                "type": "array",
-                                "title": "Data1",
-                            },
-                            "data2": {
-                                "items": {
-                                    "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2b__ItemInList"
-                                },
-                                "type": "array",
-                                "title": "Data2",
-                            },
-                        },
-                        "type": "object",
-                        "required": ["data1", "data2"],
-                        "title": "Body_handle_v2_items_in_list_to_v1_item_in_list_v2_to_v1_list_of_items_to_list_of_items_post",
-                    },
-                    "Body_handle_v2_same_name_to_v1_v2_to_v1_same_name_post": {
-                        "properties": {
-                            "item1": {
-                                "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item-Input"
-                            },
-                            "item2": {
-                                "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2b__Item"
-                            },
-                        },
-                        "type": "object",
-                        "required": ["item1", "item2"],
-                        "title": "Body_handle_v2_same_name_to_v1_v2_to_v1_same_name_post",
-                    },
-                    "HTTPValidationError": {
-                        "properties": {
-                            "detail": {
-                                "items": {
-                                    "$ref": "#/components/schemas/ValidationError"
-                                },
-                                "type": "array",
-                                "title": "Detail",
-                            }
-                        },
-                        "type": "object",
-                        "title": "HTTPValidationError",
-                    },
-                    "ValidationError": {
-                        "properties": {
-                            "loc": {
-                                "items": {
-                                    "anyOf": [
-                                        {"type": "string"},
-                                        {"type": "integer"},
-                                    ]
-                                },
-                                "type": "array",
-                                "title": "Location",
-                            },
-                            "msg": {"type": "string", "title": "Message"},
-                            "type": {"type": "string", "title": "Error Type"},
-                        },
-                        "type": "object",
-                        "required": ["loc", "msg", "type"],
-                        "title": "ValidationError",
-                    },
-                    "tests__test_pydantic_v1_v2_multifile__modelsv1__Item": {
-                        "properties": {
-                            "title": {"type": "string", "title": "Title"},
-                            "size": {"type": "integer", "title": "Size"},
-                            "description": {
-                                "type": "string",
-                                "title": "Description",
-                            },
-                            "sub": {
-                                "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__SubItem"
-                            },
-                            "multi": {
-                                "items": {
-                                    "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__SubItem"
-                                },
-                                "type": "array",
-                                "title": "Multi",
-                                "default": [],
-                            },
-                        },
-                        "type": "object",
-                        "required": ["title", "size", "sub"],
-                        "title": "Item",
-                    },
-                    "tests__test_pydantic_v1_v2_multifile__modelsv1__ItemInList": {
-                        "properties": {"name1": {"type": "string", "title": "Name1"}},
-                        "type": "object",
-                        "required": ["name1"],
-                        "title": "ItemInList",
-                    },
-                    "tests__test_pydantic_v1_v2_multifile__modelsv1__SubItem": {
-                        "properties": {"name": {"type": "string", "title": "Name"}},
-                        "type": "object",
-                        "required": ["name"],
-                        "title": "SubItem",
-                    },
-                    "tests__test_pydantic_v1_v2_multifile__modelsv2__Item": {
-                        "properties": {
-                            "new_title": {
-                                "type": "string",
-                                "title": "New Title",
-                            },
-                            "new_size": {
-                                "type": "integer",
-                                "title": "New Size",
-                            },
-                            "new_description": {
-                                "anyOf": [{"type": "string"}, {"type": "null"}],
-                                "title": "New Description",
-                            },
-                            "new_sub": {
-                                "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__SubItem"
-                            },
-                            "new_multi": {
-                                "items": {
-                                    "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__SubItem"
-                                },
-                                "type": "array",
-                                "title": "New Multi",
-                                "default": [],
-                            },
-                        },
-                        "type": "object",
-                        "required": ["new_title", "new_size", "new_sub"],
-                        "title": "Item",
-                    },
-                    "tests__test_pydantic_v1_v2_multifile__modelsv2__Item-Input": {
-                        "properties": {
-                            "new_title": {
-                                "type": "string",
-                                "title": "New Title",
-                            },
-                            "new_size": {
-                                "type": "integer",
-                                "title": "New Size",
-                            },
-                            "new_description": {
-                                "anyOf": [{"type": "string"}, {"type": "null"}],
-                                "title": "New Description",
-                            },
-                            "new_sub": {
-                                "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__SubItem"
-                            },
-                            "new_multi": {
-                                "items": {
-                                    "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__SubItem"
-                                },
-                                "type": "array",
-                                "title": "New Multi",
-                                "default": [],
-                            },
-                        },
-                        "type": "object",
-                        "required": ["new_title", "new_size", "new_sub"],
-                        "title": "Item",
-                    },
-                    "tests__test_pydantic_v1_v2_multifile__modelsv2__ItemInList": {
-                        "properties": {"name2": {"type": "string", "title": "Name2"}},
-                        "type": "object",
-                        "required": ["name2"],
-                        "title": "ItemInList",
-                    },
-                    "tests__test_pydantic_v1_v2_multifile__modelsv2__SubItem": {
-                        "properties": {
-                            "new_sub_name": {
-                                "type": "string",
-                                "title": "New Sub Name",
-                            }
-                        },
-                        "type": "object",
-                        "required": ["new_sub_name"],
-                        "title": "SubItem",
-                    },
-                    "tests__test_pydantic_v1_v2_multifile__modelsv2b__Item": {
-                        "properties": {
-                            "dup_title": {
-                                "type": "string",
-                                "title": "Dup Title",
-                            },
-                            "dup_size": {
-                                "type": "integer",
-                                "title": "Dup Size",
-                            },
-                            "dup_description": {
-                                "anyOf": [{"type": "string"}, {"type": "null"}],
-                                "title": "Dup Description",
-                            },
-                            "dup_sub": {
-                                "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2b__SubItem"
-                            },
-                            "dup_multi": {
-                                "items": {
-                                    "$ref": "#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2b__SubItem"
-                                },
-                                "type": "array",
-                                "title": "Dup Multi",
-                                "default": [],
-                            },
-                        },
-                        "type": "object",
-                        "required": ["dup_title", "dup_size", "dup_sub"],
-                        "title": "Item",
-                    },
-                    "tests__test_pydantic_v1_v2_multifile__modelsv2b__ItemInList": {
-                        "properties": {
-                            "dup_name2": {
-                                "type": "string",
-                                "title": "Dup Name2",
-                            }
-                        },
-                        "type": "object",
-                        "required": ["dup_name2"],
-                        "title": "ItemInList",
-                    },
-                    "tests__test_pydantic_v1_v2_multifile__modelsv2b__SubItem": {
-                        "properties": {
-                            "dup_sub_name": {
-                                "type": "string",
-                                "title": "Dup Sub Name",
-                            }
-                        },
-                        "type": "object",
-                        "required": ["dup_sub_name"],
-                        "title": "SubItem",
-                    },
-                },
-            },
-        }
-    )
diff --git a/tests/test_pydantic_v1_v2_noneable.py b/tests/test_pydantic_v1_v2_noneable.py
deleted file mode 100644 (file)
index ba98b56..0000000
+++ /dev/null
@@ -1,692 +0,0 @@
-import sys
-import warnings
-from typing import Any, Union
-
-from tests.utils import skip_module_if_py_gte_314
-
-if sys.version_info >= (3, 14):
-    skip_module_if_py_gte_314()
-
-from fastapi import FastAPI
-from fastapi._compat.v1 import BaseModel
-from fastapi.testclient import TestClient
-from inline_snapshot import snapshot
-from pydantic import BaseModel as NewBaseModel
-
-
-class SubItem(BaseModel):
-    name: str
-
-
-class Item(BaseModel):
-    title: str
-    size: int
-    description: Union[str, None] = None
-    sub: SubItem
-    multi: list[SubItem] = []
-
-
-class NewSubItem(NewBaseModel):
-    new_sub_name: str
-
-
-class NewItem(NewBaseModel):
-    new_title: str
-    new_size: int
-    new_description: Union[str, None] = None
-    new_sub: NewSubItem
-    new_multi: list[NewSubItem] = []
-
-
-app = FastAPI()
-
-with warnings.catch_warnings(record=True):
-    warnings.simplefilter("always")
-
-    @app.post("/v1-to-v2/")
-    def handle_v1_item_to_v2(data: Item) -> Union[NewItem, None]:
-        if data.size < 0:
-            return None
-        return NewItem(
-            new_title=data.title,
-            new_size=data.size,
-            new_description=data.description,
-            new_sub=NewSubItem(new_sub_name=data.sub.name),
-            new_multi=[NewSubItem(new_sub_name=s.name) for s in data.multi],
-        )
-
-    @app.post("/v1-to-v2/item-filter", response_model=Union[NewItem, None])
-    def handle_v1_item_to_v2_filter(data: Item) -> Any:
-        if data.size < 0:
-            return None
-        result = {
-            "new_title": data.title,
-            "new_size": data.size,
-            "new_description": data.description,
-            "new_sub": {
-                "new_sub_name": data.sub.name,
-                "new_sub_secret": "sub_hidden",
-            },
-            "new_multi": [
-                {"new_sub_name": s.name, "new_sub_secret": "sub_hidden"}
-                for s in data.multi
-            ],
-            "secret": "hidden_v1_to_v2",
-        }
-        return result
-
-    @app.post("/v2-to-v1/item")
-    def handle_v2_item_to_v1(data: NewItem) -> Union[Item, None]:
-        if data.new_size < 0:
-            return None
-        return Item(
-            title=data.new_title,
-            size=data.new_size,
-            description=data.new_description,
-            sub=SubItem(name=data.new_sub.new_sub_name),
-            multi=[SubItem(name=s.new_sub_name) for s in data.new_multi],
-        )
-
-    @app.post("/v2-to-v1/item-filter", response_model=Union[Item, None])
-    def handle_v2_item_to_v1_filter(data: NewItem) -> Any:
-        if data.new_size < 0:
-            return None
-        result = {
-            "title": data.new_title,
-            "size": data.new_size,
-            "description": data.new_description,
-            "sub": {"name": data.new_sub.new_sub_name, "sub_secret": "sub_hidden"},
-            "multi": [
-                {"name": s.new_sub_name, "sub_secret": "sub_hidden"}
-                for s in data.new_multi
-            ],
-            "secret": "hidden_v2_to_v1",
-        }
-        return result
-
-
-client = TestClient(app)
-
-
-def test_v1_to_v2_item_success():
-    response = client.post(
-        "/v1-to-v2/",
-        json={
-            "title": "Old Item",
-            "size": 100,
-            "description": "V1 description",
-            "sub": {"name": "V1 Sub"},
-            "multi": [{"name": "M1"}, {"name": "M2"}],
-        },
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "new_title": "Old Item",
-        "new_size": 100,
-        "new_description": "V1 description",
-        "new_sub": {"new_sub_name": "V1 Sub"},
-        "new_multi": [{"new_sub_name": "M1"}, {"new_sub_name": "M2"}],
-    }
-
-
-def test_v1_to_v2_item_returns_none():
-    response = client.post(
-        "/v1-to-v2/",
-        json={"title": "Invalid Item", "size": -10, "sub": {"name": "Sub"}},
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() is None
-
-
-def test_v1_to_v2_item_minimal():
-    response = client.post(
-        "/v1-to-v2/", json={"title": "Minimal", "size": 50, "sub": {"name": "MinSub"}}
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "new_title": "Minimal",
-        "new_size": 50,
-        "new_description": None,
-        "new_sub": {"new_sub_name": "MinSub"},
-        "new_multi": [],
-    }
-
-
-def test_v1_to_v2_item_filter_success():
-    response = client.post(
-        "/v1-to-v2/item-filter",
-        json={
-            "title": "Filtered Item",
-            "size": 50,
-            "sub": {"name": "Sub"},
-            "multi": [{"name": "Multi1"}],
-        },
-    )
-    assert response.status_code == 200, response.text
-    result = response.json()
-    assert result["new_title"] == "Filtered Item"
-    assert result["new_size"] == 50
-    assert result["new_sub"]["new_sub_name"] == "Sub"
-    assert result["new_multi"][0]["new_sub_name"] == "Multi1"
-    # Verify secret fields are filtered out
-    assert "secret" not in result
-    assert "new_sub_secret" not in result["new_sub"]
-    assert "new_sub_secret" not in result["new_multi"][0]
-
-
-def test_v1_to_v2_item_filter_returns_none():
-    response = client.post(
-        "/v1-to-v2/item-filter",
-        json={"title": "Invalid", "size": -1, "sub": {"name": "Sub"}},
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() is None
-
-
-def test_v2_to_v1_item_success():
-    response = client.post(
-        "/v2-to-v1/item",
-        json={
-            "new_title": "New Item",
-            "new_size": 200,
-            "new_description": "V2 description",
-            "new_sub": {"new_sub_name": "V2 Sub"},
-            "new_multi": [{"new_sub_name": "N1"}, {"new_sub_name": "N2"}],
-        },
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "title": "New Item",
-        "size": 200,
-        "description": "V2 description",
-        "sub": {"name": "V2 Sub"},
-        "multi": [{"name": "N1"}, {"name": "N2"}],
-    }
-
-
-def test_v2_to_v1_item_returns_none():
-    response = client.post(
-        "/v2-to-v1/item",
-        json={
-            "new_title": "Invalid New",
-            "new_size": -5,
-            "new_sub": {"new_sub_name": "NewSub"},
-        },
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() is None
-
-
-def test_v2_to_v1_item_minimal():
-    response = client.post(
-        "/v2-to-v1/item",
-        json={
-            "new_title": "MinimalNew",
-            "new_size": 75,
-            "new_sub": {"new_sub_name": "MinNewSub"},
-        },
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "title": "MinimalNew",
-        "size": 75,
-        "description": None,
-        "sub": {"name": "MinNewSub"},
-        "multi": [],
-    }
-
-
-def test_v2_to_v1_item_filter_success():
-    response = client.post(
-        "/v2-to-v1/item-filter",
-        json={
-            "new_title": "Filtered New",
-            "new_size": 75,
-            "new_sub": {"new_sub_name": "NewSub"},
-            "new_multi": [],
-        },
-    )
-    assert response.status_code == 200, response.text
-    result = response.json()
-    assert result["title"] == "Filtered New"
-    assert result["size"] == 75
-    assert result["sub"]["name"] == "NewSub"
-    # Verify secret fields are filtered out
-    assert "secret" not in result
-    assert "sub_secret" not in result["sub"]
-
-
-def test_v2_to_v1_item_filter_returns_none():
-    response = client.post(
-        "/v2-to-v1/item-filter",
-        json={
-            "new_title": "Invalid Filtered",
-            "new_size": -100,
-            "new_sub": {"new_sub_name": "Sub"},
-        },
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() is None
-
-
-def test_v1_to_v2_validation_error():
-    response = client.post("/v1-to-v2/", json={"title": "Missing fields"})
-    assert response.status_code == 422, response.text
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "loc": ["body", "size"],
-                    "msg": "field required",
-                    "type": "value_error.missing",
-                },
-                {
-                    "loc": ["body", "sub"],
-                    "msg": "field required",
-                    "type": "value_error.missing",
-                },
-            ]
-        }
-    )
-
-
-def test_v1_to_v2_nested_validation_error():
-    response = client.post(
-        "/v1-to-v2/",
-        json={"title": "Bad sub", "size": 100, "sub": {"wrong_field": "value"}},
-    )
-    assert response.status_code == 422, response.text
-    error_detail = response.json()["detail"]
-    assert len(error_detail) == 1
-    assert error_detail[0]["loc"] == ["body", "sub", "name"]
-
-
-def test_v1_to_v2_type_validation_error():
-    response = client.post(
-        "/v1-to-v2/",
-        json={"title": "Bad type", "size": "not_a_number", "sub": {"name": "Sub"}},
-    )
-    assert response.status_code == 422, response.text
-    error_detail = response.json()["detail"]
-    assert len(error_detail) == 1
-    assert error_detail[0]["loc"] == ["body", "size"]
-
-
-def test_v2_to_v1_validation_error():
-    response = client.post("/v2-to-v1/item", json={"new_title": "Missing fields"})
-    assert response.status_code == 422, response.text
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "type": "missing",
-                    "loc": ["body", "new_size"],
-                    "msg": "Field required",
-                    "input": {"new_title": "Missing fields"},
-                },
-                {
-                    "type": "missing",
-                    "loc": ["body", "new_sub"],
-                    "msg": "Field required",
-                    "input": {"new_title": "Missing fields"},
-                },
-            ]
-        }
-    )
-
-
-def test_v2_to_v1_nested_validation_error():
-    response = client.post(
-        "/v2-to-v1/item",
-        json={
-            "new_title": "Bad sub",
-            "new_size": 200,
-            "new_sub": {"wrong_field": "value"},
-        },
-    )
-    assert response.status_code == 422, response.text
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "type": "missing",
-                    "loc": ["body", "new_sub", "new_sub_name"],
-                    "msg": "Field required",
-                    "input": {"wrong_field": "value"},
-                }
-            ]
-        }
-    )
-
-
-def test_v2_to_v1_type_validation_error():
-    response = client.post(
-        "/v2-to-v1/item",
-        json={
-            "new_title": "Bad type",
-            "new_size": "not_a_number",
-            "new_sub": {"new_sub_name": "Sub"},
-        },
-    )
-    assert response.status_code == 422, response.text
-    assert response.json() == snapshot(
-        {
-            "detail": [
-                {
-                    "type": "int_parsing",
-                    "loc": ["body", "new_size"],
-                    "msg": "Input should be a valid integer, unable to parse string as an integer",
-                    "input": "not_a_number",
-                }
-            ]
-        }
-    )
-
-
-def test_v1_to_v2_with_multi_items():
-    response = client.post(
-        "/v1-to-v2/",
-        json={
-            "title": "Complex Item",
-            "size": 300,
-            "description": "Item with multiple sub-items",
-            "sub": {"name": "Main Sub"},
-            "multi": [{"name": "Sub1"}, {"name": "Sub2"}, {"name": "Sub3"}],
-        },
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == snapshot(
-        {
-            "new_title": "Complex Item",
-            "new_size": 300,
-            "new_description": "Item with multiple sub-items",
-            "new_sub": {"new_sub_name": "Main Sub"},
-            "new_multi": [
-                {"new_sub_name": "Sub1"},
-                {"new_sub_name": "Sub2"},
-                {"new_sub_name": "Sub3"},
-            ],
-        }
-    )
-
-
-def test_v2_to_v1_with_multi_items():
-    response = client.post(
-        "/v2-to-v1/item",
-        json={
-            "new_title": "Complex New Item",
-            "new_size": 400,
-            "new_description": "New item with multiple sub-items",
-            "new_sub": {"new_sub_name": "Main New Sub"},
-            "new_multi": [{"new_sub_name": "NewSub1"}, {"new_sub_name": "NewSub2"}],
-        },
-    )
-    assert response.status_code == 200, response.text
-    assert response.json() == snapshot(
-        {
-            "title": "Complex New Item",
-            "size": 400,
-            "description": "New item with multiple sub-items",
-            "sub": {"name": "Main New Sub"},
-            "multi": [{"name": "NewSub1"}, {"name": "NewSub2"}],
-        }
-    )
-
-
-def test_openapi_schema():
-    response = client.get("/openapi.json")
-    assert response.status_code == 200, response.text
-    assert response.json() == snapshot(
-        {
-            "openapi": "3.1.0",
-            "info": {"title": "FastAPI", "version": "0.1.0"},
-            "paths": {
-                "/v1-to-v2/": {
-                    "post": {
-                        "summary": "Handle V1 Item To V2",
-                        "operationId": "handle_v1_item_to_v2_v1_to_v2__post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "allOf": [
-                                            {"$ref": "#/components/schemas/Item"}
-                                        ],
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "anyOf": [
-                                                {
-                                                    "$ref": "#/components/schemas/NewItem"
-                                                },
-                                                {"type": "null"},
-                                            ],
-                                            "title": "Response Handle V1 Item To V2 V1 To V2  Post",
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v1-to-v2/item-filter": {
-                    "post": {
-                        "summary": "Handle V1 Item To V2 Filter",
-                        "operationId": "handle_v1_item_to_v2_filter_v1_to_v2_item_filter_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "allOf": [
-                                            {"$ref": "#/components/schemas/Item"}
-                                        ],
-                                        "title": "Data",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "anyOf": [
-                                                {
-                                                    "$ref": "#/components/schemas/NewItem"
-                                                },
-                                                {"type": "null"},
-                                            ],
-                                            "title": "Response Handle V1 Item To V2 Filter V1 To V2 Item Filter Post",
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v2-to-v1/item": {
-                    "post": {
-                        "summary": "Handle V2 Item To V1",
-                        "operationId": "handle_v2_item_to_v1_v2_to_v1_item_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {"$ref": "#/components/schemas/NewItem"}
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {"$ref": "#/components/schemas/Item"}
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-                "/v2-to-v1/item-filter": {
-                    "post": {
-                        "summary": "Handle V2 Item To V1 Filter",
-                        "operationId": "handle_v2_item_to_v1_filter_v2_to_v1_item_filter_post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {"$ref": "#/components/schemas/NewItem"}
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {"$ref": "#/components/schemas/Item"}
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                },
-            },
-            "components": {
-                "schemas": {
-                    "HTTPValidationError": {
-                        "properties": {
-                            "detail": {
-                                "items": {
-                                    "$ref": "#/components/schemas/ValidationError"
-                                },
-                                "type": "array",
-                                "title": "Detail",
-                            }
-                        },
-                        "type": "object",
-                        "title": "HTTPValidationError",
-                    },
-                    "Item": {
-                        "properties": {
-                            "title": {"type": "string", "title": "Title"},
-                            "size": {"type": "integer", "title": "Size"},
-                            "description": {"type": "string", "title": "Description"},
-                            "sub": {"$ref": "#/components/schemas/SubItem"},
-                            "multi": {
-                                "items": {"$ref": "#/components/schemas/SubItem"},
-                                "type": "array",
-                                "title": "Multi",
-                                "default": [],
-                            },
-                        },
-                        "type": "object",
-                        "required": ["title", "size", "sub"],
-                        "title": "Item",
-                    },
-                    "NewItem": {
-                        "properties": {
-                            "new_title": {"type": "string", "title": "New Title"},
-                            "new_size": {"type": "integer", "title": "New Size"},
-                            "new_description": {
-                                "anyOf": [{"type": "string"}, {"type": "null"}],
-                                "title": "New Description",
-                            },
-                            "new_sub": {"$ref": "#/components/schemas/NewSubItem"},
-                            "new_multi": {
-                                "items": {"$ref": "#/components/schemas/NewSubItem"},
-                                "type": "array",
-                                "title": "New Multi",
-                                "default": [],
-                            },
-                        },
-                        "type": "object",
-                        "required": ["new_title", "new_size", "new_sub"],
-                        "title": "NewItem",
-                    },
-                    "NewSubItem": {
-                        "properties": {
-                            "new_sub_name": {"type": "string", "title": "New Sub Name"}
-                        },
-                        "type": "object",
-                        "required": ["new_sub_name"],
-                        "title": "NewSubItem",
-                    },
-                    "SubItem": {
-                        "properties": {"name": {"type": "string", "title": "Name"}},
-                        "type": "object",
-                        "required": ["name"],
-                        "title": "SubItem",
-                    },
-                    "ValidationError": {
-                        "properties": {
-                            "loc": {
-                                "items": {
-                                    "anyOf": [{"type": "string"}, {"type": "integer"}]
-                                },
-                                "type": "array",
-                                "title": "Location",
-                            },
-                            "msg": {"type": "string", "title": "Message"},
-                            "type": {"type": "string", "title": "Error Type"},
-                        },
-                        "type": "object",
-                        "required": ["loc", "msg", "type"],
-                        "title": "ValidationError",
-                    },
-                }
-            },
-        }
-    )
index a195634b8aebd32fcb9f84844ecb2d3a2229823e..cd7389252afe51026f125f23575da8a808f97b1c 100644 (file)
@@ -1,12 +1,9 @@
-import warnings
 from typing import Any
 
 from fastapi import FastAPI
 from fastapi.testclient import TestClient
 from pydantic import BaseModel, ConfigDict
 
-from .utils import needs_pydanticv1
-
 
 def test_read_with_orm_mode() -> None:
     class PersonBase(BaseModel):
@@ -44,50 +41,3 @@ def test_read_with_orm_mode() -> None:
     assert data["name"] == person_data["name"]
     assert data["lastname"] == person_data["lastname"]
     assert data["full_name"] == person_data["name"] + " " + person_data["lastname"]
-
-
-@needs_pydanticv1
-def test_read_with_orm_mode_pv1() -> None:
-    from pydantic import v1
-
-    class PersonBase(v1.BaseModel):
-        name: str
-        lastname: str
-
-    class Person(PersonBase):
-        @property
-        def full_name(self) -> str:
-            return f"{self.name} {self.lastname}"
-
-        class Config:
-            orm_mode = True
-            read_with_orm_mode = True
-
-    class PersonCreate(PersonBase):
-        pass
-
-    class PersonRead(PersonBase):
-        full_name: str
-
-        class Config:
-            orm_mode = True
-
-    app = FastAPI()
-
-    with warnings.catch_warnings(record=True):
-        warnings.simplefilter("always")
-
-        @app.post("/people/", response_model=PersonRead)
-        def create_person(person: PersonCreate) -> Any:
-            db_person = Person.from_orm(person)
-            return db_person
-
-    client = TestClient(app)
-
-    person_data = {"name": "Dive", "lastname": "Wilson"}
-    response = client.post("/people/", json=person_data)
-    data = response.json()
-    assert response.status_code == 200, response.text
-    assert data["name"] == person_data["name"]
-    assert data["lastname"] == person_data["lastname"]
-    assert data["full_name"] == person_data["name"] + " " + person_data["lastname"]
index 9e527d6a01010b79b1fb16940d8e1a1ce0c1c836..58fba89f1ab1723c70f432cc6497f645180a1dcd 100644 (file)
@@ -1,4 +1,3 @@
-import warnings
 from typing import Union
 
 import pytest
@@ -8,8 +7,6 @@ from fastapi.responses import JSONResponse, Response
 from fastapi.testclient import TestClient
 from pydantic import BaseModel
 
-from tests.utils import needs_pydanticv1
-
 
 class BaseUser(BaseModel):
     name: str
@@ -512,29 +509,6 @@ def test_invalid_response_model_field():
     assert "parameter response_model=None" in e.value.args[0]
 
 
-# TODO: remove when dropping Pydantic v1 support
-@needs_pydanticv1
-def test_invalid_response_model_field_pv1():
-    from fastapi._compat import v1
-
-    app = FastAPI()
-
-    class Model(v1.BaseModel):
-        foo: str
-
-    with warnings.catch_warnings(record=True):
-        warnings.simplefilter("always")
-
-        with pytest.raises(FastAPIError) as e:
-
-            @app.get("/")
-            def read_root() -> Union[Response, Model, None]:
-                return Response(content="Foo")  # pragma: no cover
-
-    assert "valid Pydantic field type" in e.value.args[0]
-    assert "parameter response_model=None" in e.value.args[0]
-
-
 def test_openapi_schema():
     response = client.get("/openapi.json")
     assert response.status_code == 200, response.text
diff --git a/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial007_pv1.py b/tests/test_tutorial/test_path_operation_advanced_configurations/test_tutorial007_pv1.py
deleted file mode 100644 (file)
index 62b67a9..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-import importlib
-
-import pytest
-from fastapi.testclient import TestClient
-
-from ...utils import needs_pydanticv1
-
-
-@pytest.fixture(
-    name="client",
-    params=[
-        pytest.param("tutorial007_pv1_py39"),
-    ],
-)
-def get_client(request: pytest.FixtureRequest):
-    mod = importlib.import_module(
-        f"docs_src.path_operation_advanced_configuration.{request.param}"
-    )
-
-    client = TestClient(mod.app)
-    return client
-
-
-@needs_pydanticv1
-def test_post(client: TestClient):
-    yaml_data = """
-        name: Deadpoolio
-        tags:
-        - x-force
-        - x-men
-        - x-avengers
-        """
-    response = client.post("/items/", content=yaml_data)
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "name": "Deadpoolio",
-        "tags": ["x-force", "x-men", "x-avengers"],
-    }
-
-
-@needs_pydanticv1
-def test_post_broken_yaml(client: TestClient):
-    yaml_data = """
-        name: Deadpoolio
-        tags:
-        x - x-force
-        x - x-men
-        x - x-avengers
-        """
-    response = client.post("/items/", content=yaml_data)
-    assert response.status_code == 422, response.text
-    assert response.json() == {"detail": "Invalid YAML"}
-
-
-@needs_pydanticv1
-def test_post_invalid(client: TestClient):
-    yaml_data = """
-        name: Deadpoolio
-        tags:
-        - x-force
-        - x-men
-        - x-avengers
-        - sneaky: object
-        """
-    response = client.post("/items/", content=yaml_data)
-    assert response.status_code == 422, response.text
-    assert response.json() == {
-        "detail": [
-            {"loc": ["tags", 3], "msg": "str type expected", "type": "type_error.str"}
-        ]
-    }
-
-
-@needs_pydanticv1
-def test_openapi_schema(client: TestClient):
-    response = client.get("/openapi.json")
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "openapi": "3.1.0",
-        "info": {"title": "FastAPI", "version": "0.1.0"},
-        "paths": {
-            "/items/": {
-                "post": {
-                    "summary": "Create Item",
-                    "operationId": "create_item_items__post",
-                    "requestBody": {
-                        "content": {
-                            "application/x-yaml": {
-                                "schema": {
-                                    "title": "Item",
-                                    "required": ["name", "tags"],
-                                    "type": "object",
-                                    "properties": {
-                                        "name": {"title": "Name", "type": "string"},
-                                        "tags": {
-                                            "title": "Tags",
-                                            "type": "array",
-                                            "items": {"type": "string"},
-                                        },
-                                    },
-                                }
-                            }
-                        },
-                        "required": True,
-                    },
-                    "responses": {
-                        "200": {
-                            "description": "Successful Response",
-                            "content": {"application/json": {"schema": {}}},
-                        }
-                    },
-                }
-            }
-        },
-    }
diff --git a/tests/test_tutorial/test_pydantic_v1_in_v2/__init__.py b/tests/test_tutorial/test_pydantic_v1_in_v2/__init__.py
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial001.py b/tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial001.py
deleted file mode 100644 (file)
index 4090eba..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-import sys
-from typing import Any
-
-import pytest
-
-from tests.utils import skip_module_if_py_gte_314
-
-if sys.version_info >= (3, 14):
-    skip_module_if_py_gte_314()
-
-
-import importlib
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(
-    name="mod",
-    params=[
-        "tutorial001_an_py39",
-        pytest.param("tutorial001_an_py310", marks=needs_py310),
-    ],
-)
-def get_mod(request: pytest.FixtureRequest):
-    mod = importlib.import_module(f"docs_src.pydantic_v1_in_v2.{request.param}")
-    return mod
-
-
-def test_model(mod: Any):
-    item = mod.Item(name="Foo", size=3.4)
-    assert item.dict() == {"name": "Foo", "description": None, "size": 3.4}
diff --git a/tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial002.py b/tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial002.py
deleted file mode 100644 (file)
index 9d1baf8..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-import sys
-import warnings
-
-import pytest
-from fastapi.exceptions import FastAPIDeprecationWarning
-from inline_snapshot import snapshot
-
-from tests.utils import skip_module_if_py_gte_314
-
-if sys.version_info >= (3, 14):
-    skip_module_if_py_gte_314()
-
-
-import importlib
-
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(
-    name="client",
-    params=[
-        "tutorial002_an_py39",
-        pytest.param("tutorial002_an_py310", marks=needs_py310),
-    ],
-)
-def get_client(request: pytest.FixtureRequest):
-    with warnings.catch_warnings(record=True):
-        warnings.filterwarnings(
-            "ignore",
-            message=r"pydantic\.v1 is deprecated and will soon stop being supported by FastAPI\..*",
-            category=FastAPIDeprecationWarning,
-        )
-        mod = importlib.import_module(f"docs_src.pydantic_v1_in_v2.{request.param}")
-
-    c = TestClient(mod.app)
-    return c
-
-
-def test_call(client: TestClient):
-    response = client.post("/items/", json={"name": "Foo", "size": 3.4})
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "name": "Foo",
-        "description": None,
-        "size": 3.4,
-    }
-
-
-def test_openapi_schema(client: TestClient):
-    response = client.get("/openapi.json")
-    assert response.status_code == 200, response.text
-    assert response.json() == snapshot(
-        {
-            "openapi": "3.1.0",
-            "info": {"title": "FastAPI", "version": "0.1.0"},
-            "paths": {
-                "/items/": {
-                    "post": {
-                        "summary": "Create Item",
-                        "operationId": "create_item_items__post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "allOf": [
-                                            {"$ref": "#/components/schemas/Item"}
-                                        ],
-                                        "title": "Item",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {"$ref": "#/components/schemas/Item"}
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                }
-            },
-            "components": {
-                "schemas": {
-                    "HTTPValidationError": {
-                        "properties": {
-                            "detail": {
-                                "items": {
-                                    "$ref": "#/components/schemas/ValidationError"
-                                },
-                                "type": "array",
-                                "title": "Detail",
-                            }
-                        },
-                        "type": "object",
-                        "title": "HTTPValidationError",
-                    },
-                    "Item": {
-                        "properties": {
-                            "name": {"type": "string", "title": "Name"},
-                            "description": {"type": "string", "title": "Description"},
-                            "size": {"type": "number", "title": "Size"},
-                        },
-                        "type": "object",
-                        "required": ["name", "size"],
-                        "title": "Item",
-                    },
-                    "ValidationError": {
-                        "properties": {
-                            "loc": {
-                                "items": {
-                                    "anyOf": [{"type": "string"}, {"type": "integer"}]
-                                },
-                                "type": "array",
-                                "title": "Location",
-                            },
-                            "msg": {"type": "string", "title": "Message"},
-                            "type": {"type": "string", "title": "Error Type"},
-                        },
-                        "type": "object",
-                        "required": ["loc", "msg", "type"],
-                        "title": "ValidationError",
-                    },
-                }
-            },
-        }
-    )
diff --git a/tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial003.py b/tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial003.py
deleted file mode 100644 (file)
index 23b2368..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-import sys
-import warnings
-
-import pytest
-from fastapi.exceptions import FastAPIDeprecationWarning
-from inline_snapshot import snapshot
-
-from tests.utils import skip_module_if_py_gte_314
-
-if sys.version_info >= (3, 14):
-    skip_module_if_py_gte_314()
-
-
-import importlib
-
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(
-    name="client",
-    params=[
-        "tutorial003_an_py39",
-        pytest.param("tutorial003_an_py310", marks=needs_py310),
-    ],
-)
-def get_client(request: pytest.FixtureRequest):
-    with warnings.catch_warnings(record=True):
-        warnings.filterwarnings(
-            "ignore",
-            message=r"pydantic\.v1 is deprecated and will soon stop being supported by FastAPI\..*",
-            category=FastAPIDeprecationWarning,
-        )
-        mod = importlib.import_module(f"docs_src.pydantic_v1_in_v2.{request.param}")
-
-    c = TestClient(mod.app)
-    return c
-
-
-def test_call(client: TestClient):
-    response = client.post("/items/", json={"name": "Foo", "size": 3.4})
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "name": "Foo",
-        "description": None,
-        "size": 3.4,
-    }
-
-
-def test_openapi_schema(client: TestClient):
-    response = client.get("/openapi.json")
-    assert response.status_code == 200, response.text
-    assert response.json() == snapshot(
-        {
-            "openapi": "3.1.0",
-            "info": {"title": "FastAPI", "version": "0.1.0"},
-            "paths": {
-                "/items/": {
-                    "post": {
-                        "summary": "Create Item",
-                        "operationId": "create_item_items__post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "allOf": [
-                                            {"$ref": "#/components/schemas/Item"}
-                                        ],
-                                        "title": "Item",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/ItemV2"
-                                        }
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                }
-            },
-            "components": {
-                "schemas": {
-                    "HTTPValidationError": {
-                        "properties": {
-                            "detail": {
-                                "items": {
-                                    "$ref": "#/components/schemas/ValidationError"
-                                },
-                                "type": "array",
-                                "title": "Detail",
-                            }
-                        },
-                        "type": "object",
-                        "title": "HTTPValidationError",
-                    },
-                    "Item": {
-                        "properties": {
-                            "name": {"type": "string", "title": "Name"},
-                            "description": {"type": "string", "title": "Description"},
-                            "size": {"type": "number", "title": "Size"},
-                        },
-                        "type": "object",
-                        "required": ["name", "size"],
-                        "title": "Item",
-                    },
-                    "ItemV2": {
-                        "properties": {
-                            "name": {"type": "string", "title": "Name"},
-                            "description": {
-                                "anyOf": [{"type": "string"}, {"type": "null"}],
-                                "title": "Description",
-                            },
-                            "size": {"type": "number", "title": "Size"},
-                        },
-                        "type": "object",
-                        "required": ["name", "size"],
-                        "title": "ItemV2",
-                    },
-                    "ValidationError": {
-                        "properties": {
-                            "loc": {
-                                "items": {
-                                    "anyOf": [{"type": "string"}, {"type": "integer"}]
-                                },
-                                "type": "array",
-                                "title": "Location",
-                            },
-                            "msg": {"type": "string", "title": "Message"},
-                            "type": {"type": "string", "title": "Error Type"},
-                        },
-                        "type": "object",
-                        "required": ["loc", "msg", "type"],
-                        "title": "ValidationError",
-                    },
-                }
-            },
-        }
-    )
diff --git a/tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial004.py b/tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial004.py
deleted file mode 100644 (file)
index 61c0f63..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-import sys
-import warnings
-
-import pytest
-from fastapi.exceptions import FastAPIDeprecationWarning
-from inline_snapshot import snapshot
-
-from tests.utils import skip_module_if_py_gte_314
-
-if sys.version_info >= (3, 14):
-    skip_module_if_py_gte_314()
-
-
-import importlib
-
-from fastapi.testclient import TestClient
-
-from ...utils import needs_py310
-
-
-@pytest.fixture(
-    name="client",
-    params=[
-        pytest.param("tutorial004_an_py39"),
-        pytest.param("tutorial004_an_py310", marks=needs_py310),
-    ],
-)
-def get_client(request: pytest.FixtureRequest):
-    with warnings.catch_warnings(record=True):
-        warnings.filterwarnings(
-            "ignore",
-            message=r"pydantic\.v1 is deprecated and will soon stop being supported by FastAPI\..*",
-            category=FastAPIDeprecationWarning,
-        )
-        mod = importlib.import_module(f"docs_src.pydantic_v1_in_v2.{request.param}")
-
-    c = TestClient(mod.app)
-    return c
-
-
-def test_call(client: TestClient):
-    response = client.post("/items/", json={"item": {"name": "Foo", "size": 3.4}})
-    assert response.status_code == 200, response.text
-    assert response.json() == {
-        "name": "Foo",
-        "description": None,
-        "size": 3.4,
-    }
-
-
-def test_openapi_schema(client: TestClient):
-    response = client.get("/openapi.json")
-    assert response.status_code == 200, response.text
-    assert response.json() == snapshot(
-        {
-            "openapi": "3.1.0",
-            "info": {"title": "FastAPI", "version": "0.1.0"},
-            "paths": {
-                "/items/": {
-                    "post": {
-                        "summary": "Create Item",
-                        "operationId": "create_item_items__post",
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "allOf": [
-                                            {
-                                                "$ref": "#/components/schemas/Body_create_item_items__post"
-                                            }
-                                        ],
-                                        "title": "Body",
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {"$ref": "#/components/schemas/Item"}
-                                    }
-                                },
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                }
-            },
-            "components": {
-                "schemas": {
-                    "Body_create_item_items__post": {
-                        "properties": {
-                            "item": {
-                                "allOf": [{"$ref": "#/components/schemas/Item"}],
-                                "title": "Item",
-                            }
-                        },
-                        "type": "object",
-                        "required": ["item"],
-                        "title": "Body_create_item_items__post",
-                    },
-                    "HTTPValidationError": {
-                        "properties": {
-                            "detail": {
-                                "items": {
-                                    "$ref": "#/components/schemas/ValidationError"
-                                },
-                                "type": "array",
-                                "title": "Detail",
-                            }
-                        },
-                        "type": "object",
-                        "title": "HTTPValidationError",
-                    },
-                    "Item": {
-                        "properties": {
-                            "name": {"type": "string", "title": "Name"},
-                            "description": {"type": "string", "title": "Description"},
-                            "size": {"type": "number", "title": "Size"},
-                        },
-                        "type": "object",
-                        "required": ["name", "size"],
-                        "title": "Item",
-                    },
-                    "ValidationError": {
-                        "properties": {
-                            "loc": {
-                                "items": {
-                                    "anyOf": [{"type": "string"}, {"type": "integer"}]
-                                },
-                                "type": "array",
-                                "title": "Location",
-                            },
-                            "msg": {"type": "string", "title": "Message"},
-                            "type": {"type": "string", "title": "Error Type"},
-                        },
-                        "type": "object",
-                        "required": ["loc", "msg", "type"],
-                        "title": "ValidationError",
-                    },
-                }
-            },
-        }
-    )
diff --git a/tests/test_tutorial/test_request_form_models/test_tutorial002_pv1.py b/tests/test_tutorial/test_request_form_models/test_tutorial002_pv1.py
deleted file mode 100644 (file)
index 50be458..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-import importlib
-import warnings
-
-import pytest
-from fastapi.exceptions import FastAPIDeprecationWarning
-from fastapi.testclient import TestClient
-
-from ...utils import needs_pydanticv1
-
-
-@pytest.fixture(
-    name="client",
-    params=[
-        "tutorial002_pv1_py39",
-        "tutorial002_pv1_an_py39",
-    ],
-)
-def get_client(request: pytest.FixtureRequest):
-    with warnings.catch_warnings(record=True):
-        warnings.filterwarnings(
-            "ignore",
-            message=r"pydantic\.v1 is deprecated and will soon stop being supported by FastAPI\..*",
-            category=FastAPIDeprecationWarning,
-        )
-        mod = importlib.import_module(f"docs_src.request_form_models.{request.param}")
-
-    client = TestClient(mod.app)
-    return client
-
-
-# TODO: remove when deprecating Pydantic v1
-@needs_pydanticv1
-def test_post_body_form(client: TestClient):
-    response = client.post("/login/", data={"username": "Foo", "password": "secret"})
-    assert response.status_code == 200
-    assert response.json() == {"username": "Foo", "password": "secret"}
-
-
-# TODO: remove when deprecating Pydantic v1
-@needs_pydanticv1
-def test_post_body_extra_form(client: TestClient):
-    response = client.post(
-        "/login/", data={"username": "Foo", "password": "secret", "extra": "extra"}
-    )
-    assert response.status_code == 422
-    assert response.json() == {
-        "detail": [
-            {
-                "type": "value_error.extra",
-                "loc": ["body", "extra"],
-                "msg": "extra fields not permitted",
-            }
-        ]
-    }
-
-
-# TODO: remove when deprecating Pydantic v1
-@needs_pydanticv1
-def test_post_body_form_no_password(client: TestClient):
-    response = client.post("/login/", data={"username": "Foo"})
-    assert response.status_code == 422
-    assert response.json() == {
-        "detail": [
-            {
-                "type": "value_error.missing",
-                "loc": ["body", "password"],
-                "msg": "field required",
-            }
-        ]
-    }
-
-
-# TODO: remove when deprecating Pydantic v1
-@needs_pydanticv1
-def test_post_body_form_no_username(client: TestClient):
-    response = client.post("/login/", data={"password": "secret"})
-    assert response.status_code == 422
-    assert response.json() == {
-        "detail": [
-            {
-                "type": "value_error.missing",
-                "loc": ["body", "username"],
-                "msg": "field required",
-            }
-        ]
-    }
-
-
-# TODO: remove when deprecating Pydantic v1
-@needs_pydanticv1
-def test_post_body_form_no_data(client: TestClient):
-    response = client.post("/login/")
-    assert response.status_code == 422
-    assert response.json() == {
-        "detail": [
-            {
-                "type": "value_error.missing",
-                "loc": ["body", "username"],
-                "msg": "field required",
-            },
-            {
-                "type": "value_error.missing",
-                "loc": ["body", "password"],
-                "msg": "field required",
-            },
-        ]
-    }
-
-
-# TODO: remove when deprecating Pydantic v1
-@needs_pydanticv1
-def test_post_body_json(client: TestClient):
-    response = client.post("/login/", json={"username": "Foo", "password": "secret"})
-    assert response.status_code == 422, response.text
-    assert response.json() == {
-        "detail": [
-            {
-                "type": "value_error.missing",
-                "loc": ["body", "username"],
-                "msg": "field required",
-            },
-            {
-                "type": "value_error.missing",
-                "loc": ["body", "password"],
-                "msg": "field required",
-            },
-        ]
-    }
diff --git a/tests/test_tutorial/test_schema_extra_example/test_tutorial001_pv1.py b/tests/test_tutorial/test_schema_extra_example/test_tutorial001_pv1.py
deleted file mode 100644 (file)
index 83c7176..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-import importlib
-import warnings
-
-import pytest
-from fastapi.exceptions import FastAPIDeprecationWarning
-from fastapi.testclient import TestClient
-from inline_snapshot import snapshot
-
-from ...utils import needs_py310, needs_pydanticv1
-
-
-@pytest.fixture(
-    name="client",
-    params=[
-        pytest.param("tutorial001_pv1_py39"),
-        pytest.param("tutorial001_pv1_py310", marks=needs_py310),
-    ],
-)
-def get_client(request: pytest.FixtureRequest):
-    with warnings.catch_warnings(record=True):
-        warnings.filterwarnings(
-            "ignore",
-            message=r"pydantic\.v1 is deprecated and will soon stop being supported by FastAPI\..*",
-            category=FastAPIDeprecationWarning,
-        )
-        mod = importlib.import_module(f"docs_src.schema_extra_example.{request.param}")
-
-    client = TestClient(mod.app)
-    return client
-
-
-@needs_pydanticv1
-def test_post_body_example(client: TestClient):
-    response = client.put(
-        "/items/5",
-        json={
-            "name": "Foo",
-            "description": "A very nice Item",
-            "price": 35.4,
-            "tax": 3.2,
-        },
-    )
-    assert response.status_code == 200
-
-
-@needs_pydanticv1
-def test_openapi_schema(client: TestClient):
-    response = client.get("/openapi.json")
-    assert response.status_code == 200, response.text
-    assert response.json() == snapshot(
-        {
-            "openapi": "3.1.0",
-            "info": {"title": "FastAPI", "version": "0.1.0"},
-            "paths": {
-                "/items/{item_id}": {
-                    "put": {
-                        "summary": "Update Item",
-                        "operationId": "update_item_items__item_id__put",
-                        "parameters": [
-                            {
-                                "required": True,
-                                "schema": {"type": "integer", "title": "Item Id"},
-                                "name": "item_id",
-                                "in": "path",
-                            }
-                        ],
-                        "requestBody": {
-                            "content": {
-                                "application/json": {
-                                    "schema": {
-                                        "title": "Item",
-                                        "allOf": [
-                                            {"$ref": "#/components/schemas/Item"}
-                                        ],
-                                    }
-                                }
-                            },
-                            "required": True,
-                        },
-                        "responses": {
-                            "200": {
-                                "description": "Successful Response",
-                                "content": {"application/json": {"schema": {}}},
-                            },
-                            "422": {
-                                "description": "Validation Error",
-                                "content": {
-                                    "application/json": {
-                                        "schema": {
-                                            "$ref": "#/components/schemas/HTTPValidationError"
-                                        }
-                                    }
-                                },
-                            },
-                        },
-                    }
-                }
-            },
-            "components": {
-                "schemas": {
-                    "HTTPValidationError": {
-                        "properties": {
-                            "detail": {
-                                "items": {
-                                    "$ref": "#/components/schemas/ValidationError"
-                                },
-                                "type": "array",
-                                "title": "Detail",
-                            }
-                        },
-                        "type": "object",
-                        "title": "HTTPValidationError",
-                    },
-                    "Item": {
-                        "properties": {
-                            "name": {"type": "string", "title": "Name"},
-                            "description": {"type": "string", "title": "Description"},
-                            "price": {"type": "number", "title": "Price"},
-                            "tax": {"type": "number", "title": "Tax"},
-                        },
-                        "type": "object",
-                        "required": ["name", "price"],
-                        "title": "Item",
-                        "examples": [
-                            {
-                                "name": "Foo",
-                                "description": "A very nice Item",
-                                "price": 35.4,
-                                "tax": 3.2,
-                            }
-                        ],
-                    },
-                    "ValidationError": {
-                        "properties": {
-                            "loc": {
-                                "items": {
-                                    "anyOf": [{"type": "string"}, {"type": "integer"}]
-                                },
-                                "type": "array",
-                                "title": "Location",
-                            },
-                            "msg": {"type": "string", "title": "Message"},
-                            "type": {"type": "string", "title": "Error Type"},
-                        },
-                        "type": "object",
-                        "required": ["loc", "msg", "type"],
-                        "title": "ValidationError",
-                    },
-                }
-            },
-        }
-    )
index 06e82398d1e3d171f816abd5aba7d9029423e522..72de49796710f9c787c48e230a1d2816933f9a9a 100644 (file)
@@ -5,8 +5,6 @@ import pytest
 from fastapi.testclient import TestClient
 from pytest import MonkeyPatch
 
-from ...utils import needs_pydanticv1
-
 
 @pytest.fixture(
     name="mod_path",
@@ -34,16 +32,6 @@ def test_settings(main_mod: ModuleType, monkeypatch: MonkeyPatch):
     assert settings.items_per_user == 50
 
 
-@needs_pydanticv1
-def test_settings_pv1(mod_path: str, monkeypatch: MonkeyPatch):
-    monkeypatch.setenv("ADMIN_EMAIL", "admin@example.com")
-    config_mod = importlib.import_module(f"{mod_path}.config_pv1")
-    settings = config_mod.Settings()
-    assert settings.app_name == "Awesome API"
-    assert settings.admin_email == "admin@example.com"
-    assert settings.items_per_user == 50
-
-
 def test_endpoint(main_mod: ModuleType, monkeypatch: MonkeyPatch):
     monkeypatch.setenv("ADMIN_EMAIL", "admin@example.com")
     client = TestClient(main_mod.app)
index 6a08096989d03310f956dd63e2db51de7cf17e3f..f4576a0d21b746cc29b38bcd15a06f806e309872 100644 (file)
@@ -4,16 +4,8 @@ import pytest
 from fastapi.testclient import TestClient
 from pytest import MonkeyPatch
 
-from ...utils import needs_pydanticv1
 
-
-@pytest.fixture(
-    name="app",
-    params=[
-        pytest.param("tutorial001_py39"),
-        pytest.param("tutorial001_pv1_py39", marks=needs_pydanticv1),
-    ],
-)
+@pytest.fixture(name="app", params=[pytest.param("tutorial001_py39")])
 def get_app(request: pytest.FixtureRequest, monkeypatch: MonkeyPatch):
     monkeypatch.setenv("ADMIN_EMAIL", "admin@example.com")
     mod = importlib.import_module(f"docs_src.settings.{request.param}")
index b896d4527f57beab7db43e2a327e236ff23eaee9..efa0bfd52b0e70dd4c47e09e919d1b915a97045f 100644 (file)
@@ -10,8 +10,6 @@ needs_py_lt_314 = pytest.mark.skipif(
     sys.version_info >= (3, 14), reason="requires python3.13-"
 )
 
-needs_pydanticv1 = needs_py_lt_314
-
 
 def skip_module_if_py_gte_314():
     """Skip entire module on Python 3.14+ at import time."""