Allow annotated marker to be anywhere in the annotation list (#939)

This commit is contained in:
Brian Pugh 2025-12-04 13:12:43 -05:00 committed by GitHub
parent 244deee75f
commit 51f8187202
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 33 additions and 18 deletions

View File

@ -682,23 +682,18 @@ def _unpatch_attribute(patched: PatchedAttribute) -> None:
def _extract_marker(parameter: inspect.Parameter) -> Optional["_Marker"]: def _extract_marker(parameter: inspect.Parameter) -> Optional["_Marker"]:
if get_origin(parameter.annotation) is Annotated: if get_origin(parameter.annotation) is Annotated:
args = get_args(parameter.annotation) candidates = get_args(parameter.annotation)[1:]
if len(args) > 1:
marker = args[1]
else:
marker = None
else: else:
marker = parameter.default candidates = (parameter.default,)
for marker_extractor in MARKER_EXTRACTORS: for marker in candidates:
if _marker := marker_extractor(marker): for marker_extractor in MARKER_EXTRACTORS:
marker = _marker if _marker := marker_extractor(marker):
break marker = _marker
break
if not isinstance(marker, _Marker): if _is_marker(marker):
return None return marker
return None
return marker
@cache @cache
@ -1223,9 +1218,11 @@ def _get_members_and_annotated(obj: Any) -> Iterable[Tuple[str, Any]]:
for annotation_name, annotation in annotations.items(): for annotation_name, annotation in annotations.items():
if get_origin(annotation) is Annotated: if get_origin(annotation) is Annotated:
args = get_args(annotation) args = get_args(annotation)
if len(args) > 1: # Search through all metadata items (args[1:]) for a DI marker
member = args[1] for arg in args[1:]:
members.append((annotation_name, member)) if _is_marker(arg):
members.append((annotation_name, arg))
break
return members return members

View File

@ -124,3 +124,10 @@ def test_class_decorator(service: Annotated[Service, Provide[Container.service]]
def test_container(container: Annotated[Container, Provide[Container]]): def test_container(container: Annotated[Container, Provide[Container]]):
return container.service() return container.service()
@inject
def test_annotated_with_non_di_metadata_first(
service: Annotated[Service, "some other annotated value", Provide[Container.service]],
):
return service

View File

@ -174,3 +174,14 @@ def test_class_decorator():
def test_container(): def test_container():
service = module.test_container() service = module.test_container()
assert isinstance(service, Service) assert isinstance(service, Service)
def test_annotated_with_non_di_metadata_first():
"""Test that Annotated works when DI marker is not the first metadata item.
This tests the case where Annotated has other metadata (like docstrings or
other annotations) before the Provide marker, e.g.:
Annotated[Service, "some doc", Provide[Container.service]]
"""
service = module.test_annotated_with_non_di_metadata_first()
assert isinstance(service, Service)