Skip to content

Commit 05b3ce7

Browse files
authored
GH-103718: Correctly cache and restore f-string buffers when needed (GH-103719)
1 parent 5041c2b commit 05b3ce7

File tree

3 files changed

+35
-11
lines changed

3 files changed

+35
-11
lines changed

Lib/test/test_fstring.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1532,5 +1532,10 @@ def test_syntax_error_for_starred_expressions(self):
15321532
"f-string: expecting a valid expression after '{'"):
15331533
compile("f'{**a}'", "?", "exec")
15341534

1535+
def test_not_closing_quotes(self):
1536+
self.assertAllRaise(SyntaxError, "unterminated f-string literal", ['f"', "f'"])
1537+
self.assertAllRaise(SyntaxError, "unterminated triple-quoted f-string literal",
1538+
['f"""', "f'''"])
1539+
15351540
if __name__ == '__main__':
15361541
unittest.main()

Parser/tokenizer.c

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -361,21 +361,35 @@ tok_concatenate_interactive_new_line(struct tok_state *tok, const char *line) {
361361
return 0;
362362
}
363363

364-
365-
/* Traverse and update all f-string buffers with the value */
364+
/* Traverse and remember all f-string buffers, in order to be able to restore
365+
them after reallocating tok->buf */
366366
static void
367-
update_fstring_buffers(struct tok_state *tok, char value, int regular, int multiline)
367+
remember_fstring_buffers(struct tok_state *tok)
368368
{
369369
int index;
370370
tokenizer_mode *mode;
371371

372372
for (index = tok->tok_mode_stack_index; index >= 0; --index) {
373373
mode = &(tok->tok_mode_stack[index]);
374-
if (regular && mode->f_string_start != NULL) {
375-
mode->f_string_start += value;
374+
if (mode->kind == TOK_FSTRING_MODE) {
375+
mode->f_string_start_offset = mode->f_string_start - tok->buf;
376+
mode->f_string_multi_line_start_offset = mode->f_string_multi_line_start - tok->buf;
376377
}
377-
if (multiline && mode->f_string_multi_line_start != NULL) {
378-
mode->f_string_multi_line_start += value;
378+
}
379+
}
380+
381+
/* Traverse and restore all f-string buffers after reallocating tok->buf */
382+
static void
383+
restore_fstring_buffers(struct tok_state *tok)
384+
{
385+
int index;
386+
tokenizer_mode *mode;
387+
388+
for (index = tok->tok_mode_stack_index; index >= 0; --index) {
389+
mode = &(tok->tok_mode_stack[index]);
390+
if (mode->kind == TOK_FSTRING_MODE) {
391+
mode->f_string_start = tok->buf + mode->f_string_start_offset;
392+
mode->f_string_multi_line_start = tok->buf + mode->f_string_multi_line_start_offset;
379393
}
380394
}
381395
}
@@ -476,7 +490,7 @@ tok_reserve_buf(struct tok_state *tok, Py_ssize_t size)
476490
Py_ssize_t start = tok->start == NULL ? -1 : tok->start - tok->buf;
477491
Py_ssize_t line_start = tok->start == NULL ? -1 : tok->line_start - tok->buf;
478492
Py_ssize_t multi_line_start = tok->multi_line_start - tok->buf;
479-
update_fstring_buffers(tok, -*tok->buf, /*regular=*/1, /*multiline=*/1);
493+
remember_fstring_buffers(tok);
480494
newbuf = (char *)PyMem_Realloc(newbuf, newsize);
481495
if (newbuf == NULL) {
482496
tok->done = E_NOMEM;
@@ -489,7 +503,7 @@ tok_reserve_buf(struct tok_state *tok, Py_ssize_t size)
489503
tok->start = start < 0 ? NULL : tok->buf + start;
490504
tok->line_start = line_start < 0 ? NULL : tok->buf + line_start;
491505
tok->multi_line_start = multi_line_start < 0 ? NULL : tok->buf + multi_line_start;
492-
update_fstring_buffers(tok, *tok->buf, /*regular=*/1, /*multiline=*/1);
506+
restore_fstring_buffers(tok);
493507
}
494508
return 1;
495509
}
@@ -1051,7 +1065,7 @@ tok_underflow_interactive(struct tok_state *tok) {
10511065
}
10521066
else if (tok->start != NULL) {
10531067
Py_ssize_t cur_multi_line_start = tok->multi_line_start - tok->buf;
1054-
update_fstring_buffers(tok, -*tok->buf, /*regular=*/0, /*multiline=*/1);
1068+
remember_fstring_buffers(tok);
10551069
size_t size = strlen(newtok);
10561070
ADVANCE_LINENO();
10571071
if (!tok_reserve_buf(tok, size + 1)) {
@@ -1064,7 +1078,7 @@ tok_underflow_interactive(struct tok_state *tok) {
10641078
PyMem_Free(newtok);
10651079
tok->inp += size;
10661080
tok->multi_line_start = tok->buf + cur_multi_line_start;
1067-
update_fstring_buffers(tok, *tok->buf, /*regular=*/0, /*multiline=*/1);
1081+
restore_fstring_buffers(tok);
10681082
}
10691083
else {
10701084
ADVANCE_LINENO();
@@ -2207,6 +2221,8 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t
22072221
the_current_tok->f_string_quote_size = quote_size;
22082222
the_current_tok->f_string_start = tok->start;
22092223
the_current_tok->f_string_multi_line_start = tok->line_start;
2224+
the_current_tok->f_string_start_offset = -1;
2225+
the_current_tok->f_string_multi_line_start_offset = -1;
22102226
the_current_tok->last_expr_buffer = NULL;
22112227
the_current_tok->last_expr_size = 0;
22122228
the_current_tok->last_expr_end = -1;

Parser/tokenizer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ typedef struct _tokenizer_mode {
5252
const char* f_string_start;
5353
const char* f_string_multi_line_start;
5454

55+
Py_ssize_t f_string_start_offset;
56+
Py_ssize_t f_string_multi_line_start_offset;
57+
5558
Py_ssize_t last_expr_size;
5659
Py_ssize_t last_expr_end;
5760
char* last_expr_buffer;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy