Perspective on why some of the current behavior and user types are the way they are might help here.
Please don’t read the below as “we need to enforce LSP on __init__
/ __new__
”, that’s a way forward, but it’s not the only one, and as prior discussions got into, there’s reasons why this wasn’t done.
-
This is only the case due to type checkers not enforcing LSP for
__init__
and might indicate that prior proposals to copy a signature for sure elsewhere paired with intersections would help here (ie.T & Callable[T.__init__, T]
) This would ensure that a type could be constructed as expected. -
While it might be better to define a complex protocol for some of those cases, I don’t see many people being willing to do it when
type[T]
appears to work until someone makes a subclass that violates LSP on__init__
I don’t think it’s a good idea to have a situation where Annotating with the inferred type without an annotation (Any) changes the behavior. This feels like it also is partially necessary due to how __init__
and __new__
are not required to be LSP compatible.
There might be a better inferrable type than Any using the method that was mentioned in the discussion of subtyping involving Any, but this isn’t something that would be user-denotable currently or for a while.
The reason that pyright looks at
__init__
methods first when converting to a callable is that__init__
method is typically richer in type information.__new__
method signatures often consist of(cls, *args, **kwargs)
which is not very useful from a typing perspective. It appears that mypy does the same — presumably for the same reason.
The ability to partially copy signatures might lead to more people typing __new__
accurately, speaking for only myself, the inability to do so is the primary reason I don’t give new more detailed typing. The secondary reason is that type checkers largely ignore it anyway.