Skip to content

Commit 8efad61

Browse files
authored
bpo-41064: Improve syntax error for invalid usage of '**' in f-strings (GH-25006)
1 parent 4958f5d commit 8efad61

File tree

4 files changed

+47
-1
lines changed

4 files changed

+47
-1
lines changed

Grammar/python.gram

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,8 @@ invalid_for_target:
842842
invalid_group:
843843
| '(' a=starred_expression ')' {
844844
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "can't use starred expression here") }
845+
| '(' a='**' expression ')' {
846+
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "can't use double starred expression here") }
845847
invalid_import_from_targets:
846848
| import_from_as_names ',' {
847849
RAISE_SYNTAX_ERROR("trailing comma not allowed without surrounding parentheses") }

Lib/test/test_fstring.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,5 +1275,14 @@ def test_with_an_underscore_and_a_comma_in_format_specifier(self):
12751275
with self.assertRaisesRegex(ValueError, error_msg):
12761276
f'{1:_,}'
12771277

1278+
def test_syntax_error_for_starred_expressions(self):
1279+
error_msg = re.escape("can't use starred expression here")
1280+
with self.assertRaisesRegex(SyntaxError, error_msg):
1281+
compile("f'{*a}'", "?", "exec")
1282+
1283+
error_msg = re.escape("can't use double starred expression here")
1284+
with self.assertRaisesRegex(SyntaxError, error_msg):
1285+
compile("f'{**a}'", "?", "exec")
1286+
12781287
if __name__ == '__main__':
12791288
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Improve the syntax error for invalid usage of double starred elements ('**')
2+
in f-strings. Patch by Pablo Galindo.

Parser/parser.c

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18285,7 +18285,7 @@ invalid_for_target_rule(Parser *p)
1828518285
return _res;
1828618286
}
1828718287

18288-
// invalid_group: '(' starred_expression ')'
18288+
// invalid_group: '(' starred_expression ')' | '(' '**' expression ')'
1828918289
static void *
1829018290
invalid_group_rule(Parser *p)
1829118291
{
@@ -18326,6 +18326,39 @@ invalid_group_rule(Parser *p)
1832618326
D(fprintf(stderr, "%*c%s invalid_group[%d-%d]: %s failed!\n", p->level, ' ',
1832718327
p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'(' starred_expression ')'"));
1832818328
}
18329+
{ // '(' '**' expression ')'
18330+
if (p->error_indicator) {
18331+
D(p->level--);
18332+
return NULL;
18333+
}
18334+
D(fprintf(stderr, "%*c> invalid_group[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'(' '**' expression ')'"));
18335+
Token * _literal;
18336+
Token * _literal_1;
18337+
Token * a;
18338+
expr_ty expression_var;
18339+
if (
18340+
(_literal = _PyPegen_expect_token(p, 7)) // token='('
18341+
&&
18342+
(a = _PyPegen_expect_token(p, 35)) // token='**'
18343+
&&
18344+
(expression_var = expression_rule(p)) // expression
18345+
&&
18346+
(_literal_1 = _PyPegen_expect_token(p, 8)) // token=')'
18347+
)
18348+
{
18349+
D(fprintf(stderr, "%*c+ invalid_group[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'(' '**' expression ')'"));
18350+
_res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "can't use double starred expression here" );
18351+
if (_res == NULL && PyErr_Occurred()) {
18352+
p->error_indicator = 1;
18353+
D(p->level--);
18354+
return NULL;
18355+
}
18356+
goto done;
18357+
}
18358+
p->mark = _mark;
18359+
D(fprintf(stderr, "%*c%s invalid_group[%d-%d]: %s failed!\n", p->level, ' ',
18360+
p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'(' '**' expression ')'"));
18361+
}
1832918362
_res = NULL;
1833018363
done:
1833118364
D(p->level--);

0 commit comments

Comments
 (0)