bpo-39166: Fix trace of last iteration of async for loops (#17800)

This commit is contained in:
Pablo Galindo 2020-01-10 09:24:22 +00:00 committed by Mark Shannon
parent 850a8856e1
commit 4c53e63cc9
3 changed files with 87 additions and 5 deletions

View File

@ -526,6 +526,82 @@ class TraceTestCase(unittest.TestCase):
(7, 'line'),
(7, 'return')])
def test_20_async_for_loop(self):
class AsyncIteratorWrapper:
def __init__(self, obj):
self._it = iter(obj)
def __aiter__(self):
return self
async def __anext__(self):
try:
return next(self._it)
except StopIteration:
raise StopAsyncIteration
async def doit_async():
async for letter in AsyncIteratorWrapper("abc"):
x = letter
y = 42
def run(tracer):
x = doit_async()
try:
sys.settrace(tracer)
x.send(None)
finally:
sys.settrace(None)
tracer = self.make_tracer()
events = [
(0, 'call'),
(1, 'line'),
(-12, 'call'),
(-11, 'line'),
(-11, 'return'),
(-9, 'call'),
(-8, 'line'),
(-8, 'return'),
(-6, 'call'),
(-5, 'line'),
(-4, 'line'),
(-4, 'return'),
(1, 'exception'),
(2, 'line'),
(1, 'line'),
(-6, 'call'),
(-5, 'line'),
(-4, 'line'),
(-4, 'return'),
(1, 'exception'),
(2, 'line'),
(1, 'line'),
(-6, 'call'),
(-5, 'line'),
(-4, 'line'),
(-4, 'return'),
(1, 'exception'),
(2, 'line'),
(1, 'line'),
(-6, 'call'),
(-5, 'line'),
(-4, 'line'),
(-4, 'exception'),
(-3, 'line'),
(-2, 'line'),
(-2, 'exception'),
(-2, 'return'),
(1, 'exception'),
(3, 'line'),
(3, 'return')]
try:
run(tracer.trace)
except Exception:
pass
self.compare_events(doit_async.__code__.co_firstlineno,
tracer.events, events)
class SkipLineEventsTraceTestCase(TraceTestCase):
"""Repeat the trace tests, but with per-line events skipped"""

View File

@ -0,0 +1,2 @@
Fix incorrect line execution reporting in trace functions when tracing the
last iteration of asynchronous for loops. Patch by Pablo Galindo.

View File

@ -3610,11 +3610,15 @@ exception_unwind:
PUSH(val);
PUSH(exc);
JUMPTO(handler);
if (_Py_TracingPossible(ceval) &&
((f->f_lasti < instr_lb || f->f_lasti >= instr_ub) ||
!(f->f_lasti == instr_lb || f->f_lasti < instr_prev))) {
/* Make sure that we trace line after exception */
instr_prev = INT_MAX;
if (_Py_TracingPossible(ceval)) {
int needs_new_execution_window = (f->f_lasti < instr_lb || f->f_lasti >= instr_ub);
int needs_line_update = (f->f_lasti == instr_lb || f->f_lasti < instr_prev);
/* Make sure that we trace line after exception if we are in a new execution
* window or we don't need a line update and we are not in the first instruction
* of the line. */
if (needs_new_execution_window || (!needs_line_update && instr_lb > 0)) {
instr_prev = INT_MAX;
}
}
/* Resume normal execution */
goto main_loop;