mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	bpo-39166: Fix trace of last iteration of async for loops (#17800)
This commit is contained in:
		
							parent
							
								
									850a8856e1
								
							
						
					
					
						commit
						4c53e63cc9
					
				
					 3 changed files with 87 additions and 5 deletions
				
			
		|  | @ -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""" | ||||
|  |  | |||
|  | @ -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. | ||||
|  | @ -3610,12 +3610,16 @@ _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 */ | ||||
|                 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; | ||||
|             } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Pablo Galindo
						Pablo Galindo