coveragepy icon indicating copy to clipboard operation
coveragepy copied to clipboard

Branch coverage missing in `async for` loop w/ `break` (CPython 3.10.2)

Open vytas7 opened this issue 3 years ago • 2 comments

Describe the bug When an async for loop contains a conditional break (or return) from the loop, it manifests as missing branch coverage under CPython 3.10.2. The branch is correctly covered under CPython 3.8.x.

To Reproduce How can we reproduce the problem? Please be specific. Don't link to a failing CI job. Answer the questions below:

  1. What version of Python are you using? CPython 3.10.2

  2. What version of coverage.py shows the problem? The output of coverage debug sys is helpful. coverage_version: 6.3.1

  3. What versions of what packages do you have installed? The output of pip freeze is helpful. Only coverage.

  4. What code shows the problem? Give us a specific commit of a specific repo that we can check out. If you've already worked around the problem, please provide a commit before that fix. Suppose there are two files in a directory, test.py and .coveragerc, respectively:

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
            if accumulated > 10:
                break

        return accumulated


async def main():
    print(await CoverMe(1).do_something())
    print(await CoverMe(3).do_something())
    print(await CoverMe(10).do_something())


if __name__ == '__main__':
    asyncio.run(main())
[run]
branch = True

[report]
show_missing = True

What commands did you run? Inside the same directory with the two described files, run the following commands:

$ coverage erase
$ coverage run test.py
0
3
15
$ coverage report
Name      Stmts   Miss Branch BrPart  Cover   Missing
-----------------------------------------------------
test.py      20      0     10      2    93%   16->14, 28->exit
-----------------------------------------------------
TOTAL        20      0     10      2    93%

Expected behavior Repeating the same steps on CPython 3.8.x yields:

Name      Stmts   Miss Branch BrPart  Cover   Missing
-----------------------------------------------------
test.py      20      0     10      1    97%   28->exit
-----------------------------------------------------
TOTAL        20      0     10      1    97%

... which illustrates that I didn't bother to try importing this module as opposed to running as the main script. But the branch is now covered as expected.

vytas7 avatar Feb 18 '22 22:02 vytas7

I am also seeing this with async with statements, when they contain a branch within the block. On Python 3.10.2 and coverage 6.3.2

aaliddell avatar Mar 07 '22 10:03 aaliddell

I think this is a CPython bug. I've written a report here: https://github.com/python/cpython/issues/93061

nedbat avatar May 21 '22 23:05 nedbat

I think this is a CPython bug. I've written a report here: python/cpython#93061

The fix is on CPython 3.11 and 3.10. There's no backport for 3.8.

Kludex avatar Oct 31 '22 11:10 Kludex

AFAICT this works correctly on CPython 3.8. There is no backport to 3.9.

vytas7 avatar Oct 31 '22 11:10 vytas7

Sorry, I've misread the description. Yep, what you said. :pray:

Kludex avatar Oct 31 '22 11:10 Kludex