Skip to content

Commit 2f8b08d

Browse files
authored
gh-129958: Properly disallow newlines in format specs in single-quoted f-strings (GH-130063)
1 parent e01e582 commit 2f8b08d

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
@@ -1795,6 +1795,31 @@ def test_gh129093(self):
17951795
self.assertEqual(f'{f'{1!=2=}'=}', "f'{1!=2=}'='1!=2=True'")
17961796
self.assertEqual(f'{f'{1 != 2=}'=}', "f'{1 != 2=}'='1 != 2=True'")
17971797

1798+
def test_newlines_in_format_specifiers(self):
1799+
cases = [
1800+
"""f'{1:d\n}'""",
1801+
"""f'__{
1802+
1:d
1803+
}__'""",
1804+
'''f"{value:.
1805+
{'2f'}}"''',
1806+
'''f"{value:
1807+
{'.2f'}f}"''',
1808+
'''f"{value:
1809+
#{'x'}}"''',
1810+
]
1811+
self.assertAllRaise(SyntaxError, "f-string: newlines are not allowed in format specifiers", cases)
1812+
1813+
valid_cases = [
1814+
"""f'''__{
1815+
1:d
1816+
}__'''""",
1817+
"""f'''{1:d\n}'''""",
1818+
]
1819+
1820+
for case in valid_cases:
1821+
compile(case, "<string>", "exec")
1822+
17981823

17991824
if __name__ == '__main__':
18001825
unittest.main()

Lib/test/test_tokenize.py

Lines changed: 4 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -605,22 +605,6 @@ def test_string(self):
605605
OP '}' (6, 0) (6, 1)
606606
FSTRING_MIDDLE '__' (6, 1) (6, 3)
607607
FSTRING_END "'''" (6, 3) (6, 6)
608-
""")
609-
self.check_tokenize("""\
610-
f'__{
611-
x:d
612-
}__'""", """\
613-
FSTRING_START "f'" (1, 0) (1, 2)
614-
FSTRING_MIDDLE '__' (1, 2) (1, 4)
615-
OP '{' (1, 4) (1, 5)
616-
NL '\\n' (1, 5) (1, 6)
617-
NAME 'x' (2, 4) (2, 5)
618-
OP ':' (2, 5) (2, 6)
619-
FSTRING_MIDDLE 'd' (2, 6) (2, 7)
620-
NL '\\n' (2, 7) (2, 8)
621-
OP '}' (3, 0) (3, 1)
622-
FSTRING_MIDDLE '__' (3, 1) (3, 3)
623-
FSTRING_END "'" (3, 3) (3, 4)
624608
""")
625609

626610
self.check_tokenize("""\
@@ -2471,21 +2455,6 @@ def test_string(self):
24712455
RBRACE '}' (6, 0) (6, 1)
24722456
FSTRING_MIDDLE '__' (6, 1) (6, 3)
24732457
FSTRING_END "'''" (6, 3) (6, 6)
2474-
""")
2475-
2476-
self.check_tokenize("""\
2477-
f'__{
2478-
x:d
2479-
}__'""", """\
2480-
FSTRING_START "f'" (1, 0) (1, 2)
2481-
FSTRING_MIDDLE '__' (1, 2) (1, 4)
2482-
LBRACE '{' (1, 4) (1, 5)
2483-
NAME 'x' (2, 4) (2, 5)
2484-
COLON ':' (2, 5) (2, 6)
2485-
FSTRING_MIDDLE 'd' (2, 6) (2, 7)
2486-
RBRACE '}' (3, 0) (3, 1)
2487-
FSTRING_MIDDLE '__' (3, 1) (3, 3)
2488-
FSTRING_END "'" (3, 3) (3, 4)
24892458
""")
24902459

24912460
def test_function(self):
@@ -3041,6 +3010,10 @@ def get_tokens(string):
30413010
"'''sdfsdf''",
30423011
"("*1000+"a"+")"*1000,
30433012
"]",
3013+
"""\
3014+
f'__{
3015+
x:d
3016+
}__'""",
30443017
]:
30453018
with self.subTest(case=case):
30463019
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
@@ -1341,6 +1341,14 @@ tok_get_fstring_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct
13411341
// it means that the format spec ends here and we should
13421342
// return to the regular mode.
13431343
if (in_format_spec && c == '\n') {
1344+
if (current_tok->f_string_quote_size == 1) {
1345+
return MAKE_TOKEN(
1346+
_PyTokenizer_syntaxerror(
1347+
tok,
1348+
"f-string: newlines are not allowed in format specifiers for single quoted f-strings"
1349+
)
1350+
);
1351+
}
13441352
tok_backup(tok, c);
13451353
TOK_GET_MODE(tok)->kind = TOK_REGULAR_MODE;
13461354
current_tok->in_format_spec = 0;

0 commit comments

Comments
 (0)