Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delete recursive aliases flags #16346

Merged
merged 1 commit into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 0 additions & 14 deletions mypy/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -999,15 +999,6 @@ def add_invertible_flag(
action="store_true",
help="Enable new experimental type inference algorithm",
)
internals_group.add_argument(
"--disable-recursive-aliases",
action="store_true",
help="Disable experimental support for recursive type aliases",
)
# Deprecated reverse variant of the above.
internals_group.add_argument(
"--enable-recursive-aliases", action="store_true", help=argparse.SUPPRESS
)
parser.add_argument(
"--enable-incomplete-feature",
action="append",
Expand Down Expand Up @@ -1392,11 +1383,6 @@ def set_strict_flags() -> None:
if options.logical_deps:
options.cache_fine_grained = True

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

Expand Down
4 changes: 0 additions & 4 deletions mypy/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,10 +362,6 @@ def __init__(self) -> None:
self.many_errors_threshold = defaults.MANY_ERRORS_THRESHOLD
# Enable new experimental type inference algorithm.
self.new_type_inference = False
# Disable recursive type aliases (currently experimental)
self.disable_recursive_aliases = False
# Deprecated reverse version of the above, do not use.
self.enable_recursive_aliases = False
# Export line-level, limited, fine-grained dependency information in cache data
# (undocumented feature).
self.export_ref_info = False
Expand Down
4 changes: 2 additions & 2 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -3608,7 +3608,7 @@ def check_and_set_up_type_alias(self, s: AssignmentStmt) -> bool:
)
if not res:
return False
if not self.options.disable_recursive_aliases and not self.is_func_scope():
if not self.is_func_scope():
# Only marking incomplete for top-level placeholders makes recursive aliases like
# `A = Sequence[str | A]` valid here, similar to how we treat base classes in class
# definitions, allowing `class str(Sequence[str]): ...`
Expand Down Expand Up @@ -6296,7 +6296,7 @@ def process_placeholder(
def cannot_resolve_name(self, name: str | None, kind: str, ctx: Context) -> None:
name_format = f' "{name}"' if name else ""
self.fail(f"Cannot resolve {kind}{name_format} (possible cyclic definition)", ctx)
if not self.options.disable_recursive_aliases and self.is_func_scope():
if self.is_func_scope():
self.note("Recursive types are not allowed at function scope", ctx)

def qualified_name(self, name: str) -> str:
Expand Down
6 changes: 2 additions & 4 deletions mypy/semanal_namedtuple.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,7 @@ def check_namedtuple_classdef(
# it would be inconsistent with type aliases.
analyzed = self.api.anal_type(
stmt.type,
allow_placeholder=not self.options.disable_recursive_aliases
and not self.api.is_func_scope(),
allow_placeholder=not self.api.is_func_scope(),
prohibit_self_type="NamedTuple item type",
)
if analyzed is None:
Expand Down Expand Up @@ -450,8 +449,7 @@ def parse_namedtuple_fields_with_types(
# We never allow recursive types at function scope.
analyzed = self.api.anal_type(
type,
allow_placeholder=not self.options.disable_recursive_aliases
and not self.api.is_func_scope(),
allow_placeholder=not self.api.is_func_scope(),
prohibit_self_type="NamedTuple item type",
)
# Workaround #4987 and avoid introducing a bogus UnboundType
Expand Down
3 changes: 1 addition & 2 deletions mypy/semanal_newtype.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,7 @@ def check_newtype_args(
self.api.anal_type(
unanalyzed_type,
report_invalid_types=False,
allow_placeholder=not self.options.disable_recursive_aliases
and not self.api.is_func_scope(),
allow_placeholder=not self.api.is_func_scope(),
)
)
should_defer = False
Expand Down
11 changes: 3 additions & 8 deletions mypy/semanal_typeddict.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,7 @@ def analyze_base_args(self, base: IndexExpr, ctx: Context) -> list[Type] | None:
self.fail("Invalid TypedDict type argument", ctx)
return None
analyzed = self.api.anal_type(
type,
allow_required=True,
allow_placeholder=not self.options.disable_recursive_aliases
and not self.api.is_func_scope(),
type, allow_required=True, allow_placeholder=not self.api.is_func_scope()
)
if analyzed is None:
return None
Expand Down Expand Up @@ -307,8 +304,7 @@ def analyze_typeddict_classdef_fields(
analyzed = self.api.anal_type(
stmt.type,
allow_required=True,
allow_placeholder=not self.options.disable_recursive_aliases
and not self.api.is_func_scope(),
allow_placeholder=not self.api.is_func_scope(),
prohibit_self_type="TypedDict item type",
)
if analyzed is None:
Expand Down Expand Up @@ -504,8 +500,7 @@ def parse_typeddict_fields_with_types(
analyzed = self.api.anal_type(
type,
allow_required=True,
allow_placeholder=not self.options.disable_recursive_aliases
and not self.api.is_func_scope(),
allow_placeholder=not self.api.is_func_scope(),
prohibit_self_type="TypedDict item type",
)
if analyzed is None:
Expand Down
2 changes: 1 addition & 1 deletion mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@ def cannot_resolve_type(self, t: UnboundType) -> None:
# need access to MessageBuilder here. Also move the similar
# message generation logic in semanal.py.
self.api.fail(f'Cannot resolve name "{t.name}" (possible cyclic definition)', t)
if not self.options.disable_recursive_aliases and self.api.is_func_scope():
if self.api.is_func_scope():
self.note("Recursive types are not allowed at function scope", t)

def apply_concatenate_operator(self, t: UnboundType) -> Type:
Expand Down
31 changes: 17 additions & 14 deletions test-data/unit/check-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -5002,12 +5002,13 @@ class A(Tuple[int, str]): pass
-- -----------------------

[case testCrashOnSelfRecursiveNamedTupleVar]
# flags: --disable-recursive-aliases
from typing import NamedTuple

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

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

[case testCorrectJoinOfSelfRecursiveTypedDicts]
# flags: --disable-recursive-aliases
from mypy_extensions import TypedDict

class N(TypedDict):
x: N # E: Cannot resolve name "N" (possible cyclic definition)
class M(TypedDict):
x: M # E: Cannot resolve name "M" (possible cyclic definition)

n: N
m: M
lst = [n, m]
reveal_type(lst[0]['x']) # N: Revealed type is "Any"
def test() -> None:
class N(TypedDict):
x: N # E: Cannot resolve name "N" (possible cyclic definition) \
# N: Recursive types are not allowed at function scope
class M(TypedDict):
x: M # E: Cannot resolve name "M" (possible cyclic definition) \
# N: Recursive types are not allowed at function scope

n: N
m: M
lst = [n, m]
reveal_type(lst[0]['x']) # N: Revealed type is "Any"
[builtins fixtures/isinstancelist.pyi]

[case testCrashInForwardRefToNamedTupleWithIsinstance]
Expand Down
15 changes: 4 additions & 11 deletions test-data/unit/check-incremental.test
Original file line number Diff line number Diff line change
Expand Up @@ -4594,7 +4594,6 @@ def outer() -> None:
[out2]

[case testRecursiveAliasImported]
# flags: --disable-recursive-aliases
import a

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

[builtins fixtures/list.pyi]
[out]
tmp/lib.pyi:4: error: Module "other" has no attribute "B"
tmp/other.pyi:3: error: Cannot resolve name "B" (possible cyclic definition)
[out2]
tmp/lib.pyi:4: error: Module "other" has no attribute "B"
tmp/other.pyi:3: error: Cannot resolve name "B" (possible cyclic definition)
tmp/a.py:3: note: Revealed type is "builtins.list[Any]"

[case testRecursiveNamedTupleTypedDict-skip]
# https://github.com/python/mypy/issues/7125
tmp/a.py:3: note: Revealed type is "builtins.list[builtins.list[...]]"

[case testRecursiveNamedTupleTypedDict]
import a
[file a.py]
import lib
Expand All @@ -4641,15 +4634,15 @@ reveal_type(x.x['x'])
[file lib.pyi]
from typing import NamedTuple
from other import B
A = NamedTuple('A', [('x', B)]) # type: ignore
A = NamedTuple('A', [('x', B)])
[file other.pyi]
from mypy_extensions import TypedDict
from lib import A
B = TypedDict('B', {'x': A})
[builtins fixtures/dict.pyi]
[out]
[out2]
tmp/a.py:3: note: Revealed type is "Tuple[TypedDict('other.B', {'x': Any}), fallback=lib.A]"
tmp/a.py:3: note: Revealed type is "Tuple[TypedDict('other.B', {'x': Tuple[..., fallback=lib.A]}), fallback=lib.A]"

[case testFollowImportSkipNotInvalidatedOnPresent]
# flags: --follow-imports=skip
Expand Down
Loading