Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use __wrapped__ attribute #5077

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
8 changes: 7 additions & 1 deletion fastapi/dependencies/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,13 @@ def is_scalar_sequence_field(field: ModelField) -> bool:

def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
signature = inspect.signature(call)
globalns = getattr(call, "__globals__", {})
nsobj = call
while hasattr(nsobj, "__wrapped__"):
# The __wrapped__ attribute is set by decorators, e.g. functools.wraps.
# This while loop allows rereferencing forward references on decorated
# methods.
nsobj = nsobj.__wrapped__ # type: ignore
lucaswiman marked this conversation as resolved.
Show resolved Hide resolved
globalns = getattr(nsobj, "__globals__", {})
typed_params = [
inspect.Parameter(
name=param.name,
Expand Down
9 changes: 9 additions & 0 deletions tests/forward_reference_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from pydantic import BaseModel


def forwardref_method(input: "ForwardRef") -> "ForwardRef":
return ForwardRef(x=input.x + 1)


class ForwardRef(BaseModel):
x: int = 0
31 changes: 31 additions & 0 deletions tests/test_wrapped_method_forward_reference.py
Original file line number Diff line number Diff line change
@@ -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, 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}