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)