File python-pydantic-annotations.patch of Package python-pydantic

From 024e2929dd69f0e746d7df4a2e26ca67025a8d79 Mon Sep 17 00:00:00 2001
From: Victorien <65306057+Viicos@users.noreply.github.com>
Date: Wed, 8 Oct 2025 16:10:54 +0200
Subject: [PATCH] Do not evaluate annotations when inspecting validators and
 serializers (#12355)

---
 pydantic/_internal/_decorators.py  | 23 +++++++++++++-----
 tests/test_deferred_annotations.py | 38 +++++++++++++++++++++++++++++-
 2 files changed, 54 insertions(+), 7 deletions(-)

diff --git a/pydantic/_internal/_decorators.py b/pydantic/_internal/_decorators.py
index 36d0642fa..1c48ad1e9 100644
--- a/pydantic/_internal/_decorators.py
+++ b/pydantic/_internal/_decorators.py
@@ -2,6 +2,7 @@
 
 from __future__ import annotations as _annotations
 
+import sys
 import types
 from collections import deque
 from collections.abc import Iterable
@@ -534,7 +535,7 @@ def inspect_validator(validator: Callable[..., Any], mode: FieldValidatorModes)
         Whether the validator takes an info argument.
     """
     try:
-        sig = signature(validator)
+        sig = _signature_no_eval(validator)
     except (ValueError, TypeError):
         # `inspect.signature` might not be able to infer a signature, e.g. with C objects.
         # In this case, we assume no info argument is present:
@@ -572,7 +573,7 @@ def inspect_field_serializer(serializer: Callable[..., Any], mode: Literal['plai
         Tuple of (is_field_serializer, info_arg).
     """
     try:
-        sig = signature(serializer)
+        sig = _signature_no_eval(serializer)
     except (ValueError, TypeError):
         # `inspect.signature` might not be able to infer a signature, e.g. with C objects.
         # In this case, we assume no info argument is present and this is not a method:
@@ -610,7 +611,7 @@ def inspect_annotated_serializer(serializer: Callable[..., Any], mode: Literal['
         info_arg
     """
     try:
-        sig = signature(serializer)
+        sig = _signature_no_eval(serializer)
     except (ValueError, TypeError):
         # `inspect.signature` might not be able to infer a signature, e.g. with C objects.
         # In this case, we assume no info argument is present:
@@ -642,7 +643,7 @@ def inspect_model_serializer(serializer: Callable[..., Any], mode: Literal['plai
             '`@model_serializer` must be applied to instance methods', code='model-serializer-instance-method'
         )
 
-    sig = signature(serializer)
+    sig = _signature_no_eval(serializer)
     info_arg = _serializer_info_arg(mode, count_positional_required_params(sig))
     if info_arg is None:
         raise PydanticUserError(
@@ -690,7 +691,7 @@ def is_instance_method_from_sig(function: AnyDecoratorCallable) -> bool:
     Returns:
         `True` if the function is an instance method, `False` otherwise.
     """
-    sig = signature(unwrap_wrapped_function(function))
+    sig = _signature_no_eval(unwrap_wrapped_function(function))
     first = next(iter(sig.parameters.values()), None)
     if first and first.name == 'self':
         return True
@@ -714,7 +715,7 @@ def ensure_classmethod_based_on_signature(function: AnyDecoratorCallable) -> Any
 
 
 def _is_classmethod_from_sig(function: AnyDecoratorCallable) -> bool:
-    sig = signature(unwrap_wrapped_function(function))
+    sig = _signature_no_eval(unwrap_wrapped_function(function))
     first = next(iter(sig.parameters.values()), None)
     if first and first.name == 'cls':
         return True
@@ -842,3 +843,13 @@ def ensure_property(f: Any) -> Any:
         return f
     else:
         return property(f)
+
+
+def _signature_no_eval(f: Callable[..., Any]) -> Signature:
+    """Get the signature of a callable without evaluating any annotations."""
+    if sys.version_info >= (3, 14):
+        from annotationlib import Format
+
+        return signature(f, annotation_format=Format.FORWARDREF)
+    else:
+        return signature(f)
diff --git a/tests/test_deferred_annotations.py b/tests/test_deferred_annotations.py
index 1b2ae48e5..c6408d100 100644
--- a/tests/test_deferred_annotations.py
+++ b/tests/test_deferred_annotations.py
@@ -7,7 +7,15 @@ from typing import Annotated
 import pytest
 from annotated_types import MaxLen
 
-from pydantic import BaseModel, Field, ValidationError
+from pydantic import (
+    BaseModel,
+    Field,
+    ValidationError,
+    field_serializer,
+    field_validator,
+    model_serializer,
+    model_validator,
+)
 from pydantic.dataclasses import dataclass
 
 pytestmark = pytest.mark.skipif(
@@ -80,3 +88,31 @@ def test_deferred_annotations_pydantic_dataclass_pydantic_field() -> None:
     Int = int
 
     assert A(a='1').a == 1
+
+
+def test_deferred_annotations_return_values() -> None:
+    class Model(BaseModel):
+        a: int
+
+        @model_validator(mode='after')
+        def check(self) -> Model:
+            return self
+
+        @model_validator(mode='before')
+        def before(cls, data) -> MyDict:
+            return data
+
+        @model_serializer(mode='plain')
+        def ser(self) -> MyDict:
+            return {'a': self.a}
+
+        @field_validator('a', mode='before')
+        def validate_a(cls, v) -> MyInt:
+            return v
+
+        @field_serializer('a', mode='plain')
+        def serialize_a(self, v) -> MyInt:
+            return v
+
+    MyDict = dict
+    MyInt = int
-- 
2.51.0