mirror of https://github.com/tiangolo/fastapi.git
✨ Add support for wrapped functions (e.g. `@functools.wraps()`) used with forward references (#5077)
Co-authored-by: Yurii Karabas <1998uriyyo@gmail.com> Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
This commit is contained in:
parent
930b27e5fa
commit
1c1e584abd
|
|
@ -192,7 +192,8 @@ def get_flat_params(dependant: Dependant) -> List[ModelField]:
|
||||||
|
|
||||||
def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
|
def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
|
||||||
signature = inspect.signature(call)
|
signature = inspect.signature(call)
|
||||||
globalns = getattr(call, "__globals__", {})
|
unwrapped = inspect.unwrap(call)
|
||||||
|
globalns = getattr(unwrapped, "__globals__", {})
|
||||||
typed_params = [
|
typed_params = [
|
||||||
inspect.Parameter(
|
inspect.Parameter(
|
||||||
name=param.name,
|
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:
|
def get_typed_return_annotation(call: Callable[..., Any]) -> Any:
|
||||||
signature = inspect.signature(call)
|
signature = inspect.signature(call)
|
||||||
|
unwrapped = inspect.unwrap(call)
|
||||||
annotation = signature.return_annotation
|
annotation = signature.return_annotation
|
||||||
|
|
||||||
if annotation is inspect.Signature.empty:
|
if annotation is inspect.Signature.empty:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
globalns = getattr(call, "__globals__", {})
|
globalns = getattr(unwrapped, "__globals__", {})
|
||||||
return get_typed_annotation(annotation, globalns)
|
return get_typed_annotation(annotation, globalns)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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}
|
||||||
Loading…
Reference in New Issue