← pod_r5_v1i

fastapi_14186

failed WRONG_FIX wrong_fix · 30 tool calls · 176 s · fastapi/fastapi

Task input

🐛 Fix internal Pydantic v1 compatibility (warnings) for Python 3.14 and Pydantic 2.12.1

The Python 3.14 tests started failing with the Pydantic v.2.12.1 release, because of the combination of https://github.com/fastapi/fastapi/pull/14168 and https://github.com/pydantic/pydantic/pull/12367.

Basically we were importing Pydantic `v1` functionality even when the user had (only) Pydantic v2 installed. This was done in https://github.com/fastapi/fastapi/pull/14168 to support more easy migration from v1 to v2 by allowing mixing versions within the same app.

Here, we prevent importing any `v1` functionality by creating dummy classes for Python 3.14 / Pydantic 2.12.0 onwards, which [drops support](https://github.com/pydantic/pydantic/releases/tag/v2.12.0) for `v1` and emits a [warning](https://github.com/pydantic/pydantic/pull/12367) when importing `v1` functionality.

### TODO

- [x] fix coverage
- [ ] check warning is still shown to users at some point, and not just swallowed by this code.

Tool calls (31)

#ToolArgumentsResult
1run_command
{
 "command": "( grep -rn 'pydantic.v1' --include=*.py . || echo '<IMPORTANT> NO MATCH FOUND. DO NOT TRY SAME COMMAND.' ) | head -40\n"
}
{"status": "ok", "stdout": "./tests/test_compat_params_v1.py:13:from fastapi.temp_pydantic_v1_params import (\n./tests/test_tutorial/test_pydantic_v1_in_v2/test
full
{"status": "ok", "stdout": "./tests/test_compat_params_v1.py:13:from fastapi.temp_pydantic_v1_params import (\n./tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial002.py:32:    mod = importlib.import_module(f\"docs_src.pydantic_v1_in_v2.{request.param}\")\n./tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial003.py:31:    mod = importlib.import_module(f\"docs_src.pydantic_v1_in_v2.{request.param}\")\n./tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial004.py:32:    mod = importlib.import_module(f\"docs_src.pydantic_v1_in_v2.{request.param}\")\n./tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial001.py:31:    mod = importlib.import_module(f\"docs_src.pydantic_v1_in_v2.{request.param}\")\n./tests/test_pydantic_v1_v2_multifile/test_multifile.py:300:                                                        \"$ref\": \"#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item\"\n./tests/test_pydantic_v1_v2_multifile/test_multifile.py:308:                                                \"$ref\": \"#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item\"\n./tests/test_pydantic_v1_v2_multifile/test_multifile.py:322:                                            \"$ref\": \"#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item\"\n./tests/test_pydantic_v1_v2_multifile/test_multifile.py:350:                                                \"$ref\": \"#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item-Input\"\n./tests/test_pydantic_v1_v2_multifile/test_multifile.py:355:                                                \"$ref\": \"#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv2__Item\"\n./tests/test_pydantic_v1_v2_multifile/test_multifile.py:369:                                            \"$ref\": \"#/components/schemas/tests__test_pydantic_v1_v2_multifile__modelsv1__Item\"\n./tests/test_pydantic_v1_v2_multifile/test_multifile.py:399:                                 
2run_command
{
 "command": "( grep -rn 'pydantic.v1' --include=*.py fastapi || echo '<IMPORTANT> NO MATCH FOUND. DO NOT TRY SAME COMMAND.' ) | head -40\n"
}
{"status": "ok", "stdout": "fastapi/dependencies/utils.py:51:from fastapi._compat.shared import annotation_is_pydantic_v1\nfastapi/dependencies/utils.py:79:from
full
{"status": "ok", "stdout": "fastapi/dependencies/utils.py:51:from fastapi._compat.shared import annotation_is_pydantic_v1\nfastapi/dependencies/utils.py:79:from .. import temp_pydantic_v1_params\nfastapi/dependencies/utils.py:323:            param_details.field.field_info, (params.Body, temp_pydantic_v1_params.Body)\nfastapi/dependencies/utils.py:392:                    temp_pydantic_v1_params.Param,\nfastapi/dependencies/utils.py:394:                    temp_pydantic_v1_params.Body,\nfastapi/dependencies/utils.py:483:            if annotation_is_pydantic_v1(use_annotation):\nfastapi/dependencies/utils.py:484:                field_info = temp_pydantic_v1_params.Body(\nfastapi/dependencies/utils.py:500:                field_info, (params.Path, temp_pydantic_v1_params.Path)\nfastapi/dependencies/utils.py:506:            isinstance(field_info, (params.Param, temp_pydantic_v1_params.Param))\nfastapi/dependencies/utils.py:515:        if isinstance(field_info, (params.Form, temp_pydantic_v1_params.Form)):\nfastapi/dependencies/utils.py:534:        elif isinstance(field_info, (params.Query, temp_pydantic_v1_params.Query)):\nfastapi/dependencies/utils.py:761:            isinstance(field.field_info, (params.Form, temp_pydantic_v1_params.Form))\nfastapi/dependencies/utils.py:827:        assert isinstance(field_info, (params.Param, temp_pydantic_v1_params.Param)), (\nfastapi/dependencies/utils.py:839:        assert isinstance(field_info, (params.Param, temp_pydantic_v1_params.Param)), (\nfastapi/dependencies/utils.py:888:        isinstance(first_field.field_info, (params.Form, temp_pydantic_v1_params.Form))\nfastapi/dependencies/utils.py:906:            isinstance(field_info, (params.File, temp_pydantic_v1_params.File))\nfastapi/dependencies/utils.py:913:            and isinstance(field_info, (params.File, temp_pydantic_v1_params.File))\nfastapi/dependencies/utils.py:1020:        isinstance(f.field_info, temp_pydantic_v1_params.File)\nfastapi/dependencies/utils.py:1023:       
3run_command
{
 "command": "sed -n '60,100p' fastapi/_compat/v1.py\n"
}
{"status": "ok", "stdout": " from pydantic.schema import (\n get_annotation_from_field_info as get_annotation_from_field_info,\n )\n from pydantic.schema import
full
{"status": "ok", "stdout": "    from pydantic.schema import (\n        get_annotation_from_field_info as get_annotation_from_field_info,\n    )\n    from pydantic.schema import get_flat_models_from_field as get_flat_models_from_field\n    from pydantic.schema import get_model_name_map as get_model_name_map\n    from pydantic.types import SecretBytes as SecretBytes\n    from pydantic.types import SecretStr as SecretStr\n    from pydantic.typing import evaluate_forwardref as evaluate_forwardref\n    from pydantic.utils import lenient_issubclass as lenient_issubclass\n\n\nelse:\n    from pydantic.v1 import BaseConfig as BaseConfig  # type: ignore[assignment]\n    from pydantic.v1 import BaseModel as BaseModel  # type: ignore[assignment]\n    from pydantic.v1 import (  # type: ignore[assignment]\n        ValidationError as ValidationError,\n    )\n    from pydantic.v1 import create_model as create_model  # type: ignore[no-redef]\n    from pydantic.v1.class_validators import Validator as Validator\n    from pydantic.v1.color import Color as Color  # type: ignore[assignment]\n    from pydantic.v1.error_wrappers import ErrorWrapper as ErrorWrapper\n    from pydantic.v1.errors import MissingError\n    from pydantic.v1.fields import (\n        SHAPE_FROZENSET,\n        SHAPE_LIST,\n        SHAPE_SEQUENCE,\n        SHAPE_SET,\n        SHAPE_SINGLETON,\n        SHAPE_TUPLE,\n        SHAPE_TUPLE_ELLIPSIS,\n    )\n    from pydantic.v1.fields import FieldInfo as FieldInfo  # type: ignore[assignment]\n    from pydantic.v1.fields import ModelField as ModelField\n    from pydantic.v1.fields import Undefined as Undefined\n    from pydantic.v1.fields import UndefinedType as UndefinedType\n    from pydantic.v1.networks import AnyUrl as AnyUrl\n    from pydantic.v1.networks import (  # type: ignore[assignment]\n        NameEmail as NameEmail,\n    )\n    from pydantic.v1.schema import TypeModelSet as TypeModelSet\n    from pydantic.v1.schema import (\n", "stderr": "", "exit_code": 0}
4run_command
{
 "command": "sed -n '1,70p' fastapi/_compat/v1.py\n"
}
{"status": "ok", "stdout": "from copy import copy\nfrom dataclasses import dataclass, is_dataclass\nfrom enum import Enum\nfrom typing import (\n Any,\n Callabl
full
{"status": "ok", "stdout": "from copy import copy\nfrom dataclasses import dataclass, is_dataclass\nfrom enum import Enum\nfrom typing import (\n    Any,\n    Callable,\n    Dict,\n    List,\n    Sequence,\n    Set,\n    Tuple,\n    Type,\n    Union,\n)\n\nfrom fastapi._compat import shared\nfrom fastapi.openapi.constants import REF_PREFIX as REF_PREFIX\nfrom fastapi.types import ModelNameMap\nfrom pydantic.version import VERSION as PYDANTIC_VERSION\nfrom typing_extensions import Literal\n\nPYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(\".\")[:2])\nPYDANTIC_V2 = PYDANTIC_VERSION_MINOR_TUPLE[0] == 2\n# Keeping old \"Required\" functionality from Pydantic V1, without\n# shadowing typing.Required.\nRequiredParam: Any = Ellipsis\n\nif not PYDANTIC_V2:\n    from pydantic import BaseConfig as BaseConfig\n    from pydantic import BaseModel as BaseModel\n    from pydantic import ValidationError as ValidationError\n    from pydantic import create_model as create_model\n    from pydantic.class_validators import Validator as Validator\n    from pydantic.color import Color as Color\n    from pydantic.error_wrappers import ErrorWrapper as ErrorWrapper\n    from pydantic.errors import MissingError\n    from pydantic.fields import (  # type: ignore[attr-defined]\n        SHAPE_FROZENSET,\n        SHAPE_LIST,\n        SHAPE_SEQUENCE,\n        SHAPE_SET,\n        SHAPE_SINGLETON,\n        SHAPE_TUPLE,\n        SHAPE_TUPLE_ELLIPSIS,\n    )\n    from pydantic.fields import FieldInfo as FieldInfo\n    from pydantic.fields import ModelField as ModelField  # type: ignore[attr-defined]\n    from pydantic.fields import Undefined as Undefined  # type: ignore[attr-defined]\n    from pydantic.fields import (  # type: ignore[attr-defined]\n        UndefinedType as UndefinedType,\n    )\n    from pydantic.networks import AnyUrl as AnyUrl\n    from pydantic.networks import NameEmail as NameEmail\n    from pydantic.schema import TypeModelSet as TypeModelSet\n    from
5run_command
{
 "command": "sed -n '70,110p' fastapi/_compat/v1.py\n"
}
{"status": "ok", "stdout": "\nelse:\n from pydantic.v1 import BaseConfig as BaseConfig # type: ignore[assignment]\n from pydantic.v1 import BaseModel as BaseMod
full
{"status": "ok", "stdout": "\nelse:\n    from pydantic.v1 import BaseConfig as BaseConfig  # type: ignore[assignment]\n    from pydantic.v1 import BaseModel as BaseModel  # type: ignore[assignment]\n    from pydantic.v1 import (  # type: ignore[assignment]\n        ValidationError as ValidationError,\n    )\n    from pydantic.v1 import create_model as create_model  # type: ignore[no-redef]\n    from pydantic.v1.class_validators import Validator as Validator\n    from pydantic.v1.color import Color as Color  # type: ignore[assignment]\n    from pydantic.v1.error_wrappers import ErrorWrapper as ErrorWrapper\n    from pydantic.v1.errors import MissingError\n    from pydantic.v1.fields import (\n        SHAPE_FROZENSET,\n        SHAPE_LIST,\n        SHAPE_SEQUENCE,\n        SHAPE_SET,\n        SHAPE_SINGLETON,\n        SHAPE_TUPLE,\n        SHAPE_TUPLE_ELLIPSIS,\n    )\n    from pydantic.v1.fields import FieldInfo as FieldInfo  # type: ignore[assignment]\n    from pydantic.v1.fields import ModelField as ModelField\n    from pydantic.v1.fields import Undefined as Undefined\n    from pydantic.v1.fields import UndefinedType as UndefinedType\n    from pydantic.v1.networks import AnyUrl as AnyUrl\n    from pydantic.v1.networks import (  # type: ignore[assignment]\n        NameEmail as NameEmail,\n    )\n    from pydantic.v1.schema import TypeModelSet as TypeModelSet\n    from pydantic.v1.schema import (\n        field_schema,\n        get_flat_models_from_fields,\n        model_process_schema,\n    )\n    from pydantic.v1.schema import (\n        get_annotation_from_field_info as get_annotation_from_field_info,\n    )\n    from pydantic.v1.schema import (\n        get_flat_models_from_field as get_flat_models_from_field,\n    )\n", "stderr": "", "exit_code": 0}
6run_command
{
 "command": "sed -n '110,140p' fastapi/_compat/v1.py\n"
}
{"status": "ok", "stdout": " )\n from pydantic.v1.schema import get_model_name_map as get_model_name_map\n from pydantic.v1.types import ( # type: ignore[assign
full
{"status": "ok", "stdout": "    )\n    from pydantic.v1.schema import get_model_name_map as get_model_name_map\n    from pydantic.v1.types import (  # type: ignore[assignment]\n        SecretBytes as SecretBytes,\n    )\n    from pydantic.v1.types import (  # type: ignore[assignment]\n        SecretStr as SecretStr,\n    )\n    from pydantic.v1.typing import evaluate_forwardref as evaluate_forwardref\n    from pydantic.v1.utils import lenient_issubclass as lenient_issubclass\n\n\nGetJsonSchemaHandler = Any\nJsonSchemaValue = Dict[str, Any]\nCoreSchema = Any\nUrl = AnyUrl\n\nsequence_shapes = {\n    SHAPE_LIST,\n    SHAPE_SET,\n    SHAPE_FROZENSET,\n    SHAPE_TUPLE,\n    SHAPE_SEQUENCE,\n    SHAPE_TUPLE_ELLIPSIS,\n}\nsequence_shape_to_type = {\n    SHAPE_LIST: list,\n    SHAPE_SET: set,\n    SHAPE_TUPLE: tuple,\n    SHAPE_SEQUENCE: list,\n    SHAPE_TUPLE_ELLIPSIS: list,\n", "stderr": "", "exit_code": 0}
7run_command
{
 "command": "python3 -c \"import sys; print(sys.version)\"\n"
}
{"status": "ok", "stdout": "3.13.15 (main, Sep 1 2026, 14:05:45) [Clang 22.1.3 ]\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "3.13.15 (main, Sep  1 2026, 14:05:45) [Clang 22.1.3 ]\n", "stderr": "", "exit_code": 0}
8run_command
{
 "command": "python3 -c \"import pydantic; print(pydantic.__version__)\"\n"
}
{"status": "ok", "stdout": "2.13.5\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "2.13.5\n", "stderr": "", "exit_code": 0}
9run_command
{
 "command": "cat > /tmp/check_pydantic_v1.py <<'PY'\ntry:\n    import pydantic.v1\n    print(\"Successfully imported pydantic.v1\")\nexcept ImportError as e:\n    print(f\"Failed to import pydantic.v1: {e}\")\nexcept Exception as e:\n    print(f\"An unexpected error occurred: {e}\")\nPY\npython3 /tmp/check_pydantic_v1.py\n"
}
{"status": "ok", "stdout": "Successfully imported pydantic.v1\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "Successfully imported pydantic.v1\n", "stderr": "", "exit_code": 0}
10run_command
{
 "command": "cat > /tmp/check_pydantic_v1_warning.py <<'PY'\nimport warnings\nwith warnings.catch_warnings(record=True) as w:\n    warnings.simplefilter(\"always\")\n    try:\n        import pydantic.v1\n        print(\"Imported pydantic.v1\")\n        for warning in w:\n            print(f\"Warning: {warning.message}\")\n    except Exception as e:\n        print(f\"Error: {e}\")\nPY\npython3 /tmp/check_pydantic_v1_warning.py\n"
}
{"status": "ok", "stdout": "Imported pydantic.v1\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "Imported pydantic.v1\n", "stderr": "", "exit_code": 0}
11run_command
{
 "command": "sed -n '1,20p' fastapi/_compat/v1.py\n"
}
{"status": "ok", "stdout": "from copy import copy\nfrom dataclasses import dataclass, is_dataclass\nfrom enum import Enum\nfrom typing import (\n Any,\n Callabl
full
{"status": "ok", "stdout": "from copy import copy\nfrom dataclasses import dataclass, is_dataclass\nfrom enum import Enum\nfrom typing import (\n    Any,\n    Callable,\n    Dict,\n    List,\n    Sequence,\n    Set,\n    Tuple,\n    Type,\n    Union,\n)\n\nfrom fastapi._compat import shared\nfrom fastapi.openapi.constants import REF_PREFIX as REF_PREFIX\nfrom fastapi.types import ModelNameMap\nfrom pydantic.version import VERSION as PYDANTIC_VERSION\nfrom typing_extensions import Literal\n", "stderr": "", "exit_code": 0}
12run_command
{
 "command": "sed -n '21,30p' fastapi/_compat/v1.py\n"
}
{"status": "ok", "stdout": "\nPYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(\".\")[:2])\nPYDANTIC_V2 = PYDANTIC_VERSION_MINOR_TUPL
full
{"status": "ok", "stdout": "\nPYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(\".\")[:2])\nPYDANTIC_V2 = PYDANTIC_VERSION_MINOR_TUPLE[0] == 2\n# Keeping old \"Required\" functionality from Pydantic V1, without\n# shadowing typing.Required.\nRequiredParam: Any = Ellipsis\n\nif not PYDANTIC_V2:\n    from pydantic import BaseConfig as BaseConfig\n    from pydantic import BaseModel as BaseModel\n", "stderr": "", "exit_code": 0}
13run_command
{
 "command": "python3 -c \"import sys; print(sys.version_info)\"\n"
}
{"status": "ok", "stdout": "sys.version_info(major=3, minor=13, micro=15, releaselevel='final', serial=0)\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "sys.version_info(major=3, minor=13, micro=15, releaselevel='final', serial=0)\n", "stderr": "", "exit_code": 0}
14run_command
{
 "command": "python3 -c \"import pydantic; print(pydantic.version.VERSION)\"\n"
}
{"status": "ok", "stdout": "2.13.5\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "2.13.5\n", "stderr": "", "exit_code": 0}
15run_command
{
 "command": "sed -n '21,70p' fastapi/_compat/v1.py\n"
}
{"status": "ok", "stdout": "\nPYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(\".\")[:2])\nPYDANTIC_V2 = PYDANTIC_VERSION_MINOR_TUPL
full
{"status": "ok", "stdout": "\nPYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(\".\")[:2])\nPYDANTIC_V2 = PYDANTIC_VERSION_MINOR_TUPLE[0] == 2\n# Keeping old \"Required\" functionality from Pydantic V1, without\n# shadowing typing.Required.\nRequiredParam: Any = Ellipsis\n\nif not PYDANTIC_V2:\n    from pydantic import BaseConfig as BaseConfig\n    from pydantic import BaseModel as BaseModel\n    from pydantic import ValidationError as ValidationError\n    from pydantic import create_model as create_model\n    from pydantic.class_validators import Validator as Validator\n    from pydantic.color import Color as Color\n    from pydantic.error_wrappers import ErrorWrapper as ErrorWrapper\n    from pydantic.errors import MissingError\n    from pydantic.fields import (  # type: ignore[attr-defined]\n        SHAPE_FROZENSET,\n        SHAPE_LIST,\n        SHAPE_SEQUENCE,\n        SHAPE_SET,\n        SHAPE_SINGLETON,\n        SHAPE_TUPLE,\n        SHAPE_TUPLE_ELLIPSIS,\n    )\n    from pydantic.fields import FieldInfo as FieldInfo\n    from pydantic.fields import ModelField as ModelField  # type: ignore[attr-defined]\n    from pydantic.fields import Undefined as Undefined  # type: ignore[attr-defined]\n    from pydantic.fields import (  # type: ignore[attr-defined]\n        UndefinedType as UndefinedType,\n    )\n    from pydantic.networks import AnyUrl as AnyUrl\n    from pydantic.networks import NameEmail as NameEmail\n    from pydantic.schema import TypeModelSet as TypeModelSet\n    from pydantic.schema import (\n        field_schema,\n        get_flat_models_from_fields,\n        model_process_schema,\n    )\n    from pydantic.schema import (\n        get_annotation_from_field_info as get_annotation_from_field_info,\n    )\n    from pydantic.schema import get_flat_models_from_field as get_flat_models_from_field\n    from pydantic.schema import get_model_name_map as get_model_name_map\n    from pydantic.types import SecretBytes as SecretBytes\n
16run_command
{
 "command": "python3 -c \"import sys; print(sys.version_info)\"\n"
}
{"status": "ok", "stdout": "sys.version_info(major=3, minor=13, micro=15, releaselevel='final', serial=0)\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "sys.version_info(major=3, minor=13, micro=15, releaselevel='final', serial=0)\n", "stderr": "", "exit_code": 0}
17run_command
{
 "command": "cat > /tmp/reproduce_warning.py <<'PY'\nimport sys\nimport warnings\nfrom pydantic.version import VERSION as PYDANTIC_VERSION\n\nprint(f\"Python version: {sys.version}\")\nprint(f\"Pydantic version: {PYDANTIC_VERSION}\")\n\nwith warnings.catch_warnings(record=True) as w:\n    warnings.simplefilter(\"always\")\n    try:\n        import pydantic.v1\n        print(\"Imported pydantic.v1\")\n        for warning in w:\n            print(f\"Warning: {warning.message}\")\n    except ImportError as e:\n        print(f\"ImportError: {e}\")\nPY\npython3 /tmp/reproduce_warning.py\n"
}
{"status": "ok", "stdout": "Python version: 3.13.15 (main, Sep 1 2026, 14:05:45) [Clang 22.1.3 ]\nPydantic version: 2.13.5\nImported pydantic.v1\n", "stderr": "
full
{"status": "ok", "stdout": "Python version: 3.13.15 (main, Sep  1 2026, 14:05:45) [Clang 22.1.3 ]\nPydantic version: 2.13.5\nImported pydantic.v1\n", "stderr": "", "exit_code": 0}
18run_command
{
 "command": "python3 -c \"import pydantic; print(pydantic.version.VERSION)\"\n"
}
{"status": "ok", "stdout": "2.13.5\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "2.13.5\n", "stderr": "", "exit_code": 0}
19run_command
{
 "command": "python3 -c \"import sys; print(sys.version_info)\"\n"
}
{"status": "ok", "stdout": "sys.version_info(major=3, minor=13, micro=15, releaselevel='final', serial=0)\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "sys.version_info(major=3, minor=13, micro=15, releaselevel='final', serial=0)\n", "stderr": "", "exit_code": 0}
20run_command
{
 "command": "sed -n '20,35p' fastapi/_compat/v1.py\n"
}
{"status": "ok", "stdout": "from typing_extensions import Literal\n\nPYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(\".\")[:2])\nPY
full
{"status": "ok", "stdout": "from typing_extensions import Literal\n\nPYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(\".\")[:2])\nPYDANTIC_V2 = PYDANTIC_VERSION_MINOR_TUPLE[0] == 2\n# Keeping old \"Required\" functionality from Pydantic V1, without\n# shadowing typing.Required.\nRequiredParam: Any = Ellipsis\n\nif not PYDANTIC_V2:\n    from pydantic import BaseConfig as BaseConfig\n    from pydantic import BaseModel as BaseModel\n    from pydantic import ValidationError as ValidationError\n    from pydantic import create_model as create_model\n    from pydantic.class_validators import Validator as Validator\n    from pydantic.color import Color as Color\n    from pydantic.error_wrappers import ErrorWrapper as ErrorWrapper\n", "stderr": "", "exit_code": 0}
21run_command
{
 "command": "python3 -c \"import pydantic.v1; print('OK')\"\n"
}
{"status": "ok", "stdout": "OK\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "OK\n", "stderr": "", "exit_code": 0}
22run_command
{
 "command": "sed -n '65,130p' fastapi/_compat/v1.py\n"
}
{"status": "ok", "stdout": " from pydantic.types import SecretBytes as SecretBytes\n from pydantic.types import SecretStr as SecretStr\n from pydantic.typing im
full
{"status": "ok", "stdout": "    from pydantic.types import SecretBytes as SecretBytes\n    from pydantic.types import SecretStr as SecretStr\n    from pydantic.typing import evaluate_forwardref as evaluate_forwardref\n    from pydantic.utils import lenient_issubclass as lenient_issubclass\n\n\nelse:\n    from pydantic.v1 import BaseConfig as BaseConfig  # type: ignore[assignment]\n    from pydantic.v1 import BaseModel as BaseModel  # type: ignore[assignment]\n    from pydantic.v1 import (  # type: ignore[assignment]\n        ValidationError as ValidationError,\n    )\n    from pydantic.v1 import create_model as create_model  # type: ignore[no-redef]\n    from pydantic.v1.class_validators import Validator as Validator\n    from pydantic.v1.color import Color as Color  # type: ignore[assignment]\n    from pydantic.v1.error_wrappers import ErrorWrapper as ErrorWrapper\n    from pydantic.v1.errors import MissingError\n    from pydantic.v1.fields import (\n        SHAPE_FROZENSET,\n        SHAPE_LIST,\n        SHAPE_SEQUENCE,\n        SHAPE_SET,\n        SHAPE_SINGLETON,\n        SHAPE_TUPLE,\n        SHAPE_TUPLE_ELLIPSIS,\n    )\n    from pydantic.v1.fields import FieldInfo as FieldInfo  # type: ignore[assignment]\n    from pydantic.v1.fields import ModelField as ModelField\n    from pydantic.v1.fields import Undefined as Undefined\n    from pydantic.v1.fields import UndefinedType as UndefinedType\n    from pydantic.v1.networks import AnyUrl as AnyUrl\n    from pydantic.v1.networks import (  # type: ignore[assignment]\n        NameEmail as NameEmail,\n    )\n    from pydantic.v1.schema import TypeModelSet as TypeModelSet\n    from pydantic.v1.schema import (\n        field_schema,\n        get_flat_models_from_fields,\n        model_process_schema,\n    )\n    from pydantic.v1.schema import (\n        get_annotation_from_field_info as get_annotation_from_field_info,\n    )\n    from pydantic.v1.schema import (\n        get_flat_models_from_field as get_flat_models_from_
23run_command
{
 "command": "python3 -c \"import sys; print(sys.version_info)\"\n"
}
{"status": "ok", "stdout": "sys.version_info(major=3, minor=13, micro=15, releaselevel='final', serial=0)\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "sys.version_info(major=3, minor=13, micro=15, releaselevel='final', serial=0)\n", "stderr": "", "exit_code": 0}
24run_command
{
 "command": "cd /workspace && python3 - <<'PY'\nfrom pathlib import Path\np = Path(\"fastapi/_compat/v1.py\")\ns = p.read_text()\n\n# Define the condition for Python 3.14 and Pydantic 2.12.0+\n# We need to import sys for this.\n# Also need to define dummy classes for all the v1 imports.\n\nold_condition = \"if not PYDANTIC_V2:\"\n# We will change the structure to:\n# if not PYDANTIC_V2:\n#     ... (existing v1 imports)\n# elif (sys.version_info >= (3, 14) and PYDANTIC_VERSION_MINOR_TUPLE >= (2, 12)):\n#     # dummy classes\n# else:\n#     # existing pydantic.v1 imports\n\n# Actually, it might be easier to wrap the pydantic.v1 imports in another if.\n\n# Let's find where the 'else:' starts.\n# The 'else:' is on line 71 (approx).\n\n# I will add 'import sys' at the top.\n# Then I will modify the if/else block.\n\n# Original:\n# if not PYDANTIC_V2:\n#     ...\n# else:\n#     from pydantic.v1 import ...\n\n# New:\n# if not PYDANTIC_V2:\n#     ...\n# elif sys.version_info >= (3, 14) and PYDANTIC_VERSION_MINOR_TUPLE >= (2, 12):\n#     # Dummy classes\n#     BaseConfig = Any\n#     BaseModel = Any\n#     ...\n# else:\n#     from pydantic.v1 import ...\n\n# But the dummy classes need to be available for everything that uses them.\n# The current file exports them.\n\n# Let's just replace the 'else:' block.\n\n# First, add import sys.\ns = s.replace(\"from copy import copy\", \"import sys\\nfrom copy import copy\")\n\n# Now replace the 'else:' block.\n# The else block starts at 'else:\\n    from pydantic.v1 import BaseConfig as BaseConfig'\n# and ends before 'GetJsonSchemaHandler = Any'\n\n# This is a large block. Let's be careful.\n\n# I will find the start of the 'else:' block.\nimport re\nmatch = re.search(r'else:\\s+from pydantic\\.v1 import BaseConfig as BaseConfig', s)\nif match:\n    start = match.start()\n    # Find the end of the imports\n    # The imports end before 'GetJsonSchemaHandler = Any'\n    end = s.find('GetJsonSchemaHandler = Any')\n    \n    # We want to replace the whole 'else' block.\n    # But we want to keep the 'else' if the condition is not met.\n\n    # Let's use a more robust replacement.\n    # We will replace:\n    # else:\n#     from pydantic.v1 import ...\n#     ...\n#     from pydantic.v1.utils import lenient_issubclass as lenient_issubclass\n\n# With:\n# elif sys.version_info >= (3, 14) and PYDANTIC_VERSION_MINOR_TUPLE >= (2, 12):\n#     # Dummy classes to avoid warnings/errors in Python 3.14+ and Pydantic 2.12+\n#     BaseConfig = Any\n#     BaseModel = Any\n#     ValidationError = Any\n#     create_model = Any\n#     Validator = Any\n#     Color = Any\n#     ErrorWrapper = Any\n#     MissingError = Any\n#     SHAPE_FROZENSET = Any\n#     SHAPE_LIST = Any\n#     SHAPE_SEQUENCE = Any\n#     SHAPE_SET = Any\n#     SHAPE_SINGLETON = Any\n#     SHAPE_TUPLE = Any\n#     SHAPE_TUPLE_ELLIPSIS = Any\n#     FieldInfo = Any\n#     ModelField = Any\n#     Undefined = Any\n#     UndefinedType = Any\n#     AnyUrl = Any\n#     NameEmail = Any\n#     TypeModelSet = Any\n#     field_schema = Any\n#     get_flat_models_from_fields = Any\n#     model_process_schema = Any\n#     get_annotation_from_field_info = Any\n#     get_flat_models_from_field = Any\n#     get_model_name_map = Any\n#     SecretBytes = Any\n#     SecretStr = Any\n#     evaluate_forwardref = Any\n#     lenient_issubclass = Any\n# else:\n#     from pydantic.v1 import ...\n\n# Wait, I can just define these as Any and then if it's NOT (3.14 and 2.12), \n# I can override them with the actual imports.\n\n# But the existing structure is:\n# if not PYDANTIC_V2:\n#     # v1 imports from pydantic\n# else:\n#     # v1 imports from pydantic.v1\n\n# So I will change:\n# else:\n#     from pydantic.v1 import ...\n\n# To:\n# elif sys.version_info >= (3, 14) and PYDANTIC_VERSION_MINOR_TUPLE >= (2, 12):\n#     # dummy classes\n#     ...\n# else:\n#     from pydantic.v1 import ...\n\n# This looks correct.\nPY\n"
}
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
25run_command
{
 "command": "cd /workspace && python3 - <<'PY'\nfrom pathlib import Path\np = Path(\"fastapi/_compat/v1.py\")\ns = p.read_text()\n\n# Add import sys\nif \"import sys\" not in s:\n    s = s.replace(\"from copy import copy\", \"import sys\\nfrom copy import copy\")\n\n# Identify the else block to be replaced\n# The else block starts at 'else:' and contains imports from pydantic.v1\n# It ends before 'GetJsonSchemaHandler = Any'\n\nimport re\nmatch = re.search(r'else:\\n    from pydantic\\.v1 import BaseConfig as BaseConfig', s)\nif match:\n    start = match.start()\n    end = s.find('GetJsonSchemaHandler = Any')\n    else_block = s[start:end]\n    \n    # The imports inside the else block\n    # We want to extract the names of the imported symbols.\n    # This is tricky because of the multiple import styles.\n    \n    # Let's just define the dummy names manually based on the current code.\n    dummies = [\n        \"BaseConfig\", \"BaseModel\", \"ValidationError\", \"create_model\", \"Validator\",\n        \"Color\", \"ErrorWrapper\", \"MissingError\", \"SHAPE_FROZENSET\", \"SHAPE_LIST\",\n        \"SHAPE_SEQUENCE\", \"SHAPE_SET\", \"SHAPE_SINGLETON\", \"SHAPE_TUPLE\",\n        \"SHAPE_TUPLE_ELLIPSIS\", \"FieldInfo\", \"ModelField\", \"Undefined\",\n        \"UndefinedType\", \"AnyUrl\", \"NameEmail\", \"TypeModelSet\", \"field_schema\",\n        \"get_flat_models_from_fields\", \"model_process_schema\",\n        \"get_annotation_from_field_info\", \"get_flat_models_from_field\",\n        \"get_model_name_map\", \"SecretBytes\", \"SecretStr\", \"evaluate_forwardref\",\n        \"lenient_issubclass\"\n    ]\n    \n    dummy_assignments = \"\\n\".join([f\"    {name} = Any\" for name in dummies])\n    \n    new_block = f\"elif sys.version_info >= (3, 14) and PYDANTIC_VERSION_MINOR_TUPLE >= (2, 12):\\n{dummy_assignments}\\nelse:\\n{else_block[len('else:'):]}\"\n    \n    s = s[:start] + new_block + s[end:]\n\np.write_text(s)\nPY\n"
}
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
26run_command
{
 "command": "sed -n '65,150p' fastapi/_compat/v1.py\n"
}
{"status": "ok", "stdout": " from pydantic.schema import get_model_name_map as get_model_name_map\n from pydantic.types import SecretBytes as SecretBytes\n from
full
{"status": "ok", "stdout": "    from pydantic.schema import get_model_name_map as get_model_name_map\n    from pydantic.types import SecretBytes as SecretBytes\n    from pydantic.types import SecretStr as SecretStr\n    from pydantic.typing import evaluate_forwardref as evaluate_forwardref\n    from pydantic.utils import lenient_issubclass as lenient_issubclass\n\n\nelif sys.version_info >= (3, 14) and PYDANTIC_VERSION_MINOR_TUPLE >= (2, 12):\n    BaseConfig = Any\n    BaseModel = Any\n    ValidationError = Any\n    create_model = Any\n    Validator = Any\n    Color = Any\n    ErrorWrapper = Any\n    MissingError = Any\n    SHAPE_FROZENSET = Any\n    SHAPE_LIST = Any\n    SHAPE_SEQUENCE = Any\n    SHAPE_SET = Any\n    SHAPE_SINGLETON = Any\n    SHAPE_TUPLE = Any\n    SHAPE_TUPLE_ELLIPSIS = Any\n    FieldInfo = Any\n    ModelField = Any\n    Undefined = Any\n    UndefinedType = Any\n    AnyUrl = Any\n    NameEmail = Any\n    TypeModelSet = Any\n    field_schema = Any\n    get_flat_models_from_fields = Any\n    model_process_schema = Any\n    get_annotation_from_field_info = Any\n    get_flat_models_from_field = Any\n    get_model_name_map = Any\n    SecretBytes = Any\n    SecretStr = Any\n    evaluate_forwardref = Any\n    lenient_issubclass = Any\nelse:\n\n    from pydantic.v1 import BaseConfig as BaseConfig  # type: ignore[assignment]\n    from pydantic.v1 import BaseModel as BaseModel  # type: ignore[assignment]\n    from pydantic.v1 import (  # type: ignore[assignment]\n        ValidationError as ValidationError,\n    )\n    from pydantic.v1 import create_model as create_model  # type: ignore[no-redef]\n    from pydantic.v1.class_validators import Validator as Validator\n    from pydantic.v1.color import Color as Color  # type: ignore[assignment]\n    from pydantic.v1.error_wrappers import ErrorWrapper as ErrorWrapper\n    from pydantic.v1.errors import MissingError\n    from pydantic.v1.fields import (\n        SHAPE_FROZENSET,\n        SHAPE_LIST,\n        SHAP
27run_command
{
 "command": "python3 -m py_compile fastapi/_compat/v1.py\n"
}
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
28run_command
{
 "command": "python3 -c \"import fastapi._compat.v1; print('OK')\"\n"
}
{"status": "ok", "stdout": "OK\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "OK\n", "stderr": "", "exit_code": 0}
29run_command
{
 "command": "cd /workspace && python3 - <<'PY'\nfrom pathlib import Path\np = Path(\"fastapi/_compat/v1.py\")\ns = p.read_text()\n# Remove the extra newline after 'else:'\ns = s.replace(\"else:\\n\\n    from pydantic.v1 import BaseConfig as BaseConfig\", \"else:\\n    from pydantic.v1 import BaseConfig as BaseConfig\")\np.write_text(s)\nPY\n"
}
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
30run_command
{
 "command": "git diff\n"
}
{"status": "ok", "stdout": "diff --git a/fastapi/_compat/v1.py b/fastapi/_compat/v1.py\nindex f0ac5163..c422df9e 100644\n--- a/fastapi/_compat/v1.py\n+++ b/fast
full
{"status": "ok", "stdout": "diff --git a/fastapi/_compat/v1.py b/fastapi/_compat/v1.py\nindex f0ac5163..c422df9e 100644\n--- a/fastapi/_compat/v1.py\n+++ b/fastapi/_compat/v1.py\n@@ -1,3 +1,4 @@\n+import sys\n from copy import copy\n from dataclasses import dataclass, is_dataclass\n from enum import Enum\n@@ -68,6 +69,39 @@ if not PYDANTIC_V2:\n     from pydantic.utils import lenient_issubclass as lenient_issubclass\n \n \n+elif sys.version_info >= (3, 14) and PYDANTIC_VERSION_MINOR_TUPLE >= (2, 12):\n+    BaseConfig = Any\n+    BaseModel = Any\n+    ValidationError = Any\n+    create_model = Any\n+    Validator = Any\n+    Color = Any\n+    ErrorWrapper = Any\n+    MissingError = Any\n+    SHAPE_FROZENSET = Any\n+    SHAPE_LIST = Any\n+    SHAPE_SEQUENCE = Any\n+    SHAPE_SET = Any\n+    SHAPE_SINGLETON = Any\n+    SHAPE_TUPLE = Any\n+    SHAPE_TUPLE_ELLIPSIS = Any\n+    FieldInfo = Any\n+    ModelField = Any\n+    Undefined = Any\n+    UndefinedType = Any\n+    AnyUrl = Any\n+    NameEmail = Any\n+    TypeModelSet = Any\n+    field_schema = Any\n+    get_flat_models_from_fields = Any\n+    model_process_schema = Any\n+    get_annotation_from_field_info = Any\n+    get_flat_models_from_field = Any\n+    get_model_name_map = Any\n+    SecretBytes = Any\n+    SecretStr = Any\n+    evaluate_forwardref = Any\n+    lenient_issubclass = Any\n else:\n     from pydantic.v1 import BaseConfig as BaseConfig  # type: ignore[assignment]\n     from pydantic.v1 import BaseModel as BaseModel  # type: ignore[assignment]\n", "stderr": "", "exit_code": 0}
31submit_patch
{}
{"status": "ok", "patch_size": 1454, "files_changed": 1}
full
{"status": "ok", "patch_size": 1454, "files_changed": 1}

Patch

diff --git a/fastapi/_compat/v1.py b/fastapi/_compat/v1.py
index f0ac5163..c422df9e 100644
--- a/fastapi/_compat/v1.py
+++ b/fastapi/_compat/v1.py
@@ -1,3 +1,4 @@
+import sys
 from copy import copy
 from dataclasses import dataclass, is_dataclass
 from enum import Enum
@@ -68,6 +69,39 @@ if not PYDANTIC_V2:
     from pydantic.utils import lenient_issubclass as lenient_issubclass
 
 
+elif sys.version_info >= (3, 14) and PYDANTIC_VERSION_MINOR_TUPLE >= (2, 12):
+    BaseConfig = Any
+    BaseModel = Any
+    ValidationError = Any
+    create_model = Any
+    Validator = Any
+    Color = Any
+    ErrorWrapper = Any
+    MissingError = Any
+    SHAPE_FROZENSET = Any
+    SHAPE_LIST = Any
+    SHAPE_SEQUENCE = Any
+    SHAPE_SET = Any
+    SHAPE_SINGLETON = Any
+    SHAPE_TUPLE = Any
+    SHAPE_TUPLE_ELLIPSIS = Any
+    FieldInfo = Any
+    ModelField = Any
+    Undefined = Any
+    UndefinedType = Any
+    AnyUrl = Any
+    NameEmail = Any
+    TypeModelSet = Any
+    field_schema = Any
+    get_flat_models_from_fields = Any
+    model_process_schema = Any
+    get_annotation_from_field_info = Any
+    get_flat_models_from_field = Any
+    get_model_name_map = Any
+    SecretBytes = Any
+    SecretStr = Any
+    evaluate_forwardref = Any
+    lenient_issubclass = Any
 else:
     from pydantic.v1 import BaseConfig as BaseConfig  # type: ignore[assignment]
     from pydantic.v1 import BaseModel as BaseModel  # type: ignore[assignment]

Test output

show
==================================== ERRORS ====================================
____________________ ERROR collecting tests/test_compat.py _____________________
ImportError while importing test module '/private/tmp/swe_work/pod_r5_v1i/fastapi_14186/b/workspace/tests/test_compat.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
/Users/jp/.local/share/uv/python/cpython-3.13.15-macos-aarch64-none/lib/python3.13/importlib/__init__.py:88: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
tests/test_compat.py:4: in <module>
    from fastapi._compat import (
E   ImportError: cannot import name 'may_v1' from 'fastapi._compat' (/private/tmp/swe_work/pod_r5_v1i/fastapi_14186/b/workspace/fastapi/_compat/__init__.py)
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 error in 0.40s