diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-10-22-17-37-27.gh-issue-160268.gJ2iDx.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-22-17-37-27.gh-issue-160268.gJ2iDx.rst new file mode 100644 index 00000000000000..5132085d21258d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-22-17-37-27.gh-issue-160268.gJ2iDx.rst @@ -0,0 +1,3 @@ +Fix a crash in :mod:`tokenize` when a mismatched closing bracket follows the +opening brace of an f-string or t-string replacement field, as in ``f'{]a'``. +It now raises the same "unmatched" error as the parser. diff --git a/Parser/lexer/lexer.c b/Parser/lexer/lexer.c index 52ed6f79975e0a..496c2df58c5517 100644 --- a/Parser/lexer/lexer.c +++ b/Parser/lexer/lexer.c @@ -1293,9 +1293,9 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t if (tok->level > 0) { tok->level--; int opening = tok->parenstack[tok->level]; - if (!tok->tok_extra_tokens && !((opening == '(' && c == ')') || - (opening == '[' && c == ']') || - (opening == '{' && c == '}'))) { + if (!((opening == '(' && c == ')') || + (opening == '[' && c == ']') || + (opening == '{' && c == '}'))) { /* If the opening bracket belongs to an f-string's expression part (e.g. f"{)}") and the closing bracket is an arbitrary nested expression, then instead of matching a different @@ -1309,7 +1309,10 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t "%c-string: unmatched '%c'", TOK_GET_STRING_PREFIX(tok), c)); } } - if (tok->parenlinenostack[tok->level] != tok->lineno) { + if (tok->tok_extra_tokens) { + /* Allow other mismatched brackets. */ + } + else if (tok->parenlinenostack[tok->level] != tok->lineno) { return MAKE_TOKEN(_PyTokenizer_syntaxerror(tok, "closing parenthesis '%c' does not match " "opening parenthesis '%c' on line %d",