"""Annotation-based dependency extraction from ``Annotated`` type hints.""" from __future__ import annotations from typing import Annotated, Any, get_args, get_origin, get_type_hints from collections.abc import Callable from .base import Dependency _annotation_cache: dict[Callable[..., Any], dict[str, list[Dependency[Any]]]] = {} def get_annotation_dependencies( function: Callable[..., Any], ) -> dict[str, list[Dependency[Any]]]: """Find ``Dependency`` instances in ``Annotated`` type-hint metadata. Bare ``Dependency`` subclasses are also accepted as a shorthand for a parameterless instance: ``Annotated[T, Dep]`` is equivalent to ``Annotated[T, Dep()]``. """ if function in _annotation_cache: return _annotation_cache[function] result: dict[str, list[Dependency[Any]]] = {} try: hints = get_type_hints(function, include_extras=True) except Exception: _annotation_cache[function] = result return result for name, hint in hints.items(): if name == "return": continue if get_origin(hint) is not Annotated: continue dependencies: list[Dependency[Any]] = [] for a in get_args(hint)[1:]: if isinstance(a, Dependency): dependencies.append(a) elif isinstance(a, type) and issubclass(a, Dependency): dependencies.append(a()) if dependencies: result[name] = dependencies _annotation_cache[function] = result return result