Skip to content

Commit e093f64

Browse files
committed
Format callables with argnames if their return types are compatible
1 parent 42a97bb commit e093f64

File tree

2 files changed

+50
-1
lines changed

2 files changed

+50
-1
lines changed

mypy/messages.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2855,7 +2855,26 @@ def format_type_distinctly(*types: Type, options: Options, bare: bool = False) -
28552855
quoting them (such as prepending * or **) should use this.
28562856
"""
28572857
overlapping = find_type_overlaps(*types)
2858-
for verbosity in range(2):
2858+
2859+
min_verbosity = 0
2860+
# Prevent emitting weird errors like:
2861+
# ... has incompatible type "Callable[[int], Child]"; expected "Callable[[int], Parent]"
2862+
if len(types) == 2:
2863+
left, right = types
2864+
left = get_proper_type(left)
2865+
right = get_proper_type(right)
2866+
# If the right type has named arguments, they may be the reason for incompatibility.
2867+
# This excludes cases when right is Callable[[Something], None] without named args,
2868+
# because that's usually the right thing to do.
2869+
if (
2870+
isinstance(left, CallableType)
2871+
and isinstance(right, CallableType)
2872+
and is_subtype(left.ret_type, right.ret_type)
2873+
and any(right.arg_names)
2874+
):
2875+
min_verbosity = 1
2876+
2877+
for verbosity in range(min_verbosity, 2):
28592878
strs = [
28602879
format_type_inner(type, verbosity=verbosity, options=options, fullnames=overlapping)
28612880
for type in types

test-data/unit/check-functions.test

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3472,3 +3472,33 @@ class Qux(Bar):
34723472
def baz(self, x) -> None:
34733473
pass
34743474
[builtins fixtures/tuple.pyi]
3475+
3476+
[case testDistinctFormatting]
3477+
from typing import Awaitable, Callable, ParamSpec
3478+
3479+
P = ParamSpec("P")
3480+
3481+
class A: pass
3482+
class B(A): pass
3483+
3484+
def decorator(f: Callable[P, None]) -> Callable[[Callable[P, A]], None]:
3485+
return lambda _: None
3486+
3487+
def key(x: int) -> None: ...
3488+
def fn_b(b: int) -> B: ...
3489+
3490+
decorator(key)(fn_b) # E: Argument 1 has incompatible type "Callable[[Arg(int, 'b')], B]"; expected "Callable[[Arg(int, 'x')], A]"
3491+
3492+
def decorator2(f: Callable[P, None]) -> Callable[
3493+
[Callable[P, Awaitable[None]]],
3494+
Callable[P, Awaitable[None]],
3495+
]:
3496+
return lambda f: f
3497+
3498+
def key2(x: int) -> None:
3499+
...
3500+
3501+
@decorator2(key2) # E: Argument 1 has incompatible type "Callable[[Arg(int, 'y')], Coroutine[Any, Any, None]]"; expected "Callable[[Arg(int, 'x')], Awaitable[None]]"
3502+
async def foo2(y: int) -> None:
3503+
...
3504+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)