]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
⬆️ Update Pydantic v2 code to address deprecations (#15101)
authorSofie Van Landeghem <svlandeg@users.noreply.github.com>
Thu, 23 Apr 2026 16:40:29 +0000 (18:40 +0200)
committerGitHub <noreply@github.com>
Thu, 23 Apr 2026 16:40:29 +0000 (18:40 +0200)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
fastapi/_compat/__init__.py
fastapi/_compat/v2.py
fastapi/dependencies/utils.py
fastapi/encoders.py
tests/test_jsonable_encoder.py

index 40810eab7a5f874db7ec3cbd998f9d2cf64a03a2..4581c38c88ede0d70f9c40a67fbc9ee79529cffd 100644 (file)
@@ -26,7 +26,7 @@ from .v2 import Undefined as Undefined
 from .v2 import Url as Url
 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  # ty: ignore[deprecated]
+from .v2 import evaluate_forwardref as evaluate_forwardref
 from .v2 import get_cached_model_fields as get_cached_model_fields
 from .v2 import get_definitions as get_definitions
 from .v2 import get_flat_models_from_fields as get_flat_models_from_fields
index 535af0784991d953ff2d34b9e06700e6d1fbc644..3b64fba76c900dfd3542d8016b41927a5d6a76c1 100644 (file)
@@ -22,10 +22,10 @@ from pydantic import BaseModel, ConfigDict, Field, TypeAdapter, create_model
 from pydantic import PydanticSchemaGenerationError as PydanticSchemaGenerationError
 from pydantic import PydanticUndefinedAnnotation as PydanticUndefinedAnnotation
 from pydantic import ValidationError as ValidationError
+from pydantic._internal import _typing_extra as _pydantic_typing_extra
 from pydantic._internal._schema_generation_shared import (  # type: ignore[attr-defined]  # ty: ignore[unused-ignore-comment]
     GetJsonSchemaHandler as GetJsonSchemaHandler,
 )
-from pydantic._internal._typing_extra import eval_type_lenient  # ty: ignore[deprecated]
 from pydantic.fields import FieldInfo as FieldInfo
 from pydantic.json_schema import GenerateJsonSchema as _GenerateJsonSchema
 from pydantic.json_schema import JsonSchemaValue as JsonSchemaValue
@@ -38,7 +38,20 @@ from pydantic_core.core_schema import (
 
 RequiredParam = PydanticUndefined
 Undefined = PydanticUndefined
-evaluate_forwardref = eval_type_lenient  # ty: ignore[deprecated]
+
+
+def evaluate_forwardref(
+    value: Any,
+    globalns: dict[str, Any] | None = None,
+    localns: dict[str, Any] | None = None,
+) -> Any:
+    # eval_type_lenient has been deprecated since Pydantic v2.10.0b1 (PR #10530)
+    try_eval_type = getattr(_pydantic_typing_extra, "try_eval_type", None)
+    if try_eval_type is not None:
+        return try_eval_type(value, globalns, localns)[0]
+    return _pydantic_typing_extra.eval_type_lenient(  # ty: ignore[deprecated]
+        value, globalns, localns
+    )
 
 
 class GenerateJsonSchema(_GenerateJsonSchema):
index 6b14dac8dc55e963e9e5f46b860361e14d4092c4..aceca6a1d3a658435d107c342d92515eb489b053 100644 (file)
@@ -33,7 +33,7 @@ from fastapi._compat import (
     Undefined,
     copy_field_info,
     create_body_model,
-    evaluate_forwardref,  # ty: ignore[deprecated]
+    evaluate_forwardref,
     field_annotation_is_scalar,
     field_annotation_is_scalar_sequence,
     field_annotation_is_sequence,
@@ -245,7 +245,7 @@ def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
 def get_typed_annotation(annotation: Any, globalns: dict[str, Any]) -> Any:
     if isinstance(annotation, str):
         annotation = ForwardRef(annotation)
-        annotation = evaluate_forwardref(annotation, globalns, globalns)  # ty: ignore[deprecated]
+        annotation = evaluate_forwardref(annotation, globalns, globalns)
         if annotation is type(None):
             return None
     return annotation
index 84893dc808ad85b8ce0514ea61db5df43865a92c..43f24101b6f8b559b5133f546dff9e95a9666ce8 100644 (file)
@@ -22,7 +22,6 @@ from annotated_doc import Doc
 from fastapi.exceptions import PydanticV1NotSupportedError
 from fastapi.types import IncEx
 from pydantic import BaseModel
-from pydantic.color import Color  # ty: ignore[deprecated]
 from pydantic.networks import AnyUrl, NameEmail
 from pydantic.types import SecretBytes, SecretStr
 from pydantic_core import PydanticUndefinedType
@@ -32,6 +31,23 @@ from ._compat import (
     is_pydantic_v1_model_instance,
 )
 
+try:
+    # pydantic.color.Color is deprecated since v2.0b3, but supporting for bwd-compat
+    from pydantic.color import Color  # ty: ignore[deprecated]
+except ImportError:  # pragma: no cover
+
+    class Color:  # type: ignore[no-redef]  # ty: ignore[unused-ignore-comment]
+        pass
+
+
+try:
+    # Supporting the new Color format for newer versions of Pydantic
+    from pydantic_extra_types.color import Color as PyExtraColor
+except ImportError:  # pragma: no cover
+
+    class PyExtraColor:  # type: ignore[no-redef]  # ty: ignore[unused-ignore-comment]
+        pass
+
 
 # Taken from Pydantic v1 as is
 def isoformat(o: datetime.date | datetime.time) -> str:
@@ -67,7 +83,8 @@ def decimal_encoder(dec_value: Decimal) -> int | float:
 
 ENCODERS_BY_TYPE: dict[type[Any], Callable[[Any], Any]] = {
     bytes: lambda o: o.decode(),
-    Color: str,  # ty: ignore[deprecated]
+    Color: str,
+    PyExtraColor: str,
     datetime.date: isoformat,
     datetime.datetime: isoformat,
     datetime.time: isoformat,
index 595202beaf371044823a35c9c79479ac53d2d52b..c23a9e5d7918aee9783a2157b06b188f0900ea9f 100644 (file)
@@ -311,3 +311,21 @@ def test_encode_deque_encodes_child_models():
 def test_encode_pydantic_undefined():
     data = {"value": Undefined}
     assert jsonable_encoder(data) == {"value": None}
+
+
+@pytest.mark.filterwarnings("ignore::DeprecationWarning")
+@pytest.mark.parametrize(
+    "module_path",
+    [
+        pytest.param("pydantic.color"),
+        pytest.param("pydantic_extra_types.color"),
+    ],
+)
+def test_encode_color(module_path):
+    try:
+        Color = __import__(module_path, fromlist=["Color"]).Color
+    except ImportError:  # pragma: no cover
+        pytest.skip(f"{module_path} not available")
+
+    data = {"color": Color("blue")}
+    assert jsonable_encoder(data) == {"color": "blue"}