Skip to content

Commit e1e1333

Browse files
committed
Fix joining of fixed-length tuples with mismatching lengths
For example: Tuple[bool, int] + Tuple[bool] becomes Tuple[int, ...] Previously Mypy simply punted and returned `object`. The handling of fixed tuple + variadic tuple will be implemented separately.
1 parent 150d492 commit e1e1333

File tree

3 files changed

+58
-1
lines changed

3 files changed

+58
-1
lines changed

mypy/join.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ def visit_tuple_type(self, t: TupleType) -> ProperType:
265265
# When given two fixed-length tuples:
266266
# * If they have the same length, join their subtypes item-wise:
267267
# Tuple[int, bool] + Tuple[bool, bool] becomes Tuple[int, bool]
268+
# * If lengths do not match, return a variadic tuple:
269+
# Tuple[bool, int] + Tuple[bool] becomes Tuple[int, ...]
268270
#
269271
# Otherwise, `t` is a fixed-length tuple but `self.s` is NOT:
270272
# * Joining with a variadic tuple returns variadic tuple:
@@ -278,7 +280,10 @@ def visit_tuple_type(self, t: TupleType) -> ProperType:
278280
fallback = join_instances(mypy.typeops.tuple_fallback(self.s),
279281
mypy.typeops.tuple_fallback(t))
280282
assert isinstance(fallback, Instance)
281-
return TupleType(items, fallback)
283+
if self.s.length() == t.length():
284+
return TupleType(items, fallback)
285+
else:
286+
return fallback
282287
else:
283288
return join_types(self.s, mypy.typeops.tuple_fallback(t))
284289

mypy/test/testtypes.py

+6
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,12 @@ def test_tuples(self) -> None:
505505
self.assert_join(self.tuple(self.fx.a),
506506
self.tuple(self.fx.a, self.fx.a),
507507
self.var_tuple(self.fx.a))
508+
self.assert_join(self.tuple(self.fx.b),
509+
self.tuple(self.fx.a, self.fx.c),
510+
self.var_tuple(self.fx.a))
511+
self.assert_join(self.tuple(),
512+
self.tuple(self.fx.a),
513+
self.var_tuple(self.fx.a))
508514

509515
def test_var_tuples(self) -> None:
510516
self.assert_join(self.tuple(self.fx.a),

test-data/unit/check-tuples.test

+46
Original file line numberDiff line numberDiff line change
@@ -1158,6 +1158,52 @@ reveal_type(subtup if int() else vartup) # N: Revealed type is 'builtins.tuple[
11581158
[builtins fixtures/tuple.pyi]
11591159
[out]
11601160

1161+
[case testTupleJoinIrregular]
1162+
from typing import Tuple
1163+
1164+
tup1 = None # type: Tuple[bool, int]
1165+
tup2 = None # type: Tuple[bool]
1166+
1167+
reveal_type(tup1 if int() else tup2) # N: Revealed type is 'builtins.tuple[builtins.int]'
1168+
reveal_type(tup2 if int() else tup1) # N: Revealed type is 'builtins.tuple[builtins.int]'
1169+
1170+
reveal_type(tup1 if int() else ()) # N: Revealed type is 'builtins.tuple[builtins.int]'
1171+
reveal_type(() if int() else tup1) # N: Revealed type is 'builtins.tuple[builtins.int]'
1172+
1173+
reveal_type(tup2 if int() else ()) # N: Revealed type is 'builtins.tuple[builtins.bool]'
1174+
reveal_type(() if int() else tup2) # N: Revealed type is 'builtins.tuple[builtins.bool]'
1175+
1176+
[builtins fixtures/tuple.pyi]
1177+
[out]
1178+
1179+
[case testTupleSubclassJoinIrregular]
1180+
from typing import Tuple, NamedTuple
1181+
1182+
class NTup1(NamedTuple):
1183+
a: bool
1184+
1185+
class NTup2(NamedTuple):
1186+
a: bool
1187+
b: bool
1188+
1189+
class SubTuple(Tuple[bool, int, int]): ...
1190+
1191+
tup1 = None # type: NTup1
1192+
tup2 = None # type: NTup2
1193+
subtup = None # type: SubTuple
1194+
1195+
reveal_type(tup1 if int() else tup2) # N: Revealed type is 'builtins.tuple[builtins.bool]'
1196+
reveal_type(tup2 if int() else tup1) # N: Revealed type is 'builtins.tuple[builtins.bool]'
1197+
1198+
reveal_type(tup1 if int() else subtup) # N: Revealed type is 'builtins.tuple[builtins.int]'
1199+
reveal_type(subtup if int() else tup1) # N: Revealed type is 'builtins.tuple[builtins.int]'
1200+
1201+
reveal_type(tup2 if int() else subtup) # N: Revealed type is 'builtins.tuple[builtins.int]'
1202+
reveal_type(subtup if int() else tup2) # N: Revealed type is 'builtins.tuple[builtins.int]'
1203+
1204+
[builtins fixtures/tuple.pyi]
1205+
[out]
1206+
11611207
[case testTupleWithUndersizedContext]
11621208
a = ([1], 'x')
11631209
if int():

0 commit comments

Comments
 (0)