-
Notifications
You must be signed in to change notification settings - Fork 81
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
Add support for Annotated types #219
Conversation
injector_test.py
Outdated
def test_annotated_instance_integration_works(): | ||
UserID = Annotated[int, "user_id"] | ||
|
||
def configure(binder): | ||
binder.bind(UserID, to=123) | ||
|
||
injector = Injector([configure]) | ||
assert injector.get(UserID) == 123 | ||
|
||
|
||
def test_annotated_class_integration(): | ||
class Shape(abc.ABC): | ||
pass | ||
|
||
class Circle(Shape): | ||
pass | ||
|
||
first = Annotated[Shape, "first"] | ||
|
||
def configure(binder): | ||
binder.bind(first, to=Circle) | ||
|
||
injector = Injector([configure]) | ||
assert isinstance(injector.get(first), Circle) |
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 your contribution!
I'd like to see another test or two, with multiple bindings to see that Injector can keep them apart. For example:
- Making sure that
Annotated[int, "first"]
andAnnotated[int, "second"]
get separate bindings/values - Making sure that
Annotated[int, "user_id"]
andint
get separate bindings/values
What do you think?
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 the feedback! Makes sense, I will add some more tests and take a look at the failing ones.
There are a few failing tests as well, with different problems for different versions. |
@davidparsson have you had time to take a look at the changes? 🙂 |
Sorry about that.. I've now tested with 3.7-3.11 locally and everything passes. There are now less checks to recognise I think the |
That's an interesting feature, I've been wondering about some kind of Annotated support for a while. I'm wondering if we're not missing some edge cases with this approach that will become obvious later, I'll have another look at this PR when I have a moment |
Codecov ReportAll modified and coverable lines are covered by tests ✅
❗ Your organization needs to install the Codecov GitHub app to enable full functionality. Additional details and impacted files@@ Coverage Diff @@
## master #219 +/- ##
==========================================
+ Coverage 95.20% 95.22% +0.01%
==========================================
Files 1 1
Lines 563 565 +2
Branches 95 96 +1
==========================================
+ Hits 536 538 +2
Misses 20 20
Partials 7 7 ☔ View full report in Codecov by Sentry. |
Have you had time to take a look yet @jstasiak ? I would like to start using this 🙂 |
This depends on this being true: assert Annotated[int, "first"] == Annotated[int, "first"] which I didn't know if it were or not, so I checked, and I think this holds due to I have checked this to be the case in python 3.10. I have also checked that the impl is order sensitive, e.g assert Annotated[int, "first", "second"] != Annotated[int, "second", "first"] holds. A funny corner case due to the equality requirement is that providing |
@tobni thanks for chiming in on this.
Not sure what you mean by this, I just added a test for both of your examples and both seem to work fine. Note that x = float("nan")
x == x # False
y = Annotated[int, float("nan")]
y == y # True |
Ah I was thinking of Annotated[int, object()] == Annotated[int, object()] # False Which is not an impossible (but unlikely!) scenario. In essence using bare annotations without "aliasing", and not having the metadatas But this is immaterial, the equality impl of |
Ok, I see what you mean now, when passing in an annotated type with another instance in the metadata it indeed breaks down. I'd argue that's expected behaviour though, since it's clear that what you're looking up from the injector is not what you bound. It would be better to throw an error in that case though, because currently foo = Annotated[int, object()]
def configure(binder):
binder.bind(foo, to=123)
injector = Injector([configure])
injector.get(foo) # returns 123
injector.get(Annotated[int, object()]) # returns 0 Not clear to my why it's returning 0 in the second case, I'll check that. |
Probably because |
Very nice test coverage, I'm convinced this would be good to merge but I need to think of a thing or two first – stay tuned and thanks for your patience. |
is this PR supposed to also add support for Foo = Annotated[int, object()]
def configure(binder):
binder.bind(Foo, to=123)
@inject
@dataclass
class MyClass:
foo: Foo
injector = Injector([configure])
instance = injector.get(MyClass)
assert instance.foo == 123 It looks like |
+1 to what @ricardo-passthrough mentioned, this PR doesn't work with |
The implementation so far has just been released in version 0.22.0. |
Add support for
Annotated
types.This is a relatively simple and straightforward implementation with minimal changes and seems to work fine.
Resolves #217