Skip to content

Commit b5b2396

Browse files
committed
gh-129958: Properly disallow newlines in format specs in single-quoted f-strings
1 parent e094420 commit b5b2396

File tree

4 files changed

+39
-31
lines changed

4 files changed

+39
-31
lines changed

Lib/test/test_fstring.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1782,6 +1782,31 @@ def test_gh129093(self):
17821782
self.assertEqual(f'{f'{1!=2=}'=}', "f'{1!=2=}'='1!=2=True'")
17831783
self.assertEqual(f'{f'{1 != 2=}'=}', "f'{1 != 2=}'='1 != 2=True'")
17841784

1785+
def test_newlines_in_format_specifiers(self):
1786+
cases = [
1787+
"""f'{1:d\n}'""",
1788+
"""f'__{
1789+
1:d
1790+
}__'""",
1791+
'''f"{value:.
1792+
{'2f'}}"''',
1793+
'''f"{value:
1794+
{'.2f'}f}"''',
1795+
'''f"{value:
1796+
#{'x'}}"''',
1797+
]
1798+
self.assertAllRaise(SyntaxError, "f-string: newlines are not allowed in format specifiers", cases)
1799+
1800+
valid_cases = [
1801+
"""f'''__{
1802+
1:d
1803+
}__'''""",
1804+
"""f'''{1:d\n}'''""",
1805+
]
1806+
1807+
for case in valid_cases:
1808+
compile(case, "<string>", "exec")
1809+
17851810

17861811
if __name__ == '__main__':
17871812
unittest.main()

Lib/test/test_tokenize.py

Lines changed: 4 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -600,22 +600,6 @@ def test_string(self):
600600
OP '}' (6, 0) (6, 1)
601601
FSTRING_MIDDLE '__' (6, 1) (6, 3)
602602
FSTRING_END "'''" (6, 3) (6, 6)
603-
""")
604-
self.check_tokenize("""\
605-
f'__{
606-
x:d
607-
}__'""", """\
608-
FSTRING_START "f'" (1, 0) (1, 2)
609-
FSTRING_MIDDLE '__' (1, 2) (1, 4)
610-
OP '{' (1, 4) (1, 5)
611-
NL '\\n' (1, 5) (1, 6)
612-
NAME 'x' (2, 4) (2, 5)
613-
OP ':' (2, 5) (2, 6)
614-
FSTRING_MIDDLE 'd' (2, 6) (2, 7)
615-
NL '\\n' (2, 7) (2, 8)
616-
OP '}' (3, 0) (3, 1)
617-
FSTRING_MIDDLE '__' (3, 1) (3, 3)
618-
FSTRING_END "'" (3, 3) (3, 4)
619603
""")
620604

621605
self.check_tokenize("""\
@@ -2466,21 +2450,6 @@ def test_string(self):
24662450
RBRACE '}' (6, 0) (6, 1)
24672451
FSTRING_MIDDLE '__' (6, 1) (6, 3)
24682452
FSTRING_END "'''" (6, 3) (6, 6)
2469-
""")
2470-
2471-
self.check_tokenize("""\
2472-
f'__{
2473-
x:d
2474-
}__'""", """\
2475-
FSTRING_START "f'" (1, 0) (1, 2)
2476-
FSTRING_MIDDLE '__' (1, 2) (1, 4)
2477-
LBRACE '{' (1, 4) (1, 5)
2478-
NAME 'x' (2, 4) (2, 5)
2479-
COLON ':' (2, 5) (2, 6)
2480-
FSTRING_MIDDLE 'd' (2, 6) (2, 7)
2481-
RBRACE '}' (3, 0) (3, 1)
2482-
FSTRING_MIDDLE '__' (3, 1) (3, 3)
2483-
FSTRING_END "'" (3, 3) (3, 4)
24842453
""")
24852454

24862455
def test_function(self):
@@ -3036,6 +3005,10 @@ def get_tokens(string):
30363005
"'''sdfsdf''",
30373006
"("*1000+"a"+")"*1000,
30383007
"]",
3008+
"""\
3009+
f'__{
3010+
x:d
3011+
}__'""",
30393012
]:
30403013
with self.subTest(case=case):
30413014
self.assertRaises(tokenize.TokenError, get_tokens, case)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a bug that was allowing newlines inconsitently in format specifiers for
2+
single-quoted f-strings. Patch by Pablo Galindo.

Parser/lexer/lexer.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,6 +1339,14 @@ tok_get_fstring_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct
13391339
// it means that the format spec ends here and we should
13401340
// return to the regular mode.
13411341
if (in_format_spec && c == '\n') {
1342+
if (current_tok->f_string_quote_size == 1) {
1343+
return MAKE_TOKEN(
1344+
_PyTokenizer_syntaxerror(
1345+
tok,
1346+
"f-string: newlines are not allowed in format specifiers for single quoted f-strings"
1347+
)
1348+
);
1349+
}
13421350
tok_backup(tok, c);
13431351
TOK_GET_MODE(tok)->kind = TOK_REGULAR_MODE;
13441352
current_tok->in_format_spec = 0;

0 commit comments

Comments
 (0)