Repository navigation
Support recursive NamedTuple #9397
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
Activity
ramalho commentedon Jul 16, 2021
To illustrate the bug with a more "realistic" example, here is the top of an
Orderclass to demonstrate the Strategy pattern, where a concrete strategy is named apromotion, and it must be a callable that accepts anOrderargument:Mypy 0.910 accepts the code above, but crashes with the code below:
I put
# type: ignoreinstead of# the Strategyon that line, but it did not help: Mypy still crashed.The error:
AlexWaygood commentedon Mar 26, 2022
I've added the "crash" label in addition to the "bug" label — although the original report is a bug, @ramalho's example is clearly a crash.
[-]Unexpected behaviour with NamedTuple and cycle detection[/-][+]Support recursive NamedTuple[/+]JelleZijlstra commentedon Apr 20, 2022
I opened #12629 to discuss the crash. This issue should be focused on the enhancement request (support for recursive namedtuples). Any crashes should get their own issue.
mrolle45 commentedon Apr 20, 2022
I believe the source of the problem is in the
TypeAnalyzer.visit_unbound_type_nonoptionalmethod:Before the final iteration, the reference is considered incomplete. In the final iteration, it is still a placeholder, and reported as an error. Also, the type is deemed to be Any, rather than the type of the class being defined.
Why this does not happen with non-NamedTuple classes, I cannot tell. mypy can form a correct TypeInfo for the class and for each of its members.
Handle recursive namedtuple classes (python#9397)
Handle recursive namedtuple classes
Support recursive NamedTuple classes
ilevkivskyi commentedon Aug 23, 2022
This now works on master with
--enable-recursive-aliases.