Skip to content

Commit

Permalink
Dedupe logic to see if new syntax is allowed
Browse files Browse the repository at this point in the history
  • Loading branch information
erikkemperman committed May 24, 2023
1 parent 9ca93de commit 7281285
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 26 deletions.
25 changes: 14 additions & 11 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions mypy/semanal_shared.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 4 additions & 15 deletions mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 7281285

Please sign in to comment.