diff --git a/.coveragerc-cython.toml b/.coveragerc-cython.toml index 91336d47dd9..96262659fb6 100644 --- a/.coveragerc-cython.toml +++ b/.coveragerc-cython.toml @@ -1,20 +1,81 @@ -[run] -branch = true -plugins = [ - 'Cython.Coverage', -] -omit = [ - 'site-packages', +[html] +show_contexts = true +skip_covered = false + +[paths] +_site-packages-to-src-mapping = [ + '.', + '*/lib/pypy*/site-packages', + '*/lib/python*/site-packages', + '*\Lib\site-packages', ] [report] -partial_also = [ - 'if not TYPE_CHECKING', -] exclude_also = [ 'if TYPE_CHECKING', 'assert False', ': \.\.\.(\s*#.*)?$', '^ +\.\.\.$', - 'pytest.fail\(' + 'pytest.fail\(', + # '^\s*@pytest\.mark\.xfail', # important for Dreamsorcerer +] +# fail_under = 100 +partial_also = [ + 'if not TYPE_CHECKING', +] +show_missing = true +skip_covered = true +skip_empty = true + +[run] +branch = true +# NOTE: `ctrace` tracing method is needed because the `sysmon` tracer +# NOTE: which is default on Python 3.14, causes unprecedented slow-down +# NOTE: of the test runs. Also, Cython the `Cython.Coverage` plugin does +# NOTE: not support `sysmon`. +# Ref: https://github.com/coveragepy/coveragepy/issues/2099 +core = 'ctrace' +cover_pylib = false +# NOTE: `disable_warnings` is needed when `pytest-cov` runs in tandem +# NOTE: with `pytest-xdist`. These warnings are false negative in this +# NOTE: context. +# +# NOTE: It's `coveragepy` that emits the warnings and previously they +# NOTE: wouldn't get on the radar of `pytest`'s `filterwarnings` +# NOTE: mechanism. This changed, however, with `pytest >= 8.4`. And +# NOTE: since we set `filterwarnings = error`, those warnings are being +# NOTE: raised as exceptions, cascading into `pytest`'s internals and +# NOTE: causing tracebacks and crashes of the test sessions. +# +# Ref: +# * https://github.com/pytest-dev/pytest-cov/issues/693 +# * https://github.com/pytest-dev/pytest-cov/pull/695 +# * https://github.com/pytest-dev/pytest-cov/pull/696 +disable_warnings = [ + 'module-not-measured', +] +# https://coverage.rtfd.io/en/latest/contexts.html#dynamic-contexts +# dynamic_context = 'test_function' # conflicts with `pytest-cov` if set here +omit = [ + 'setup.py', +] +# NOTE: tests/autobahn/test_autobahn.py::test_{client,server} +# NOTE: pass `-a|--append` to `coverage run` wich conflicts +# NOTE: with `-p|--parallel-mode`. We cannot override it on +# NOTE: the CLI level, but instead define an environment +# NOTE: variable to implement this. +# +# Refs: +# * https://discord.com/channels/267624335836053506/1253355750684753950/1537906072474488973 +# * https://discord.com/channels/267624335836053506/1253355750684753950/1537914331629486130 +parallel = '${COVERAGE_PARALLEL_MODE-true}' +plugins = [ + 'Cython.Coverage', +] +relative_files = true +source = [ + '.', +] +source_pkgs = [ + 'aiohttp', ] diff --git a/.coveragerc.toml b/.coveragerc.toml index 409061e3514..98eb1b26e52 100644 --- a/.coveragerc.toml +++ b/.coveragerc.toml @@ -1,26 +1,81 @@ +[html] +show_contexts = true +skip_covered = false + +[paths] +_site-packages-to-src-mapping = [ + '.', + '*/lib/pypy*/site-packages', + '*/lib/python*/site-packages', + '*\Lib\site-packages', +] + +[report] +exclude_also = [ + 'if TYPE_CHECKING', + 'assert False', + ': \.\.\.(\s*#.*)?$', + '^ +\.\.\.$', + 'pytest.fail\(', + # '^\s*@pytest\.mark\.xfail', # important for Dreamsorcerer +] +# fail_under = 100 +partial_also = [ + 'if not TYPE_CHECKING', +] +show_missing = true +skip_covered = true +skip_empty = true + [run] branch = true # NOTE: `ctrace` tracing method is needed because the `sysmon` tracer # NOTE: which is default on Python 3.14, causes unprecedented slow-down -# NOTE: of the test runs. +# NOTE: of the test runs. Also, Cython the `Cython.Coverage` plugin does +# NOTE: not support `sysmon`. # Ref: https://github.com/coveragepy/coveragepy/issues/2099 core = 'ctrace' -source = [ - 'aiohttp', - 'tests', +cover_pylib = false +# NOTE: `disable_warnings` is needed when `pytest-cov` runs in tandem +# NOTE: with `pytest-xdist`. These warnings are false negative in this +# NOTE: context. +# +# NOTE: It's `coveragepy` that emits the warnings and previously they +# NOTE: wouldn't get on the radar of `pytest`'s `filterwarnings` +# NOTE: mechanism. This changed, however, with `pytest >= 8.4`. And +# NOTE: since we set `filterwarnings = error`, those warnings are being +# NOTE: raised as exceptions, cascading into `pytest`'s internals and +# NOTE: causing tracebacks and crashes of the test sessions. +# +# Ref: +# * https://github.com/pytest-dev/pytest-cov/issues/693 +# * https://github.com/pytest-dev/pytest-cov/pull/695 +# * https://github.com/pytest-dev/pytest-cov/pull/696 +disable_warnings = [ + 'module-not-measured', ] +# https://coverage.rtfd.io/en/latest/contexts.html#dynamic-contexts +# dynamic_context = 'test_function' # conflicts with `pytest-cov` if set here omit = [ - 'site-packages', + 'setup.py', ] - -[report] -partial_also = [ - 'if not TYPE_CHECKING', +# NOTE: tests/autobahn/test_autobahn.py::test_{client,server} +# NOTE: pass `-a|--append` to `coverage run` wich conflicts +# NOTE: with `-p|--parallel-mode`. We cannot override it on +# NOTE: the CLI level, but instead define an environment +# NOTE: variable to implement this. +# +# Refs: +# * https://discord.com/channels/267624335836053506/1253355750684753950/1537906072474488973 +# * https://discord.com/channels/267624335836053506/1253355750684753950/1537914331629486130 +parallel = '${COVERAGE_PARALLEL_MODE-true}' +# plugins = [ +# 'covdefaults', +# ] +relative_files = true +source = [ + '.', ] -exclude_also = [ - 'if TYPE_CHECKING', - 'assert False', - ': \.\.\.(\s*#.*)?$', - '^ +\.\.\.$', - 'pytest.fail\(' +source_pkgs = [ + 'aiohttp', ] diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 6b7ec04f321..e38f292a472 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -317,7 +317,7 @@ jobs: AIOHTTP_NO_EXTENSIONS: ${{ matrix.no-extensions }} PIP_USER: 1 run: >- - pytest --junitxml=junit.xml --numprocesses=auto --cov=aiohttp/ --cov=tests/ -m 'not dev_mode and not autobahn' + pytest --junitxml=junit.xml --numprocesses=auto --cov -m 'not dev_mode and not autobahn' shell: bash - name: Re-run the failing tests with maximum verbosity if: failure() @@ -333,14 +333,8 @@ jobs: AIOHTTP_NO_EXTENSIONS: ${{ matrix.no-extensions }} PIP_USER: 1 PYTHONDEVMODE: 1 - run: pytest -m dev_mode --cov=aiohttp/ --cov=tests/ --cov-append + run: pytest -m dev_mode --cov --cov-append --cov-report=xml shell: bash - - name: Turn coverage into xml - env: - COLOR: 'yes' - PIP_USER: 1 - run: | - python -m coverage xml - name: Upload coverage uses: codecov/codecov-action@v7 with: @@ -474,14 +468,9 @@ jobs: PIP_USER: 1 run: >- PATH="${HOME}/Library/Python/3.11/bin:${HOME}/.local/bin:${PATH}" - pytest --junitxml=junit.xml --cov=aiohttp/ --cov=tests/ --timeout=0 -m autobahn + pytest --junitxml=junit.xml --cov --cov-report=xml + --timeout=0 -m autobahn shell: bash - - name: Turn coverage into xml - env: - COLOR: 'yes' - PIP_USER: 1 - run: | - python -m coverage xml - name: Upload coverage uses: codecov/codecov-action@v7 with: @@ -596,12 +585,11 @@ jobs: PIP_USER: 1 run: >- pytest tests/test_client_functional.py tests/test_http_parser.py tests/test_http_writer.py tests/test_web_functional.py tests/test_web_response.py tests/test_websocket_parser.py - --cov-config=.coveragerc-cython.toml --cov=aiohttp/ --cov=tests/ --numprocesses=auto + --cov-config=.coveragerc-cython.toml --cov + --cov-report=xml:cython-coverage.xml + --numprocesses=auto -m 'not dev_mode and not autobahn' shell: bash - - name: Turn coverage into xml - run: | - python -m coverage xml -o cython-coverage.xml --rcfile=.coveragerc-cython.toml - name: Upload coverage uses: codecov/codecov-action@v7 with: diff --git a/CHANGES/13422.contrib.rst b/CHANGES/13422.contrib.rst new file mode 100644 index 00000000000..71f97d67e4a --- /dev/null +++ b/CHANGES/13422.contrib.rst @@ -0,0 +1,17 @@ +Synchronized the ``coverage.py`` configuration (:file:`.coveragerc.toml` and +:file:`.coveragerc-cython.toml`) with the pattern already established in +:external+yarl:doc:`yarl `, :external+multidict:doc:`multidict +`, ``frozenlist`` and other sibling projects +-- by :user:`webknjaz`. + +Both files now anchor package discovery through ``source_pkgs`` instead of +relying on a same-named directory happening to exist relative to the +working directory, and add a ``[paths]`` mapping so coverage recorded +against an installed copy of ``aiohttp`` still combines correctly with +coverage recorded from the Git checkout. CI now lets ``pytest-cov`` write +``coverage.xml`` directly via ``--cov-report=xml`` instead of a separate +``coverage xml`` step, and the Autobahn testsuite's subprocess-based +coverage collection (which uses ``coverage run --append``, incompatible +with parallel mode) now opts out per-invocation via a +``COVERAGE_PARALLEL_MODE`` environment variable instead of trying to +override it on the command line. diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt index ed746b4aedc..beb650819a4 100644 --- a/docs/spelling_wordlist.txt +++ b/docs/spelling_wordlist.txt @@ -343,6 +343,7 @@ subclasses subdirectory submodules subpackage +subprocess subprotocol subprotocols subtype @@ -359,6 +360,7 @@ TCP teardown Teardown TestClient +testsuite Testsuite Tf timestamps diff --git a/setup.cfg b/setup.cfg index cecc0c501fe..2a24abcb0cc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -34,11 +34,6 @@ combine_as_imports=True known_third_party=jinja2,pytest,multidict,yarl,gunicorn,freezegun known_first_party=aiohttp,aiohttp_jinja2,aiopg -[report] -exclude_lines = - @abc.abstractmethod - @abstractmethod - [tool:pytest] addopts = # show 10 slowest invocations: diff --git a/tests/autobahn/test_autobahn.py b/tests/autobahn/test_autobahn.py index 1812a3a3bec..deb35341b48 100644 --- a/tests/autobahn/test_autobahn.py +++ b/tests/autobahn/test_autobahn.py @@ -1,7 +1,9 @@ import json +import os import pprint import socket import subprocess +import sys import time from collections.abc import Iterator from pathlib import Path @@ -103,7 +105,20 @@ def test_client(report_dir: Path, request: pytest.FixtureRequest) -> None: ) try: wait_for_port(9001) - subprocess.run(("coverage", "run", "-a", "tests/autobahn/client/client.py")) + subprocess.run( + ( + sys.executable, + "-m", + "coverage", + "run", + "--append", + "tests/autobahn/client/client.py", + ), + env={ + "COVERAGE_PARALLEL_MODE": "false", + **os.environ.copy(), + }, + ) finally: autobahn_container.stop() @@ -141,7 +156,18 @@ def test_client(report_dir: Path, request: pytest.FixtureRequest) -> None: @pytest.mark.autobahn def test_server(report_dir: Path, request: pytest.FixtureRequest) -> None: server = subprocess.Popen( - ("coverage", "run", "-a", "tests/autobahn/server/server.py") + ( + sys.executable, + "-m", + "coverage", + "run", + "--append", + "tests/autobahn/server/server.py", + ), + env={ + "COVERAGE_PARALLEL_MODE": "false", + **os.environ.copy(), + }, ) try: wait_for_port(9001)