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"]:
if get_origin(parameter.annotation) is Annotated:
args = get_args(parameter.annotation)
if len(args) > 1:
marker = args[1]
else:
marker = None
candidates = get_args(parameter.annotation)[1:]
else:
marker = parameter.default
candidates = (parameter.default,)
for marker_extractor in MARKER_EXTRACTORS:
if _marker := marker_extractor(marker):
marker = _marker
break
if not isinstance(marker, _Marker):
return None
return marker
for marker in candidates:
for marker_extractor in MARKER_EXTRACTORS:
if _marker := marker_extractor(marker):
marker = _marker
break
if _is_marker(marker):
return marker
return None
@cache
@ -1223,9 +1218,11 @@ def _get_members_and_annotated(obj: Any) -> Iterable[Tuple[str, Any]]:
for annotation_name, annotation in annotations.items():
if get_origin(annotation) is Annotated:
args = get_args(annotation)
if len(args) > 1:
member = args[1]
members.append((annotation_name, member))
# Search through all metadata items (args[1:]) for a DI marker
for arg in args[1:]:
if _is_marker(arg):
members.append((annotation_name, arg))
break
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]]):
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():
service = module.test_container()
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)