Author nedbat
Recipients Mark.Shannon, nedbat
Date 2021-07-13.13:03:51
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1626181431.87.0.924795967065.issue44622@roundup.psfhosted.org>
In-reply-to
Content
In Python 3.10, the traces at the end of an async-for loop are incorrect and different than at the end of a for-loop.

------------------------------
import linecache, sys

def trace(frame, event, arg):
    # The weird globals here is to avoid a NameError on shutdown...
    if frame.f_code.co_filename == globals().get("__file__"):
        lineno = frame.f_lineno
        print("{} {}: {}".format(event[:4], lineno, linecache.getline(__file__, lineno).rstrip()))
    return trace

import asyncio

class AsyncIter:
    def __init__(self, items): self.items = items

    async def __aiter__(self):
        for i in self.items: yield i

async def test1():
    async for i in AsyncIter([1]):
        print(f"test1 {i}")

def test2():
    for i in [1]:
        print(f"test2 {i}")

print(sys.version)
sys.settrace(trace)

asyncio.run(test1())
test2()
------------------------------------

In 3.7, 3.8 and 3.9, the two for loops behave the same: the loop jumps back to the "for" statement, and then returns (the arrowed lines):

  3.9.5 (default, May  5 2021, 06:50:43)
  [Clang 12.0.0 (clang-1200.0.32.29)]
  call 18: async def test1():
  line 19:     async for i in AsyncIter([1]):
  call 13:     def __init__(self, items): self.items = items
  line 13:     def __init__(self, items): self.items = items
  retu 13:     def __init__(self, items): self.items = items
  call 15:     async def __aiter__(self):
  line 16:         for i in self.items: yield i
  retu 16:         for i in self.items: yield i
  exce 19:     async for i in AsyncIter([1]):
  line 20:         print(f"test1 {i}")
  test1 1
  line 19:     async for i in AsyncIter([1]):
  call 16:         for i in self.items: yield i
  line 16:         for i in self.items: yield i
  retu 16:         for i in self.items: yield i
  exce 19:     async for i in AsyncIter([1]):
> retu 19:     async for i in AsyncIter([1]):
  call 22: def test2():
  line 23:     for i in [1]:
  line 24:         print(f"test2 {i}")
  test2 1
  line 23:     for i in [1]:
> retu 23:     for i in [1]:


In 3.10, the for loop behaves the same, but now the async-for traces the body once more when it doesn't execute, and returns from the body of the loop (the starred line):

  3.10.0b4 (default, Jul 11 2021, 13:51:53) [Clang 12.0.0 (clang-1200.0.32.29)]
  call 18: async def test1():
  line 19:     async for i in AsyncIter([1]):
  call 13:     def __init__(self, items): self.items = items
  line 13:     def __init__(self, items): self.items = items
  retu 13:     def __init__(self, items): self.items = items
  call 15:     async def __aiter__(self):
  line 16:         for i in self.items: yield i
  retu 16:         for i in self.items: yield i
  exce 19:     async for i in AsyncIter([1]):
  line 20:         print(f"test1 {i}")
  test1 1
  line 19:     async for i in AsyncIter([1]):
  call 16:         for i in self.items: yield i
  line 16:         for i in self.items: yield i
  retu 16:         for i in self.items: yield i
  exce 19:     async for i in AsyncIter([1]):
* line 20:         print(f"test1 {i}")
  retu 20:         print(f"test1 {i}")
  call 22: def test2():
  line 23:     for i in [1]:
  line 24:         print(f"test2 {i}")
  test2 1
  line 23:     for i in [1]:
> retu 23:     for i in [1]:
History
Date User Action Args
2021-07-13 13:03:51nedbatsetrecipients: + nedbat, Mark.Shannon
2021-07-13 13:03:51nedbatsetmessageid: <1626181431.87.0.924795967065.issue44622@roundup.psfhosted.org>
2021-07-13 13:03:51nedbatlinkissue44622 messages
2021-07-13 13:03:51nedbatcreate