[Python-checkins] bpo-44600: Fix line numbers for pattern matching cleanup code (GH-27346)
brandtbucher
webhook-mailer at python.org
Sun Jul 25 19:42:29 EDT 2021
https://github.com/python/cpython/commit/4214f470f0cb9b6fef9a90758756fbc00ba95b5a
commit: 4214f470f0cb9b6fef9a90758756fbc00ba95b5a
branch: main
author: Charles Burkland <charles.aburkland at gmail.com>
committer: brandtbucher <brandtbucher at gmail.com>
date: 2021-07-25T16:42:07-07:00
summary:
bpo-44600: Fix line numbers for pattern matching cleanup code (GH-27346)
files:
A Misc/NEWS.d/next/Security/2021-07-25-20-04-54.bpo-44600.0WMldg.rst
M Lib/test/test_patma.py
M Python/compile.c
diff --git a/Lib/test/test_patma.py b/Lib/test/test_patma.py
index 69a648a5a79fae..96c1726b0f3c9d 100644
--- a/Lib/test/test_patma.py
+++ b/Lib/test/test_patma.py
@@ -3,6 +3,7 @@
import dataclasses
import enum
import inspect
+import sys
import unittest
@@ -3056,6 +3057,81 @@ class Keys:
self.assertIs(z, None)
+class TestTracing(unittest.TestCase):
+
+ def _test_trace(self, func, expected_linenos, *f_args):
+ actual_linenos = set()
+ def trace(frame, event, arg):
+ if frame.f_code.co_name == func.__name__:
+ relative_lineno = frame.f_lineno - func.__code__.co_firstlineno
+ actual_linenos.add(relative_lineno)
+ return trace
+
+ sys.settrace(trace)
+ func(*f_args)
+ sys.settrace(None)
+ self.assertSetEqual(actual_linenos, expected_linenos)
+
+ def test_default_case_traces_correctly_a(self):
+ def default_no_assign(command): # 0
+ match command.split(): # 1
+ case ["go", direction] if direction in "nesw": # 2
+ return f"go {direction}" # 3
+ case ["go", _]: # 4
+ return "no go" # 5
+ case _: # 6
+ return "default" # 7
+
+ self._test_trace(default_no_assign, {0, 1, 2, 3}, "go n")
+ self._test_trace(default_no_assign, {0, 1, 2, 4, 5}, "go x")
+ self._test_trace(default_no_assign, {0, 1, 2, 4, 6, 7}, "spam")
+
+ def test_default_case_traces_correctly_b(self):
+ def default_wildcard_assign(command): # 0
+ match command.split(): # 1
+ case ["go", direction] if direction in "nesw": # 2
+ return f"go {direction}" # 3
+ case ["go", _]: # 4
+ return "no go" # 5
+ case x: # 6
+ return x # 7
+
+ self._test_trace(default_wildcard_assign, {0, 1, 2, 3}, "go n")
+ self._test_trace(default_wildcard_assign, {0, 1, 2, 4, 5}, "go x")
+ self._test_trace(default_wildcard_assign, {0, 1, 2, 4, 6, 7}, "spam")
+
+ def test_default_case_traces_correctly_c(self):
+ def no_default(command): # 0
+ match command.split(): # 1
+ case ["go", direction] if direction in "nesw": # 2
+ return f"go {direction}" # 3
+ case ["go", _]: # 4
+ return "no go" # 5
+
+ self._test_trace(no_default, {0, 1, 2, 3}, "go n")
+ self._test_trace(no_default, {0, 1, 2, 4, 5}, "go x")
+ self._test_trace(no_default, {0, 1, 2, 4}, "spam")
+
+ def test_default_case_traces_correctly_d(self):
+ def only_default_no_assign(command): # 0
+ match command.split(): # 1
+ case _: # 2
+ return "default" # 3
+
+ self._test_trace(only_default_no_assign, {0, 1, 2, 3}, "go n")
+ self._test_trace(only_default_no_assign, {0, 1, 2, 3} , "go x")
+ self._test_trace(only_default_no_assign, {0, 1, 2, 3}, "spam")
+
+ def test_default_case_traces_correctly_e(self):
+ def only_default_wildcard_assign(command): # 0
+ match command.split(): # 1
+ case x: # 2
+ return x # 3
+
+ self._test_trace(only_default_wildcard_assign, {0, 1, 2, 3}, "go n")
+ self._test_trace(only_default_wildcard_assign, {0, 1, 2, 3} , "go x")
+ self._test_trace(only_default_wildcard_assign, {0, 1, 2, 3}, "spam")
+
if __name__ == "__main__":
"""
# From inside environment using this Python, with pyperf installed:
diff --git a/Misc/NEWS.d/next/Security/2021-07-25-20-04-54.bpo-44600.0WMldg.rst b/Misc/NEWS.d/next/Security/2021-07-25-20-04-54.bpo-44600.0WMldg.rst
new file mode 100644
index 00000000000000..ea4e04f6da911c
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2021-07-25-20-04-54.bpo-44600.0WMldg.rst
@@ -0,0 +1 @@
+Fix incorrect line numbers while tracing some failed patterns in :ref:`match <match>` statements. Patch by Charles Burkland.
\ No newline at end of file
diff --git a/Python/compile.c b/Python/compile.c
index 3a20f6b57eb367..7fb8abf7749bbe 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -6576,17 +6576,25 @@ compiler_match_inner(struct compiler *c, stmt_ty s, pattern_context *pc)
}
VISIT_SEQ(c, stmt, m->body);
ADDOP_JUMP(c, JUMP_FORWARD, end);
+ // If the pattern fails to match, we want the line number of the
+ // cleanup to be associated with the failed pattern, not the last line
+ // of the body
+ SET_LOC(c, m->pattern);
RETURN_IF_FALSE(emit_and_reset_fail_pop(c, pc));
}
if (has_default) {
- if (cases == 1) {
- // No matches. Done with the subject:
- ADDOP(c, POP_TOP);
- }
// A trailing "case _" is common, and lets us save a bit of redundant
// pushing and popping in the loop above:
m = asdl_seq_GET(s->v.Match.cases, cases - 1);
SET_LOC(c, m->pattern);
+ if (cases == 1) {
+ // No matches. Done with the subject:
+ ADDOP(c, POP_TOP);
+ }
+ else {
+ // Show line coverage for default case (it doesn't create bytecode)
+ ADDOP(c, NOP);
+ }
if (m->guard) {
RETURN_IF_FALSE(compiler_jump_if(c, m->guard, end, 0));
}
More information about the Python-checkins
mailing list