← oracle_full

fastapi_14609

resolved RESOLVED UNSUBMITTED PASS · None tool calls · 0 s · fastapi/fastapi

Task input

➖ Drop support for `pydantic.v1`

➖ Drop support for `pydantic.v1`

When trying to use a `pydantic.v1` model with FastAPI, an error is raised.

Tool calls (0)

#ToolArgumentsResult
No trace captured.

Patch

--- a/docs_src/request_form_models/tutorial002_pv1_an_py39.py
+++ b/docs_src/request_form_models/tutorial002_pv1_an_py39.py
@@ -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
--- a/docs_src/request_form_models/tutorial002_pv1_py39.py
+++ b/docs_src/request_form_models/tutorial002_pv1_py39.py
@@ -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
--- a/fastapi/_compat/__init__.py
+++ b/fastapi/_compat/__init__.py
@@ -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 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,
+)
--- a/fastapi/_compat/main.py
+++ b/fastapi/_compat/main.py
@@ -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]
--- a/fastapi/_compat/may_v1.py
+++ b/fastapi/_compat/may_v1.py
@@ -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
--- a/fastapi/_compat/model_field.py
+++ b/fastapi/_compat/model_field.py
@@ -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: ...
--- a/fastapi/_compat/shared.py
+++ b/fastapi/_compat/shared.py
@@ -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 @@
     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) 

Test output

show
....................................                                     [100%]
=============================== warnings summary ===============================
../../../../../../../Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45
  /Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45: DeprecationWarning: The anyio.abc.BlockingPortal alias is deprecated, use anyio.from_thread.BlockingPortal instead.
    _PortalFactoryType = Callable[[], AbstractContextManager[anyio.abc.BlockingPortal]]

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
36 passed, 1 warning in 0.58s