async coroutine with "if await" doesn't detect branch coverage correctly
It seems in some cases coverage doesn't mark lines which are proven to be run in an async function. I couldn't find a bug report for it yet. Deactivating branch coverage doesn't help either.
for … in …:
if not await something():
return False
return True
When trying to build a reproducer, it works whatever I try.
Here is the original code:
After assigning the result of await something() to a variable, the return True becomes covered, as well as all yellow branches. But the return False case is still red.
coverage_version: 7.9.2
python: 3.11.2 (main, Nov 30 2024, 21:22:50) [GCC 12.2.0]
platform: Linux-6.5.0-0.deb12.1-amd64-x86_64-with-glibc2.36
implementation: CPython
build: main
Nov 30 2024 21:22:50
Reproducer
Caution: This will download 159 MB openldap container! Affected code: https://github.com/Free-IAM/freeiam/blob/80a52e65798ee79e9675d7ecd477ffd63fe21a21/src/freeiam/ldap/connection.py#L716-L731
git clone https://github.com/Free-IAM/freeiam
cd freeiam
git checkout 80a52e65798ee79e9675d7ecd477ffd63fe21a21
sed -i 's/equal =\(.*\)/if not\1:/; /if not equal/d;' src/freeiam/ldap/connection.py
tox -e py311 --develop
. .tox/py311/bin/activate
pytest tests/test_ldap_connection.py -k test_compare_dn
Hmm, on my Mac:
% tox -e py311 --develop
py311: venv> /Users/ned/.local/share/uv/tools/tox/bin/uv venv -p cpython3.11 --allow-existing --python-preference system /System/Volumes/Data/src/bugs/bug2014/freeiam/.tox/py311
py311: install_deps> /Users/ned/.local/share/uv/tools/tox/bin/uv pip install '.[test]'
.pkg-cpython311: venv> /Users/ned/.local/share/uv/tools/tox/bin/uv venv -p cpython3.11 --allow-existing --python-preference system /System/Volumes/Data/src/bugs/bug2014/freeiam/.tox/.pkg-cpython311
.pkg-cpython311: install_requires> /Users/ned/.local/share/uv/tools/tox/bin/uv pip install setuptools setuptools_scm
.pkg-cpython311: _optional_hooks> python /Users/ned/.local/share/uv/tools/tox/lib/python3.13/site-packages/pyproject_api/_backend.py True setuptools.build_meta
.pkg-cpython311: get_requires_for_build_editable> python /Users/ned/.local/share/uv/tools/tox/lib/python3.13/site-packages/pyproject_api/_backend.py True setuptools.build_meta
.pkg-cpython311: build_editable> python /Users/ned/.local/share/uv/tools/tox/lib/python3.13/site-packages/pyproject_api/_backend.py True setuptools.build_meta
py311: install_package_deps> /Users/ned/.local/share/uv/tools/tox/bin/uv pip install python-ldap
py311: install_package> /Users/ned/.local/share/uv/tools/tox/bin/uv pip install --reinstall --no-deps freeiam@/System/Volumes/Data/src/bugs/bug2014/freeiam/.tox/.tmp/package/1/freeiam-0.1.dev10+g80a52e6.d20250802-0.editable-py3-none-any.whl
py311: commands[0]> pytest --tb=native -s -l -vvv --doctest-modules --show-capture=log --cov=freeiam tests
====================================================================== test session starts =======================================================================
platform darwin -- Python 3.11.13, pytest-8.4.1, pluggy-1.6.0 -- /System/Volumes/Data/src/bugs/bug2014/freeiam/.tox/py311/bin/python3
cachedir: .tox/py311/.pytest_cache
benchmark: 5.1.0 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
rootdir: /System/Volumes/Data/src/bugs/bug2014/freeiam
configfile: pytest.ini
plugins: asyncio-1.1.0, timeout-2.4.0, cov-6.2.1, benchmark-5.1.0
asyncio: mode=Mode.STRICT, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function
timeout: 30.0s
timeout method: signal
timeout func_only: False
collected 0 items / 8 errors
============================================================================= ERRORS =============================================================================
_________________________________________________________ ERROR collecting tests/test_ldap_connection.py _________________________________________________________
Traceback (most recent call last):
File "/System/Volumes/Data/src/bugs/bug2014/freeiam/.tox/py311/lib/python3.11/site-packages/_pytest/runner.py", line 344, in from_call
result: TResult | None = func()
^^^^^^
File "/System/Volumes/Data/src/bugs/bug2014/freeiam/.tox/py311/lib/python3.11/site-packages/_pytest/runner.py", line 389, in collect
return list(collector.collect())
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/System/Volumes/Data/src/bugs/bug2014/freeiam/.tox/py311/lib/python3.11/site-packages/_pytest/doctest.py", line 566, in collect
module = self.obj
^^^^^^^^
File "/System/Volumes/Data/src/bugs/bug2014/freeiam/.tox/py311/lib/python3.11/site-packages/_pytest/python.py", line 280, in obj
self._obj = obj = self._getobj()
^^^^^^^^^^^^^^
File "/System/Volumes/Data/src/bugs/bug2014/freeiam/.tox/py311/lib/python3.11/site-packages/_pytest/python.py", line 551, in _getobj
return importtestmodule(self.path, self.config)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/System/Volumes/Data/src/bugs/bug2014/freeiam/.tox/py311/lib/python3.11/site-packages/_pytest/python.py", line 498, in importtestmodule
mod = import_path(
^^^^^^^^^^^^
File "/System/Volumes/Data/src/bugs/bug2014/freeiam/.tox/py311/lib/python3.11/site-packages/_pytest/pathlib.py", line 587, in import_path
importlib.import_module(module_name)
File "/usr/local/pyenv/pyenv/versions/3.11.13/lib/python3.11/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<frozen importlib._bootstrap>", line 1204, in _gcd_import
File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
File "<frozen importlib._bootstrap>", line 1147, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
File "/System/Volumes/Data/src/bugs/bug2014/freeiam/.tox/py311/lib/python3.11/site-packages/_pytest/assertion/rewrite.py", line 186, in exec_module
exec(co, module.__dict__)
File "/System/Volumes/Data/src/bugs/bug2014/freeiam/tests/test_ldap_connection.py", line 11, in <module>
from freeiam import errors, ldap
File "/System/Volumes/Data/src/bugs/bug2014/freeiam/src/freeiam/ldap/__init__.py", line 8, in <module>
from freeiam.ldap.connection import Connection
File "/System/Volumes/Data/src/bugs/bug2014/freeiam/src/freeiam/ldap/connection.py", line 19, in <module>
from freeiam.ldap._wrapper import Result, _Response
File "/System/Volumes/Data/src/bugs/bug2014/freeiam/src/freeiam/ldap/_wrapper.py", line 9, in <module>
from freeiam.ldap.constants import ResponseType
File "/System/Volumes/Data/src/bugs/bug2014/freeiam/src/freeiam/ldap/constants.py", line 66, in <module>
class Option(IntEnum):
File "/System/Volumes/Data/src/bugs/bug2014/freeiam/src/freeiam/ldap/constants.py", line 97, in Option
TCPUserTimeout = ldap.OPT_TCP_USER_TIMEOUT
^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: module 'ldap' has no attribute 'OPT_TCP_USER_TIMEOUT'
plus many more:
==================================================================== short test summary info =====================================================================
ERROR tests/test_ldap_connection.py - AttributeError: module 'ldap' has no attribute 'OPT_TCP_USER_TIMEOUT'
ERROR tests/test_ldap_connection.py - AttributeError: module 'ldap' has no attribute 'OPT_TCP_USER_TIMEOUT'
ERROR tests/test_ldap_connection_sync.py - AttributeError: module 'ldap' has no attribute 'OPT_TCP_USER_TIMEOUT'
ERROR tests/test_ldap_connection_sync.py - AttributeError: module 'ldap' has no attribute 'OPT_TCP_USER_TIMEOUT'
ERROR tests/test_ldap_dn.py - AttributeError: module 'ldap' has no attribute 'OPT_TCP_USER_TIMEOUT'
ERROR tests/test_ldap_dn.py - AttributeError: module 'ldap' has no attribute 'OPT_TCP_USER_TIMEOUT'
ERROR tests/test_ldap_various.py - AttributeError: module 'ldap' has no attribute 'OPT_TCP_USER_TIMEOUT'
ERROR tests/test_ldap_various.py - AttributeError: module 'ldap' has no attribute 'OPT_TCP_USER_TIMEOUT'
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 8 errors during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!