nedbat / coveragepy

The code coverage tool for Python
https://coverage.readthedocs.io
Apache License 2.0
2.89k stars 419 forks source link

Missing coverage when using the "redis" library #1671

Open krieghan opened 10 months ago

krieghan commented 10 months ago

Describe the bug When using asyncio with redis.asyncio (redispy), some lines that are definitely hit remain mysteriously uncovered, even when using a combination of different concurrency settings

To Reproduce

  1. Python version 3.11.4, with coverage 7.3.0 and redis 5.0.0 (OS is Ubuntu 22.04)
  2. My .coveragerc file:
[run]
source = .
parallel = True
concurrency = 
    thread
    gevent
    multiprocessing

(I've tried various combinations of concurrency, but I most often leave it blank. I suspect it's not even appropriate for my application, though I'm not certain)

My minimal example (coverage_test.py):

import asyncio

import redis.asyncio as redis

async def main():
    client = redis.Redis()
    result = await client.get('key')
    print('running main')

if __name__ == '__main__':
    asyncio.run(main())
    print('finished main')

I issue the coverage command with a bash script (coverage.sh) as follows:

coverage erase
coverage run coverage_test.py
coverage combine
coverage html
xdg-open htmlcov/index.html

The output of the script is:

running main
finished main

And the coverage of the file looks like this:

image

I would expect the last print statement (which is executed, according to the output) to be marked green. I see there are some similar issues (most notably https://github.com/nedbat/coveragepy/issues/1082 and https://github.com/nedbat/coveragepy/issues/1598), but as I mentioned I haven't found a concurrency setting that mitigates this.

nedbat commented 10 months ago

Is there a way to reproduce this without running a Redis instance? I get a ConnectionError.

krieghan commented 10 months ago

Oh wow, that's a solid point. I don't know of a way around it at the moment, but I can look into it. Thanks for the response!

krieghan commented 10 months ago

I've tried for a bit to reproduce this issue with asyncio streams (which, drilling down, seems to be where the issue comes up). I haven't been able to do it with just the naive echo client/server that python's documentation lays out. There's something here happening that seems specific to redis-py, and I don't know how to reproduce the issue without a redis instance running :/. I've left an inquiry on their project (basically the same information as here, but with a longer example included, as well) - I'm hoping they have some insights: https://github.com/redis/redis-py/issues/2912

krieghan commented 10 months ago

I saw that python released a new version for python 3.11.5. I've tried everything again with this new version. I still see the same behavior in coverage, but I am now seeing an additional traceback from redis as the script closes. I thought it might be related to this line of missing coverage. I'm including it here:

Exception ignored in: <function StreamWriter.del at 0x7fcd228adb20> Traceback (most recent call last): File "/usr/local/lib/python3.11/asyncio/streams.py", line 396, in del self.close() File "/usr/local/lib/python3.11/asyncio/streams.py", line 344, in close return self._transport.close() ^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/asyncio/selector_events.py", line 860, in close self._loop.call_soon(self._call_connection_lost, None) File "/usr/local/lib/python3.11/asyncio/base_events.py", line 761, in call_soon self._check_closed() File "/usr/local/lib/python3.11/asyncio/base_events.py", line 519, in _check_closed raise RuntimeError('Event loop is closed') RuntimeError: Event loop is closed

aw-was-here commented 7 months ago

Is there a way to reproduce this without running a Redis instance? I get a ConnectionError.

I'm seeing it using fakeredis, which is what I use to test redis code.

nedbat commented 7 months ago

I'm seeing it using fakeredis, which is what I use to test redis code.

Any chance you can give us a way to reproduce it?

aw-was-here commented 7 months ago

I'm seeing it using fakeredis, which is what I use to test redis code.

Any chance you can give us a way to reproduce it?

This repo was setup in a hurry so things in pyproject.toml aren't correct, but it should be good enough to demonstrate:

https://github.com/aw-was-here/redis-cov-test

Screen Shot 2023-12-07 at 11 05 02 PM