Description
🐛 Bug Report
NamedTuple's appear to behave in unexpected ways with recursion, especially when compared to frozen dataclasses (which are rather similar, as far as the user is concerned).
To Reproduce
from __future__ import annotations
from typing import NamedTuple, Optional
class A(NamedTuple):
a: Optional[A]
Produces a "mypy-sample1.py:5: error: Cannot resolve name "A" (possible cyclic definition)". However,
from __future__ import annotations
from typing import Optional
from dataclasses import dataclass
@dataclass(frozen=True)
class A:
a: Optional[A]
Passes MyPy just fine, and as far as a user is concerned, presents a very similar surface for MyPy to type check (i.e. both have a read-only attribute a which is either None or of class A).
Expected Behavior
The expected behaviour would be that both NamedTuple and frozen dataclass behave the same, as NamedTuple and frozen dataclasses present logically similar behaviour with regards to the attribute 'a'.
Actual Behavior
In addition to the odd behaviour with cycle checker triggering on NamedTuple but not on dataclasses, it's possible to fool the cycle checker completely and have MyPy crash by recursing too far (e.g. issue #8695 and although that issue has been reported with a recursive type that could never be instantiated, I've encountered the same behaviour with type definitions that can be instantiated, but not in a minimal example friendly format). I think this specific behaviour is the root cause, as the real problem is that for some reason NamedTuple's are behaving differently to what would be expected.
Your Environment
- Mypy version used: mypy 0.782
- Mypy command-line flags: none
- Mypy configuration options from
mypy.ini
(and other config files): none - Python version used: 3.8.2
- Operating system and version: Linux