Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
20 changes: 20 additions & 0 deletions docs/source/kinds_of_types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,26 @@ Python 3.6 introduced an alternative, class-based syntax for named tuples with t

p = Point(x=1, y='x') # Argument has incompatible type "str"; expected "int"

.. note::

You can use raw ``NamedTuple`` pseudo-class to annotate type
Comment thread
sobolevn marked this conversation as resolved.
Resolved
where any ``NamedTuple`` is expected.

For example, it can be useful for deserialization:

.. code-block:: python

def deserialize_named_tuple(arg: NamedTuple) -> Dict[str, Any]:
return arg._asdict()

Point = namedtuple('Point', ['x', 'y'])
Person = NamedTuple('Person', [('name', str), ('age', int)])

deserialize_named_tuple(Point(x=1, y=2)) # ok
deserialize_named_tuple(Person(name='Nikita', age=18)) # ok

deserialize_named_tuple((1, 2)) # Argument 1 to "deserialize_named_tuple" has incompatible type "Tuple[int, int]"; expected "NamedTuple"

.. _type-of-class:

The type of class objects
Expand Down
6 changes: 5 additions & 1 deletion mypy/subtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,11 @@ def visit_instance(self, left: Instance) -> bool:
rname = right.type.fullname
# Always try a nominal check if possible,
# there might be errors that a user wants to silence *once*.
if ((left.type.has_base(rname) or rname == 'builtins.object') and
# NamedTuples are a special case, because `NamedTuple` is not listed
# in `TypeInfo.mro`, so when `(a: NamedTuple) -> None` is used,
# we need to check for `is_named_tuple` property
if ((left.type.has_base(rname) or rname == 'builtins.object'
or (rname == 'typing.NamedTuple' and left.type.is_named_tuple)) and
not self.ignore_declared_variance):
# Map left type to corresponding right instances.
t = map_instance_to_supertype(left, right.type)
Expand Down
55 changes: 55 additions & 0 deletions test-data/unit/check-namedtuple.test
Original file line number Diff line number Diff line change
Expand Up @@ -973,3 +973,58 @@ B = namedtuple('X', ['a']) # E: First argument to namedtuple() should be
C = NamedTuple('X', [('a', 'Y')]) # E: First argument to namedtuple() should be "C", not "X"
class Y: ...
[builtins fixtures/tuple.pyi]

[case testNamedTupleTypeIsASuperTypeOfOtherNamedTuples]
from typing import Tuple, NamedTuple

class Bar(NamedTuple):
name: str = "Bar"

class Baz(NamedTuple):
a: str
b: str
Comment thread
sobolevn marked this conversation as resolved.
Resolved

def print_namedtuple(obj: NamedTuple) -> None:
reveal_type(obj._source) # N: Revealed type is "builtins.str"

b1: Bar
b2: Baz
print_namedtuple(b1) # ok
print_namedtuple(b2) # ok

print_namedtuple(1) # E: Argument 1 to "print_namedtuple" has incompatible type "int"; expected "NamedTuple"
print_namedtuple(('bar',)) # E: Argument 1 to "print_namedtuple" has incompatible type "Tuple[str]"; expected "NamedTuple"
print_namedtuple((1, 2)) # E: Argument 1 to "print_namedtuple" has incompatible type "Tuple[int, int]"; expected "NamedTuple"
print_namedtuple((b1,)) # E: Argument 1 to "print_namedtuple" has incompatible type "Tuple[Bar]"; expected "NamedTuple"
t: Tuple[str, ...]
print_namedtuple(t) # E: Argument 1 to "print_namedtuple" has incompatible type "Tuple[str, ...]"; expected "NamedTuple"

[builtins fixtures/tuple.pyi]
[typing fixtures/typing-namedtuple.pyi]

[case testNamedTupleTypeIsASuperTypeOfOtherNamedTuplesReturns]
from typing import Tuple, NamedTuple

class Bar(NamedTuple):
name: int

class Baz(NamedTuple):
a: str
b: str

def good1() -> NamedTuple:
b: Bar
return b
def good2() -> NamedTuple:
b: Baz
return b

def bad1() -> NamedTuple:
return 1 # E: Incompatible return value type (got "int", expected "NamedTuple")
def bad2() -> NamedTuple:
return () # E: Incompatible return value type (got "Tuple[]", expected "NamedTuple")
def bad3() -> NamedTuple:
return (1, 2) # E: Incompatible return value type (got "Tuple[int, int]", expected "NamedTuple")

[builtins fixtures/tuple.pyi]
[typing fixtures/typing-namedtuple.pyi]
15 changes: 15 additions & 0 deletions test-data/unit/fixtures/typing-namedtuple.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
TypeVar = 0
Generic = 0
Any = 0
overload = 0
Type = 0

T_co = TypeVar('T_co', covariant=True)

class Iterable(Generic[T_co]): pass
class Iterator(Iterable[T_co]): pass
class Sequence(Iterable[T_co]): pass

class Tuple(Sequence): pass
class NamedTuple(Tuple):
_source: str
26 changes: 26 additions & 0 deletions test-data/unit/pythoneval.test
Original file line number Diff line number Diff line change
Expand Up @@ -1390,6 +1390,32 @@ x = X(a=1, b='s')
[out]
_testNamedTupleNew.py:12: note: Revealed type is "Tuple[builtins.int, fallback=_testNamedTupleNew.Child]"

[case testNamedTupleTypeInheritanceSpecialCase]
from typing import NamedTuple, Tuple
from collections import namedtuple

A = NamedTuple('A', [('param', int)])
B = namedtuple('B', ['param'])

def accepts_named_tuple(arg: NamedTuple):
reveal_type(arg._asdict())
reveal_type(arg._fields)
reveal_type(arg._field_defaults)

a = A(1)
b = B(1)

accepts_named_tuple(a)
accepts_named_tuple(b)
accepts_named_tuple(1)
accepts_named_tuple((1, 2))
[out]
_testNamedTupleTypeInheritanceSpecialCase.py:8: note: Revealed type is "collections.OrderedDict[builtins.str, Any]"
_testNamedTupleTypeInheritanceSpecialCase.py:9: note: Revealed type is "builtins.tuple[builtins.str]"
_testNamedTupleTypeInheritanceSpecialCase.py:10: note: Revealed type is "builtins.dict[builtins.str, Any]"
_testNamedTupleTypeInheritanceSpecialCase.py:17: error: Argument 1 to "accepts_named_tuple" has incompatible type "int"; expected "NamedTuple"
_testNamedTupleTypeInheritanceSpecialCase.py:18: error: Argument 1 to "accepts_named_tuple" has incompatible type "Tuple[int, int]"; expected "NamedTuple"

[case testNewAnalyzerBasicTypeshed_newsemanal]
from typing import Dict, List, Tuple

Expand Down