summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPablo Galindo <Pablogsal@gmail.com>2020-01-10 09:24:22 +0000
committerMark Shannon <mark@hotpy.org>2020-01-10 09:24:22 +0000
commit4c53e63cc966f98e141a09bc435b9f9c713b152d (patch)
tree27435c8386cf6a99db3de9300b15a6ac6f70640d
parent850a8856e120f8cba15e557a0e791f93b43d6989 (diff)
downloadcpython-git-4c53e63cc966f98e141a09bc435b9f9c713b152d.tar.gz
bpo-39166: Fix trace of last iteration of async for loops (#17800)
-rw-r--r--Lib/test/test_sys_settrace.py76
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2020-01-02-22-22-03.bpo-39166.Qt-UeD.rst2
-rw-r--r--Python/ceval.c14
3 files changed, 87 insertions, 5 deletions
diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py
index a0d1122fad..bead57c44b 100644
--- a/Lib/test/test_sys_settrace.py
+++ b/Lib/test/test_sys_settrace.py
@@ -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"""
diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-01-02-22-22-03.bpo-39166.Qt-UeD.rst b/Misc/NEWS.d/next/Core and Builtins/2020-01-02-22-22-03.bpo-39166.Qt-UeD.rst
new file mode 100644
index 0000000000..4737e9c4d2
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2020-01-02-22-22-03.bpo-39166.Qt-UeD.rst
@@ -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.
diff --git a/Python/ceval.c b/Python/ceval.c
index bd9454b281..3bbd0ca966 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -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;