From 009cce84e29e568c00506f463979f2ee7c13d4c8 Mon Sep 17 00:00:00 2001 From: Christophe Haen Date: Wed, 27 Mar 2024 05:08:34 +0100 Subject: [PATCH] Fix evaluating stringified annotations in Python 3.10 (also from __future__ import annotations) --- fastapi/dependencies/utils.py | 11 +++++++-- tests/test_future/__init__.py | 0 tests/test_future/loging_tool.py | 10 ++++++++ tests/test_future/test_future_6465.py | 34 +++++++++++++++++++++++++++ tests/test_future/test_future_9095.py | 28 ++++++++++++++++++++++ 5 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 tests/test_future/__init__.py create mode 100644 tests/test_future/loging_tool.py create mode 100644 tests/test_future/test_future_6465.py create mode 100644 tests/test_future/test_future_9095.py diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index 02284b4ed..b316aab7e 100644 --- a/fastapi/dependencies/utils.py +++ b/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: diff --git a/tests/test_future/__init__.py b/tests/test_future/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/test_future/loging_tool.py b/tests/test_future/loging_tool.py new file mode 100644 index 000000000..bc11fe2f7 --- /dev/null +++ b/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 diff --git a/tests/test_future/test_future_6465.py b/tests/test_future/test_future_6465.py new file mode 100644 index 000000000..57362c5c7 --- /dev/null +++ b/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 diff --git a/tests/test_future/test_future_9095.py b/tests/test_future/test_future_9095.py new file mode 100644 index 000000000..44224f71b --- /dev/null +++ b/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