Skip to content

Commit

Permalink
Merge pull request #1277 from gvanrossum/master
Browse files Browse the repository at this point in the history
Distinguish between namedtuple without items and errors.
  • Loading branch information
gvanrossum committed Mar 6, 2016
2 parents e308f2f + d4f6f33 commit 2f89c79
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 9 deletions.
20 changes: 11 additions & 9 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1351,8 +1351,8 @@ def check_namedtuple(self, node: Node) -> TypeInfo:
fullname = callee.fullname
if fullname not in ('collections.namedtuple', 'typing.NamedTuple'):
return None
items, types = self.parse_namedtuple_args(call, fullname)
if not items:
items, types, ok = self.parse_namedtuple_args(call, fullname)
if not ok:
# Error. Construct dummy return value.
return self.build_namedtuple_typeinfo('namedtuple', [], [])
else:
Expand All @@ -1362,7 +1362,7 @@ def check_namedtuple(self, node: Node) -> TypeInfo:
return info

def parse_namedtuple_args(self, call: CallExpr,
fullname: str) -> Tuple[List[str], List[Type]]:
fullname: str) -> Tuple[List[str], List[Type], bool]:
# TODO Share code with check_argument_count in checkexpr.py?
args = call.args
if len(args) < 2:
Expand All @@ -1375,6 +1375,7 @@ def parse_namedtuple_args(self, call: CallExpr,
return self.fail_namedtuple_arg(
"namedtuple() expects a string literal as the first argument", call)
types = [] # type: List[Type]
ok = True
if not isinstance(args[1], ListExpr):
if fullname == 'collections.namedtuple' and isinstance(args[1], StrExpr):
str_expr = cast(StrExpr, args[1])
Expand All @@ -1392,13 +1393,13 @@ def parse_namedtuple_args(self, call: CallExpr,
items = [cast(StrExpr, item).value for item in listexpr.items]
else:
# The fields argument contains (name, type) tuples.
items, types = self.parse_namedtuple_fields_with_types(listexpr.items, call)
items, types, ok = self.parse_namedtuple_fields_with_types(listexpr.items, call)
if not types:
types = [AnyType() for _ in items]
return items, types
return items, types, ok

def parse_namedtuple_fields_with_types(self, nodes: List[Node],
context: Context) -> Tuple[List[str], List[Type]]:
context: Context) -> Tuple[List[str], List[Type], bool]:
items = [] # type: List[str]
types = [] # type: List[Type]
for item in nodes:
Expand All @@ -1418,11 +1419,12 @@ def parse_namedtuple_fields_with_types(self, nodes: List[Node],
types.append(self.anal_type(type))
else:
return self.fail_namedtuple_arg("Tuple expected as NamedTuple() field", item)
return items, types
return items, types, True

def fail_namedtuple_arg(self, message: str, context: Context) -> Tuple[List[str], List[Type]]:
def fail_namedtuple_arg(self, message: str,
context: Context) -> Tuple[List[str], List[Type], bool]:
self.fail(message, context)
return [], []
return [], [], False

def build_namedtuple_typeinfo(self, name: str, items: List[str],
types: List[Type]) -> TypeInfo:
Expand Down
5 changes: 5 additions & 0 deletions mypy/test/data/check-namedtuple.test
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,8 @@ a = (1,) # E: Incompatible types in assignment (expression has type "Tuple[int]
import collections
MyNamedTuple = collections.namedtuple('MyNamedTuple', ['spam', 'eggs'])
MyNamedTuple.x # E: "MyNamedTuple" has no attribute "x"

[case testNamedTupleEmptyItems]
from typing import NamedTuple
A = NamedTuple('A', [])
[builtins fixtures/list.py]

0 comments on commit 2f89c79

Please sign in to comment.