-
Notifications
You must be signed in to change notification settings - Fork 93
Support optional parameters #276
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?
Support optional parameters #276
Conversation
@davidparsson @jstasiak wanted to get your thoughts on this. Feel free to tag anyone else for discussion that I may have missed! |
@AvlWx2014, thanks for your contribution. Your use case is reasonable, but I will have to consider the implications of this. I am pretty sure this will break things for me, and probably for other people as well, since I think Injector's handling of optional arguments is not ideal either, and while it's not the same issue it might be related. The obvious workaround without breaking typing with the current version would in your example be to only provide |
242dc39
to
5866e12
Compare
Hi @davidparsson sorry for the delayed reply, I was on vacation 😄
I see what you mean here. My thinking originally was that this change would remain backwards compatible since I imagine you've seen plenty more use cases than I have which might prove my assumptions wrong, so let me know what you think!
I definitely could, yes, and there's also the option to use the null object pattern here. Something like: @dataclass
class NullConfig:
pass
def __bool__(self) -> bool:
return False
AuthConfig: TypeAlias = OAuth2Config | OidcConfig | NullConfig
@dataclass
class AppConfig:
auth: AuthConfig = NullConfig() but I think supporting requests for optional types is still useful. |
It seems I'm not as fast myself either. 🐢
I was hoping for that as well, and since I found no real evidence that this was working, I created a test on this branch myself and found that it failed:
It actually seems that only the last one of these four tests I created pass, and I would ideally like all of them to pass, althogh the one with
I also need to refresh my memory on how |
It seems parametrizing the types works well, and would show all combinations:
|
...but regarding the tests with parametrization, I'm not sure it's a great idea to let |
Thanks for sharing this test case I think it's useful to add in to this PR to demonstrate what we're discussing here so I'll do that and push a new patch set.
When you say it like this my immediate reaction is "I agree", which means that my original thinking that this would remain backwards compatible is flawed. I still think support for optional parameters is useful, though, so we could label it a breaking change on the basis that |
Overview
Support bindings for optional parameters.
Background
In application code some pieces of configuration can be considered optional. For example, consider an API server that supports authentication/authorization in staging and production, but can have this feature "turned off" for development by simply omitting the
auth
section of the server configuration.injector
already supports providers for optional types e.g.:In this example, I can easily set up an injector and get
AuthConfig | None
from the graph:When this is run it prints:
However, if I then write a provider function which requests
AuthConfig | None
as a dependency like:The injector raises indicating there is no provider for
Union[OAuth2Config, OidcConfig]
(type names shortened for brevity). Given theAuthConfig: TypeAlias = OAuth2Config | OidcConfig
alias from earlier this message implies something somewhere is requesting plainAuthConfig
as a dependency rather thanAuthConfig | None
, which has a provider in the graph.However, after combing my whole dependency graph I could not find a place where this was actually happening in my application code so I started digging further. After a lot of debugging I found that the function
_infer_injected_bindings
removesNoneType
from unions before resolving the rest of the injectable dependencies from a function signature:After removing the set difference operation from the assignment to
new_members
the code examples I share above start working instead of raising an exception about missing providers.Solution
The solution proposed here removes
- {type(None)}
fromnew_members = ...
to allow optional dependencies to be fulfilled if there is a provider configured for them.I also included a test to demonstrate the behavior in
test_get_bindings
. In the first commit the new test fails, but in the second commit with the proposed change applied it passes.This patch induces a change in the test for
test_get_bindings_for_pep_604
since nowa: int | None
resolves toint | None
instead of justint
.The rest of the test suite passes with this patch in place, so from what I can tell there aren't any unintended side effects on the rest of the codebase, but let me know if I'm missing something. Looking forward to getting your thoughts on this!