diff options
author | Nick Coghlan <ncoghlan@gmail.com> | 2021-04-29 05:58:44 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-04-29 05:58:44 (GMT) |
commit | 1e7b858575d0ad782939f86aae4a2fa1c29e9f14 (patch) | |
tree | 9445a7a82905c5bb253564853f33dacfceac6e93 /Lib | |
parent | e52ab42cedd2a5ef4c3c1a47d0cf96a8f06d051f (diff) | |
download | cpython-1e7b858575d0ad782939f86aae4a2fa1c29e9f14.zip cpython-1e7b858575d0ad782939f86aae4a2fa1c29e9f14.tar.gz cpython-1e7b858575d0ad782939f86aae4a2fa1c29e9f14.tar.bz2 |
bpo-43892: Make match patterns explicit in the AST (GH-25585)
Co-authored-by: Brandt Bucher <brandtbucher@gmail.com>
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/ast.py | 79 | ||||
-rw-r--r-- | Lib/test/test_patma.py | 16 | ||||
-rw-r--r-- | Lib/test/test_unparse.py | 3 |
3 files changed, 93 insertions, 5 deletions
@@ -798,6 +798,9 @@ class _Unparser(NodeVisitor): else: super().visit(node) + # Note: as visit() resets the output text, do NOT rely on + # NodeVisitor.generic_visit to handle any nodes (as it calls back in to + # the subclass visit() method, which resets self._source to an empty list) def visit(self, node): """Outputs a source code string that, if converted back to an ast (using ast.parse) will generate an AST equivalent to *node*""" @@ -1587,11 +1590,79 @@ class _Unparser(NodeVisitor): with self.block(): self.traverse(node.body) + def visit_MatchValue(self, node): + self.traverse(node.value) + + def visit_MatchSingleton(self, node): + self._write_constant(node.value) + + def visit_MatchSequence(self, node): + with self.delimit("[", "]"): + self.interleave( + lambda: self.write(", "), self.traverse, node.patterns + ) + + def visit_MatchStar(self, node): + name = node.name + if name is None: + name = "_" + self.write(f"*{name}") + + def visit_MatchMapping(self, node): + def write_key_pattern_pair(pair): + k, p = pair + self.traverse(k) + self.write(": ") + self.traverse(p) + + with self.delimit("{", "}"): + keys = node.keys + self.interleave( + lambda: self.write(", "), + write_key_pattern_pair, + zip(keys, node.patterns, strict=True), + ) + rest = node.rest + if rest is not None: + if keys: + self.write(", ") + self.write(f"**{rest}") + + def visit_MatchClass(self, node): + self.set_precedence(_Precedence.ATOM, node.cls) + self.traverse(node.cls) + with self.delimit("(", ")"): + patterns = node.patterns + self.interleave( + lambda: self.write(", "), self.traverse, patterns + ) + attrs = node.kwd_attrs + if attrs: + def write_attr_pattern(pair): + attr, pattern = pair + self.write(f"{attr}=") + self.traverse(pattern) + + if patterns: + self.write(", ") + self.interleave( + lambda: self.write(", "), + write_attr_pattern, + zip(attrs, node.kwd_patterns, strict=True), + ) + def visit_MatchAs(self, node): - with self.require_parens(_Precedence.TEST, node): - self.set_precedence(_Precedence.BOR, node.pattern) - self.traverse(node.pattern) - self.write(f" as {node.name}") + name = node.name + pattern = node.pattern + if name is None: + self.write("_") + elif pattern is None: + self.write(node.name) + else: + with self.require_parens(_Precedence.TEST, node): + self.set_precedence(_Precedence.BOR, node.pattern) + self.traverse(node.pattern) + self.write(f" as {node.name}") def visit_MatchOr(self, node): with self.require_parens(_Precedence.BOR, node): diff --git a/Lib/test/test_patma.py b/Lib/test/test_patma.py index 40580be..f327552 100644 --- a/Lib/test/test_patma.py +++ b/Lib/test/test_patma.py @@ -2857,6 +2857,22 @@ class TestPatma(unittest.TestCase): self.assertIs(y, None) self.assertIs(z, None) + @no_perf + def test_patma_283(self): + self.assert_syntax_error(""" + match ...: + case {0+0: _}: + pass + """) + + @no_perf + def test_patma_284(self): + self.assert_syntax_error(""" + match ...: + case {f"": _}: + pass + """) + class PerfPatma(TestPatma): diff --git a/Lib/test/test_unparse.py b/Lib/test/test_unparse.py index ce03272..9f67b49 100644 --- a/Lib/test/test_unparse.py +++ b/Lib/test/test_unparse.py @@ -518,7 +518,8 @@ class DirectoryTestCase(ASTTestCase): lib_dir = pathlib.Path(__file__).parent / ".." test_directories = (lib_dir, lib_dir / "test") run_always_files = {"test_grammar.py", "test_syntax.py", "test_compile.py", - "test_ast.py", "test_asdl_parser.py", "test_fstring.py"} + "test_ast.py", "test_asdl_parser.py", "test_fstring.py", + "test_patma.py"} _files_to_test = None |