https://github.com/python/cpython/commit/69ab93082d14425aaac48b8393711c716575b132
commit: 69ab93082d14425aaac48b8393711c716575b132
branch: main
author: Frank Hoffmann <[email protected]>
committer: pablogsal <[email protected]>
date: 2024-02-21T10:24:08Z
summary:

gh-112364: Correct unparsing of backslashes and quotes in ast.unparse (#115696)

files:
A Misc/NEWS.d/next/Library/2024-02-20-07-38-15.gh-issue-112364.EX7uGI.rst
M Lib/ast.py
M Lib/test/test_unparse.py

diff --git a/Lib/ast.py b/Lib/ast.py
index 43703a8325cc5e..b8c4ce6f919e6b 100644
--- a/Lib/ast.py
+++ b/Lib/ast.py
@@ -1269,14 +1269,18 @@ def visit_JoinedStr(self, node):
         quote_type = quote_types[0]
         self.write(f"{quote_type}{value}{quote_type}")
 
-    def _write_fstring_inner(self, node, escape_newlines=False):
+    def _write_fstring_inner(self, node, is_format_spec=False):
         if isinstance(node, JoinedStr):
             # for both the f-string itself, and format_spec
             for value in node.values:
-                self._write_fstring_inner(value, 
escape_newlines=escape_newlines)
+                self._write_fstring_inner(value, is_format_spec=is_format_spec)
         elif isinstance(node, Constant) and isinstance(node.value, str):
             value = node.value.replace("{", "{{").replace("}", "}}")
-            if escape_newlines:
+
+            if is_format_spec:
+                value = value.replace("\\", "\\\\")
+                value = value.replace("'", "\\'")
+                value = value.replace('"', '\\"')
                 value = value.replace("\n", "\\n")
             self.write(value)
         elif isinstance(node, FormattedValue):
@@ -1300,10 +1304,7 @@ def unparse_inner(inner):
                 self.write(f"!{chr(node.conversion)}")
             if node.format_spec:
                 self.write(":")
-                self._write_fstring_inner(
-                    node.format_spec,
-                    escape_newlines=True
-                )
+                self._write_fstring_inner(node.format_spec, 
is_format_spec=True)
 
     def visit_Name(self, node):
         self.write(node.id)
diff --git a/Lib/test/test_unparse.py b/Lib/test/test_unparse.py
index 6f698a8d891815..77ce18cbf4cbfb 100644
--- a/Lib/test/test_unparse.py
+++ b/Lib/test/test_unparse.py
@@ -649,6 +649,21 @@ def test_multiquote_joined_string(self):
         self.check_ast_roundtrip("""f'''""\"''\\'{"\\n\\"'"}''' """)
         self.check_ast_roundtrip("""f'''""\"''\\'{""\"\\n\\"'''""\" 
'''\\n'''}''' """)
 
+    def test_backslash_in_format_spec(self):
+        self.check_ast_roundtrip("""f"{x:\\ }" """)
+        self.check_ast_roundtrip("""f"{x:\\\\ }" """)
+        self.check_ast_roundtrip("""f"{x:\\\\\\ }" """)
+        self.check_ast_roundtrip("""f"{x:\\\\\\\\ }" """)
+
+    def test_quote_in_format_spec(self):
+        self.check_ast_roundtrip("""f"{x:'}" """)
+        self.check_ast_roundtrip("""f"{x:\\'}" """)
+        self.check_ast_roundtrip("""f"{x:\\\\'}" """)
+
+        self.check_ast_roundtrip("""f'\\'{x:"}' """)
+        self.check_ast_roundtrip("""f'\\'{x:\\"}' """)
+        self.check_ast_roundtrip("""f'\\'{x:\\\\"}' """)
+
 
 class ManualASTCreationTestCase(unittest.TestCase):
     """Test that AST nodes created without a type_params field unparse 
correctly."""
diff --git 
a/Misc/NEWS.d/next/Library/2024-02-20-07-38-15.gh-issue-112364.EX7uGI.rst 
b/Misc/NEWS.d/next/Library/2024-02-20-07-38-15.gh-issue-112364.EX7uGI.rst
new file mode 100644
index 00000000000000..6af71e60ec2a8e
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-02-20-07-38-15.gh-issue-112364.EX7uGI.rst
@@ -0,0 +1 @@
+Fixed :func:`ast.unparse` to handle format_spec with ``"``, ``'`` or ``\\``. 
Patched by Frank Hoffmann.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to