Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Dependency injection fails for callable instances with PEP 563 annotations#14377

Unanswered
aliikamel asked this question inQuestions
Discussion options

First Check

  • I added a very descriptive title here.
  • I used the GitHub search to find a similar question and didn't find it.
  • I searched the FastAPI documentation, with the integrated search.
  • I already searched in Google "How to X in FastAPI" and didn't find any information.
  • I already read and followed all the tutorial in the docs and didn't find an answer.
  • I already checked if it is not related to FastAPI but toPydantic.
  • I already checked if it is not related to FastAPI but toSwagger UI.
  • I already checked if it is not related to FastAPI but toReDoc.

Commit to Help

  • I commit to help with one of those options 👆

Example Code

from __future__importannotationsfromtypingimportAnnotatedfromfastapiimportDepends,FastAPI,Requestapp=FastAPI()defget_dependency()->str:return"injected_value"# FAILS: Callable instanceclassCallableAuth:def__init__(self,privileges:list[str]):self.privileges=privilegesasyncdef__call__(self,request:Request,dep:Annotated[str,Depends(get_dependency)]):# ...do auth workreturn {"dep":dep}# WORKS: Plain functionasyncdeffunction_auth(request:Request,dep:Annotated[str,Depends(get_dependency)]):return {"dep":dep}@app.get("/class")asyncdefwith_class(auth:Annotated[dict,Depends(CallableAuth(["SUPER_ADMIN","ADMIN"]))]):returnauth# Fails: Field required for 'dep' (treated as query param)@app.get("/function")asyncdefwith_function(auth:Annotated[dict,Depends(function_auth)]):returnauth# Works correctly

Description

  • Open the browser and go to/docs. You'll find an error fetching the /openapi.json
    image

    Error:

    pydantic.errors.PydanticUserError: 'TypeAdapter[typing.Annotated[ForwardRef('Request'), Query(PydanticUndefined)]]' is not fully defined; you should define 'typing.Annotated[ForwardRef('Request'), Query(PydanticUndefined)]' and all referenced types, then call '.rebuild()' on the instance. For further information visit https://errors.pydantic.dev/2.11/u/class-not-fully-defined

Submit a cURL to test the endpoint,

curl --location '{your-dev-server}/class'

Response:

{"detail": [        {"type":"missing","loc": ["query","request"            ],"msg":"Field required","input":null        },        {"type":"missing","loc": ["query","dep"            ],"msg":"Field required","input":null        }    ]}

Operating System

Windows

Operating System Details

No response

FastAPI Version

0.118.0

Pydantic Version

2.11.10

Python Version

Python 3.13.3

Additional Context

Context

I discovered a bug in FastAPI's dependency injection system that affects callable instances when usingfrom __future__ import annotations (PEP 563). Since this will become the default behavior in Python 3.14, I wanted to bring it to your attention.

The Issue

When passing a callable instance (an object with__call__) toDepends(), FastAPI incorrectly treats dependency parameters as query parameters instead of sub-dependencies. This only happens whenfrom __future__ import annotations is present.

Root Cause

Infastapi/dependencies/utils.py, theget_typed_signature() function extracts__globals__ to resolve string annotations:

defget_typed_signature(call:Callable[...,Any])->inspect.Signature:signature=inspect.signature(call)globalns=getattr(call,"__globals__", {})# Returns {} for instances!

The problem:

  • For functions:getattr(func, "__globals__", {}) returns the module's globals ✅
  • For instances:getattr(instance, "__globals__", {}) returns{} because instances don't have__globals__
  • With empty globals, Pydantic'stry_eval_type() can't resolveForwardRef('Annotated[str, Depends(...)]')
  • FastAPI receives unresolvedForwardRef objects, can't find theDepends() marker, and falls back to treating them as query parameters

Proposed Fix

defget_typed_signature(call:Callable[...,Any])->inspect.Signature:signature=inspect.signature(call)globalns=getattr(call,"__globals__", {})# For callable instances, get __globals__ from the __call__ methodifnotglobalnsandhasattr(call,"__call__"):globalns=getattr(call.__call__,"__globals__", {})typed_params= [inspect.Parameter(name=param.name,kind=param.kind,default=param.default,annotation=get_typed_annotation(param.annotation,globalns),        )forparaminsignature.parameters.values()    ]typed_signature=inspect.Signature(typed_params)returntyped_signature

This fix:

  • Only activates when__globals__ is empty (no impact on working cases)
  • Correctly extracts globals from the__call__ method
  • Is minimal and safe

Why This Matters

  1. Python 3.14: PEP 563 becomes default, making this bug affect everyone
  2. Documented feature: FastAPI docs show callable classes work withDepends()
  3. Common pattern: Many users use callable classes for stateful dependencies, as seen in the example above used in the Example Code.

Testing

I've verified the fix resolves the issue. I'm happy to submit a PR with test cases if you'd like.

You must be logged in to vote

Replies: 2 comments

Comment options

Hey@aliikamel ,

Your explanation regardinggetattr(call, "__globals__", {}) returning an empty dict for class instances (vs functions) explains exactly why the types aren't being resolved correctly when PEP 563 is active. TheForwardRef failing to evaluate because it lacks the global namespace context is definitely the culprit here.

Since you already have the fix and the test case identified,please do submit that PR! 🚀

It is a very valid fix, especially given thatfrom __future__ import annotations is becoming more common (and eventually default).

In the meantime, for anyone else facing this issue who needs an immediate workaround without modifying FastAPI source code, you can manually assign__globals__ to your instance, though it is admittedly hacky:

auth_instance=CallableAuth(["SUPER_ADMIN"])# Hack: Inject globals so FastAPI can find themauth_instance.__globals__=globals()@app.get("/class")asyncdefwith_class(auth:Annotated[dict,Depends(auth_instance)]):pass

But your proposed fix toget_typed_signature is definitely the correct long-term solution. Looking forward to your PR!

You must be logged in to vote
0 replies

This comment was marked as disruptive content.

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Labels
questionQuestion or problem
3 participants
@aliikamel@engripaye@JunjieAraoXiong

[8]ページ先頭

©2009-2025 Movatter.jp