-
Notifications
You must be signed in to change notification settings - Fork 95
fix: Multibind scopes #284
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #284 +/- ##
==========================================
+ Coverage 95.84% 95.98% +0.13%
==========================================
Files 1 1
Lines 602 623 +21
Branches 103 106 +3
==========================================
+ Hits 577 598 +21
Misses 19 19
Partials 6 6 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Thank you. This looks promising. I'll try to find time to review this properly. |
| # HACK: generate a pseudo-type for this element in the list. | ||
| # This is needed for scopes to work properly. Some, like the Singleton scope, | ||
| # key instances by type, so we need one that is unique to this binding. | ||
| pseudo_type = type(f"multibind-type-{id(provider)}", (provider.__class__,), {}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please elaborate a bit on in what case this is needed? Is it required for the case when multiple different types are bound with the singleton scope in the same multibound type? Just like PluginA and PluginC in the case below:
def test_multibind_dict_scopes_applies_to_the_bound_items() -> None:
def configure(binder: Binder) -> None:
binder.multibind(Dict[str, Plugin], to={'a': PluginA}, scope=singleton)
binder.multibind(Dict[str, Plugin], to={'b': PluginB})
binder.multibind(Dict[str, Plugin], to={'c': PluginC}, scope=singleton)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I quickly tried to reproduce the problem without the pseudo-type, by instead passing the resolved type, which is Plugin for the example above. I got a weird problem in that test case that I haven't yet resolved, but for multibound lists every case I can come up with seems to work as expected. This makes me suspect it's not really needed.
davidparsson
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for one more nice contribution!
I'm planning on merging this, and tweaking a few minor things before releasing, but I would appreciate if you could take the time to respond to my comments.
| isinstance(binding.provider, ClassProvider) | ||
| and binding.scope is NoScope | ||
| and self._binder.parent | ||
| and self._binder.parent.has_explicit_binding_for(binding.provider._cls) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm planning on removing the has_explicit_binding_for() condition, since an explicit binding (that should be respected) may exist on parent.parent.parent as well. That change also makes sure that classes decorated with @singleton are instantiated as far up in the hierarchy as possible, which makes sure the instance is shared among all child injectors.
Do you have any argument against doing this? All tests still pass.
| # HACK: generate a pseudo-type for this element in the list. | ||
| # This is needed for scopes to work properly. Some, like the Singleton scope, | ||
| # key instances by type, so we need one that is unique to this binding. | ||
| pseudo_type = type(f"multibind-type-{id(provider)}", (provider.__class__,), {}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I quickly tried to reproduce the problem without the pseudo-type, by instead passing the resolved type, which is Plugin for the example above. I got a weird problem in that test case that I haven't yet resolved, but for multibound lists every case I can come up with seems to work as expected. This makes me suspect it's not really needed.
| and self._binder.parent | ||
| and self._binder.parent.has_explicit_binding_for(binding.provider._cls) | ||
| ): | ||
| parent_binding, _ = self._binder.parent.get_binding(binding.provider._cls) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should respect the parent provider here as well, for the same reasons as for scopes. I'll fix this.
What
Change how the
scopeargument of themultibindmethod works, so that it applies to the target rather than the containing list or dictionary. Also, consider explicit class bindings when resolving multibindings, and honor the scope of the class binding when the multibinding has no scope.Why
So that it becomes possible to control the scope of individual multi-bindings. E.g.
Closes #283