coveragepy icon indicating copy to clipboard operation
coveragepy copied to clipboard

async coroutine with "if await" doesn't detect branch coverage correctly

Open spaceone opened this issue 5 months ago • 2 comments

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: Image

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

spaceone avatar Aug 01 '25 19:08 spaceone

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

spaceone avatar Aug 02 '25 11:08 spaceone

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 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

nedbat avatar Aug 02 '25 19:08 nedbat