Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f56f49c

Browse files
committedApr 27, 2017
Fix #3262: allow subtypes to define more overloads than their supertype
1 parent 665a810 commit f56f49c

File tree

3 files changed

+48
-22
lines changed

3 files changed

+48
-22
lines changed
 

‎mypy/checker.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -1012,7 +1012,14 @@ def check_override(self, override: FunctionLike, original: FunctionLike,
10121012
# Use boolean variable to clarify code.
10131013
fail = False
10141014
if not is_subtype(override, original, ignore_pos_arg_names=True):
1015-
fail = True
1015+
if (isinstance(override, Overloaded) and
1016+
isinstance(original, Overloaded) and
1017+
name not in nodes.reverse_op_methods.keys()):
1018+
# Allow subtype overloads to be greater than their supertype.
1019+
fail = not is_subtype(original, override,
1020+
ignore_pos_arg_names=True)
1021+
else:
1022+
fail = True
10161023
elif (not isinstance(original, Overloaded) and
10171024
isinstance(override, Overloaded) and
10181025
name in nodes.reverse_op_methods.keys()):

‎mypy/subtypes.py

+13-4
Original file line numberDiff line numberDiff line change
@@ -249,13 +249,22 @@ def visit_overloaded(self, left: Overloaded) -> bool:
249249
return True
250250
return False
251251
elif isinstance(right, Overloaded):
252-
# TODO: this may be too restrictive
253-
if len(left.items()) != len(right.items()):
252+
if len(left.items()) > len(right.items()):
254253
return False
255-
for i in range(len(left.items())):
256-
if not is_subtype(left.items()[i], right.items()[i], self.check_type_parameter,
254+
255+
# Ensure each overload in the left side is accounted for.
256+
sub_overloads = left.items()[:]
257+
while sub_overloads:
258+
left_item = sub_overloads[-1]
259+
for right_item in right.items():
260+
if is_subtype(left_item, right_item, self.check_type_parameter,
257261
ignore_pos_arg_names=self.ignore_pos_arg_names):
262+
sub_overloads.pop()
263+
break
264+
else:
265+
# One of the overloads was not present in the right side.
258266
return False
267+
259268
return True
260269
elif isinstance(right, UnboundType):
261270
return True

‎test-data/unit/check-classes.test

+27-17
Original file line numberDiff line numberDiff line change
@@ -1419,23 +1419,6 @@ class B(A):
14191419
[out]
14201420
tmp/foo.pyi:8: error: Signature of "__add__" incompatible with supertype "A"
14211421

1422-
[case testOverloadedOperatorMethodOverrideWithSwitchedItemOrder]
1423-
from foo import *
1424-
[file foo.pyi]
1425-
from typing import overload, Any
1426-
class A:
1427-
@overload
1428-
def __add__(self, x: 'B') -> 'B': pass
1429-
@overload
1430-
def __add__(self, x: 'A') -> 'A': pass
1431-
class B(A):
1432-
@overload
1433-
def __add__(self, x: 'A') -> 'A': pass
1434-
@overload
1435-
def __add__(self, x: 'B') -> 'B': pass
1436-
[out]
1437-
tmp/foo.pyi:8: error: Signature of "__add__" incompatible with supertype "A"
1438-
14391422
[case testReverseOperatorMethodArgumentType]
14401423
from typing import Any
14411424
class A: pass
@@ -2494,6 +2477,33 @@ reveal_type(f(BChild())) # E: Revealed type is 'foo.B'
24942477
[builtins fixtures/classmethod.pyi]
24952478
[out]
24962479

2480+
[case testSubtypeWithMoreOverloadsThanSupertypeSucceeds]
2481+
from foo import *
2482+
[file foo.pyi]
2483+
from typing import overload
2484+
2485+
2486+
class X: pass
2487+
class Y: pass
2488+
class Z: pass
2489+
2490+
2491+
class A:
2492+
@overload
2493+
def f(self, x: X) -> X: pass
2494+
@overload
2495+
def f(self, y: Y) -> Y: pass
2496+
2497+
class B(A):
2498+
@overload
2499+
def f(self, x: X) -> X: pass
2500+
@overload
2501+
def f(self, y: Y) -> Y: pass
2502+
@overload
2503+
def f(self, z: Z) -> Z: pass
2504+
[builtins fixtures/classmethod.pyi]
2505+
[out]
2506+
24972507
[case testTypeTypeOverlapsWithObjectAndType]
24982508
from foo import *
24992509
[file foo.pyi]

0 commit comments

Comments
 (0)
Please sign in to comment.