]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-112364: Correct unparsing of backslashes and quotes in ast.unparse (#115696)
authorFrank Hoffmann <44680962+15r10nk@users.noreply.github.com>
Wed, 21 Feb 2024 10:24:08 +0000 (11:24 +0100)
committerGitHub <noreply@github.com>
Wed, 21 Feb 2024 10:24:08 +0000 (10:24 +0000)
Lib/ast.py
Lib/test/test_unparse.py
Misc/NEWS.d/next/Library/2024-02-20-07-38-15.gh-issue-112364.EX7uGI.rst [new file with mode: 0644]

index 43703a8325cc5ec15815d151a2816cfc34b9e3d2..b8c4ce6f919e6b4c5e7614a415cf4ffc65d17fe0 100644 (file)
@@ -1269,14 +1269,18 @@ class _Unparser(NodeVisitor):
         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 @@ class _Unparser(NodeVisitor):
                 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)
index 6f698a8d89181542ca74b458a0a9cbafe5345ecc..77ce18cbf4cbfb5e03ba4ceeaa129c10b78562ae 100644 (file)
@@ -649,6 +649,21 @@ class CosmeticTestCase(ASTTestCase):
         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 (file)
index 0000000..6af71e6
--- /dev/null
@@ -0,0 +1 @@
+Fixed :func:`ast.unparse` to handle format_spec with ``"``, ``'`` or ``\\``. Patched by Frank Hoffmann.