Fix ParamSpec constraint for types as callable#14153
Fix ParamSpec constraint for types as callable#14153hauntsaninja merged 3 commits intopython:masterfrom
Conversation
Most types can be considered as callables, constructing the type itself. When a constraint was created for a ParamSpec variable, the return type would be set to NoneType, which conflicts with assumptions that CallableType makes when it is the constructor of another type, crashing mypy. This patch replaces the return type by UninhabitedType instead, which stops CallableType from considering itself as a constructor.
This comment has been minimized.
This comment has been minimized.
hauntsaninja
left a comment
There was a problem hiding this comment.
Thanks for finding and fixing! This looks great :-)
mypy_primer also looks promising, but haven't yet confirmed it's correct
For my reference, this is the guard mentioned in the PR description:
Line 1773 in 401798f
|
The mypy_primer diff boils down to the following example: from typing import Callable, ParamSpec, Union, Any
P = ParamSpec("P")
def wrapper(coro: Callable[P, int]) -> Callable[P, int]:
return coro
f: Union[Callable[[str], int], Any]
g = wrapper(f)
# Before: error: Argument 1 to "wrapper" has incompatible type "Union[Callable[[str], int], Any]"; expected "Callable[[VarArg(<nothing>), KwArg(<nothing>)], int]"
# After: No error
# --no-strict-optional: No error
reveal_type(g)
# Before: Revealed type is "def (*<nothing>, **<nothing>) -> int"
# After: Revealed type is "def (builtins.str) -> int"
# --no-strict-optional: Revealed type is "def (builtins.str) -> int"The main difference is that the constraint on |
Co-authored-by: Shantanu <[email protected]>
92f9899 to
5ff55dc
Compare
This comment has been minimized.
This comment has been minimized.
hauntsaninja
left a comment
There was a problem hiding this comment.
Thanks again! I resolved a merge conflict, will merge once tests pass again
|
Diff from mypy_primer, showing the effect of this PR on open source code: discord.py (https://github.com/Rapptz/discord.py)
- discord/ext/commands/context.py:605: error: Argument 1 to "wrap_callback" has incompatible type "Callable[[Cog], Coroutine[Any, Any, None]]"; expected "Callable[[Mapping[Optional[Cog], List[Command[Any, [VarArg(Any), KwArg(Any)], Any]]]], Coroutine[Any, Any, None]]" [arg-type]
+ discord/ext/commands/context.py:605: error: Incompatible types in assignment (expression has type "Callable[[Cog], Coroutine[Any, Any, None]]", variable has type "Callable[[Mapping[Optional[Cog], List[Command[Any, [VarArg(Any), KwArg(Any)], Any]]]], Coroutine[Any, Any, None]]") [assignment]
- discord/ext/commands/context.py:608: error: Argument 1 to "wrap_callback" has incompatible type "Callable[[Group[Any, [VarArg(Any), KwArg(Any)], Any]], Coroutine[Any, Any, None]]"; expected "Callable[[Mapping[Optional[Cog], List[Command[Any, [VarArg(Any), KwArg(Any)], Any]]]], Coroutine[Any, Any, None]]" [arg-type]
+ discord/ext/commands/context.py:608: error: Incompatible types in assignment (expression has type "Callable[[Group[Any, [VarArg(Any), KwArg(Any)], Any]], Coroutine[Any, Any, None]]", variable has type "Callable[[Mapping[Optional[Cog], List[Command[Any, [VarArg(Any), KwArg(Any)], Any]]]], Coroutine[Any, Any, None]]") [assignment]
- discord/ext/commands/context.py:611: error: Argument 1 to "wrap_callback" has incompatible type "Callable[[Command[Any, [VarArg(Any), KwArg(Any)], Any]], Coroutine[Any, Any, None]]"; expected "Callable[[Mapping[Optional[Cog], List[Command[Any, [VarArg(Any), KwArg(Any)], Any]]]], Coroutine[Any, Any, None]]" [arg-type]
+ discord/ext/commands/context.py:611: error: Incompatible types in assignment (expression has type "Callable[[Command[Any, [VarArg(Any), KwArg(Any)], Any]], Coroutine[Any, Any, None]]", variable has type "Callable[[Mapping[Optional[Cog], List[Command[Any, [VarArg(Any), KwArg(Any)], Any]]]], Coroutine[Any, Any, None]]") [assignment]
- discord/ext/commands/core.py:641: error: Argument 1 to "wrap_callback" has incompatible type "Union[Callable[[Context[BotT], Exception], Coroutine[Any, Any, None]], Any]"; expected "Callable[[VarArg(<nothing>), KwArg(<nothing>)], Coroutine[Any, Any, Optional[Any]]]" [arg-type]
- discord/ext/commands/core.py:642: error: Argument 1 has incompatible type "Context[BotT]"; expected <nothing> [arg-type]
- discord/ext/commands/core.py:642: error: Argument 2 has incompatible type "CommandError"; expected <nothing> [arg-type]
- discord/ext/commands/core.py:1271: error: Argument 1 to "maybe_coroutine" has incompatible type "Union[Callable[[Context[BotT]], bool], Any]"; expected "Callable[[VarArg(<nothing>), KwArg(<nothing>)], Union[Any, Awaitable[Any]]]" [arg-type]
- discord/ext/commands/core.py:1271: error: Argument 2 to "maybe_coroutine" has incompatible type "Context[BotT]"; expected <nothing> [arg-type]
+ discord/ext/commands/core.py:1271: error: Argument 2 to "maybe_coroutine" has incompatible type "Context[BotT]"; expected "Context[BotT]" [arg-type]
|
Most types can be considered as callables, constructing the type itself. When a constraint was created for a ParamSpec variable, the return type would be set to NoneType, which conflicts with assumptions that CallableType makes when it is the constructor of another type, crashing mypy. This patch replaces the return type by UninhabitedType instead, which stops CallableType from considering itself as a constructor.