Browse Source

Merge 7f85a410a3 into 6db05770f6

pull/10647/merge
Pastukhov Nikita 6 days ago
committed by GitHub
parent
commit
af9aebbde3
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 37
      fastapi/dependencies/utils.py
  2. 32
      tests/test_get_request_body.py

37
fastapi/dependencies/utils.py

@ -231,12 +231,17 @@ 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__", {}) globalns = getattr(call, "__globals__", {})
typed_params = [ typed_params = [
inspect.Parameter( inspect.Parameter(
name=param.name, name=param.name,
kind=param.kind, kind=param.kind,
default=param.default, default=param.default,
annotation=get_typed_annotation(param.annotation, globalns), annotation=get_typed_annotation(
param.annotation,
globalns,
collect_outer_locals(),
),
) )
for param in signature.parameters.values() for param in signature.parameters.values()
] ]
@ -244,10 +249,30 @@ def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
return typed_signature return typed_signature
def get_typed_annotation(annotation: Any, globalns: Dict[str, Any]) -> Any: def collect_outer_locals() -> Dict[str, Any]:
frame = inspect.currentframe()
locals = {}
finded = False
while frame is not None:
if finded:
locals.update(frame.f_locals)
# Find first FastAPI outer frame
if frame.f_code.co_name == "decorator":
finded = True
frame = frame.f_back
return locals
def get_typed_annotation(
annotation: Any, globalns: Dict[str, Any], localns: Dict[str, Any]
) -> Any:
if isinstance(annotation, str): if isinstance(annotation, str):
annotation = ForwardRef(annotation) annotation = ForwardRef(annotation)
annotation = evaluate_forwardref(annotation, globalns, globalns) annotation = evaluate_forwardref(annotation, globalns, localns)
return annotation return annotation
@ -259,7 +284,11 @@ def get_typed_return_annotation(call: Callable[..., Any]) -> Any:
return None return None
globalns = getattr(call, "__globals__", {}) globalns = getattr(call, "__globals__", {})
return get_typed_annotation(annotation, globalns) return get_typed_annotation(
annotation,
globalns,
collect_outer_locals(),
)
def get_dependant( def get_dependant(

32
tests/test_get_request_body.py

@ -1,3 +1,7 @@
from __future__ import annotations
from typing import Any
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from pydantic import BaseModel from pydantic import BaseModel
@ -105,3 +109,31 @@ def test_openapi_schema():
} }
}, },
} }
def test_get_with_local_declared_body():
def wrap(application: FastAPI, *args: Any):
def wrapper(func):
return application.get(*args)(func)
return wrapper
def init_app() -> FastAPI:
application = FastAPI()
class LocalProduct(BaseModel):
name: str
description: str = None # type: ignore
price: float
@wrap(application, "/product")
async def create_item(product: LocalProduct) -> LocalProduct:
return product
return application
client = TestClient(init_app())
body = {"name": "Foo", "description": "Some description", "price": 5.5}
response = client.request("GET", "/product", json=body)
assert response.json() == body

Loading…
Cancel
Save