summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2023-09-18 14:44:13 (GMT)
committerGitHub <noreply@github.com>2023-09-18 14:44:13 (GMT)
commit2401b980da571cac49c804edb66696766c4cd9c0 (patch)
tree577811e52835fc9c662694150ceea4d764c917cd
parentf6fc831aa96f82b74c8dae9a20dc88c89e457967 (diff)
downloadcpython-2401b980da571cac49c804edb66696766c4cd9c0.zip
cpython-2401b980da571cac49c804edb66696766c4cd9c0.tar.gz
cpython-2401b980da571cac49c804edb66696766c4cd9c0.tar.bz2
[3.12] gh-108843: fix ast.unparse for f-string with many quotes (GH-108981) (#109541)
gh-108843: fix ast.unparse for f-string with many quotes (GH-108981) (cherry picked from commit 23f9f6f46454455bc6015e83ae5b5e946dae7698) Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
-rw-r--r--Lib/ast.py21
-rw-r--r--Lib/test/test_unparse.py14
-rw-r--r--Misc/NEWS.d/next/Library/2023-09-06-06-17-23.gh-issue-108843.WJMhsS.rst1
3 files changed, 35 insertions, 1 deletions
diff --git a/Lib/ast.py b/Lib/ast.py
index f4e542c..0704470 100644
--- a/Lib/ast.py
+++ b/Lib/ast.py
@@ -1234,17 +1234,36 @@ class _Unparser(NodeVisitor):
new_fstring_parts = []
quote_types = list(_ALL_QUOTES)
+ fallback_to_repr = False
for value, is_constant in fstring_parts:
if is_constant:
- value, quote_types = self._str_literal_helper(
+ value, new_quote_types = self._str_literal_helper(
value,
quote_types=quote_types,
escape_special_whitespace=True,
)
+ if set(new_quote_types).isdisjoint(quote_types):
+ fallback_to_repr = True
+ break
+ quote_types = new_quote_types
elif "\n" in value:
quote_types = [q for q in quote_types if q in _MULTI_QUOTES]
+ assert quote_types
new_fstring_parts.append(value)
+ 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:
+ if is_constant:
+ value = repr('"' + value) # force repr to use single quotes
+ expected_prefix = "'\""
+ assert value.startswith(expected_prefix), repr(value)
+ value = value[len(expected_prefix):-1]
+ new_fstring_parts.append(value)
+
value = "".join(new_fstring_parts)
quote_type = quote_types[0]
self.write(f"{quote_type}{value}{quote_type}")
diff --git a/Lib/test/test_unparse.py b/Lib/test/test_unparse.py
index 38c59e6..bdf7b05 100644
--- a/Lib/test/test_unparse.py
+++ b/Lib/test/test_unparse.py
@@ -635,6 +635,20 @@ class CosmeticTestCase(ASTTestCase):
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}""\\"' """)
+
+ self.check_ast_roundtrip("""f"'''{"\\n"}""\\"" """)
+ self.check_ast_roundtrip("""f'""\"{"\\n"}''' """)
+ self.check_ast_roundtrip("""f'""\"{"\\n"}""\\"' """)
+
+ self.check_ast_roundtrip("""f'''""\"''\\'{"\\n"}''' """)
+ self.check_ast_roundtrip("""f'''""\"''\\'{"\\n\\"'"}''' """)
+ self.check_ast_roundtrip("""f'''""\"''\\'{""\"\\n\\"'''""\" '''\\n'''}''' """)
+
class ManualASTCreationTestCase(unittest.TestCase):
"""Test that AST nodes created without a type_params field unparse correctly."""
diff --git a/Misc/NEWS.d/next/Library/2023-09-06-06-17-23.gh-issue-108843.WJMhsS.rst b/Misc/NEWS.d/next/Library/2023-09-06-06-17-23.gh-issue-108843.WJMhsS.rst
new file mode 100644
index 0000000..0f15761
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-09-06-06-17-23.gh-issue-108843.WJMhsS.rst
@@ -0,0 +1 @@
+Fix an issue in :func:`ast.unparse` when unparsing f-strings containing many quote types.