Skip to content

Commit 450b043

Browse files
added patch for __radd__
1 parent bd1f51a commit 450b043

File tree

2 files changed

+37
-7
lines changed

2 files changed

+37
-7
lines changed

mypy/checkexpr.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
from mypy.semanal_enum import ENUM_BASES
112112
from mypy.state import state
113113
from mypy.subtypes import (
114+
covers_at_runtime,
114115
find_member,
115116
is_equivalent,
116117
is_same_type,
@@ -4048,14 +4049,20 @@ def lookup_definer(typ: Instance, attr_name: str) -> str | None:
40484049

40494050
variants_raw = [(op_name, left_op, left_type, right_expr)]
40504051
elif (
4051-
is_subtype(right_type, left_type)
4052-
and isinstance(left_type, Instance)
4053-
and isinstance(right_type, Instance)
4054-
and not (
4055-
left_type.type.alt_promote is not None
4056-
and left_type.type.alt_promote.type is right_type.type
4052+
# Note: use covers_at_runtime instead of is_subtype.
4053+
# fixes https://github.com/python/mypy/issues/19006
4054+
covers_at_runtime(right_type, left_type)
4055+
and (
4056+
not (isinstance(left_type, Instance) and isinstance(right_type, Instance))
4057+
or (
4058+
(
4059+
left_type.type.alt_promote is None
4060+
or left_type.type.alt_promote.type is not right_type.type
4061+
)
4062+
and lookup_definer(left_type, op_name)
4063+
!= lookup_definer(right_type, rev_op_name)
4064+
)
40574065
)
4058-
and lookup_definer(left_type, op_name) != lookup_definer(right_type, rev_op_name)
40594066
):
40604067
# When we do "A() + B()" where B is a subclass of A, we'll actually try calling
40614068
# B's __radd__ method first, but ONLY if B explicitly defines or overrides the

test-data/unit/check-expressions.test

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,29 @@ class B:
681681
s: str
682682
s = A() + B() # E: Unsupported operand types for + ("A" and "B")
683683

684+
[case testReverseBinaryOperator4]
685+
686+
from typing import assert_type, Never
687+
688+
class Size(tuple[int, ...]):
689+
def __add__(self, other: tuple[int, ...], /) -> "Size": return Size()
690+
def __radd__(self, other: tuple[int, ...], /) -> "Size": return Size()
691+
692+
size: Size = Size([3, 4])
693+
tup0: tuple[()] = ()
694+
tup1: tuple[int] = (1,)
695+
tup2: tuple[int, int] = (1, 2)
696+
tupN: tuple[int, ...] = (1, 2, 3)
697+
tupX: tuple[Never, ...] = ()
698+
699+
assert_type(tup0 + size, Size)
700+
assert_type(tup1 + size, Size)
701+
assert_type(tup2 + size, Size)
702+
assert_type(tupN + size, Size)
703+
assert_type(tupX + size, Size)
704+
705+
[builtins fixtures/tuple.pyi]
706+
684707
[case testBinaryOperatorWithAnyRightOperand]
685708
from typing import Any, cast
686709
class A: pass

0 commit comments

Comments
 (0)