-
-
Notifications
You must be signed in to change notification settings - Fork 203
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
Allow type checkers to infer trait types #788
Conversation
Typings can still be improved, and more tests would be needed, but this demonstrates the concept.
for more information, see https://pre-commit.ci
Nice! You may want to play with the |
I've been looking into if we can make a distinction between the instance and class access: class IntField:
@typing.overload
def __get__(self, instance: None=None, owner:object=...) -> "IntField":
...
@typing.overload
def __get__(self, instance: object, owner:object=...) -> int:
...
def __get__(self, instance:typing.Any=None, owner:object=None) -> typing.Union["IntField", int]:
if instance is None:
return self
print(instance, owner)
return 42
def __set__(self, instance:object, value: int) -> None:
print("setting", value)
def lala(self):
print("lala")
def on_value(self, handler: typing.Callable[[int], None]) -> None:
pass
class Lala:
x = IntField()
Lala.x.lala()
lala = Lala()
print(lala.x)
# lala.x = "dsa" # gives type error :+1+
lala.x = 42 This seems to work with traitlets as well: diff --git a/traitlets/traitlets.py b/traitlets/traitlets.py
index 0d2649e..2259289 100644
--- a/traitlets/traitlets.py
+++ b/traitlets/traitlets.py
@@ -677,7 +677,15 @@ class TraitType(BaseDescriptor, t.Generic[G, S]):
else:
return value # type: ignore
- def __get__(self, obj: "HasTraits", cls: t.Any = None) -> t.Optional[G]:
+ @t.overload
+ def __get__(self, obj: None=None, cls:t.Any=...) -> "TraitType[G, S]":
+ ...
+
+ @t.overload
+ def __get__(self, obj: "HasTraits", cls:t.Any=...) -> G:
+ ...
+
+ def __get__(self, obj: t.Any=None, cls: t.Any=None) -> t.Union[G, "TraitType[G, S]"]:
"""Get the value of the trait by self.name for the instance.
Default values are instantiated when :meth:`HasTraits.__new__`
@@ -688,7 +696,7 @@ class TraitType(BaseDescriptor, t.Generic[G, S]):
if obj is None:
return self
else:
- return self.get(obj, cls)
+ return t.cast(G, self.get(obj, cls))
def set(self, obj, value):
new_value = self._validate(obj, value) |
@maartenbreddels I thought mypy already did that for class access (it isn't using |
@vidartf, it does not. MyPy checks for polymorphic behavior on |
it is, therefore the Thanks @rmorshea for the gist. Do you have an idea how to approach the |
This appears to work as expected. You can overload the |
for more information, see https://pre-commit.ci
Thanks for the input. I thought I had checked it, but I must have dreamed it. Now added a test for the typing implicitly via using .tag() |
@@ -855,7 +857,7 @@ def set_metadata(self, key, value): | |||
warn("Deprecated in traitlets 4.1, " + msg, DeprecationWarning, stacklevel=2) | |||
self.metadata[key] = value | |||
|
|||
def tag(self, **metadata): | |||
def tag(self, **metadata) -> "TraitType[G, S]": |
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 with the typing_extension
package we can do this:
def tag(self, **metadata) -> "TraitType[G, S]": | |
def tag(self, **metadata) -> typing_extension.Self": |
The failures are due to davidfritzsche/pytest-mypy-testing#35 |
Whow, thanks, you never stop learning with typing tricks! I think it's acceptable, the number of traits types isn't huge, and it will give us really good type checks with pylance and mypy! |
We can pin to |
It turns out we will still need the explicit types on the |
We are currently encoding typing information for traits without integrating with the Python static typing system. This change is for allowing traitlets users to get static typing "for free" by using our trait types.
Ideally, the typing system would allow for default types, such that
TraitType == TraitType[t.Any, t.Any]
, andTraitType[U] == TraitType[U, U]
, but for now we have to be verbose for this to work.This is currently in a draft state, as some of the typings are still not accurately reflecting all the information we have. Potential improvements:
Optional
part depend on allow_none (is it possible?).TraitType
?cc @maartenbreddels