Skip to content

Commit 9011ca8

Browse files
authored
Delete recursive aliases flags (#16346)
FWIW I decided to keep the old tests (where possible), just to be sure we will not re-introduce various crashes at function scope, where recursive aliases are not allowed.
1 parent 42f7cf1 commit 9011ca8

15 files changed

+216
-230
lines changed

mypy/main.py

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -999,15 +999,6 @@ def add_invertible_flag(
999999
action="store_true",
10001000
help="Enable new experimental type inference algorithm",
10011001
)
1002-
internals_group.add_argument(
1003-
"--disable-recursive-aliases",
1004-
action="store_true",
1005-
help="Disable experimental support for recursive type aliases",
1006-
)
1007-
# Deprecated reverse variant of the above.
1008-
internals_group.add_argument(
1009-
"--enable-recursive-aliases", action="store_true", help=argparse.SUPPRESS
1010-
)
10111002
parser.add_argument(
10121003
"--enable-incomplete-feature",
10131004
action="append",
@@ -1392,11 +1383,6 @@ def set_strict_flags() -> None:
13921383
if options.logical_deps:
13931384
options.cache_fine_grained = True
13941385

1395-
if options.enable_recursive_aliases:
1396-
print(
1397-
"Warning: --enable-recursive-aliases is deprecated;"
1398-
" recursive types are enabled by default"
1399-
)
14001386
if options.strict_concatenate and not strict_option_set:
14011387
print("Warning: --strict-concatenate is deprecated; use --extra-checks instead")
14021388

mypy/options.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -362,10 +362,6 @@ def __init__(self) -> None:
362362
self.many_errors_threshold = defaults.MANY_ERRORS_THRESHOLD
363363
# Enable new experimental type inference algorithm.
364364
self.new_type_inference = False
365-
# Disable recursive type aliases (currently experimental)
366-
self.disable_recursive_aliases = False
367-
# Deprecated reverse version of the above, do not use.
368-
self.enable_recursive_aliases = False
369365
# Export line-level, limited, fine-grained dependency information in cache data
370366
# (undocumented feature).
371367
self.export_ref_info = False

mypy/semanal.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3608,7 +3608,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool:
36083608
)
36093609
if not res:
36103610
return False
3611-
if not self.options.disable_recursive_aliases and not self.is_func_scope():
3611+
if not self.is_func_scope():
36123612
# Only marking incomplete for top-level placeholders makes recursive aliases like
36133613
# `A = Sequence[str | A]` valid here, similar to how we treat base classes in class
36143614
# definitions, allowing `class str(Sequence[str]): ...`
@@ -6296,7 +6296,7 @@ def process_placeholder(
62966296
def cannot_resolve_name(self, name: str | None, kind: str, ctx: Context) -> None:
62976297
name_format = f' "{name}"' if name else ""
62986298
self.fail(f"Cannot resolve {kind}{name_format} (possible cyclic definition)", ctx)
6299-
if not self.options.disable_recursive_aliases and self.is_func_scope():
6299+
if self.is_func_scope():
63006300
self.note("Recursive types are not allowed at function scope", ctx)
63016301

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

mypy/semanal_namedtuple.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,7 @@ def check_namedtuple_classdef(
182182
# it would be inconsistent with type aliases.
183183
analyzed = self.api.anal_type(
184184
stmt.type,
185-
allow_placeholder=not self.options.disable_recursive_aliases
186-
and not self.api.is_func_scope(),
185+
allow_placeholder=not self.api.is_func_scope(),
187186
prohibit_self_type="NamedTuple item type",
188187
)
189188
if analyzed is None:
@@ -450,8 +449,7 @@ def parse_namedtuple_fields_with_types(
450449
# We never allow recursive types at function scope.
451450
analyzed = self.api.anal_type(
452451
type,
453-
allow_placeholder=not self.options.disable_recursive_aliases
454-
and not self.api.is_func_scope(),
452+
allow_placeholder=not self.api.is_func_scope(),
455453
prohibit_self_type="NamedTuple item type",
456454
)
457455
# Workaround #4987 and avoid introducing a bogus UnboundType

mypy/semanal_newtype.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,7 @@ def check_newtype_args(
207207
self.api.anal_type(
208208
unanalyzed_type,
209209
report_invalid_types=False,
210-
allow_placeholder=not self.options.disable_recursive_aliases
211-
and not self.api.is_func_scope(),
210+
allow_placeholder=not self.api.is_func_scope(),
212211
)
213212
)
214213
should_defer = False

mypy/semanal_typeddict.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -228,10 +228,7 @@ def analyze_base_args(self, base: IndexExpr, ctx: Context) -> list[Type] | None:
228228
self.fail("Invalid TypedDict type argument", ctx)
229229
return None
230230
analyzed = self.api.anal_type(
231-
type,
232-
allow_required=True,
233-
allow_placeholder=not self.options.disable_recursive_aliases
234-
and not self.api.is_func_scope(),
231+
type, allow_required=True, allow_placeholder=not self.api.is_func_scope()
235232
)
236233
if analyzed is None:
237234
return None
@@ -307,8 +304,7 @@ def analyze_typeddict_classdef_fields(
307304
analyzed = self.api.anal_type(
308305
stmt.type,
309306
allow_required=True,
310-
allow_placeholder=not self.options.disable_recursive_aliases
311-
and not self.api.is_func_scope(),
307+
allow_placeholder=not self.api.is_func_scope(),
312308
prohibit_self_type="TypedDict item type",
313309
)
314310
if analyzed is None:
@@ -504,8 +500,7 @@ def parse_typeddict_fields_with_types(
504500
analyzed = self.api.anal_type(
505501
type,
506502
allow_required=True,
507-
allow_placeholder=not self.options.disable_recursive_aliases
508-
and not self.api.is_func_scope(),
503+
allow_placeholder=not self.api.is_func_scope(),
509504
prohibit_self_type="TypedDict item type",
510505
)
511506
if analyzed is None:

mypy/typeanal.py

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

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

test-data/unit/check-classes.test

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5002,12 +5002,13 @@ class A(Tuple[int, str]): pass
50025002
-- -----------------------
50035003

50045004
[case testCrashOnSelfRecursiveNamedTupleVar]
5005-
# flags: --disable-recursive-aliases
50065005
from typing import NamedTuple
50075006

5008-
N = NamedTuple('N', [('x', N)]) # E: Cannot resolve name "N" (possible cyclic definition)
5009-
n: N
5010-
reveal_type(n) # N: Revealed type is "Tuple[Any, fallback=__main__.N]"
5007+
def test() -> None:
5008+
N = NamedTuple('N', [('x', N)]) # E: Cannot resolve name "N" (possible cyclic definition) \
5009+
# N: Recursive types are not allowed at function scope
5010+
n: N
5011+
reveal_type(n) # N: Revealed type is "Tuple[Any, fallback=__main__.N@4]"
50115012
[builtins fixtures/tuple.pyi]
50125013

50135014
[case testCrashOnSelfRecursiveTypedDictVar]
@@ -5032,18 +5033,20 @@ lst = [n, m]
50325033
[builtins fixtures/isinstancelist.pyi]
50335034

50345035
[case testCorrectJoinOfSelfRecursiveTypedDicts]
5035-
# flags: --disable-recursive-aliases
50365036
from mypy_extensions import TypedDict
50375037

5038-
class N(TypedDict):
5039-
x: N # E: Cannot resolve name "N" (possible cyclic definition)
5040-
class M(TypedDict):
5041-
x: M # E: Cannot resolve name "M" (possible cyclic definition)
5042-
5043-
n: N
5044-
m: M
5045-
lst = [n, m]
5046-
reveal_type(lst[0]['x']) # N: Revealed type is "Any"
5038+
def test() -> None:
5039+
class N(TypedDict):
5040+
x: N # E: Cannot resolve name "N" (possible cyclic definition) \
5041+
# N: Recursive types are not allowed at function scope
5042+
class M(TypedDict):
5043+
x: M # E: Cannot resolve name "M" (possible cyclic definition) \
5044+
# N: Recursive types are not allowed at function scope
5045+
5046+
n: N
5047+
m: M
5048+
lst = [n, m]
5049+
reveal_type(lst[0]['x']) # N: Revealed type is "Any"
50475050
[builtins fixtures/isinstancelist.pyi]
50485051

50495052
[case testCrashInForwardRefToNamedTupleWithIsinstance]

test-data/unit/check-incremental.test

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4594,7 +4594,6 @@ def outer() -> None:
45944594
[out2]
45954595

45964596
[case testRecursiveAliasImported]
4597-
# flags: --disable-recursive-aliases
45984597
import a
45994598

46004599
[file a.py]
@@ -4620,16 +4619,10 @@ B = List[A]
46204619

46214620
[builtins fixtures/list.pyi]
46224621
[out]
4623-
tmp/lib.pyi:4: error: Module "other" has no attribute "B"
4624-
tmp/other.pyi:3: error: Cannot resolve name "B" (possible cyclic definition)
46254622
[out2]
4626-
tmp/lib.pyi:4: error: Module "other" has no attribute "B"
4627-
tmp/other.pyi:3: error: Cannot resolve name "B" (possible cyclic definition)
4628-
tmp/a.py:3: note: Revealed type is "builtins.list[Any]"
4629-
4630-
[case testRecursiveNamedTupleTypedDict-skip]
4631-
# https://github.com/python/mypy/issues/7125
4623+
tmp/a.py:3: note: Revealed type is "builtins.list[builtins.list[...]]"
46324624

4625+
[case testRecursiveNamedTupleTypedDict]
46334626
import a
46344627
[file a.py]
46354628
import lib
@@ -4641,15 +4634,15 @@ reveal_type(x.x['x'])
46414634
[file lib.pyi]
46424635
from typing import NamedTuple
46434636
from other import B
4644-
A = NamedTuple('A', [('x', B)]) # type: ignore
4637+
A = NamedTuple('A', [('x', B)])
46454638
[file other.pyi]
46464639
from mypy_extensions import TypedDict
46474640
from lib import A
46484641
B = TypedDict('B', {'x': A})
46494642
[builtins fixtures/dict.pyi]
46504643
[out]
46514644
[out2]
4652-
tmp/a.py:3: note: Revealed type is "Tuple[TypedDict('other.B', {'x': Any}), fallback=lib.A]"
4645+
tmp/a.py:3: note: Revealed type is "Tuple[TypedDict('other.B', {'x': Tuple[..., fallback=lib.A]}), fallback=lib.A]"
46534646

46544647
[case testFollowImportSkipNotInvalidatedOnPresent]
46554648
# flags: --follow-imports=skip

0 commit comments

Comments
 (0)