Skip to content

Commit ddd9177

Browse files
authored
Flip the default for recursive aliases flag (#13516)
I don't think we need to wait long time for this. As soon as next release goes out, I think we can flip the default. Otherwise, this feature may degrade, because there are just several dozen tests with this flag on. (Also I am curious to see `mypy_primer` on this.) I manually checked each of couple dozen tests where I currently disable recursive aliases (they essentially just test that there is no crash and emit various errors like `Cannot resolve name`).
1 parent 0f4e0fb commit ddd9177

18 files changed

+57
-97
lines changed

mypy/main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -975,9 +975,9 @@ def add_invertible_flag(
975975
help="Use a custom typing module",
976976
)
977977
internals_group.add_argument(
978-
"--enable-recursive-aliases",
978+
"--disable-recursive-aliases",
979979
action="store_true",
980-
help="Experimental support for recursive type aliases",
980+
help="Disable experimental support for recursive type aliases",
981981
)
982982
internals_group.add_argument(
983983
"--custom-typeshed-dir", metavar="DIR", help="Use the custom typeshed in DIR"

mypy/options.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -312,8 +312,8 @@ def __init__(self) -> None:
312312
# skip most errors after this many messages have been reported.
313313
# -1 means unlimited.
314314
self.many_errors_threshold = defaults.MANY_ERRORS_THRESHOLD
315-
# Enable recursive type aliases (currently experimental)
316-
self.enable_recursive_aliases = False
315+
# Disable recursive type aliases (currently experimental)
316+
self.disable_recursive_aliases = False
317317

318318
# To avoid breaking plugin compatibility, keep providing new_semantic_analyzer
319319
@property

mypy/semanal.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3238,7 +3238,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool:
32383238
)
32393239
if not res:
32403240
return False
3241-
if self.options.enable_recursive_aliases and not self.is_func_scope():
3241+
if not self.options.disable_recursive_aliases and not self.is_func_scope():
32423242
# Only marking incomplete for top-level placeholders makes recursive aliases like
32433243
# `A = Sequence[str | A]` valid here, similar to how we treat base classes in class
32443244
# definitions, allowing `class str(Sequence[str]): ...`
@@ -5749,7 +5749,7 @@ def process_placeholder(self, name: str, kind: str, ctx: Context) -> None:
57495749

57505750
def cannot_resolve_name(self, name: str, kind: str, ctx: Context) -> None:
57515751
self.fail(f'Cannot resolve {kind} "{name}" (possible cyclic definition)', ctx)
5752-
if self.options.enable_recursive_aliases and self.is_func_scope():
5752+
if not self.options.disable_recursive_aliases and self.is_func_scope():
57535753
self.note("Recursive types are not allowed at function scope", ctx)
57545754

57555755
def qualified_name(self, name: str) -> str:

mypy/semanal_namedtuple.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ def check_namedtuple_classdef(
176176
# it would be inconsistent with type aliases.
177177
analyzed = self.api.anal_type(
178178
stmt.type,
179-
allow_placeholder=self.options.enable_recursive_aliases
179+
allow_placeholder=not self.options.disable_recursive_aliases
180180
and not self.api.is_func_scope(),
181181
)
182182
if analyzed is None:
@@ -443,7 +443,7 @@ def parse_namedtuple_fields_with_types(
443443
# We never allow recursive types at function scope.
444444
analyzed = self.api.anal_type(
445445
type,
446-
allow_placeholder=self.options.enable_recursive_aliases
446+
allow_placeholder=not self.options.disable_recursive_aliases
447447
and not self.api.is_func_scope(),
448448
)
449449
# Workaround #4987 and avoid introducing a bogus UnboundType

mypy/semanal_newtype.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ def check_newtype_args(
203203
self.api.anal_type(
204204
unanalyzed_type,
205205
report_invalid_types=False,
206-
allow_placeholder=self.options.enable_recursive_aliases
206+
allow_placeholder=not self.options.disable_recursive_aliases
207207
and not self.api.is_func_scope(),
208208
)
209209
)

mypy/semanal_typeddict.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ def analyze_base_args(self, base: IndexExpr, ctx: Context) -> list[Type] | None:
218218
analyzed = self.api.anal_type(
219219
type,
220220
allow_required=True,
221-
allow_placeholder=self.options.enable_recursive_aliases
221+
allow_placeholder=not self.options.disable_recursive_aliases
222222
and not self.api.is_func_scope(),
223223
)
224224
if analyzed is None:
@@ -289,7 +289,7 @@ def analyze_typeddict_classdef_fields(
289289
analyzed = self.api.anal_type(
290290
stmt.type,
291291
allow_required=True,
292-
allow_placeholder=self.options.enable_recursive_aliases
292+
allow_placeholder=not self.options.disable_recursive_aliases
293293
and not self.api.is_func_scope(),
294294
)
295295
if analyzed is None:
@@ -484,7 +484,7 @@ def parse_typeddict_fields_with_types(
484484
analyzed = self.api.anal_type(
485485
type,
486486
allow_required=True,
487-
allow_placeholder=self.options.enable_recursive_aliases
487+
allow_placeholder=not self.options.disable_recursive_aliases
488488
and not self.api.is_func_scope(),
489489
)
490490
if analyzed is None:

mypy/typeanal.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ def cannot_resolve_type(self, t: UnboundType) -> None:
402402
# need access to MessageBuilder here. Also move the similar
403403
# message generation logic in semanal.py.
404404
self.api.fail(f'Cannot resolve name "{t.name}" (possible cyclic definition)', t)
405-
if self.options.enable_recursive_aliases and self.api.is_func_scope():
405+
if not self.options.disable_recursive_aliases and self.api.is_func_scope():
406406
self.note("Recursive types are not allowed at function scope", t)
407407

408408
def apply_concatenate_operator(self, t: UnboundType) -> Type:

test-data/unit/check-classes.test

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4779,7 +4779,7 @@ class A(Tuple[int, str]): pass
47794779
-- -----------------------
47804780

47814781
[case testCrashOnSelfRecursiveNamedTupleVar]
4782-
4782+
# flags: --disable-recursive-aliases
47834783
from typing import NamedTuple
47844784

47854785
N = NamedTuple('N', [('x', N)]) # E: Cannot resolve name "N" (possible cyclic definition)
@@ -4809,7 +4809,7 @@ lst = [n, m]
48094809
[builtins fixtures/isinstancelist.pyi]
48104810

48114811
[case testCorrectJoinOfSelfRecursiveTypedDicts]
4812-
4812+
# flags: --disable-recursive-aliases
48134813
from mypy_extensions import TypedDict
48144814

48154815
class N(TypedDict):

test-data/unit/check-dataclasses.test

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1915,7 +1915,6 @@ takes_cp(MyDataclass)
19151915
[builtins fixtures/dataclasses.pyi]
19161916

19171917
[case testDataclassTypeAnnotationAliasUpdated]
1918-
# flags: --enable-recursive-aliases
19191918
import a
19201919
[file a.py]
19211920
from dataclasses import dataclass

test-data/unit/check-incremental.test

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4600,7 +4600,7 @@ def outer() -> None:
46004600
[out2]
46014601

46024602
[case testRecursiveAliasImported]
4603-
4603+
# flags: --disable-recursive-aliases
46044604
import a
46054605

46064606
[file a.py]
@@ -5759,7 +5759,7 @@ class C:
57595759
[builtins fixtures/tuple.pyi]
57605760

57615761
[case testNamedTupleUpdateNonRecursiveToRecursiveCoarse]
5762-
# flags: --enable-recursive-aliases
5762+
# flags: --strict-optional
57635763
import c
57645764
[file a.py]
57655765
from b import M
@@ -5802,7 +5802,7 @@ tmp/c.py:5: error: Incompatible types in assignment (expression has type "Option
58025802
tmp/c.py:7: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins.int, fallback=b.M], None], builtins.int, fallback=a.N]"
58035803

58045804
[case testTupleTypeUpdateNonRecursiveToRecursiveCoarse]
5805-
# flags: --enable-recursive-aliases
5805+
# flags: --strict-optional
58065806
import c
58075807
[file a.py]
58085808
from b import M
@@ -5835,7 +5835,7 @@ tmp/c.py:4: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins
58355835
tmp/c.py:5: error: Incompatible types in assignment (expression has type "Optional[N]", variable has type "int")
58365836

58375837
[case testTypeAliasUpdateNonRecursiveToRecursiveCoarse]
5838-
# flags: --enable-recursive-aliases
5838+
# flags: --strict-optional
58395839
import c
58405840
[file a.py]
58415841
from b import M
@@ -5868,7 +5868,7 @@ tmp/c.py:4: note: Revealed type is "Tuple[Union[Tuple[Union[..., None], builtins
58685868
tmp/c.py:5: error: Incompatible types in assignment (expression has type "Optional[N]", variable has type "int")
58695869

58705870
[case testTypedDictUpdateNonRecursiveToRecursiveCoarse]
5871-
# flags: --enable-recursive-aliases
5871+
# flags: --strict-optional
58725872
import c
58735873
[file a.py]
58745874
from b import M

test-data/unit/check-namedtuple.test

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ tmp/b.py:4: note: Revealed type is "Tuple[Any, fallback=a.N]"
617617
tmp/b.py:7: note: Revealed type is "Tuple[Any, fallback=a.N]"
618618

619619
[case testSimpleSelfReferentialNamedTuple]
620-
620+
# flags: --disable-recursive-aliases
621621
from typing import NamedTuple
622622
class MyNamedTuple(NamedTuple):
623623
parent: 'MyNamedTuple' # E: Cannot resolve name "MyNamedTuple" (possible cyclic definition)
@@ -655,7 +655,7 @@ class B:
655655
[out]
656656

657657
[case testSelfRefNT1]
658-
658+
# flags: --disable-recursive-aliases
659659
from typing import Tuple, NamedTuple
660660

661661
Node = NamedTuple('Node', [
@@ -667,7 +667,7 @@ reveal_type(n) # N: Revealed type is "Tuple[builtins.str, builtins.tuple[Any, ..
667667
[builtins fixtures/tuple.pyi]
668668

669669
[case testSelfRefNT2]
670-
670+
# flags: --disable-recursive-aliases
671671
from typing import Tuple, NamedTuple
672672

673673
A = NamedTuple('A', [
@@ -683,7 +683,7 @@ reveal_type(n) # N: Revealed type is "Tuple[builtins.str, builtins.tuple[Any, ..
683683
[builtins fixtures/tuple.pyi]
684684

685685
[case testSelfRefNT3]
686-
686+
# flags: --disable-recursive-aliases
687687
from typing import NamedTuple, Tuple
688688

689689
class B(NamedTuple):
@@ -703,7 +703,7 @@ reveal_type(lst[0]) # N: Revealed type is "Tuple[builtins.object, builtins.objec
703703
[builtins fixtures/tuple.pyi]
704704

705705
[case testSelfRefNT4]
706-
706+
# flags: --disable-recursive-aliases
707707
from typing import NamedTuple
708708

709709
class B(NamedTuple):
@@ -719,7 +719,7 @@ reveal_type(n.y[0]) # N: Revealed type is "Any"
719719
[builtins fixtures/tuple.pyi]
720720

721721
[case testSelfRefNT5]
722-
722+
# flags: --disable-recursive-aliases
723723
from typing import NamedTuple
724724

725725
B = NamedTuple('B', [
@@ -737,7 +737,7 @@ reveal_type(f) # N: Revealed type is "def (m: Tuple[Any, builtins.int, fallback=
737737
[builtins fixtures/tuple.pyi]
738738

739739
[case testRecursiveNamedTupleInBases]
740-
740+
# flags: --disable-recursive-aliases
741741
from typing import List, NamedTuple, Union
742742

743743
Exp = Union['A', 'B'] # E: Cannot resolve name "Exp" (possible cyclic definition) \
@@ -781,7 +781,7 @@ tp = NamedTuple('tp', [('x', int)])
781781
[out]
782782

783783
[case testSubclassOfRecursiveNamedTuple]
784-
784+
# flags: --disable-recursive-aliases
785785
from typing import List, NamedTuple
786786

787787
class Command(NamedTuple):

test-data/unit/check-newsemanal.test

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,7 @@ def main() -> None:
434434
x # E: Name "x" is not defined
435435

436436
[case testNewAnalyzerCyclicDefinitions]
437+
# flags: --disable-recursive-aliases
437438
gx = gy # E: Cannot resolve name "gy" (possible cyclic definition)
438439
gy = gx
439440
def main() -> None:
@@ -1499,6 +1500,7 @@ reveal_type(x[0][0]) # N: Revealed type is "__main__.C"
14991500
[builtins fixtures/list.pyi]
15001501

15011502
[case testNewAnalyzerAliasToNotReadyDirectBase]
1503+
# flags: --disable-recursive-aliases
15021504
from typing import List
15031505

15041506
x: B
@@ -1509,11 +1511,11 @@ reveal_type(x)
15091511
reveal_type(x[0][0])
15101512
[builtins fixtures/list.pyi]
15111513
[out]
1512-
main:3: error: Cannot resolve name "B" (possible cyclic definition)
15131514
main:4: error: Cannot resolve name "B" (possible cyclic definition)
1514-
main:4: error: Cannot resolve name "C" (possible cyclic definition)
1515-
main:7: note: Revealed type is "Any"
1515+
main:5: error: Cannot resolve name "B" (possible cyclic definition)
1516+
main:5: error: Cannot resolve name "C" (possible cyclic definition)
15161517
main:8: note: Revealed type is "Any"
1518+
main:9: note: Revealed type is "Any"
15171519

15181520
[case testNewAnalyzerAliasToNotReadyTwoDeferralsFunction]
15191521
import a
@@ -1532,6 +1534,7 @@ reveal_type(f) # N: Revealed type is "def (x: builtins.list[a.C]) -> builtins.l
15321534
[builtins fixtures/list.pyi]
15331535

15341536
[case testNewAnalyzerAliasToNotReadyDirectBaseFunction]
1537+
# flags: --disable-recursive-aliases
15351538
import a
15361539
[file a.py]
15371540
from typing import List
@@ -2119,6 +2122,7 @@ class B(List[C]):
21192122
[builtins fixtures/list.pyi]
21202123

21212124
[case testNewAnalyzerNewTypeForwardClassAliasDirect]
2125+
# flags: --disable-recursive-aliases
21222126
from typing import NewType, List
21232127

21242128
x: D
@@ -2131,12 +2135,12 @@ class B(D):
21312135
pass
21322136
[builtins fixtures/list.pyi]
21332137
[out]
2134-
main:3: error: Cannot resolve name "D" (possible cyclic definition)
2135-
main:4: note: Revealed type is "Any"
2136-
main:6: error: Cannot resolve name "D" (possible cyclic definition)
2137-
main:6: error: Cannot resolve name "C" (possible cyclic definition)
2138-
main:7: error: Argument 2 to NewType(...) must be a valid type
2139-
main:7: error: Cannot resolve name "B" (possible cyclic definition)
2138+
main:4: error: Cannot resolve name "D" (possible cyclic definition)
2139+
main:5: note: Revealed type is "Any"
2140+
main:7: error: Cannot resolve name "D" (possible cyclic definition)
2141+
main:7: error: Cannot resolve name "C" (possible cyclic definition)
2142+
main:8: error: Argument 2 to NewType(...) must be a valid type
2143+
main:8: error: Cannot resolve name "B" (possible cyclic definition)
21402144

21412145
-- Copied from check-classes.test (tricky corner cases).
21422146
[case testNewAnalyzerNoCrashForwardRefToBrokenDoubleNewTypeClass]
@@ -2153,6 +2157,7 @@ class C:
21532157
[builtins fixtures/dict.pyi]
21542158

21552159
[case testNewAnalyzerForwardTypeAliasInBase]
2160+
# flags: --disable-recursive-aliases
21562161
from typing import List, Generic, TypeVar, NamedTuple
21572162
T = TypeVar('T')
21582163

@@ -2593,6 +2598,7 @@ import n
25932598
def __getattr__(x): pass
25942599

25952600
[case testNewAnalyzerReportLoopInMRO2]
2601+
# flags: --disable-recursive-aliases
25962602
def f() -> None:
25972603
class A(A): ... # E: Cannot resolve name "A" (possible cyclic definition)
25982604

0 commit comments

Comments
 (0)