-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Make sure to keep type variables even if unused. #15248
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
Conversation
This is useful if (via ParamSpec) you get `[T] () -> (T) -> T`. Otherwise this would become `(<nothing>) -> <nothing>` which is just wrong! This fixes python#12595.
This comment has been minimized.
This comment has been minimized.
Did not realize renaming branches via GitHub's interface did that. |
Diff from mypy_primer, showing the effect of this PR on open source code: dacite (https://github.com/konradhalas/dacite) got 3.40x faster (7.5s -> 2.2s)
prefect (https://github.com/PrefectHQ/prefect)
- src/prefect/client/base.py:36: error: Argument 1 to "asynccontextmanager" has incompatible type "Callable[[Any], AbstractContextManager[None]]"; expected "Callable[[Any], AsyncIterator[<nothing>]]" [arg-type]
+ src/prefect/client/base.py:36: error: Argument 1 to "asynccontextmanager" has incompatible type "Callable[[Any], AbstractContextManager[None]]"; expected "Callable[[Any], AsyncIterator[_T_co]]" [arg-type]
- src/prefect/context.py:380: error: Argument 1 to "contextmanager" has incompatible type "Callable[[VarArg(str)], set[str]]"; expected "Callable[[VarArg(str)], Iterator[<nothing>]]" [arg-type]
+ src/prefect/context.py:380: error: Argument 1 to "contextmanager" has incompatible type "Callable[[VarArg(str)], set[str]]"; expected "Callable[[VarArg(str)], Iterator[_T_co]]" [arg-type]
ibis (https://github.com/ibis-project/ibis)
- ibis/backends/tests/test_client.py:1208: error: Argument 1 to "contextmanager" has incompatible type "Callable[[BaseBackend], str]"; expected "Callable[[BaseBackend], Iterator[<nothing>]]" [arg-type]
+ ibis/backends/tests/test_client.py:1208: error: Argument 1 to "contextmanager" has incompatible type "Callable[[BaseBackend], str]"; expected "Callable[[BaseBackend], Iterator[_T_co]]" [arg-type]
|
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.
Sorry, but I don't think this is a good solution.
@@ -3105,7 +3105,8 @@ T = TypeVar('T') | |||
|
|||
def f(x: Optional[T] = None) -> Callable[..., T]: ... | |||
|
|||
x = f() # E: Need type annotation for "x" | |||
# Question: Is this alright? |
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.
No, this test specifically tests the error will be shown.
# def () -> def [T] (T) -> T | ||
for i, argument in enumerate(inferred_args): | ||
if isinstance(get_proper_type(argument), UninhabitedType): | ||
# un-"freshen" the type variable :^) |
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 don't like this. A type def [T] () -> <anything with T>
doesn't make sense, and we should figure out why it was inferred/constructed in the first place. This is really just sweeping dust under the carpet.
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 can try to explain why this happens:
Have a function that takes a Callable[Concatenate[T, P], ...]
and then returns a Callable[P, Callable[[T], ...]]
. This could be some utility function that curries (in reverse), for example.
Now, the problem is that if you use this on def [T] (T, int) -> ...
then this will turn into... def [T] (int) -> (T) -> ...
. At least, as currently implemented. Thus, I see two solutions:
- Fix the new function at call time (what I do here) when we have all this information
- Fix the function at construction time, which I'm not sure how to do: I'm not quite sure how to scan through the parameters to find if type variables are still used. For example, this should still work:
def [T] (T, T) -> ...
intodef [T] (T) -> T -> ...
.
Sorry if this is a confusing explanation!
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.
Ah just to be clear, the problem with def [T] (int) -> (T) -> ...
is we'll infer an UninhabitedType
for T
, meaning that we can't actually trust the return type anymore.
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.
Interesting. I am actually working on a (tangentially related) PR that will likely fix this (or at least will allow a more principled solution). I will post a link here when it is ready.
transform: Callable[Concatenate[T, P], U] | ||
) -> Callable[P, Callable[[T], U]]: ... | ||
|
||
u = pop_off(t) |
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.
TBH I don't think this situation is what is going on in the issue. IIUC you tried to simplify the example, but it seems to me you may have simplified too much.
I'll close this while waiting for that other PR. Also, this way I have one less PR to worry over! Just wanted to add one more thing: while mypy's current behavior could been seen as right ("you shouldn't pop off parameters like that"), I believe the pragmatic choice is to support some form of this and the API I want for my library necessitates this! |
I'm finally breaking up #14903 into smaller pieces. Sorry about that!
When mypy sees a typevar is unconstrained, it tries to set it to
<nothing>
(UninhabitedType
). This PR modifies this keep the typevar in the returned callable. This is useful if (via ParamSpec) you get[T] () -> (T) -> T
. Otherwise this would become(<nothing>) -> <nothing>
which is just bad, I think! I believe this is significantly simpler than actually catching that ParamSpec case (in which you'd have to tell if theTypeVar
is used somewhere in the args.), but if it isn't that is probably cleaner.This fixes #12595.