Skip to content

fix: recognise PEP 695 type aliases as Depends() parameters - #261

Closed
cezar-hapiko wants to merge 1 commit into
JasperSui:mainfrom
cezar-hapiko:fix/pep695-type-alias-depends
Closed

cezar-hapiko wants to merge 1 commit into
JasperSui:mainfrom
cezar-hapiko:fix/pep695-type-alias-depends

Conversation

@cezar-hapiko

Copy link
Copy Markdown

Problem

Since 1.5.0, _build_dependency_only_callable pre-filters parameters with _has_depends, which checks get_origin(param.annotation) is Annotated. For a PEP 695 alias:

type SettingsDep = Annotated[Settings, Depends(get_settings)]

@injectable
async def build_worker(settings: SettingsDep) -> Worker: ...

param.annotation is a TypeAliasType, get_origin returns None, the parameter is stripped before FastAPI sees it, and the call fails with:

TypeError: build_worker() missing 1 required positional argument: 'settings'

FastAPI's own resolver unwraps these aliases, so the same providers work in routes; only @injectable breaks. 1.4.6 passed the function straight to get_dependant and worked. This bit us on upgrade from 1.4.6 to 1.6.1: every @injectable worker factory in a codebase that uses type XDep = Annotated[...] aliases throughout failed at startup.

Fix

Unwrap TypeAliasType before inspecting the annotation. Both typing.TypeAliasType and typing_extensions.TypeAliasType are handled — they are distinct classes on 3.12 and 3.13, so checking only one misses the other.

Test

test_injectable_resolves_pep695_type_alias_dependency is parametrized over both alias classes (the typing case skips below 3.12). It fails on main with the TypeError above and passes with this change. Full suite: 231 passed on 3.10, 232 passed on 3.12; mypy src and ruff clean.

Also verified downstream: a ~3000-test application suite that failed at worker startup on 1.6.1 passes fully with this patch applied.

`_has_depends` checks `get_origin(param.annotation) is Annotated`, but for a
PEP 695 alias such as `type SettingsDep = Annotated[Settings, Depends(get_settings)]`
the annotation is a `TypeAliasType` and `get_origin` returns None. Since 1.5.0
`_build_dependency_only_callable` therefore strips every such parameter before
FastAPI sees it, and the wrapped callable fails with
"missing 1 required positional argument". FastAPI's own resolver unwraps these
aliases, so the same providers work in routes and only `@injectable` breaks.

Unwrap `TypeAliasType` (both `typing` and `typing_extensions`, which are
distinct classes on 3.12/3.13) before inspecting the annotation.
@codecov

codecov Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (1bc48a1) to head (c10b89e).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##              main      #261   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           11        11           
  Lines          595       603    +8     
  Branches        72        73    +1     
=========================================
+ Hits           595       603    +8     
Files with missing lines Coverage Δ
src/fastapi_injectable/main.py 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant