Skip to content

[WIP] Fix issue #4975 #8253

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 25 additions & 10 deletions mypy/join.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ def visit_instance(self, t: Instance) -> ProperType:
return join_types(t, self.s)
elif isinstance(self.s, LiteralType):
return join_types(t, self.s)
elif isinstance(self.s, TupleType):
return join_types(t, self.s)
else:
return self.default(self.s)

Expand Down Expand Up @@ -260,16 +262,29 @@ def visit_overloaded(self, t: Overloaded) -> ProperType:
return join_types(t.fallback, s)

def visit_tuple_type(self, t: TupleType) -> ProperType:
if isinstance(self.s, TupleType) and self.s.length() == t.length():
items = [] # type: List[Type]
for i in range(t.length()):
items.append(self.join(t.items[i], self.s.items[i]))
fallback = join_instances(mypy.typeops.tuple_fallback(self.s),
mypy.typeops.tuple_fallback(t))
assert isinstance(fallback, Instance)
return TupleType(items, fallback)
else:
return self.default(self.s)
if isinstance(self.s, TupleType):
if self.s.length() == t.length():
items = [] # type: List[Type]
for i in range(t.length()):
items.append(self.join(t.items[i], self.s.items[i]))
fallback = join_instances(mypy.typeops.tuple_fallback(self.s),
mypy.typeops.tuple_fallback(t))
assert isinstance(fallback, Instance)
return TupleType(items, fallback)
else:
s_type = join_type_list(self.s.items)
t_type = join_type_list(t.items)

return Instance(self.s.partial_fallback.type, [self.join(s_type, t_type)])

elif isinstance(self.s, Instance):
for typ in self.s.type.mro:
if typ.fullname == 'builtins.tuple':
# how to access the original args? i see we can recursively descend .bases
# but this seems cumbersome
return Instance(typ, [AnyType(4)]) # just a stub for now

return self.default(self.s)

def visit_typeddict_type(self, t: TypedDictType) -> ProperType:
if isinstance(self.s, TypedDictType):
Expand Down
2 changes: 1 addition & 1 deletion mypy/test/testtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,7 @@ def test_tuples(self) -> None:

self.assert_join(self.tuple(self.fx.a, self.fx.a),
self.fx.std_tuple,
self.fx.o)
self.fx.std_tuple)
self.assert_join(self.tuple(self.fx.a),
self.tuple(self.fx.a, self.fx.a),
self.fx.o)
Expand Down
83 changes: 83 additions & 0 deletions test-data/unit/check-inference.test
Original file line number Diff line number Diff line change
Expand Up @@ -3126,3 +3126,86 @@ y = defaultdict(list) # E: Need type annotation for 'y'
y['a'] = []
reveal_type(y) # N: Revealed type is 'collections.defaultdict[Any, Any]'
[builtins fixtures/dict.pyi]

[case testJoinTupleAndInstance]
from typing import Tuple
x: Tuple[int, int]
y: Tuple[int, ...]

lst = [x, y]
reveal_type(lst[0]) # N: Revealed type is 'builtins.tuple*[builtins.int]'
[builtins fixtures/list.pyi]

[case testJoinTupleAndInstancePartlySame]
from typing import Tuple
x: Tuple[int, str]
y: Tuple[int, ...]

lst = [x, y]
reveal_type(lst[0]) # N: Revealed type is 'builtins.tuple*[builtins.object]'
[builtins fixtures/tuple.pyi]

[case testJoinTupleAndInstanceAllDifferent]
from typing import Tuple
x: Tuple[float, float]
y: Tuple[int, ...]

lst = [x, y]
reveal_type(lst[0]) # N: Revealed type is 'builtins.tuple*[builtins.float]'
[builtins fixtures/tuple.pyi]

[case testJoinTupleAndEmptyTuple]
from typing import Tuple
x = ()
y: Tuple[int]
z: bool

res = x if z else y
reveal_type(res) # N: Revealed type is 'builtins.tuple[builtins.int]'

res = y if z else x
reveal_type(res) # N: Revealed type is 'builtins.tuple[builtins.int]'

[case testJoinTupleVariableAndEmptyTuple]
from typing import Tuple

x = ()
y: Tuple[int, ...]
z: bool

res = x if z else y
reveal_type(res) # N: Revealed type is 'builtins.tuple[builtins.int]'

[case testJoinTupleAndDifferentTupleLength]
from typing import Tuple
x: Tuple[int]
y: Tuple[int, int]
z: bool

res = x if z else y
reveal_type(res) # N: Revealed type is 'builtins.tuple[builtins.int]'

[case testJoinTupleAndDifferentTupleLengthType]
from typing import Tuple
x: Tuple[int, float]
y: Tuple[int]
z: bool

res = x if z else y
reveal_type(res) # N: Revealed type is 'builtins.tuple[builtins.float]'

[case testJoinTupleAndInstanceTupleSubclass]
from typing import Tuple

class A(tuple):
pass

class B(A):
pass

x: B
y: Tuple[int]
z: bool

res = x if z else y
reveal_type(res) # N: Revealed type is 'builtins.tuple[Any]'
1 change: 1 addition & 0 deletions test-data/unit/fixtures/tuple.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class int:
class slice: pass
class bool: pass
class str: pass # For convenience
class float: pass # For convenience
class unicode: pass

T = TypeVar('T')
Expand Down