diff --git a/mypy/semanal.py b/mypy/semanal.py index 790c0c6d96d7..c27b43f01e7c 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -2120,7 +2120,7 @@ def analyze_namedtuple_assign(self, s: AssignmentStmt) -> bool: """Check if s defines a namedtuple.""" if isinstance(s.rvalue, CallExpr) and isinstance(s.rvalue.analyzed, NamedTupleExpr): return True # This is a valid and analyzed named tuple definition, nothing to do here. - if len(s.lvalues) != 1 or not isinstance(s.lvalues[0], NameExpr): + if len(s.lvalues) != 1 or not isinstance(s.lvalues[0], (NameExpr, MemberExpr)): return False lvalue = s.lvalues[0] name = lvalue.name @@ -2128,6 +2128,9 @@ def analyze_namedtuple_assign(self, s: AssignmentStmt) -> bool: self.is_func_scope()) if not is_named_tuple: return False + if isinstance(s.lvalues[0], MemberExpr): + self.fail("NamedTuple type as an attribute is not supported", lvalue) + return False # Yes, it's a valid namedtuple, but defer if it is not ready. if not info: self.mark_incomplete(name, lvalue, becomes_typeinfo=True) diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test index 1a3c778b4604..0341d96eaece 100644 --- a/test-data/unit/pythoneval.test +++ b/test-data/unit/pythoneval.test @@ -1453,3 +1453,15 @@ def f_suppresses() -> int: [out] _testUnreachableWithStdlibContextManagersNoStrictOptional.py:9: error: Statement is unreachable _testUnreachableWithStdlibContextManagersNoStrictOptional.py:15: error: Statement is unreachable + +[case testNamedTupleAtRunTime] +from typing import NamedTuple + +class A: + def __init__(self) -> None: + self.b = NamedTuple('x', [('s', str), ('n', int)]) + +reveal_type(A().b) +[out] +_testNamedTupleAtRunTime.py:5: error: NamedTuple type as an attribute is not supported +_testNamedTupleAtRunTime.py:7: note: Revealed type is 'Any'