Browse Source

Fix evaluating stringified annotations in Python 3.10 (also from __future__ import annotations)

pull/11355/head
Christophe Haen 1 year ago
parent
commit
009cce84e2
  1. 11
      fastapi/dependencies/utils.py
  2. 0
      tests/test_future/__init__.py
  3. 10
      tests/test_future/loging_tool.py
  4. 34
      tests/test_future/test_future_6465.py
  5. 28
      tests/test_future/test_future_9095.py

11
fastapi/dependencies/utils.py

@ -1,4 +1,5 @@
import inspect
import sys
from contextlib import AsyncExitStack, contextmanager
from copy import deepcopy
from typing import (
@ -205,7 +206,10 @@ def get_flat_params(dependant: Dependant) -> List[ModelField]:
def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
signature = inspect.signature(call)
if sys.version_info >= (3, 10):
signature = inspect.signature(call, eval_str=True)
else:
signature = inspect.signature(call)
globalns = getattr(call, "__globals__", {})
typed_params = [
inspect.Parameter(
@ -228,7 +232,10 @@ def get_typed_annotation(annotation: Any, globalns: Dict[str, Any]) -> Any:
def get_typed_return_annotation(call: Callable[..., Any]) -> Any:
signature = inspect.signature(call)
if sys.version_info >= (3, 10):
signature = inspect.signature(call, eval_str=True)
else:
signature = inspect.signature(call)
annotation = signature.return_annotation
if annotation is inspect.Signature.empty:

0
tests/test_future/__init__.py

10
tests/test_future/loging_tool.py

@ -0,0 +1,10 @@
from functools import wraps
def login_required(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("login")
return func(*args, **kwargs)
return wrapper

34
tests/test_future/test_future_6465.py

@ -0,0 +1,34 @@
from __future__ import annotations
import sys
from ..utils import needs_py310
if sys.version_info > (3, 10):
from typing import Optional
from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel
from .loging_tool import login_required
app = FastAPI()
client = TestClient(app)
class Item(BaseModel):
name: str
description: Optional[str] = None
price: float
tax: Optional[float] = None
@app.get("/items/")
@login_required
def get_item(id: int) -> Item:
return Item(name="name", price=42.42)
@needs_py310
def test_future_6465():
res = client.get("/items?id=3")
assert res.status_code == 200

28
tests/test_future/test_future_9095.py

@ -0,0 +1,28 @@
from __future__ import annotations
import sys
from ..utils import needs_py310
if sys.version_info > (3, 10):
from fastapi import Depends, FastAPI
from fastapi.testclient import TestClient
from starlette.requests import Request
app = FastAPI()
client = TestClient(app)
class Test:
def __call__(self, request: Request):
return "test"
@app.get("/test/")
def call(test: str = Depends(Test())):
return {"test": test}
@needs_py310
def test_call():
response = client.get("/test")
assert response.status_code == 200
Loading…
Cancel
Save