Skip to content

Commit 590c15f

Browse files
elazargJukkaL
authored andcommitted
Support namedtuple as one of multiple superclasses (#2091)
Solve issue #1558 by delegate subtype checks on TupleType to fallback. This does not solve #1485. I don't know why.
1 parent f14ba14 commit 590c15f

File tree

2 files changed

+26
-9
lines changed

2 files changed

+26
-9
lines changed

mypy/subtypes.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -163,20 +163,17 @@ def visit_callable_type(self, left: CallableType) -> bool:
163163
def visit_tuple_type(self, left: TupleType) -> bool:
164164
right = self.right
165165
if isinstance(right, Instance):
166-
if is_named_instance(right, 'builtins.object'):
166+
if is_named_instance(right, 'typing.Sized'):
167167
return True
168-
if is_named_instance(right, 'builtins.tuple'):
169-
target_item_type = right.args[0]
170-
return all(is_subtype(item, target_item_type)
171-
for item in left.items)
172-
elif is_named_instance(right, 'typing.Sized'):
173-
return True
174-
elif (is_named_instance(right, 'typing.Iterable') or
168+
elif (is_named_instance(right, 'builtins.tuple') or
169+
is_named_instance(right, 'typing.Iterable') or
175170
is_named_instance(right, 'typing.Container') or
176171
is_named_instance(right, 'typing.Sequence') or
177172
is_named_instance(right, 'typing.Reversible')):
178173
iter_type = right.args[0]
179174
return all(is_subtype(li, iter_type) for li in left.items)
175+
elif is_subtype(left.fallback, right, self.check_type_parameter):
176+
return True
180177
return False
181178
elif isinstance(right, TupleType):
182179
if len(left.items) != len(right.items):

test-data/unit/check-namedtuple.test

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,4 +322,24 @@ reveal_type(X._field_types) # E: Revealed type is 'builtins.dict[builtins.str,
322322
x = None # type: X
323323
reveal_type(x._field_types) # E: Revealed type is 'builtins.dict[builtins.str, Any]'
324324

325-
[builtins fixtures/dict.pyi]
325+
[builtins fixtures/dict.pyi]
326+
327+
[case testNamedTupleAndOtherSuperclass]
328+
from typing import NamedTuple
329+
330+
class A: pass
331+
def f(x: A) -> None: pass
332+
333+
class B(NamedTuple('B', []), A): pass
334+
f(B())
335+
x = None # type: A
336+
x = B()
337+
338+
# Sanity check: fail if baseclass does not match
339+
class C: pass
340+
def g(x: C) -> None: pass
341+
class D(NamedTuple('D', []), A): pass
342+
343+
g(D()) # E: Argument 1 to "g" has incompatible type "D"; expected "C"
344+
y = None # type: C
345+
y = D() # E: Incompatible types in assignment (expression has type "D", variable has type "C")

0 commit comments

Comments
 (0)