Skip to content
Draft
2 changes: 1 addition & 1 deletion docs/source/error_code_list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,7 @@ consistently when using the call-based syntax. Example:

from typing import NamedTuple

# Error: First argument to namedtuple() should be "Point2D", not "Point"
# Error: First argument to "NamedTuple()" should be "Point2D", not "Point"
Point2D = NamedTuple("Point", [("x", int), ("y", int)])

Report syntax errors [syntax]
Expand Down
2 changes: 2 additions & 0 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -4181,6 +4181,8 @@ def visit_newtype_expr(self, e: NewTypeExpr) -> Type:
return AnyType(TypeOfAny.special_form)

def visit_namedtuple_expr(self, e: NamedTupleExpr) -> Type:
if e.call:
self.accept(e.call)
tuple_type = e.info.tuple_type
if tuple_type:
if (self.chk.options.disallow_any_unimported and
Expand Down
11 changes: 9 additions & 2 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2305,16 +2305,23 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T:
class NamedTupleExpr(Expression):
"""Named tuple expression namedtuple(...) or NamedTuple(...)."""

__slots__ = ('info', 'is_typed')
__slots__ = ('info', 'call', 'is_typed')

# The class representation of this named tuple (its tuple_type attribute contains
# the tuple item types)
info: "TypeInfo"
# We store original expr, if named tuple was created by an inline-call.
# We construct it during "semanal" phase and
# use it to type check that call is valid during "check" phase.
call: Optional[CallExpr]
is_typed: bool # whether this class was created with typing.NamedTuple

def __init__(self, info: 'TypeInfo', is_typed: bool = False) -> None:
def __init__(self, info: 'TypeInfo',
call: Optional[CallExpr] = None,
is_typed: bool = False) -> None:
super().__init__()
self.info = info
self.call = call
self.is_typed = is_typed

def accept(self, visitor: ExpressionVisitor[T]) -> T:
Expand Down
6 changes: 1 addition & 5 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -2295,12 +2295,8 @@ def analyze_namedtuple_assign(self, s: AssignmentStmt) -> bool:
if internal_name is None:
return False
if isinstance(lvalue, MemberExpr):
self.fail("NamedTuple type as an attribute is not supported", lvalue)
self.fail('Namedtuples as attributes are not supported', lvalue)
return False
if internal_name != name:
self.fail('First argument to namedtuple() should be "{}", not "{}"'.format(
name, internal_name), s.rvalue, code=codes.NAME_MATCH)
return True
# Yes, it's a valid namedtuple, but defer if it is not ready.
if not info:
self.mark_incomplete(name, lvalue, becomes_typeinfo=True)
Expand Down
Loading