Skip to content

Commit 51f8187

Browse files
authored
Allow annotated marker to be anywhere in the annotation list (#939)
1 parent 244deee commit 51f8187

File tree

3 files changed

+33
-18
lines changed

3 files changed

+33
-18
lines changed

src/dependency_injector/wiring.py

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -682,23 +682,18 @@ def _unpatch_attribute(patched: PatchedAttribute) -> None:
682682

683683
def _extract_marker(parameter: inspect.Parameter) -> Optional["_Marker"]:
684684
if get_origin(parameter.annotation) is Annotated:
685-
args = get_args(parameter.annotation)
686-
if len(args) > 1:
687-
marker = args[1]
688-
else:
689-
marker = None
685+
candidates = get_args(parameter.annotation)[1:]
690686
else:
691-
marker = parameter.default
692-
693-
for marker_extractor in MARKER_EXTRACTORS:
694-
if _marker := marker_extractor(marker):
695-
marker = _marker
696-
break
697-
698-
if not isinstance(marker, _Marker):
699-
return None
687+
candidates = (parameter.default,)
700688

701-
return marker
689+
for marker in candidates:
690+
for marker_extractor in MARKER_EXTRACTORS:
691+
if _marker := marker_extractor(marker):
692+
marker = _marker
693+
break
694+
if _is_marker(marker):
695+
return marker
696+
return None
702697

703698

704699
@cache
@@ -1223,9 +1218,11 @@ def _get_members_and_annotated(obj: Any) -> Iterable[Tuple[str, Any]]:
12231218
for annotation_name, annotation in annotations.items():
12241219
if get_origin(annotation) is Annotated:
12251220
args = get_args(annotation)
1226-
if len(args) > 1:
1227-
member = args[1]
1228-
members.append((annotation_name, member))
1221+
# Search through all metadata items (args[1:]) for a DI marker
1222+
for arg in args[1:]:
1223+
if _is_marker(arg):
1224+
members.append((annotation_name, arg))
1225+
break
12291226
return members
12301227

12311228

tests/unit/samples/wiring/module_annotated.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,10 @@ def test_class_decorator(service: Annotated[Service, Provide[Container.service]]
124124

125125
def test_container(container: Annotated[Container, Provide[Container]]):
126126
return container.service()
127+
128+
129+
@inject
130+
def test_annotated_with_non_di_metadata_first(
131+
service: Annotated[Service, "some other annotated value", Provide[Container.service]],
132+
):
133+
return service

tests/unit/wiring/provider_ids/test_main_annotated_py36.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,14 @@ def test_class_decorator():
174174
def test_container():
175175
service = module.test_container()
176176
assert isinstance(service, Service)
177+
178+
179+
def test_annotated_with_non_di_metadata_first():
180+
"""Test that Annotated works when DI marker is not the first metadata item.
181+
182+
This tests the case where Annotated has other metadata (like docstrings or
183+
other annotations) before the Provide marker, e.g.:
184+
Annotated[Service, "some doc", Provide[Container.service]]
185+
"""
186+
service = module.test_annotated_with_non_di_metadata_first()
187+
assert isinstance(service, Service)

0 commit comments

Comments
 (0)