Skip to content

Commit

Permalink
pythongh-108843: fix ast.unparse for f-string with many quotes (pytho…
Browse files Browse the repository at this point in the history
  • Loading branch information
hauntsaninja authored and csm10495 committed Sep 28, 2023
1 parent 3394750 commit 0fe458e
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 1 deletion.
21 changes: 20 additions & 1 deletion Lib/ast.py
Expand Up @@ -1236,17 +1236,36 @@ def visit_JoinedStr(self, node):

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}")
Expand Down
14 changes: 14 additions & 0 deletions Lib/test/test_unparse.py
Expand Up @@ -635,6 +635,20 @@ 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}""\\"' """)

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."""
Expand Down
@@ -0,0 +1 @@
Fix an issue in :func:`ast.unparse` when unparsing f-strings containing many quote types.

0 comments on commit 0fe458e

Please sign in to comment.