traitlets icon indicating copy to clipboard operation
traitlets copied to clipboard

Static typing

Open davidbrochart opened this issue 4 years ago • 11 comments

traitlets' types are used to perform run-time checks. Would adding type annotation on them make sense? It seems that we have some information that we could use to potentially find bugs ahead of time. For instance, should a Bool be statically typed as a bool? I don't even know if that makes sense :smile: One problem I'm seeing (for instance in https://github.com/jupyter/nbclient) is that adding type annotations on traits doesn't seem to have any effect.

davidbrochart avatar Mar 08 '21 10:03 davidbrochart

That'd definitely be a possibility, and the way you'd want to type this is via generics:

T = TypeVar("T")

class TraitType(Generic[T]):
    def __get__(self, obj, cls) -> T:
        ...
    def __set__(self, obj, val: T) -> None:
        ...

class Bool(TraitType[bool]):
    ...

With this approach you wouldn't actually have to add type annotations to your classes - you'd basically just get it for free.

rmorshea avatar Mar 08 '21 20:03 rmorshea

Interesting, thanks Ryan. I'm going to try this out with a simple case and report back on how it goes.

davidbrochart avatar Mar 08 '21 20:03 davidbrochart

I think this'd be a pretty useful feature for Traitlets since it's non-intrusive, but will have a broad positive impact on everyone downstream that shouldn't require significant code changes that will probably find a lot code smells and bugs!

Also, depending on how you see things, this addition might constitute a breaking change, because users running MyPy might experience errors in their test suite even though the Traitlets API will technically have remained unchanged. So my inclination is to say this would require a Traitlets 6.0 release. Thoughts @SylvainCorlay?

rmorshea avatar Mar 08 '21 20:03 rmorshea

Actually it brings up some interesting questions, for instance, how can allow_none=True be supported? Since this would translate into the Optional[...] static typing annotation, how to apply this conditionally to the allow_none value?

davidbrochart avatar Mar 09 '21 08:03 davidbrochart

It definitely gets a little tricky, but I think you could solve that with typing.overload:

T = TypeVar("T")

class TraitType(Generic[T]):

    @overload
    def __new__(cls, allow_none: Literal[True]) -> TraitType[Optional[T]]:
        ...
    @overload
    def __new__(cls, allow_none: Literal[False]) -> TraitType[T]:
        ...
    def __new__(cls, allow_none: bool = False) -> None:
        ...

rmorshea avatar Mar 10 '21 17:03 rmorshea

+1 to this idea, after finding that while trying to keep both traitlets and PyCharm type hints happy, I ended up writing Java :

distance: float = Float()

😆

mangecoeur avatar Oct 07 '21 12:10 mangecoeur

Actually, I'm thinking more and more that we should do things the other way around: not add static typing to a dynamic typing library, but add dynamic typing to static typing. Oh but wait, that's what Pydantic does :smile:

davidbrochart avatar Oct 07 '21 13:10 davidbrochart

You're probably right, but what to do about traitlet features like observer pattern and data binding in ipywidgets? Could they work with the pydantic approach? I don't know how that works internally perhaps it's not as disruptive as it seems... what I do know is it's incredibly useful!

mangecoeur avatar Oct 07 '21 14:10 mangecoeur

I think we could implement the observer pattern on top of pydantic, see https://github.com/davidbrochart/pyceptive. For ipywidgets, I think it would be feasible too.

davidbrochart avatar Oct 07 '21 14:10 davidbrochart

The new declare library uses type annotations:

from declare import Declare

class Foo:
    val0 = Declare[int](0)
    val1 = Declare[str]("")

Maybe worth looking at?

davidbrochart avatar Apr 22 '24 17:04 davidbrochart