]> git.ipfire.org Git - thirdparty/fastapi/fastapi.git/commitdiff
✨ Add support for wrapped functions (e.g. `@functools.wraps()`) used with forward...
author[object Object] <lucas.wiman@gmail.com>
Tue, 2 Dec 2025 17:23:14 +0000 (09:23 -0800)
committerGitHub <noreply@github.com>
Tue, 2 Dec 2025 17:23:14 +0000 (18:23 +0100)
Co-authored-by: Yurii Karabas <1998uriyyo@gmail.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
fastapi/dependencies/utils.py
tests/forward_reference_type.py [new file with mode: 0644]
tests/test_wrapped_method_forward_reference.py [new file with mode: 0644]

index ef3f56417a3f3152826283e7a11a19b2c22d9991..1a493a9fd05edf9efe423c0014dab770e7fb772f 100644 (file)
@@ -192,7 +192,8 @@ def get_flat_params(dependant: Dependant) -> List[ModelField]:
 
 def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
     signature = inspect.signature(call)
-    globalns = getattr(call, "__globals__", {})
+    unwrapped = inspect.unwrap(call)
+    globalns = getattr(unwrapped, "__globals__", {})
     typed_params = [
         inspect.Parameter(
             name=param.name,
@@ -217,12 +218,13 @@ def get_typed_annotation(annotation: Any, globalns: Dict[str, Any]) -> Any:
 
 def get_typed_return_annotation(call: Callable[..., Any]) -> Any:
     signature = inspect.signature(call)
+    unwrapped = inspect.unwrap(call)
     annotation = signature.return_annotation
 
     if annotation is inspect.Signature.empty:
         return None
 
-    globalns = getattr(call, "__globals__", {})
+    globalns = getattr(unwrapped, "__globals__", {})
     return get_typed_annotation(annotation, globalns)
 
 
diff --git a/tests/forward_reference_type.py b/tests/forward_reference_type.py
new file mode 100644 (file)
index 0000000..52a0d4a
--- /dev/null
@@ -0,0 +1,9 @@
+from pydantic import BaseModel
+
+
+def forwardref_method(input: "ForwardRefModel") -> "ForwardRefModel":
+    return ForwardRefModel(x=input.x + 1)
+
+
+class ForwardRefModel(BaseModel):
+    x: int = 0
diff --git a/tests/test_wrapped_method_forward_reference.py b/tests/test_wrapped_method_forward_reference.py
new file mode 100644 (file)
index 0000000..7403f60
--- /dev/null
@@ -0,0 +1,31 @@
+import functools
+
+from fastapi import FastAPI
+from fastapi.testclient import TestClient
+
+from .forward_reference_type import forwardref_method
+
+
+def passthrough(f):
+    @functools.wraps(f)
+    def method(*args, **kwargs):
+        return f(*args, **kwargs)
+
+    return method
+
+
+def test_wrapped_method_type_inference():
+    """
+    Regression test ensuring that when a method imported from another module
+    is decorated with something that sets the __wrapped__ attribute (functools.wraps),
+    then the types are still processed correctly, including dereferencing of forward
+    references.
+    """
+    app = FastAPI()
+    client = TestClient(app)
+    app.post("/endpoint")(passthrough(forwardref_method))
+    app.post("/endpoint2")(passthrough(passthrough(forwardref_method)))
+    with client:
+        response = client.post("/endpoint", json={"input": {"x": 0}})
+        response2 = client.post("/endpoint2", json={"input": {"x": 0}})
+    assert response.json() == response2.json() == {"x": 1}