mirror of https://github.com/python/cpython.git
[3.10] bpo-44600: Fix line numbers for pattern matching cleanup code (GH-27346) (GH-27356)
(cherry picked from commit 4214f470f0
)
Co-authored-by: Charles Burkland <charles.aburkland@gmail.com>
Automerge-Triggered-By: GH:brandtbucher
This commit is contained in:
parent
628baf6fef
commit
01601aa736
|
@ -3,6 +3,7 @@
|
||||||
import dataclasses
|
import dataclasses
|
||||||
import enum
|
import enum
|
||||||
import inspect
|
import inspect
|
||||||
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
@ -3056,6 +3057,81 @@ class Keys:
|
||||||
self.assertIs(z, None)
|
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__":
|
if __name__ == "__main__":
|
||||||
"""
|
"""
|
||||||
# From inside environment using this Python, with pyperf installed:
|
# From inside environment using this Python, with pyperf installed:
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fix incorrect line numbers while tracing some failed patterns in :ref:`match <match>` statements. Patch by Charles Burkland.
|
|
@ -6398,17 +6398,25 @@ compiler_match_inner(struct compiler *c, stmt_ty s, pattern_context *pc)
|
||||||
}
|
}
|
||||||
VISIT_SEQ(c, stmt, m->body);
|
VISIT_SEQ(c, stmt, m->body);
|
||||||
ADDOP_JUMP(c, JUMP_FORWARD, end);
|
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));
|
RETURN_IF_FALSE(emit_and_reset_fail_pop(c, pc));
|
||||||
}
|
}
|
||||||
if (has_default) {
|
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
|
// A trailing "case _" is common, and lets us save a bit of redundant
|
||||||
// pushing and popping in the loop above:
|
// pushing and popping in the loop above:
|
||||||
m = asdl_seq_GET(s->v.Match.cases, cases - 1);
|
m = asdl_seq_GET(s->v.Match.cases, cases - 1);
|
||||||
SET_LOC(c, m->pattern);
|
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) {
|
if (m->guard) {
|
||||||
RETURN_IF_FALSE(compiler_jump_if(c, m->guard, end, 0));
|
RETURN_IF_FALSE(compiler_jump_if(c, m->guard, end, 0));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue