Skip to content

Commit de9a495

Browse files
committed
Use two separate limits and update tests
1 parent 2c48eae commit de9a495

File tree

3 files changed

+26
-11
lines changed

3 files changed

+26
-11
lines changed

Lib/test/test_fstring.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,23 @@ def test_fstring_nested_too_deeply(self):
565565
self.assertAllRaise(SyntaxError,
566566
"f-string: expressions nested too deeply",
567567
['f"{1+2:{1+2:{1+1:{1}}}}"'])
568+
569+
def create_nested_fstring(n):
570+
if n == 0:
571+
return "1+1"
572+
prev = create_nested_fstring(n-1)
573+
return f'f"{{{prev}}}"'
568574

575+
self.assertAllRaise(SyntaxError,
576+
"too many nested f-strings",
577+
[create_nested_fstring(160)])
578+
579+
def test_syntax_error_in_nested_fstring(self):
580+
# See gh-104016 for more information on this crash
581+
self.assertAllRaise(SyntaxError,
582+
"invalid syntax",
583+
['f"{1 1:' + ('{f"1:' * 199)])
584+
569585
def test_double_braces(self):
570586
self.assertEqual(f'{{', '{')
571587
self.assertEqual(f'a{{', 'a{')
@@ -1355,7 +1371,6 @@ def test_filename_in_syntaxerror(self):
13551371
# see issue 38964
13561372
with temp_cwd() as cwd:
13571373
file_path = os.path.join(cwd, 't.py')
1358-
with open(file_path, 'w', encoding="utf-8") as f:
13591374
f.write('f"{a b}"') # This generates a SyntaxError
13601375
_, _, stderr = assert_python_failure(file_path,
13611376
PYTHONIOENCODING='ascii')
@@ -1549,9 +1564,5 @@ def test_syntax_error_after_debug(self):
15491564
"f'{1=}{1;}'",
15501565
])
15511566

1552-
def test_nested_fstring_max_stack_level(self):
1553-
with self.assertRaises(SyntaxError):
1554-
compile('f"{1 1:' + ('{f"1:' * 199), "?", "exec")
1555-
15561567
if __name__ == '__main__':
15571568
unittest.main()

Parser/tokenizer.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@
4343
#ifdef Py_DEBUG
4444
static inline tokenizer_mode* TOK_GET_MODE(struct tok_state* tok) {
4545
assert(tok->tok_mode_stack_index >= 0);
46-
assert(tok->tok_mode_stack_index < MAXLEVEL);
46+
assert(tok->tok_mode_stack_index < MAXFSTRINGLEVEL);
4747
return &(tok->tok_mode_stack[tok->tok_mode_stack_index]);
4848
}
4949
static inline tokenizer_mode* TOK_NEXT_MODE(struct tok_state* tok) {
5050
assert(tok->tok_mode_stack_index >= 0);
51-
assert(tok->tok_mode_stack_index + 1 < MAXLEVEL);
51+
assert(tok->tok_mode_stack_index + 1 < MAXFSTRINGLEVEL);
5252
return &(tok->tok_mode_stack[++tok->tok_mode_stack_index]);
5353
}
5454
#else
@@ -2235,6 +2235,9 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t
22352235

22362236
p_start = tok->start;
22372237
p_end = tok->cur;
2238+
if (tok->tok_mode_stack_index + 1 >= MAXFSTRINGLEVEL) {
2239+
return MAKE_TOKEN(syntaxerror(tok, "too many nested f-strings"));
2240+
}
22382241
tokenizer_mode *the_current_tok = TOK_NEXT_MODE(tok);
22392242
the_current_tok->kind = TOK_FSTRING_MODE;
22402243
the_current_tok->f_string_quote = quote;
@@ -2413,7 +2416,7 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t
24132416
case '(':
24142417
case '[':
24152418
case '{':
2416-
if (tok->level >= MAXLEVEL || tok->tok_mode_stack_index + 1 >= MAXLEVEL) {
2419+
if (tok->level >= MAXLEVEL) {
24172420
return MAKE_TOKEN(syntaxerror(tok, "too many nested parentheses"));
24182421
}
24192422
tok->parenstack[tok->level] = c;

Parser/tokenizer.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ extern "C" {
1010

1111
#include "pycore_token.h" /* For token types */
1212

13-
#define MAXINDENT 100 /* Max indentation level */
14-
#define MAXLEVEL 200 /* Max parentheses level */
13+
#define MAXINDENT 100 /* Max indentation level */
14+
#define MAXLEVEL 200 /* Max parentheses level */
15+
#define MAXFSTRINGLEVEL 150 /* Max f-string nesting level */
1516

1617
enum decoding_state {
1718
STATE_INIT,
@@ -123,7 +124,7 @@ struct tok_state {
123124
enum interactive_underflow_t interactive_underflow;
124125
int report_warnings;
125126
// TODO: Factor this into its own thing
126-
tokenizer_mode tok_mode_stack[MAXLEVEL];
127+
tokenizer_mode tok_mode_stack[MAXFSTRINGLEVEL];
127128
int tok_mode_stack_index;
128129
int tok_report_warnings;
129130
#ifdef Py_DEBUG

0 commit comments

Comments
 (0)