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

0 commit comments

Comments
 (0)