Skip to content

Commit 6986993

Browse files
authored
Do not assume that args of decorated functions can be cleanly mapped to their nodes (#20203)
Fixes #20059. If any non-trivial decorator is present, avoid trying to pick the parameter corresponding to i-th parameter of the callable type.
1 parent 66dd2c1 commit 6986993

File tree

2 files changed

+64
-1
lines changed

2 files changed

+64
-1
lines changed

mypy/checker.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2578,7 +2578,18 @@ def erase_override(t: Type) -> Type:
25782578
continue
25792579
if not is_subtype(original_arg_type, erase_override(override_arg_type)):
25802580
context: Context = node
2581-
if isinstance(node, FuncDef) and not node.is_property:
2581+
if (
2582+
isinstance(node, FuncDef)
2583+
and not node.is_property
2584+
and (
2585+
not node.is_decorated # fast path
2586+
# allow trivial decorators like @classmethod and @override
2587+
or not (sym := node.info.get(node.name))
2588+
or not isinstance(sym.node, Decorator)
2589+
or not sym.node.decorators
2590+
)
2591+
):
2592+
# If there's any decorator, we can no longer map arguments 1:1 reliably.
25822593
arg_node = node.arguments[i + override.bound()]
25832594
if arg_node.line != -1:
25842595
context = arg_node

test-data/unit/check-classes.test

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,58 @@ class B(A):
585585
@dec
586586
def f(self) -> int: pass
587587

588+
[case testOverrideWithDecoratorReturningCallable]
589+
from typing import Any, Callable, TypeVar
590+
591+
class Base:
592+
def get(self, a: str) -> None: ...
593+
594+
def dec(fn: Any) -> Callable[[Any, int], None]: ...
595+
596+
class Derived(Base):
597+
@dec
598+
def get(self) -> None: ... # E: Argument 1 of "get" is incompatible with supertype "Base"; supertype defines the argument type as "str" \
599+
# N: This violates the Liskov substitution principle \
600+
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
601+
[builtins fixtures/tuple.pyi]
602+
603+
[case testOverrideWithDecoratorReturningCallable2]
604+
# flags: --pretty
605+
from typing import Any, Callable, TypeVar
606+
607+
_C = TypeVar("_C", bound=Callable[..., Any])
608+
609+
def infer_signature(f: _C) -> Callable[[Any], _C]: ...
610+
611+
class Base:
612+
def get(self, a: str, b: str, c: str) -> None: ...
613+
def post(self, a: str, b: str) -> None: ...
614+
615+
# Third argument incompatible
616+
def get(self, a: str, b: str, c: int) -> None: ...
617+
618+
# Second argument incompatible - still should not map to **kwargs
619+
def post(self, a: str, b: int) -> None: ...
620+
621+
class Derived(Base):
622+
@infer_signature(get)
623+
def get(self, *args: Any, **kwargs: Any) -> None: ...
624+
625+
@infer_signature(post)
626+
def post(self, *args: Any, **kwargs: Any) -> None: ...
627+
[builtins fixtures/tuple.pyi]
628+
[out]
629+
main:20: error: Argument 3 of "get" is incompatible with supertype "Base"; supertype defines the argument type as "str"
630+
def get(self, *args: Any, **kwargs: Any) -> None: ...
631+
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
632+
main:20: note: This violates the Liskov substitution principle
633+
main:20: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
634+
main:23: error: Argument 2 of "post" is incompatible with supertype "Base"; supertype defines the argument type as "str"
635+
def post(self, *args: Any, **kwargs: Any) -> None: ...
636+
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
637+
main:23: note: This violates the Liskov substitution principle
638+
main:23: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides
639+
588640
[case testOverrideWithDecoratorReturningInstance]
589641
def dec(f) -> str: pass
590642

0 commit comments

Comments
 (0)