Closed
Description
Tracing an if-statement in an async-for seems to trace statements that aren't executed. It was correct in 3.9, became wrong in 3.10, and remains wrong in 3.11.
import linecache, sys
def trace(frame, event, arg):
if frame.f_code.co_filename == globals().get("__file__"):
lineno = frame.f_lineno
line = linecache.getline(__file__, lineno).rstrip()
print("{} {}: {}".format(event[:4], lineno, line))
return trace
print(sys.version)
sys.settrace(trace)
import asyncio
class CoverMe:
def __init__(self, number):
self._number = number
async def _async_range(self):
for n in range(self._number):
yield n
async def do_something(self):
accumulated = 0
async for n in self._async_range():
accumulated += n
print(f"{accumulated=}")
if accumulated > 10:
break
return accumulated
async def main():
print(await CoverMe(10).do_something())
asyncio.run(main())
Running this code with 3.11.0b1 gives:
3.11.0b1 (main, May 12 2022, 06:47:33) [Clang 12.0.0 (clang-1200.0.32.29)]
call 15: class CoverMe:
line 15: class CoverMe:
line 16: def __init__(self, number):
line 19: async def _async_range(self):
line 23: async def do_something(self):
retu 23: async def do_something(self):
call 33: async def main():
line 34: print(await CoverMe(10).do_something())
call 16: def __init__(self, number):
line 17: self._number = number
retu 17: self._number = number
call 23: async def do_something(self):
line 24: accumulated = 0
line 25: async for n in self._async_range():
call 19: async def _async_range(self):
line 20: for n in range(self._number):
line 21: yield n
retu 21: yield n
exce 25: async for n in self._async_range():
line 26: accumulated += n
line 27: print(f"{accumulated=}")
accumulated=0
line 28: if accumulated > 10:
line 29: break <<<<
line 25: async for n in self._async_range():
call 21: yield n
line 20: for n in range(self._number):
line 21: yield n
retu 21: yield n
exce 25: async for n in self._async_range():
line 26: accumulated += n
line 27: print(f"{accumulated=}")
accumulated=1
line 28: if accumulated > 10:
line 29: break <<<<
line 25: async for n in self._async_range():
call 21: yield n
line 20: for n in range(self._number):
line 21: yield n
retu 21: yield n
exce 25: async for n in self._async_range():
line 26: accumulated += n
line 27: print(f"{accumulated=}")
accumulated=3
line 28: if accumulated > 10:
line 29: break <<<<
line 25: async for n in self._async_range():
call 21: yield n
line 20: for n in range(self._number):
line 21: yield n
retu 21: yield n
exce 25: async for n in self._async_range():
line 26: accumulated += n
line 27: print(f"{accumulated=}")
accumulated=6
line 28: if accumulated > 10:
line 29: break <<<<
line 25: async for n in self._async_range():
call 21: yield n
line 20: for n in range(self._number):
line 21: yield n
retu 21: yield n
exce 25: async for n in self._async_range():
line 26: accumulated += n
line 27: print(f"{accumulated=}")
accumulated=10
line 28: if accumulated > 10:
line 29: break <<<<
line 25: async for n in self._async_range():
call 21: yield n
line 20: for n in range(self._number):
line 21: yield n
retu 21: yield n
exce 25: async for n in self._async_range():
line 26: accumulated += n
line 27: print(f"{accumulated=}")
accumulated=15
line 28: if accumulated > 10:
line 29: break
line 31: return accumulated
retu 31: return accumulated
exce 34: print(await CoverMe(10).do_something())
15
retu 34: print(await CoverMe(10).do_something())
call 21: yield n
exce 21: yield n
retu 21: yield n
The lines marked with <<<<
show a break statement being traced when it is not executed.