Skip to content

WIP: adds better namedtuple semantic analyzer #11206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from
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