From fb10e3ae1a19164474326eff2d16b341947a0e0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20P=C3=A4rsson?= Date: Fri, 9 Jan 2026 10:27:16 +0100 Subject: [PATCH 1/2] Use covariant types in multibind To avoid the need for explicit typing when using the method. --- injector/__init__.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/injector/__init__.py b/injector/__init__.py index 19057b3..a15b7cf 100644 --- a/injector/__init__.py +++ b/injector/__init__.py @@ -27,11 +27,13 @@ TYPE_CHECKING, Any, Callable, + Collection, Dict, Generator, Generic, Iterable, List, + Mapping, Optional, Set, Tuple, @@ -387,7 +389,7 @@ def multibind( element_type = get_args(_punch_through_alias(interface))[0] except IndexError: raise InvalidInterface(f"Use typing.List[T] or list[T] to specify the element type of the list") - if isinstance(to, list): + if isinstance(to, Collection): for element in to: element_binding = self._binder.create_binding(element_type, element, scope) self.append(element_binding.provider, element_binding.scope) @@ -415,7 +417,7 @@ def multibind( raise InvalidInterface( f"Use typing.Dict[K, V] or dict[K, V] to specify the value type of the dict" ) - if isinstance(to, dict): + if isinstance(to, Mapping): for key, value in to.items(): element_binding = self._binder.create_binding(value_type, value, scope) self.append(KeyValueProvider(key, element_binding.provider), element_binding.scope) @@ -544,7 +546,7 @@ def bind( def multibind( self, interface: Type[List[T]], - to: Union[List[Union[T, Type[T]]], Callable[..., List[T]], Provider[List[T]], Type[T]], + to: Union[Collection[Union[T, Type[T]]], Callable[..., List[T]], Provider[List[T]], Type[T]], scope: Union[Type['Scope'], 'ScopeDecorator', None] = None, ) -> None: # pragma: no cover pass @@ -553,7 +555,7 @@ def multibind( def multibind( self, interface: Type[Dict[K, V]], - to: Union[Dict[K, Union[V, Type[V]]], Callable[..., Dict[K, V]], Provider[Dict[K, V]]], + to: Union[Mapping[K, Union[V, Type[V]]], Callable[..., Dict[K, V]], Provider[Dict[K, V]]], scope: Union[Type['Scope'], 'ScopeDecorator', None] = None, ) -> None: # pragma: no cover pass From 1589f3f037bf54db8f830a6ff4dc48f63f2bc5b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20P=C3=A4rsson?= Date: Fri, 9 Jan 2026 11:27:32 +0100 Subject: [PATCH 2/2] test: Multibind a tuple --- injector_test.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/injector_test.py b/injector_test.py index e9f791f..9ee3f35 100644 --- a/injector_test.py +++ b/injector_test.py @@ -733,19 +733,25 @@ class PluginD(Plugin): pass +class PluginE(Plugin): + pass + + def test_multibind_list_of_plugins(): def configure(binder: Binder): binder.multibind(List[Plugin], to=PluginA) binder.multibind(List[Plugin], to=[PluginB, PluginC()]) binder.multibind(List[Plugin], to=lambda: [PluginD()]) + binder.multibind(List[Plugin], to=(PluginE,)) injector = Injector([configure]) plugins = injector.get(List[Plugin]) - assert len(plugins) == 4 + assert len(plugins) == 5 assert isinstance(plugins[0], PluginA) assert isinstance(plugins[1], PluginB) assert isinstance(plugins[2], PluginC) assert isinstance(plugins[3], PluginD) + assert isinstance(plugins[4], PluginE) def test_multibind_dict_of_plugins():