From 7281285b6b421724ece654426006760417aaa1f4 Mon Sep 17 00:00:00 2001 From: Erik Kemperman Date: Wed, 24 May 2023 16:30:37 +0200 Subject: [PATCH] Dedupe logic to see if new syntax is allowed --- mypy/semanal.py | 25 ++++++++++++++----------- mypy/semanal_shared.py | 12 ++++++++++++ mypy/typeanal.py | 19 ++++--------------- 3 files changed, 30 insertions(+), 26 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 2eb8c0c82f781..9f0f5015cfd73 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -482,6 +482,19 @@ def is_typeshed_stub_file(self) -> bool: def final_iteration(self) -> bool: return self._final_iteration + @property + def always_allow_new_syntax(self) -> bool: + return self.is_stub_file or self.is_future_flag_set("annotations") + + def check_pep585_name(self, fullname: str, name: str) -> bool: + return fullname == "typing." + name.capitalize() or ( + fullname == "builtins." + name.lower() + and ( + self.always_allow_new_syntax + or self.options.python_version >= (3, 9) + ) + ) + @contextmanager def allow_unbound_tvars_set(self) -> Iterator[None]: old = self.allow_unbound_tvars @@ -1011,17 +1024,7 @@ def is_expected_self_type(self, typ: Type, is_classmethod: bool) -> bool: if ( sym is not None and typ.args - and ( - sym.fullname == "typing.Type" - or ( - sym.fullname == "builtins.type" - and ( - self.is_stub_file - or self.is_future_flag_set("annotations") - or self.options.python_version >= (3, 9) - ) - ) - ) + and self.check_pep585_name(sym.fullname or "", "type") ): return self.is_expected_self_type(typ.args[0], is_classmethod=False) return False diff --git a/mypy/semanal_shared.py b/mypy/semanal_shared.py index c86ed828b2b9f..d5746a09f5155 100644 --- a/mypy/semanal_shared.py +++ b/mypy/semanal_shared.py @@ -128,6 +128,18 @@ def is_future_flag_set(self, flag: str) -> bool: def is_stub_file(self) -> bool: raise NotImplementedError + @property + @abstractmethod + def always_allow_new_syntax(self) -> bool: + """Should we allow new type syntax when targeting older Python versions + like 'list[int]' or 'X | Y' (allowed in stubs and with `__future__` import)? + """ + raise NotImplementedError + + @abstractmethod + def check_pep585_name(self, fullname: str, name: str) -> bool: + raise NotImplementedError + @abstractmethod def is_func_scope(self) -> bool: raise NotImplementedError diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 95acb71b45d2e..8ec75390eaa7e 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -209,11 +209,6 @@ def __init__( self.allow_tuple_literal = allow_tuple_literal # Positive if we are analyzing arguments of another (outer) type self.nesting_level = 0 - # Should we allow new type syntax when targeting older Python versions - # like 'list[int]' or 'X | Y' (allowed in stubs and with `__future__` import)? - self.always_allow_new_syntax = self.api.is_stub_file or self.api.is_future_flag_set( - "annotations" - ) # Should we accept unbound type variables? This is currently used for class bases, # and alias right hand sides (before they are analyzed as type aliases). self.allow_unbound_tvars = allow_unbound_tvars @@ -297,7 +292,7 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) if ( fullname in get_nongen_builtins(self.options.python_version) and t.args - and not self.always_allow_new_syntax + and not self.api.always_allow_new_syntax ): self.fail( no_subscript_builtin_alias(fullname, propose_alt=not self.defining_alias), t @@ -514,10 +509,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ code=codes.VALID_TYPE, ) return AnyType(TypeOfAny.from_error) - elif fullname == "typing.Tuple" or ( - fullname == "builtins.tuple" - and (self.always_allow_new_syntax or self.options.python_version >= (3, 9)) - ): + elif self.api.check_pep585_name(fullname, "tuple"): # Tuple is special because it is involved in builtin import cycle # and may be not ready when used. sym = self.api.lookup_fully_qualified_or_none("builtins.tuple") @@ -550,10 +542,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ return make_optional_type(item) elif fullname == "typing.Callable": return self.analyze_callable_type(t) - elif fullname == "typing.Type" or ( - fullname == "builtins.type" - and (self.always_allow_new_syntax or self.options.python_version >= (3, 9)) - ): + elif self.api.check_pep585_name(fullname, "type"): if len(t.args) == 0: if fullname == "typing.Type": any_type = self.get_omitted_any(t) @@ -1103,7 +1092,7 @@ def visit_union_type(self, t: UnionType) -> Type: if ( t.uses_pep604_syntax is True and t.is_evaluated is True - and not self.always_allow_new_syntax + and not self.api.always_allow_new_syntax and not self.options.python_version >= (3, 10) ): self.fail("X | Y syntax for unions requires Python 3.10", t, code=codes.SYNTAX)