Browse Source

Fix CI failures: Python 3.8 compatibility and Pydantic v1 tests

- Fix Python 3.8 compatibility by importing Annotated from typing_extensions
  when running on Python < 3.9
- Simplify get_typed_signature to not use get_type_hints which was causing
  issues with Pydantic v1 parameter detection (Header, Cookie, Query, etc.)
- Instead, expand globalns with module namespace to help resolve forward
  references while keeping the original annotation resolution logic intact

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
pull/14557/head
lif 7 months ago
parent
commit
c171f468d8
  1. 28
      fastapi/dependencies/utils.py
  2. 7
      tests/test_annotated_forward_ref.py

28
fastapi/dependencies/utils.py

@ -18,7 +18,6 @@ from typing import (
Type, Type,
Union, Union,
cast, cast,
get_type_hints,
) )
import anyio import anyio
@ -228,30 +227,19 @@ def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
unwrapped = inspect.unwrap(call) unwrapped = inspect.unwrap(call)
globalns = getattr(unwrapped, "__globals__", {}) globalns = getattr(unwrapped, "__globals__", {})
# Try to use get_type_hints first for better forward reference resolution # Get updated globalns from the module (in case classes were defined after the function)
# This properly handles Annotated types with forward references when using # This is needed for forward references when using `from __future__ import annotations`
# `from __future__ import annotations` (PEP 563)
type_hints: Dict[str, Any] = {}
try:
# include_extras=True preserves Annotated metadata (like Depends)
type_hints = get_type_hints(unwrapped, globalns=globalns, include_extras=True)
except NameError:
# If get_type_hints fails due to unresolved names, try to get updated
# globalns from the module (in case classes were defined after the function)
module_name = getattr(unwrapped, "__module__", None) module_name = getattr(unwrapped, "__module__", None)
if module_name: if module_name:
import importlib import importlib
try: try:
module = importlib.import_module(module_name) module = importlib.import_module(module_name)
updated_globalns = vars(module) # Merge module namespace with function's globalns
type_hints = get_type_hints( # Function's globalns takes precedence for imports made in the function's scope
unwrapped, globalns=updated_globalns, include_extras=True updated_globalns = {**vars(module), **globalns}
) globalns = updated_globalns
except Exception:
pass
except Exception: except Exception:
# Fall back to manual resolution if get_type_hints fails for other reasons
pass pass
typed_params = [ typed_params = [
@ -259,9 +247,7 @@ def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
name=param.name, name=param.name,
kind=param.kind, kind=param.kind,
default=param.default, default=param.default,
annotation=type_hints.get(param.name) annotation=get_typed_annotation(param.annotation, globalns),
if param.name in type_hints
else get_typed_annotation(param.annotation, globalns),
) )
for param in signature.parameters.values() for param in signature.parameters.values()
] ]

7
tests/test_annotated_forward_ref.py

@ -2,8 +2,15 @@
from __future__ import annotations from __future__ import annotations
import sys
from dataclasses import dataclass from dataclasses import dataclass
# Annotated is available in typing from Python 3.9+
# For Python 3.8, we need to import from typing_extensions
if sys.version_info >= (3, 9):
from typing import Annotated from typing import Annotated
else:
from typing_extensions import Annotated
from fastapi import Depends, FastAPI from fastapi import Depends, FastAPI
from fastapi.testclient import TestClient from fastapi.testclient import TestClient

Loading…
Cancel
Save