mirror of
https://github.com/python/cpython.git
synced 2025-09-27 10:50:04 +00:00
[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 23f9f6f464
)
Co-authored-by: Shantanu <12621235+hauntsaninja@users.noreply.github.com>
This commit is contained in:
parent
f6fc831aa9
commit
2401b980da
3 changed files with 35 additions and 1 deletions
21
Lib/ast.py
21
Lib/ast.py
|
@ -1234,17 +1234,36 @@ class _Unparser(NodeVisitor):
|
||||||
|
|
||||||
new_fstring_parts = []
|
new_fstring_parts = []
|
||||||
quote_types = list(_ALL_QUOTES)
|
quote_types = list(_ALL_QUOTES)
|
||||||
|
fallback_to_repr = False
|
||||||
for value, is_constant in fstring_parts:
|
for value, is_constant in fstring_parts:
|
||||||
if is_constant:
|
if is_constant:
|
||||||
value, quote_types = self._str_literal_helper(
|
value, new_quote_types = self._str_literal_helper(
|
||||||
value,
|
value,
|
||||||
quote_types=quote_types,
|
quote_types=quote_types,
|
||||||
escape_special_whitespace=True,
|
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:
|
elif "\n" in value:
|
||||||
quote_types = [q for q in quote_types if q in _MULTI_QUOTES]
|
quote_types = [q for q in quote_types if q in _MULTI_QUOTES]
|
||||||
|
assert quote_types
|
||||||
new_fstring_parts.append(value)
|
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)
|
value = "".join(new_fstring_parts)
|
||||||
quote_type = quote_types[0]
|
quote_type = quote_types[0]
|
||||||
self.write(f"{quote_type}{value}{quote_type}")
|
self.write(f"{quote_type}{value}{quote_type}")
|
||||||
|
|
|
@ -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")
|
||||||
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):
|
class ManualASTCreationTestCase(unittest.TestCase):
|
||||||
"""Test that AST nodes created without a type_params field unparse correctly."""
|
"""Test that AST nodes created without a type_params field unparse correctly."""
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fix an issue in :func:`ast.unparse` when unparsing f-strings containing many quote types.
|
Loading…
Add table
Add a link
Reference in a new issue