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: else:
marker = None candidates = (parameter.default,)
else:
marker = parameter.default
for marker in candidates:
for marker_extractor in MARKER_EXTRACTORS: for marker_extractor in MARKER_EXTRACTORS:
if _marker := marker_extractor(marker): if _marker := marker_extractor(marker):
marker = _marker marker = _marker
break break
if _is_marker(marker):
if not isinstance(marker, _Marker):
return None
return marker return marker
return None
@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)