Repository: python/mypy

Issue:
NamedTuple triggered mypy error inside a class
```python
from typing import NamedTuple


class A:
    def __init__(self) -> None:
        self.tuple2 = NamedTuple('x', [('y', str), ('z', bool)])

    def run2(self) -> NamedTuple:
        return self.tuple2(y='y', z=True)


print(A().run2())
```
The script will trigger `"NamedTuple" not callable  mypy(error)`, not sure if this is as expected

Reference:
https://mypy.readthedocs.io/en/latest/kinds_of_types.html#named-tuples

---

Tested with Python37 and mypy==0.701


Discussion:
This is expected (although the error message could be better). The NamedTuple constructor isn't meant to be used at runtime in a class constructor; instead you should define your NamedTuple type in the module scope.
It would be better if mypy would at least tell that defining a NamedTuple type as an attribute is not supported.
@JukkaL 
I created a test to fix this issue but the test causes some exceptions:
```
[case testNamedTupleAtRunTime]
from typing import NamedTuple

class A:
    def __init__(self) -> None:
        self.b = NamedTuple('x', [('s', str), ('n', int)])  # E: NamedTuple type as an attribute is not supported

```
could you give some hint what is wrong?
@AFanaei Try adding `[builtins fixtures/list.pyi]` to the end of the test case.

Resolving patch:
diff --git a/mypy/semanal.py b/mypy/semanal.py
--- 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)


Test patch:
diff --git a/test-data/unit/pythoneval.test b/test-data/unit/pythoneval.test
--- 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'