-
Notifications
You must be signed in to change notification settings - Fork 0
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
Type Hints #3
Comments
Past experience with adding type hints for |
Alright, so after fiddling w/ it a couple hours ( from collections.abc import Callable
from typing import Awaitable, Generic, ParamSpec, TypeVar, overload
P = ParamSpec('P')
R1 = TypeVar('R1')
R2 = TypeVar('R2')
@overload
class composable(Generic[R1, R2]):
__wrapped__: Callable[[R1], Awaitable[R2]]
def __init__(self, function: Callable[[R1], Awaitable[R2]]):
...
def __call__(self, argument: R1) -> Awaitable[R2]:
...
@overload
def __ror__(self, other: Callable[P, Awaitable[R1]]) -> 'composable[R1, Awaitable[R2]]':
...
@overload
def __ror__(self, other: Callable[P, R1]) -> 'composable[P, Awaitable[R2]]':
...
@overload
class composable(Generic[R1, R2]):
__wrapped__: Callable[[R1], R2]
def __init__(self, function: Callable[[R1], R2]):
...
def __call__(self, argument: R1) -> R2:
...
@overload
def __ror__(self, other: Callable[P, Awaitable[R1]]) -> 'composable[R1, Awaitable[R2]]':
...
@overload
def __ror__(self, other: Callable[P, R1]) -> 'composable[P, R2]':
...
@overload
class composable(Generic[P, R1]):
__wrapped__: Callable[P, Awaitable[R1]]
def __init__(self, function: Callable[P, Awaitable[R1]]):
...
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> Awaitable[R1]:
...
@overload
def __or__(self, other: Callable[[R1], Awaitable[R2]]) -> 'composable[P, Awaitable[R2]]':
...
@overload
def __or__(self, other: Callable[[R1], R2]) -> 'composable[P, Awaitable[R2]]':
...
@overload
class composable(Generic[P, R1]):
__wrapped__: Callable[P, R1]
def __init__(self, function: Callable[P, R1]):
...
def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R1:
...
@overload
def __or__(self, other: Callable[[R1], Awaitable[R2]]) -> 'composable[P, Awaitable[R2]]':
...
@overload
def __or__(self, other: Callable[[R1], R2]) -> 'composable[P, R2]':
... Couple notes:
So funny thing. MyPy trips all over it with 16 errors and Pyright only reports one error, but they have that one error in common: they assume that In retrospect, I should've expected this. Of course the simplest thing for static type checkers to do is to assume, statically, that when they see a type and a I don't blame the static type checkers here, since the general solution would require symbolically executing arbitrary python code to analyze whether or not it's overloading the |
TL;DR: The choice of Overloading that works fine at runtime, but static type checkers currently assume that |
Another problem with what I've got so far is that even though it almost works in Pyright when types are out of the picture, Pyright fails to recognize None of this was problem in |
I tried this earlier, but it actually just gave false positive successes for all compositions where types didn't match. from collections.abc import Callable
from typing import Awaitable, ParamSpec, TypeVar, overload
P = ParamSpec('P')
R1 = TypeVar('R1')
R2 = TypeVar('R2')
@overload
class composable(Callable[[R1], Awaitable[R2]]):
__wrapped__: Callable[[R1], Awaitable[R2]]
def __init__(self, function: Callable[[R1], Awaitable[R2]]):
...
@overload
def __ror__(self, other: Callable[P, Awaitable[R1]]) -> 'composable[P, Awaitable[R2]]':
...
@overload
def __ror__(self, other: Callable[P, R1]) -> 'composable[P, Awaitable[R2]]':
...
@overload
class composable(Callable[[R1], R2]):
__wrapped__: Callable[[R1], R2]
def __init__(self, function: Callable[[R1], R2]):
...
@overload
def __ror__(self, other: Callable[P, Awaitable[R1]]) -> 'composable[P, Awaitable[R2]]':
...
@overload
def __ror__(self, other: Callable[P, R1]) -> 'composable[P, R2]':
...
@overload
class composable(Callable[P, Awaitable[R1]]):
__wrapped__: Callable[P, Awaitable[R1]]
def __init__(self, function: Callable[P, Awaitable[R1]]):
...
@overload
def __or__(self, other: Callable[[R1], Awaitable[R2]]) -> 'composable[P, Awaitable[R2]]':
...
@overload
def __or__(self, other: Callable[[R1], R2]) -> 'composable[P, Awaitable[R2]]':
...
@overload
class composable(Callable[P, R1]):
__wrapped__: Callable[P, R1]
def __init__(self, function: Callable[P, R1]):
...
@overload
def __or__(self, other: Callable[[R1], Awaitable[R2]]) -> 'composable[P, Awaitable[R2]]':
...
@overload
def __or__(self, other: Callable[[R1], R2]) -> 'composable[P, R2]':
... |
I'm closing this issue to indicate that I personally am no longer actively working on it, but I still welcome comments and help from others. If someone brings a working set of type hints I'll gladly merge it, and I'd gladly add a second operator to entirely bypass the type union ambiguity. I'll even accept working type-hints for just reduced sets of cases (e.g. no I'll cut v1.0.0 without static type hints - those can be a future feature release. In the meantime: in most code-bases where I'd want static type checking, I'd use To me, the operator-overload approach has always been the kind of cheeky fun convenience that I'd use in fast prototyping, one-off scripts, and interactive REPL use - all uses where static typing is overhead more than help. Operator overload like this is far enough from the standard mainstream of the language that I wouldn't use it in a code-base that I thought might be maintained by some random junior- or mid-level Python dev in five years, because the ecosystem might move on in ways that break it further, and that dev is probably reading while expecting/assuming idiomatic code, and might more slowed than helped by needing to adjust their interpretation of I see So I don't think it's actually a major loss. I just had hoped that it would be doable enough that I might as well add it, but it no longer seems that way. |
I have an idea, but I don't know if nested overloads can combine like this, and might not have time to check for a while... would really appreciate help looking into it (and if it doesn't work, help with figuring out how to make it work or what the next best thing is):
@ruancomelli, you did some work figuring this out in pytoolz/toolz#523 (comment) - any thoughts?
The text was updated successfully, but these errors were encountered: