Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 250c32a

Browse files
committed
Add support for exceptions raised in PEP-593 Annotated dependencies - new FastAPI feature - fastapi/fastapi#4871
1 parent e345ba6 commit 250c32a

2 files changed

Lines changed: 51 additions & 4 deletions

File tree

fastapi_docx/exception_finder.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import importlib
33
import inspect
44
import textwrap
5+
import typing
56
from collections.abc import Callable, Generator, Iterable
67
from itertools import chain
78
from types import ModuleType
@@ -56,6 +57,21 @@ def is_callable_instance(obj: object) -> bool:
5657
return hasattr(obj, "__call__") and not isinstance(obj, type)
5758

5859

60+
def is_annotated_alias(annot):
61+
_AnnotatedAlias = getattr(typing, "_AnnotatedAlias")
62+
return isinstance(annot, _AnnotatedAlias)
63+
64+
65+
def get_annotated_dependency(annot):
66+
if is_annotated_alias(annot):
67+
annot_args = typing.get_args(annot)
68+
annot_dep = annot_args[1] if len(annot_args) > 1 else None
69+
if annot_dep and annot_dep.__class__.__name__ == "Depends":
70+
dependency = getattr(annot_dep, "dependency", None)
71+
return dependency
72+
return None
73+
74+
5975
def create_exc_instance(
6076
exc_class: type[Exception], exc_args: list[Any] | None = None
6177
) -> Exception | None:
@@ -104,6 +120,8 @@ def extract_exceptions(
104120
self.exceptions.extend(self.find_exceptions(function))
105121
if self.dependencyClasses:
106122
self.exceptions += self.find_dependency_exceptions(route)
123+
if self.dependencyClasses:
124+
self.exceptions += self.find_annotated_dependency_exceptions(route)
107125
if self.serviceClasses:
108126
self.exceptions += self.find_service_exceptions(route)
109127
return self.exceptions
@@ -241,6 +259,35 @@ def find_dependency_exceptions(
241259
exceptions.extend(_exceptions)
242260
return exceptions
243261

262+
def find_annotated_dependency_exceptions(
263+
self,
264+
route: APIRoute | Callable,
265+
) -> list[HTTPException]:
266+
exceptions = []
267+
assert self.dependencyClasses is not None
268+
func = route.endpoint if hasattr(route, "endpoint") else route
269+
module = inspect.getmodule(func)
270+
source = inspect.getsource(func)
271+
tree = ast.parse(source)
272+
273+
for node in find_nodes(tree, (ast.FunctionDef, ast.AsyncFunctionDef)):
274+
for kwarg in getattr(node.args, "kwonlyargs", []):
275+
if kwarg and hasattr(kwarg, "annotation"):
276+
if (
277+
annot := kwarg.annotation.id
278+
if hasattr(kwarg.annotation, "id") and kwarg.annotation.id
279+
else None
280+
):
281+
cls = None
282+
try:
283+
cls = getattr(module, annot)
284+
except (AttributeError, NameError):
285+
...
286+
if cls and (dependency := get_annotated_dependency(cls)):
287+
if _exceptions := self.find_exceptions(dependency, module):
288+
exceptions.extend(_exceptions)
289+
return exceptions
290+
244291
def create_exc_inst_from_raise_stmt(
245292
self,
246293
raise_stmt: ast.Raise,

tests/unit_tests/test_dependency_exceptions.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any
1+
from typing import Annotated, Any
22

33
from fastapi import APIRouter, Depends, FastAPI
44

@@ -13,11 +13,11 @@
1313

1414
router = APIRouter()
1515

16+
UserDict = Annotated[dict[str, Any], Depends(UserDeps.get_current_user)]
17+
1618

1719
@router.get("/me")
18-
def get_user(
19-
*, user_in, current_user: dict[str, Any] = Depends(UserDeps.get_current_user)
20-
):
20+
def get_user(*, user_in, current_user: UserDict):
2121
result = UserService.get_authenticated(user_in)
2222
return result
2323

0 commit comments

Comments
 (0)