From 2820978325e4caccbf91299f895f12acd03b7e44 Mon Sep 17 00:00:00 2001 From: fahadhewad Date: Mon, 17 Aug 2026 09:26:53 +0000 Subject: [PATCH] Allow __rpow__ to accept an optional third modulo argument check_reverse_op_method validated every reverse operator method against a signature taking exactly two positional arguments, so mypy reported "Invalid signature" for any __rpow__ declaring a third modulo argument. The data model documents __rpow__ as object.__rpow__(self, other[, modulo]), and typeshed declares the ternary form for int, float and complex. The check also runs once per overload item, so an overloaded __rpow__ was rejected even when a two argument variant was declared alongside the ternary one. __rpow__ is now accepted with either two or three positional arguments. All other reverse operator methods are unchanged, and an __rpow__ with fewer than two or more than three arguments is still reported. Fixes #10786 --- mypy/checker.py | 21 +++++++++++++-------- test-data/unit/check-classes.test | 19 +++++++++++++++++++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 33ed5387554d8..3e0ec4f2de95e 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1979,14 +1979,19 @@ def check_reverse_op_method( assert defn.info # First check for a valid signature - method_type = CallableType( - [AnyType(TypeOfAny.special_form), AnyType(TypeOfAny.special_form)], - [nodes.ARG_POS, nodes.ARG_POS], - [None, None], - AnyType(TypeOfAny.special_form), - self.named_type("builtins.function"), - ) - if not is_subtype(reverse_type, method_type): + method_types = [ + CallableType( + [AnyType(TypeOfAny.special_form)] * num_args, + [nodes.ARG_POS] * num_args, + [None] * num_args, + AnyType(TypeOfAny.special_form), + self.named_type("builtins.function"), + ) + # The data model allows __rpow__ to take an optional third "modulo" + # argument, mirroring the ternary form of __pow__. + for num_args in ([2, 3] if reverse_name == "__rpow__" else [2]) + ] + if not any(is_subtype(reverse_type, method_type) for method_type in method_types): self.msg.invalid_signature(reverse_type, context) return diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 2cd43c74ebb5b..75619803459ac 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -2509,6 +2509,25 @@ class C: tmp/foo.pyi:3: error: Invalid signature "Callable[[B], A]" tmp/foo.pyi:5: error: Invalid signature "Callable[[C, Any, Any], int]" +[case testReverseOperatorMethodTernaryPow] +from foo import * +[file foo.pyi] +from typing import overload +class A: + def __rpow__(self, other: A, modulo: A) -> A: ... +class B: + @overload + def __rpow__(self, other: B) -> B: ... + @overload + def __rpow__(self, other: B, modulo: B) -> B: ... +class C: + def __rpow__(self, other: C, modulo: C, oops: C) -> C: ... +class D: + def __radd__(self, other: D, modulo: D) -> D: ... +[out] +tmp/foo.pyi:10: error: Invalid signature "Callable[[C, C, C, C], C]" +tmp/foo.pyi:12: error: Invalid signature "Callable[[D, D, D], D]" + [case testReverseOperatorOrderingCase1] class A: def __radd__(self, other: 'A') -> int: ...