From cdd491c72dc6875c291d4a451c1a748617f27e7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristi=C3=A1n=20Maureira-Fredes?= Date: Sun, 30 Apr 2023 01:51:20 +0200 Subject: [PATCH 1/2] gh-102856: Allow comments inside multi-line f-string expresions --- Lib/test/test_fstring.py | 34 ++++++++++++++++++++++++++++++++++ Parser/tokenizer.c | 4 +++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 5e94c99ae65af1..9457e95e667545 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -653,6 +653,40 @@ def test_comments(self): ["f'{)#}'", # When wrapped in parens, this becomes # '()#)'. Make sure that doesn't compile. ]) + self.assertEqual(f'''A complex trick: { +2 # two +}''', 'A complex trick: 2') + self.assertEqual(f''' +{ +40 # fourty ++ # plus +2 # two +}''', '\n42') + self.assertEqual(f''' +{ +40 # fourty ++ # plus +2 # two +}''', '\n42') + + self.assertEqual(f''' +# this is not a comment +{ # the following operation it's +3 # this is a number +* 2}''', '\n# this is not a comment\n6') + self.assertEqual(f''' +{# f'a {comment}' +86 # constant +# nothing more +}''', '\n86') + + self.assertAllRaise(SyntaxError, r"f-string: valid expression required before '}'", + ["""f''' +{ +# only a comment +}''' +""", # this is equivalent to f'{}' + ]) def test_many_expressions(self): # Create a string with many expressions in it. Note that diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index 8de0572a1fc459..51ea86f5472bc3 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -1793,7 +1793,9 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t /* Skip comment, unless it's a type comment */ if (c == '#') { - if (INSIDE_FSTRING(tok)) { + // We don't allow # inside a one-line f-string expr, + // but we do in multi-line f-string expr + if (INSIDE_FSTRING(tok) && (tok->lineno == 1)) { return MAKE_TOKEN(syntaxerror(tok, "f-string expression part cannot include '#'")); } From 59d5c3422f5957f2c25c7d269bbf076fa243c39e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cristi=C3=A1n=20Maureira-Fredes?= Date: Sun, 30 Apr 2023 01:51:20 +0200 Subject: [PATCH 2/2] gh-102856: Allow comments inside multi-line f-string expresions --- Lib/test/test_fstring.py | 7 ++++--- Parser/tokenizer.c | 6 ------ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 9457e95e667545..d035431ed32433 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -644,10 +644,11 @@ def test_comments(self): self.assertEqual(f'{"#"}', '#') self.assertEqual(f'{d["#"]}', 'hash') - self.assertAllRaise(SyntaxError, "f-string expression part cannot include '#'", - ["f'{1#}'", # error because the expression becomes "(1#)" - "f'{3(#)}'", + self.assertAllRaise(SyntaxError, "'{' was never closed", + ["f'{1#}'", # error because everything after '#' is a comment "f'{#}'", + "f'one: {1#}'", + "f'{1# one} {2 this is a comment still#}'", ]) self.assertAllRaise(SyntaxError, r"f-string: unmatched '\)'", ["f'{)#}'", # When wrapped in parens, this becomes diff --git a/Parser/tokenizer.c b/Parser/tokenizer.c index 51ea86f5472bc3..3709807efd8927 100644 --- a/Parser/tokenizer.c +++ b/Parser/tokenizer.c @@ -1793,12 +1793,6 @@ tok_get_normal_mode(struct tok_state *tok, tokenizer_mode* current_tok, struct t /* Skip comment, unless it's a type comment */ if (c == '#') { - // We don't allow # inside a one-line f-string expr, - // but we do in multi-line f-string expr - if (INSIDE_FSTRING(tok) && (tok->lineno == 1)) { - return MAKE_TOKEN(syntaxerror(tok, "f-string expression part cannot include '#'")); - } - const char *prefix, *p, *type_start; int current_starting_col_offset;