diff options
author | Eric V. Smith <eric@trueblade.com> | 2015-09-20 19:09:15 (GMT) |
---|---|---|
committer | Eric V. Smith <eric@trueblade.com> | 2015-09-20 19:09:15 (GMT) |
commit | 608adf9c827a14795c21a14aef98b90a94eac287 (patch) | |
tree | 3a2a78d4bb08e0afda9db7839ff76e0f5cb4fdb7 | |
parent | 57b657931510957dad240d2d35243a8a7cef20c5 (diff) | |
download | cpython-608adf9c827a14795c21a14aef98b90a94eac287.zip cpython-608adf9c827a14795c21a14aef98b90a94eac287.tar.gz cpython-608adf9c827a14795c21a14aef98b90a94eac287.tar.bz2 |
Issue 25180: Fix Tools/parser/unparse.py for f-strings. Patch by Martin Panter.
-rw-r--r-- | Lib/test/test_tools/test_unparse.py | 11 | ||||
-rw-r--r-- | Tools/parser/unparse.py | 39 |
2 files changed, 48 insertions, 2 deletions
diff --git a/Lib/test/test_tools/test_unparse.py b/Lib/test/test_tools/test_unparse.py index 920fcbd..4b47916 100644 --- a/Lib/test/test_tools/test_unparse.py +++ b/Lib/test/test_tools/test_unparse.py @@ -134,6 +134,15 @@ class ASTTestCase(unittest.TestCase): class UnparseTestCase(ASTTestCase): # Tests for specific bugs found in earlier versions of unparse + def test_fstrings(self): + # See issue 25180 + self.check_roundtrip(r"""f'{f"{0}"*3}'""") + self.check_roundtrip(r"""f'{f"{y}"*3}'""") + self.check_roundtrip(r"""f'{f"{\'x\'}"*3}'""") + + self.check_roundtrip(r'''f"{r'x' f'{\"s\"}'}"''') + self.check_roundtrip(r'''f"{r'x'rf'{\"s\"}'}"''') + def test_del_statement(self): self.check_roundtrip("del x, y, z") @@ -264,8 +273,6 @@ class DirectoryTestCase(ASTTestCase): for d in self.test_directories: test_dir = os.path.join(basepath, d) for n in os.listdir(test_dir): - if n == 'test_fstring.py': - continue if n.endswith('.py') and not n.startswith('bad'): names.append(os.path.join(test_dir, n)) diff --git a/Tools/parser/unparse.py b/Tools/parser/unparse.py index c828577..9972797 100644 --- a/Tools/parser/unparse.py +++ b/Tools/parser/unparse.py @@ -322,6 +322,45 @@ class Unparser: def _Str(self, tree): self.write(repr(tree.s)) + def _JoinedStr(self, t): + self.write("f") + string = io.StringIO() + self._fstring_JoinedStr(t, string.write) + self.write(repr(string.getvalue())) + + def _FormattedValue(self, t): + self.write("f") + string = io.StringIO() + self._fstring_FormattedValue(t, string.write) + self.write(repr(string.getvalue())) + + def _fstring_JoinedStr(self, t, write): + for value in t.values: + meth = getattr(self, "_fstring_" + type(value).__name__) + meth(value, write) + + def _fstring_Str(self, t, write): + value = t.s.replace("{", "{{").replace("}", "}}") + write(value) + + def _fstring_FormattedValue(self, t, write): + write("{") + expr = io.StringIO() + Unparser(t.value, expr) + expr = expr.getvalue().rstrip("\n") + if expr.startswith("{"): + write(" ") # Separate pair of opening brackets as "{ {" + write(expr) + if t.conversion != -1: + conversion = chr(t.conversion) + assert conversion in "sra" + write(f"!{conversion}") + if t.format_spec: + write(":") + meth = getattr(self, "_fstring_" + type(t.format_spec).__name__) + meth(t.format_spec, write) + write("}") + def _Name(self, t): self.write(t.id) |