diff --git a/mypy/semanal.py b/mypy/semanal.py index ea195b5fe5ca..7910a7429b08 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1634,14 +1634,14 @@ def parse_namedtuple_args(self, call: CallExpr, "namedtuple() expects a string literal as the first argument", call) types = [] # type: List[Type] ok = True - if not isinstance(args[1], ListExpr): + if not isinstance(args[1], (ListExpr, TupleExpr)): if (fullname == 'collections.namedtuple' and isinstance(args[1], (StrExpr, BytesExpr, UnicodeExpr))): str_expr = cast(StrExpr, args[1]) items = str_expr.value.replace(',', ' ').split() else: return self.fail_namedtuple_arg( - "List literal expected as the second argument to namedtuple()", call) + "List or tuple literal expected as the second argument to namedtuple()", call) else: listexpr = args[1] if fullname == 'collections.namedtuple': diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index d8beaf62604e..c41cc5a916fd 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -9,6 +9,17 @@ a = x[1] a, b, c = x # E: Need more than 2 values to unpack (3 expected) x[2] # E: Tuple index out of range +[case testNamedTupleWithTupleFieldNamesUsedAsTuple] +from collections import namedtuple + +X = namedtuple('X', ('x', 'y')) +x = None # type: X +a, b = x +b = x[0] +a = x[1] +a, b, c = x # E: Need more than 2 values to unpack (3 expected) +x[2] # E: Tuple index out of range + [case testNamedTupleNoUnderscoreFields] from collections import namedtuple @@ -82,6 +93,19 @@ x, y = n x = y # E: Incompatible types in assignment (expression has type "str", variable has type "int") +[case testNamedTupleWithTupleFieldNamesWithItemTypes] +from typing import NamedTuple +N = NamedTuple('N', (('a', int), + ('b', str))) +n = N(1, 'x') +s = n.a # type: str # E: Incompatible types in assignment (expression has type "int", \ + variable has type "str") +i = n.b # type: int # E: Incompatible types in assignment (expression has type "str", \ + variable has type "int") +x, y = n +x = y # E: Incompatible types in assignment (expression has type "str", variable has type "int") + + [case testNamedTupleConstructorArgumentTypes] from typing import NamedTuple N = NamedTuple('N', [('a', int), diff --git a/test-data/unit/semanal-namedtuple.test b/test-data/unit/semanal-namedtuple.test index 9ff9f6af6e8a..bb2592004908 100644 --- a/test-data/unit/semanal-namedtuple.test +++ b/test-data/unit/semanal-namedtuple.test @@ -32,6 +32,22 @@ MypyFile:1( Block:3( PassStmt:3()))) +[case testTwoItemNamedtupleWithTupleFieldNames] +from collections import namedtuple +N = namedtuple('N', ('a', 'xyz')) +def f() -> N: pass +[out] +MypyFile:1( + ImportFrom:1(collections, [namedtuple]) + AssignmentStmt:2( + NameExpr(N* [__main__.N]) + NamedTupleExpr:2(N, Tuple[Any, Any])) + FuncDef:3( + f + def () -> Tuple[Any, Any, fallback=__main__.N] + Block:3( + PassStmt:3()))) + [case testTwoItemNamedtupleWithShorthandSyntax] from collections import namedtuple N = namedtuple('N', ' a xyz ') @@ -59,6 +75,17 @@ MypyFile:1( NameExpr(N* [__main__.N]) NamedTupleExpr:2(N, Tuple[builtins.int, builtins.str]))) +[case testNamedTupleWithTupleFieldNamesWithItemTypes] +from typing import NamedTuple +N = NamedTuple('N', (('a', int), + ('b', str))) +[out] +MypyFile:1( + ImportFrom:1(typing, [NamedTuple]) + AssignmentStmt:2( + NameExpr(N* [__main__.N]) + NamedTupleExpr:2(N, Tuple[builtins.int, builtins.str]))) + [case testNamedTupleBaseClass] from collections import namedtuple N = namedtuple('N', ['x']) @@ -121,7 +148,7 @@ N = namedtuple(1, ['x']) # E: namedtuple() expects a string literal as the first [case testNamedTupleWithInvalidItems] from collections import namedtuple -N = namedtuple('N', 1) # E: List literal expected as the second argument to namedtuple() +N = namedtuple('N', 1) # E: List or tuple literal expected as the second argument to namedtuple() [case testNamedTupleWithInvalidItems2] from collections import namedtuple