coveragepy
coveragepy copied to clipboard
Problem with statements under async with in Python 3.11
Describe the bug
I have a problem with coverage of statements under async with
while using fakeredis
library. This problem manifests only under Python 3.11
. Python 3.10
correctly reports 100% coverage. See minimal example below.
To Reproduce
- What version of Python are you using?
Python 3.11.2
- What version of coverage.py shows the problem? The output of
coverage debug sys
is helpful.
# coverage debug sys
-- sys -------------------------------------------------------
coverage_version: 7.2.2
coverage_module: /home/jmusilek/test/venv/lib/python3.11/site-packages/coverage/__init__.py
tracer: -none-
CTracer: available
plugins.file_tracers: -none-
plugins.configurers: -none-
plugins.context_switchers: -none-
configs_attempted: .coveragerc
setup.cfg
tox.ini
pyproject.toml
configs_read: -none-
config_file: None
config_contents: -none-
data_file: -none-
python: 3.11.2 (main, Feb 12 2023, 00:48:52) [GCC 12.2.0]
platform: Linux-6.1.0-5-amd64-x86_64-with-glibc2.36
implementation: CPython
executable: /home/jmusilek/test/venv/bin/python3.11
def_encoding: utf-8
fs_encoding: utf-8
pid: 673263
cwd: /home/jmusilek/test
path: /home/jmusilek/test/venv/bin
/usr/lib/python311.zip
/usr/lib/python3.11
/usr/lib/python3.11/lib-dynload
/home/jmusilek/test/venv/lib/python3.11/site-packages
environment: HOME = /home/jmusilek
PYENV_ROOT = /home/jmusilek/.pyenv
PYENV_SHELL = fish
command_line: /home/jmusilek/test/venv/bin/coverage debug sys
sqlite3_sqlite_version: 3.40.1
sqlite3_temp_store: 0
sqlite3_compile_options: ATOMIC_INTRINSICS=1, COMPILER=gcc-12.2.0, DEFAULT_AUTOVACUUM,
DEFAULT_CACHE_SIZE=-2000, DEFAULT_FILE_FORMAT=4,
DEFAULT_JOURNAL_SIZE_LIMIT=-1, DEFAULT_MMAP_SIZE=0, DEFAULT_PAGE_SIZE=4096,
DEFAULT_PCACHE_INITSZ=20, DEFAULT_RECURSIVE_TRIGGERS,
DEFAULT_SECTOR_SIZE=4096, DEFAULT_SYNCHRONOUS=2,
DEFAULT_WAL_AUTOCHECKPOINT=1000, DEFAULT_WAL_SYNCHRONOUS=2,
DEFAULT_WORKER_THREADS=0, ENABLE_COLUMN_METADATA, ENABLE_DBSTAT_VTAB,
ENABLE_FTS3, ENABLE_FTS3_PARENTHESIS, ENABLE_FTS3_TOKENIZER, ENABLE_FTS4,
ENABLE_FTS5, ENABLE_LOAD_EXTENSION, ENABLE_MATH_FUNCTIONS,
ENABLE_PREUPDATE_HOOK, ENABLE_RTREE, ENABLE_SESSION, ENABLE_STMTVTAB,
ENABLE_UNLOCK_NOTIFY, ENABLE_UPDATE_DELETE_LIMIT, HAVE_ISNAN,
LIKE_DOESNT_MATCH_BLOBS, MALLOC_SOFT_LIMIT=1024, MAX_ATTACHED=10,
MAX_COLUMN=2000, MAX_COMPOUND_SELECT=500, MAX_DEFAULT_PAGE_SIZE=32768,
MAX_EXPR_DEPTH=1000, MAX_FUNCTION_ARG=127, MAX_LENGTH=1000000000,
MAX_LIKE_PATTERN_LENGTH=50000, MAX_MMAP_SIZE=0x7fff0000,
MAX_PAGE_COUNT=1073741823, MAX_PAGE_SIZE=65536, MAX_SCHEMA_RETRY=25,
MAX_SQL_LENGTH=1000000000, MAX_TRIGGER_DEPTH=1000,
MAX_VARIABLE_NUMBER=250000, MAX_VDBE_OP=250000000, MAX_WORKER_THREADS=8,
MUTEX_PTHREADS, OMIT_LOOKASIDE, SECURE_DELETE, SOUNDEX, SYSTEM_MALLOC,
TEMP_STORE=1, THREADSAFE=1, USE_URI
- What versions of what packages do you have installed? The output of
pip freeze
is helpful.
# pip freeze
coverage==7.2.2
fakeredis==2.10.2
redis==4.5.2
sortedcontainers==2.4.0
- 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.
Minimal example:
import asyncio
from contextlib import asynccontextmanager
from unittest import IsolatedAsyncioTestCase
from fakeredis.aioredis import FakeRedis
cache = FakeRedis(decode_responses=True)
async def manager():
yield cache
class ManagerTest(IsolatedAsyncioTestCase):
async def test_manager(self):
async with asynccontextmanager(manager)() as cm:
await cm.set("a", 1)
self.assertEqual(await cm.get("a"), "1")
await cm.set("a", 2)
- What commands did you run?
coverage run --branch -m unittest discover; coverage report --show-missing
Expected behavior
I expect 100% coverage, which is the case under Python 3.10.10
. However, under Python 3.11.2
, I encounter this report:
Name Stmts Miss Branch BrPart Cover Missing
-------------------------------------------------------------
test_example.py 13 2 2 1 80% 16->exit, 18-19
-------------------------------------------------------------
Additional context
It seems that fakeredis
(or rather something behind it) is somehow connected to this. When I replace cache.set
and cache.get
with asyncio.sleep
, coverage shows 100% as expected.
@stinovlas Was able to reproduce this bug with the versions you pinned above. I was able to narrow it down to redis==4.5.2
. Tried with redis==4.5.4
and I get the expected result:
OK
Name Stmts Miss Branch BrPart Cover Missing
---------------------------------------------------------
test_bug.py 12 0 2 0 100%
---------------------------------------------------------
TOTAL 12 0 2 0 100%
I think I've run across the same bug in this chunk of code: https://github.com/glyph/Fritter/blob/6a5337db73c0b4ff2d055d415fcf2d3a38a4a5f3/src/fritter/test/test_repeat.py#L123-L142
I am able to work around it if I replace the with self.assertRaises
with a different Twisted idiom for suppressing the cancellation error.
I assume similar to fakeredis
, this code is performing deterministic no-I/O execution of a coroutine.
I have the same issue. It looks like async with
lines get wrongly reported as uncovered