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

Mark Shannon webhook-mailer at python.org
Fri Jan 10 04:24:30 EST 2020


https://github.com/python/cpython/commit/4c53e63cc966f98e141a09bc435b9f9c713b152d
commit: 4c53e63cc966f98e141a09bc435b9f9c713b152d
branch: master
author: Pablo Galindo <Pablogsal at gmail.com>
committer: Mark Shannon <mark at hotpy.org>
date: 2020-01-10T09:24:22Z
summary:

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

files:
A Misc/NEWS.d/next/Core and Builtins/2020-01-02-22-22-03.bpo-39166.Qt-UeD.rst
M Lib/test/test_sys_settrace.py
M Python/ceval.c

diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py
index a0d1122fad83b..bead57c44b47e 100644
--- a/Lib/test/test_sys_settrace.py
+++ b/Lib/test/test_sys_settrace.py
@@ -526,6 +526,82 @@ def func():
              (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 0000000000000..4737e9c4d2ee7
--- /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 bd9454b2812dd..3bbd0ca9667b0 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -3610,11 +3610,15 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
                 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;



More information about the Python-checkins mailing list