From 7468f008af9659c868474b14cdd33d2c3f3cac18 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sun, 10 May 2026 12:25:19 -0700 Subject: [PATCH 1/5] typeform: remove a regex call --- mypy/semanal.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 39230650e054..38555992726c 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -364,13 +364,6 @@ # string literal as a type expression. _MULTIPLE_WORDS_NONTYPE_RE = re.compile(r'\s*[^\s.\'"|\[]+\s+[^\s.\'"|\[]') -# Matches any valid Python identifier, including identifiers with Unicode characters. -# -# [^\d\W] = word character that is not a digit -# \w = word character -# \Z = match end of string; does not allow a trailing \n, unlike $ -_IDENTIFIER_RE = re.compile(r"^[^\d\W]\w*\Z", re.UNICODE) - class SemanticAnalyzer( NodeVisitor[None], SemanticAnalyzerInterface, SemanticAnalyzerPluginInterface, SplittingVisitor @@ -8052,7 +8045,7 @@ def try_parse_as_type_expression(self, maybe_type_expr: Expression) -> None: return # Filter out string literals which look like an identifier but # cannot be a type expression, for a few common reasons - if _IDENTIFIER_RE.fullmatch(str_value): + if str_value.isidentifier(): sym = self.lookup(str_value, UnboundType(str_value), suppress_errors=True) if sym is None: # Does not refer to anything in the local symbol table From 17fa7b947565b63eab3dd551c336ee28de79edfc Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sun, 10 May 2026 12:46:42 -0700 Subject: [PATCH 2/5] rearrange a bit --- mypy/semanal.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 38555992726c..e5898b25917d 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -8036,13 +8036,6 @@ def try_parse_as_type_expression(self, maybe_type_expr: Expression) -> None: return elif isinstance(maybe_type_expr, StrExpr): str_value = maybe_type_expr.value # cache - # Filter out string literals with common patterns that could not - # possibly be in a type expression - if _MULTIPLE_WORDS_NONTYPE_RE.match(str_value): - # A common pattern in string literals containing a sentence. - # But cannot be a type expression. - maybe_type_expr.as_type = None - return # Filter out string literals which look like an identifier but # cannot be a type expression, for a few common reasons if str_value.isidentifier(): @@ -8071,13 +8064,20 @@ def try_parse_as_type_expression(self, maybe_type_expr: Expression) -> None: return else: # does not look like an identifier if '"' in str_value or "'" in str_value: - # Only valid inside a Literal[...] type + # Only valid inside a Literal[...] or Annotated[..., ...] type if "[" not in str_value: - # Cannot be a Literal[...] type + # Cannot be a Literal[...] or Annotated[..., ...] type maybe_type_expr.as_type = None return - elif str_value == "": - # Empty string is not a valid type + elif str_value == "" or str_value.isspace(): + # Empty or whitespace-only string is not a valid type + maybe_type_expr.as_type = None + return + # Filter out string literals with common patterns that could not + # possibly be in a type expression + if _MULTIPLE_WORDS_NONTYPE_RE.match(str_value): + # A common pattern in string literals containing a sentence. + # But cannot be a type expression. maybe_type_expr.as_type = None return elif isinstance(maybe_type_expr, IndexExpr): From e75a7a6d759c87940cb8d8fe8930074673cd49c1 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sun, 10 May 2026 13:08:05 -0700 Subject: [PATCH 3/5] catch punctuation --- mypy/semanal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index e5898b25917d..f247471b8bf0 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -8069,7 +8069,7 @@ def try_parse_as_type_expression(self, maybe_type_expr: Expression) -> None: # Cannot be a Literal[...] or Annotated[..., ...] type maybe_type_expr.as_type = None return - elif str_value == "" or str_value.isspace(): + elif str_value == "" or str_value.isspace() or (len(str_value) == 1 and str_value in ".,/:*-=[]\\"): # Empty or whitespace-only string is not a valid type maybe_type_expr.as_type = None return From 7e3f0580204d62fc5077238f289ca8815c3aaa2f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 10 May 2026 20:09:49 +0000 Subject: [PATCH 4/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mypy/semanal.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index f247471b8bf0..838a24126ace 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -8069,7 +8069,11 @@ def try_parse_as_type_expression(self, maybe_type_expr: Expression) -> None: # Cannot be a Literal[...] or Annotated[..., ...] type maybe_type_expr.as_type = None return - elif str_value == "" or str_value.isspace() or (len(str_value) == 1 and str_value in ".,/:*-=[]\\"): + elif ( + str_value == "" + or str_value.isspace() + or (len(str_value) == 1 and str_value in ".,/:*-=[]\\") + ): # Empty or whitespace-only string is not a valid type maybe_type_expr.as_type = None return From 3419c41102bbcda324e427e946be99820ae51f19 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sun, 10 May 2026 13:43:21 -0700 Subject: [PATCH 5/5] len < 2 --- mypy/semanal.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 838a24126ace..fb6f299a3795 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -8069,12 +8069,9 @@ def try_parse_as_type_expression(self, maybe_type_expr: Expression) -> None: # Cannot be a Literal[...] or Annotated[..., ...] type maybe_type_expr.as_type = None return - elif ( - str_value == "" - or str_value.isspace() - or (len(str_value) == 1 and str_value in ".,/:*-=[]\\") - ): - # Empty or whitespace-only string is not a valid type + elif len(str_value) < 2 or str_value.isspace(): + # Whitespace-only strings cannot be valid types. Very short strings can + # only be valid if they are identifiers, but we already checked for those. maybe_type_expr.as_type = None return # Filter out string literals with common patterns that could not