Skip to content

Commit 251bc55

Browse files
authored
Merge pull request #177 from illuin-tech/pr/175
Fix multi bindings being duplicated
2 parents 75ecd55 + bc202a9 commit 251bc55

File tree

14 files changed

+261
-121
lines changed

14 files changed

+261
-121
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
Opyoid follows [semver guidelines](https://semver.org) for versioning.
44

55
## Unreleased
6+
## 3.0.2
7+
### Fixes
8+
- Fixed an issue with multi binding unexpectedly injecting multiple instances of the same class
9+
- Fixed an edge case when modules were configured multiple times when imported inside different modules
10+
611
## 3.0.1
712
### Fixes
813
- Fix NoBindingFound error being raised when using multi bindings and injecting pep585 style list arguments

opyoid/bindings/abstract_module.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818

1919
class AbstractModule:
20-
"""Base class for Modules, should not be used outside of the library."""
20+
"""Base class for Modules, should not be used outside the library."""
2121

2222
conditions: Tuple[Condition, ...] = ()
2323

@@ -28,7 +28,7 @@ def __init__(
2828
):
2929
self._is_configured = False
3030
self._binding_registry = BindingRegistry(log_bindings)
31-
self._module_instances = shared_modules or {}
31+
self._module_instances = shared_modules if shared_modules is not None else {}
3232

3333
@property
3434
def binding_registry(self) -> BindingRegistry:
@@ -64,9 +64,11 @@ def install(self, module: Union["AbstractModule", Type["AbstractModule"]]) -> No
6464
if isinstance(binding, RegisteredMultiBinding):
6565
binding = RegisteredMultiBinding(
6666
binding.raw_binding,
67+
module_instance,
6768
item_bindings=[
6869
RegisteredBinding(
6970
registered_item_binding.raw_binding,
71+
module_instance,
7072
(module_instance,) + binding.source_path,
7173
)
7274
for registered_item_binding in binding.item_bindings
@@ -75,6 +77,7 @@ def install(self, module: Union["AbstractModule", Type["AbstractModule"]]) -> No
7577
else:
7678
binding = RegisteredBinding(
7779
binding.raw_binding,
80+
module_instance,
7881
(module_instance,) + binding.source_path,
7982
)
8083
self._binding_registry.register(binding, add_self_binding=False)
@@ -181,12 +184,12 @@ def _register(self, binding: Binding[Any]) -> RegisteredBinding[Any]:
181184
if isinstance(binding, MultiBinding):
182185
registered_binding: RegisteredBinding[Any] = self._register_multi_binding(binding)
183186
else:
184-
registered_binding = RegisteredBinding(binding)
187+
registered_binding = RegisteredBinding(binding, binding_source=self)
185188
self._binding_registry.register(registered_binding)
186189
return registered_binding
187190

188191
def _register_multi_binding(self, binding: MultiBinding[InjectedT]) -> RegisteredMultiBinding[InjectedT]:
189-
registered_binding = RegisteredMultiBinding(binding)
192+
registered_binding = RegisteredMultiBinding(binding, binding_source=self)
190193
for source_item_binding in binding.item_bindings:
191194
scope = cast(
192195
Type[Scope], source_item_binding.scope if source_item_binding.scope is not EMPTY else binding.scope
@@ -219,6 +222,6 @@ def _register_multi_binding(self, binding: MultiBinding[InjectedT]) -> Registere
219222
else:
220223
raise BindingError(f"ItemBinding in {binding!r} has no instance, class or provider, one should be set")
221224

222-
registered_binding.item_bindings.append(RegisteredBinding(item_binding))
225+
registered_binding.item_bindings.append(RegisteredBinding(item_binding, binding_source=self))
223226
self._binding_registry.register(registered_binding)
224227
return registered_binding

opyoid/bindings/binding_registry.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,9 @@ def _append_to_multi_binding(
6666
new_raw_binding = cast(MultiBinding[InjectedItemT], registered_binding.raw_binding)
6767
previous_raw_binding = cast(MultiBinding[InjectedItemT], previous_binding.raw_binding)
6868
for item_binding, raw_item_binding in zip(registered_binding.item_bindings, new_raw_binding.item_bindings):
69-
previous_binding.item_bindings.append(item_binding)
70-
previous_raw_binding.item_bindings.append(raw_item_binding)
69+
if item_binding not in previous_binding.item_bindings:
70+
previous_binding.item_bindings.append(item_binding)
71+
previous_raw_binding.item_bindings.append(raw_item_binding)
7172

7273
def _create_or_override_binding(
7374
self, registered_binding: RegisteredBinding[InjectedT], previous_binding: Optional[RegisteredBinding[InjectedT]]
@@ -102,7 +103,9 @@ def _register_self_binding(self, registered_binding: RegisteredBinding[Any]) ->
102103
self_binding = binding
103104

104105
if self_binding is not None and self_binding.target not in self:
105-
self.register(RegisteredBinding(self_binding, registered_binding.source_path))
106+
self.register(
107+
RegisteredBinding(self_binding, registered_binding.binding_source, registered_binding.source_path)
108+
)
106109

107110
def get_bindings_by_target(self) -> Dict[FrozenTarget[Any], RegisteredBinding[Any]]:
108111
return self._bindings_by_target
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Generic, Tuple, TYPE_CHECKING
1+
from typing import Generic, Optional, Tuple, TYPE_CHECKING
22

33
import attr
44

@@ -7,11 +7,13 @@
77
from .binding import Binding
88

99
if TYPE_CHECKING:
10+
from .abstract_module import AbstractModule
1011
from .private_module import PrivateModule
1112

1213

1314
@attr.s(auto_attribs=True, frozen=True)
1415
class RegisteredBinding(Generic[InjectedT]):
1516
raw_binding: Binding[InjectedT]
17+
binding_source: Optional["AbstractModule"]
1618
source_path: Tuple["PrivateModule", ...] = attr.Factory(tuple)
1719
target: FrozenTarget[InjectedT] = attr.Factory(lambda self: self.raw_binding.target, takes_self=True)

opyoid/providers/providers_factories/jit_provider_factory.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ def create(self, context: InjectionContext[InjectedT]) -> Provider[InjectedT]:
1818
and isinstance(context.target.type, type)
1919
):
2020
return self._provider_factory.create(
21-
RegisteredBinding(SelfBinding(context.target.type, named=context.target.named)), context
21+
RegisteredBinding(SelfBinding(context.target.type, named=context.target.named), None), context
2222
)
2323
raise IncompatibleProviderFactory

tests/test_bindings/test_abstract_module.py

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ def configure(self) -> None:
5050
self.module.install(module)
5151
self.assertEqual(
5252
{
53-
FrozenTarget(MyType): RegisteredBinding(SelfBinding(MyType)),
54-
FrozenTarget(OtherType, "my_name"): RegisteredBinding(SelfBinding(OtherType, named="my_name")),
53+
FrozenTarget(MyType): RegisteredBinding(SelfBinding(MyType), module),
54+
FrozenTarget(OtherType, "my_name"): RegisteredBinding(SelfBinding(OtherType, named="my_name"), module),
5555
},
5656
self.module.binding_registry.get_bindings_by_target(),
5757
)
@@ -65,7 +65,7 @@ def configure(self) -> None:
6565
self.module.install(OtherModule)
6666
self.assertEqual(
6767
{
68-
FrozenTarget(MyType): RegisteredBinding(SelfBinding(MyType), source_path=(ANY,)),
68+
FrozenTarget(MyType): RegisteredBinding(SelfBinding(MyType), ANY, source_path=(ANY,)),
6969
},
7070
self.module.binding_registry.get_bindings_by_target(),
7171
)
@@ -75,7 +75,7 @@ def test_bind_class_to_itself(self):
7575

7676
self.assertEqual(
7777
{
78-
FrozenTarget(MyType): RegisteredBinding(SelfBinding(MyType)),
78+
FrozenTarget(MyType): RegisteredBinding(SelfBinding(MyType), self.module),
7979
},
8080
self.module.binding_registry.get_bindings_by_target(),
8181
)
@@ -85,8 +85,8 @@ def test_bind_class_to_another_class(self):
8585

8686
self.assertEqual(
8787
{
88-
FrozenTarget(MyType): RegisteredBinding(ClassBinding(MyType, OtherType)),
89-
FrozenTarget(OtherType): RegisteredBinding(SelfBinding(OtherType)),
88+
FrozenTarget(MyType): RegisteredBinding(ClassBinding(MyType, OtherType), self.module),
89+
FrozenTarget(OtherType): RegisteredBinding(SelfBinding(OtherType), self.module),
9090
},
9191
self.module.binding_registry.get_bindings_by_target(),
9292
)
@@ -97,7 +97,7 @@ def test_bind_instance(self):
9797

9898
self.assertEqual(
9999
{
100-
FrozenTarget(MyType): RegisteredBinding(InstanceBinding(MyType, my_instance)),
100+
FrozenTarget(MyType): RegisteredBinding(InstanceBinding(MyType, my_instance), self.module),
101101
},
102102
self.module.binding_registry.get_bindings_by_target(),
103103
)
@@ -108,8 +108,8 @@ def test_bind_multiple_overrides_binding(self):
108108

109109
self.assertEqual(
110110
{
111-
FrozenTarget(MyType): RegisteredBinding(ClassBinding(MyType, OtherType)),
112-
FrozenTarget(OtherType): RegisteredBinding(SelfBinding(OtherType)),
111+
FrozenTarget(MyType): RegisteredBinding(ClassBinding(MyType, OtherType), self.module),
112+
FrozenTarget(OtherType): RegisteredBinding(SelfBinding(OtherType), self.module),
113113
},
114114
self.module.binding_registry.get_bindings_by_target(),
115115
)
@@ -118,7 +118,7 @@ def test_bind_with_scope(self):
118118
self.module.bind(MyType, scope=PerLookupScope)
119119
self.assertEqual(
120120
{
121-
FrozenTarget(MyType): RegisteredBinding(SelfBinding(MyType, scope=PerLookupScope)),
121+
FrozenTarget(MyType): RegisteredBinding(SelfBinding(MyType, scope=PerLookupScope), self.module),
122122
},
123123
self.module.binding_registry.get_bindings_by_target(),
124124
)
@@ -132,10 +132,11 @@ def test_bind_with_name(self):
132132

133133
self.assertEqual(
134134
{
135-
FrozenTarget(MyType): RegisteredBinding(InstanceBinding(MyType, my_instance)),
136-
FrozenTarget(MyType, "my_name"): RegisteredBinding(SelfBinding(MyType, named="my_name")),
135+
FrozenTarget(MyType): RegisteredBinding(InstanceBinding(MyType, my_instance), self.module),
136+
FrozenTarget(MyType, "my_name"): RegisteredBinding(SelfBinding(MyType, named="my_name"), self.module),
137137
FrozenTarget(OtherType, "my_other_name"): RegisteredBinding(
138-
InstanceBinding(OtherType, my_other_instance, named="my_other_name")
138+
InstanceBinding(OtherType, my_other_instance, named="my_other_name"),
139+
self.module,
139140
),
140141
},
141142
self.module.binding_registry.get_bindings_by_target(),
@@ -146,10 +147,12 @@ def test_bind_provider_class(self):
146147
self.assertEqual(
147148
{
148149
FrozenTarget(MyType, "my_name"): RegisteredBinding(
149-
ProviderBinding(MyType, MyProvider, scope=PerLookupScope, named="my_name")
150+
ProviderBinding(MyType, MyProvider, scope=PerLookupScope, named="my_name"),
151+
self.module,
150152
),
151153
FrozenTarget(MyProvider, "my_name"): RegisteredBinding(
152-
SelfBinding(MyProvider, scope=PerLookupScope, named="my_name")
154+
SelfBinding(MyProvider, scope=PerLookupScope, named="my_name"),
155+
self.module,
153156
),
154157
},
155158
self.module.binding_registry.get_bindings_by_target(),
@@ -159,8 +162,8 @@ def test_bind_provider_instance(self):
159162
self.module.bind(MyType, to_provider=self.my_provider)
160163
self.assertEqual(
161164
{
162-
FrozenTarget(MyType): RegisteredBinding(ProviderBinding(MyType, self.my_provider)),
163-
FrozenTarget(MyProvider): RegisteredBinding(InstanceBinding(MyProvider, self.my_provider)),
165+
FrozenTarget(MyType): RegisteredBinding(ProviderBinding(MyType, self.my_provider), self.module),
166+
FrozenTarget(MyProvider): RegisteredBinding(InstanceBinding(MyProvider, self.my_provider), self.module),
164167
},
165168
self.module.binding_registry.get_bindings_by_target(),
166169
)
@@ -202,23 +205,27 @@ def test_multi_binding(self):
202205
named="my_name",
203206
override_bindings=False,
204207
),
208+
self.module,
205209
item_bindings=[
206210
RegisteredBinding(
207-
SelfBinding(MyType, scope=PerLookupScope, named="my_name"),
211+
SelfBinding(MyType, scope=PerLookupScope, named="my_name"), binding_source=self.module
208212
),
209213
RegisteredBinding(
210-
InstanceBinding(MyType, instance, named="my_name"),
214+
InstanceBinding(MyType, instance, named="my_name"), binding_source=self.module
211215
),
212216
RegisteredBinding(
213217
ProviderBinding(MyType, provider, scope=PerLookupScope, named="my_name"),
218+
binding_source=self.module,
214219
),
215220
],
216221
),
217222
FrozenTarget(MyProvider, "my_name"): RegisteredBinding(
218-
SelfBinding(MyProvider, scope=PerLookupScope, named="my_name")
223+
SelfBinding(MyProvider, scope=PerLookupScope, named="my_name"),
224+
self.module,
219225
),
220226
FrozenTarget(MyType, "my_name"): RegisteredBinding(
221-
SelfBinding(MyType, scope=PerLookupScope, named="my_name")
227+
SelfBinding(MyType, scope=PerLookupScope, named="my_name"),
228+
self.module,
222229
),
223230
},
224231
self.module.binding_registry.get_bindings_by_target(),
@@ -244,13 +251,10 @@ def test_multi_binding_default_parameters(self):
244251
named=None,
245252
override_bindings=False,
246253
),
247-
item_bindings=[
248-
RegisteredBinding(
249-
SelfBinding(MyType),
250-
)
251-
],
254+
self.module,
255+
item_bindings=[RegisteredBinding(SelfBinding(MyType), binding_source=self.module)],
252256
),
253-
FrozenTarget(MyType): RegisteredBinding(SelfBinding(MyType)),
257+
FrozenTarget(MyType): RegisteredBinding(SelfBinding(MyType), self.module),
254258
},
255259
self.module.binding_registry.get_bindings_by_target(),
256260
)

0 commit comments

Comments
 (0)