Python unittest branch coverage seems to miss executed generator in zip
Asked Answered
P

1

6

I don't quite understand what Python's branch coverage stats are trying to tell me. Given code of the form

def f(a, b):
    c = (i for i in a)
    d = (j for j in b)  # Line of interest
    return dict(zip(c, d))

print(f(['a', 'b'], [1, 2]))

which is imported during unit testing, Python's standard branch coverage tells me that the # Line of interest line is only partially covered (n->-n on the CLI output, "n ↛ exit [?]" in the pretty html report).

The returned dict is clearly printed out, and executing with empty lists still yields the uncovered line.

Am I misinterpreting the coverage output? Does this smell like a bug?

Python 3.5.1, Coverage 4.0.3

Polish answered 10/2, 2016 at 14:24 Comment(2)
I agree with you. Looks like coverage doesn't properly handle the generator.Teller
I also agree that it is a bug: bitbucket.org/ned/coveragepy/issues/475/…Doing
P
5

I've investigated this further, and I don't think it is a bug in coverage. When the first generator (c) terminates, zip() efficiently doesn't bother collecting any more values from the second generator (d), so branch coverage doesn't track d as run to completion, even though every element is actually extracted.

If you instead write:

def f(a, b):
    c = (i for i in a)
    d = tuple(j for j in b)  # Line of interest
    return dict(zip(c, d))

print(f(['a', 'b'], [1, 2]))

as one would expect the second generator is run to completion and coverage is happy, even though the output is identical.

I don't think there's a simple way around this, even if you write the generator expression out as a generator function containing the same for loop, you get a (slightly clearer) error that execution never jumped to function exit.

I think this is just a limitation of coverage and the exit conditions of generators, as it can't know whether a generator is supposed to exit, so it flags the uncovered case.

Polish answered 10/2, 2016 at 20:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.