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
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 trackd
as run to completion, even though every element is actually extracted.If you instead write:
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.