summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorNick Coghlan <ncoghlan@gmail.com>2021-04-29 05:58:44 (GMT)
committerGitHub <noreply@github.com>2021-04-29 05:58:44 (GMT)
commit1e7b858575d0ad782939f86aae4a2fa1c29e9f14 (patch)
tree9445a7a82905c5bb253564853f33dacfceac6e93 /Lib
parente52ab42cedd2a5ef4c3c1a47d0cf96a8f06d051f (diff)
downloadcpython-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.py79
-rw-r--r--Lib/test/test_patma.py16
-rw-r--r--Lib/test/test_unparse.py3
3 files changed, 93 insertions, 5 deletions
diff --git a/Lib/ast.py b/Lib/ast.py
index 703f68a..0c53e5c 100644
--- a/Lib/ast.py
+++ b/Lib/ast.py
@@ -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