From cf7f0eebc5824de6525efb4175e0aa3b877f02db Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Tue, 5 Sep 2023 21:22:14 -0700 Subject: [PATCH 1/3] [3.11] gh-108843: fix ast.unparse for f-string with many quotes --- Lib/ast.py | 18 +++++++++++++++++- Lib/test/test_unparse.py | 5 +++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Lib/ast.py b/Lib/ast.py index 623b9a1b805d0e..9b2d4fea1f3bd8 100644 --- a/Lib/ast.py +++ b/Lib/ast.py @@ -1175,13 +1175,29 @@ def visit_JoinedStr(self, node): new_fstring_parts = [] quote_types = list(_ALL_QUOTES) + fallback_to_repr = False for value, is_constant in fstring_parts: - value, quote_types = self._str_literal_helper( + value, new_quote_types = self._str_literal_helper( value, quote_types=quote_types, escape_special_whitespace=is_constant, ) new_fstring_parts.append(value) + if set(new_quote_types).isdisjoint(quote_types): + fallback_to_repr = True + break + quote_types = new_quote_types + + if fallback_to_repr: + # If we weren't able to find a quote type that works for all parts + # of the JoinedStr, fallback to using repr and triple single quotes. + quote_types = ["'''"] + new_fstring_parts.clear() + for value, is_constant in fstring_parts: + value = repr("'" + '"' + value) # force repr to escape all quotes + expected_prefix = r"'\'" + '"' + assert value.startswith(expected_prefix), repr(value) + new_fstring_parts.append(value[len(expected_prefix):-1]) value = "".join(new_fstring_parts) quote_type = quote_types[0] diff --git a/Lib/test/test_unparse.py b/Lib/test/test_unparse.py index f1f1dd5dc26be8..d8f0060b3a4ef9 100644 --- a/Lib/test/test_unparse.py +++ b/Lib/test/test_unparse.py @@ -662,6 +662,11 @@ def test_star_expr_assign_target_multiple(self): self.check_src_roundtrip("[a, b] = [c, d] = [e, f] = g") self.check_src_roundtrip("a, b = [c, d] = e, f = g") + def test_multiquote_joined_string(self): + self.check_ast_roundtrip("f\"'''{1}\\\"\\\"\\\"\" ") + self.check_ast_roundtrip("""f"'''{1}""\\"" """) + self.check_ast_roundtrip("""f'""\"{1}''' """) + self.check_ast_roundtrip("""f'""\"{1}""\\"' """) class DirectoryTestCase(ASTTestCase): From adf3c07f9ee03a4411064d019b5755ee63f9d1a1 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Wed, 6 Sep 2023 04:30:09 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2023-09-06-04-30-05.gh-issue-108843.WJMhsS.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2023-09-06-04-30-05.gh-issue-108843.WJMhsS.rst diff --git a/Misc/NEWS.d/next/Library/2023-09-06-04-30-05.gh-issue-108843.WJMhsS.rst b/Misc/NEWS.d/next/Library/2023-09-06-04-30-05.gh-issue-108843.WJMhsS.rst new file mode 100644 index 00000000000000..0f15761c14bb7d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-09-06-04-30-05.gh-issue-108843.WJMhsS.rst @@ -0,0 +1 @@ +Fix an issue in :func:`ast.unparse` when unparsing f-strings containing many quote types. From 543dbef89d757231abd1eaf50991432863cee96c Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Tue, 5 Sep 2023 22:28:47 -0700 Subject: [PATCH 3/3] simplify --- Lib/ast.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/ast.py b/Lib/ast.py index 9b2d4fea1f3bd8..d84d75e1f3a8e1 100644 --- a/Lib/ast.py +++ b/Lib/ast.py @@ -1194,8 +1194,8 @@ def visit_JoinedStr(self, node): quote_types = ["'''"] new_fstring_parts.clear() for value, is_constant in fstring_parts: - value = repr("'" + '"' + value) # force repr to escape all quotes - expected_prefix = r"'\'" + '"' + value = repr('"' + value) # force repr to use single quotes + expected_prefix = "'\"" assert value.startswith(expected_prefix), repr(value) new_fstring_parts.append(value[len(expected_prefix):-1])