From a7e7c69a93faf94f2e0848aa50b1ebb8bf02b917 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 29 Aug 2025 11:43:03 -0400 Subject: [PATCH 01/29] build: bump version to 7.10.7 --- CHANGES.rst | 6 ++++++ coverage/version.py | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index a115f44cb..6f347a16d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -20,6 +20,12 @@ upgrading your version of coverage.py. .. Version 9.8.1 — 2027-07-27 .. -------------------------- +Unreleased +---------- + +Nothing yet. + + .. start-releases .. _changes_7-10-6: diff --git a/coverage/version.py b/coverage/version.py index a9d77c510..5d309fbb0 100644 --- a/coverage/version.py +++ b/coverage/version.py @@ -8,8 +8,8 @@ # version_info: same semantics as sys.version_info. # _dev: the .devN suffix if any. -version_info = (7, 10, 6, "final", 0) -_dev = 0 +version_info = (7, 10, 7, "alpha", 0) +_dev = 1 def _make_version( From 1433282a7d3084bf6e432354ab0c8345e883069a Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 29 Aug 2025 11:43:59 -0400 Subject: [PATCH 02/29] chore: make upgrade --- .pre-commit-config.yaml | 2 +- doc/requirements.pip | 12 ++++-------- requirements/dev.pip | 12 ++++++------ requirements/kit.pip | 4 ++-- requirements/light-threads.pip | 4 ++-- requirements/mypy.pip | 4 ++-- requirements/pip.pip | 4 ++-- requirements/pytest.pip | 4 ++-- requirements/tox.pip | 6 +++--- 9 files changed, 24 insertions(+), 28 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 94c00e595..3c7cd7db2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: exclude: "stress_phystoken|\\.py,cover$" - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.12.10 + rev: v0.12.11 hooks: - id: ruff-format diff --git a/doc/requirements.pip b/doc/requirements.pip index 1250bbaa6..7942b2d40 100644 --- a/doc/requirements.pip +++ b/doc/requirements.pip @@ -56,8 +56,6 @@ mdurl==0.1.2 # via markdown-it-py packaging==25.0 # via sphinx -pbr==7.0.1 - # via stevedore polib==1.2.0 # via sphinx-lint pyenchant==3.2.2 @@ -81,8 +79,6 @@ roman-numerals-py==3.1.0 # via sphinx scriv==1.7.0 # via -r doc/requirements.in -setuptools==80.9.0 - # via pbr sniffio==1.3.1 # via anyio snowballstemmer==3.0.1 @@ -96,7 +92,7 @@ sphinx==8.2.3 # sphinxcontrib-jquery # sphinxcontrib-restbuilder # sphinxcontrib-spelling -sphinx-autobuild==2024.10.3 +sphinx-autobuild==2025.8.25 # via -r doc/requirements.in sphinx-code-tabs==0.5.5 # via -r doc/requirements.in @@ -122,11 +118,11 @@ sphinxcontrib-serializinghtml==2.0.0 # via sphinx sphinxcontrib-spelling==8.0.1 # via -r doc/requirements.in -starlette==0.47.2 +starlette==0.47.3 # via sphinx-autobuild -stevedore==5.4.1 +stevedore==5.5.0 # via doc8 -typing-extensions==4.14.1 ; python_full_version < '3.13' +typing-extensions==4.15.0 ; python_full_version < '3.13' # via # anyio # starlette diff --git a/requirements/dev.pip b/requirements/dev.pip index 3e6d64c3b..b4cc26a0f 100644 --- a/requirements/dev.pip +++ b/requirements/dev.pip @@ -10,7 +10,7 @@ backports-tarfile==1.2.0 ; python_full_version < '3.12' and platform_machine != # via jaraco-context build==1.3.0 # via check-manifest -cachetools==6.1.0 +cachetools==6.2.0 # via tox certifi==2025.8.3 # via requests @@ -65,7 +65,7 @@ flaky==3.8.1 # via -r requirements/pytest.in greenlet==3.2.4 # via -r requirements/dev.in -hypothesis==6.138.2 +hypothesis==6.138.7 # via -r requirements/pytest.in id==1.5.0 # via twine @@ -126,11 +126,11 @@ packaging==25.0 # pytest # tox # twine -parso==0.8.4 +parso==0.8.5 # via jedi pip==25.2 # via -r requirements/pip.in -platformdirs==4.3.8 +platformdirs==4.4.0 # via # pylint # tox @@ -181,7 +181,7 @@ rfc3986==2.0.0 # via twine rich==14.1.0 # via twine -ruff==0.12.10 +ruff==0.12.11 # via -r requirements/dev.in scriv==1.7.0 # via -r requirements/dev.in @@ -213,7 +213,7 @@ tox-gh==1.5.0 # via -r requirements/tox.in twine==6.1.0 # via -r requirements/dev.in -typing-extensions==4.14.1 ; python_full_version < '3.11' +typing-extensions==4.15.0 ; python_full_version < '3.11' # via # astroid # exceptiongroup diff --git a/requirements/kit.pip b/requirements/kit.pip index 29bb02c4e..18c0ac449 100644 --- a/requirements/kit.pip +++ b/requirements/kit.pip @@ -34,7 +34,7 @@ docutils==0.22 # via readme-renderer filelock==3.19.1 # via cibuildwheel -humanize==4.12.3 +humanize==4.13.0 # via cibuildwheel id==1.5.0 # via twine @@ -73,7 +73,7 @@ packaging==25.0 # twine patchelf==0.17.2.4 ; (platform_machine == 'aarch64' and sys_platform == 'darwin') or (platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'arm64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') # via cibuildwheel -platformdirs==4.3.8 +platformdirs==4.4.0 # via cibuildwheel pycparser==2.22 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_python_implementation != 'PyPy' and sys_platform == 'linux' # via cffi diff --git a/requirements/light-threads.pip b/requirements/light-threads.pip index f99c1e85c..7c2fcb7a8 100644 --- a/requirements/light-threads.pip +++ b/requirements/light-threads.pip @@ -6,9 +6,9 @@ cffi==1.17.1 # gevent dnspython==2.7.0 # via eventlet -eventlet==0.40.2 +eventlet==0.40.3 # via -r requirements/light-threads.in -gevent==25.5.1 +gevent==25.8.1 # via -r requirements/light-threads.in greenlet==3.2.4 # via diff --git a/requirements/mypy.pip b/requirements/mypy.pip index ccfae424e..9e3b5ea76 100644 --- a/requirements/mypy.pip +++ b/requirements/mypy.pip @@ -14,7 +14,7 @@ execnet==2.1.1 # via pytest-xdist flaky==3.8.1 # via -r requirements/pytest.in -hypothesis==6.138.2 +hypothesis==6.138.7 # via -r requirements/pytest.in iniconfig==2.1.0 # via pytest @@ -48,7 +48,7 @@ types-requests==2.32.4.20250809 # via -r requirements/mypy.in types-tabulate==0.9.0.20241207 # via -r requirements/mypy.in -typing-extensions==4.14.1 +typing-extensions==4.15.0 # via # exceptiongroup # mypy diff --git a/requirements/pip.pip b/requirements/pip.pip index ac77d17cc..d6e01e0e8 100644 --- a/requirements/pip.pip +++ b/requirements/pip.pip @@ -6,11 +6,11 @@ filelock==3.19.1 # via virtualenv pip==25.2 # via -r requirements/pip.in -platformdirs==4.3.8 +platformdirs==4.4.0 # via virtualenv setuptools==80.9.0 # via -r requirements/pip.in -typing-extensions==4.14.1 ; python_full_version < '3.11' +typing-extensions==4.15.0 ; python_full_version < '3.11' # via virtualenv virtualenv==20.34.0 # via -r requirements/pip.in diff --git a/requirements/pytest.pip b/requirements/pytest.pip index a7dd49f51..e345f445d 100644 --- a/requirements/pytest.pip +++ b/requirements/pytest.pip @@ -14,7 +14,7 @@ execnet==2.1.1 # via pytest-xdist flaky==3.8.1 # via -r requirements/pytest.in -hypothesis==6.138.2 +hypothesis==6.138.7 # via -r requirements/pytest.in iniconfig==2.1.0 # via pytest @@ -36,5 +36,5 @@ sortedcontainers==2.4.0 # via hypothesis tomli==2.2.1 ; python_full_version < '3.11' # via pytest -typing-extensions==4.14.1 ; python_full_version < '3.11' +typing-extensions==4.15.0 ; python_full_version < '3.11' # via exceptiongroup diff --git a/requirements/tox.pip b/requirements/tox.pip index 8832ffbdb..8f631577c 100644 --- a/requirements/tox.pip +++ b/requirements/tox.pip @@ -1,6 +1,6 @@ # This file was autogenerated by uv via the following command: # make upgrade -cachetools==6.1.0 +cachetools==6.2.0 # via tox chardet==5.2.0 # via tox @@ -18,7 +18,7 @@ packaging==25.0 # via # pyproject-api # tox -platformdirs==4.3.8 +platformdirs==4.4.0 # via # tox # virtualenv @@ -36,7 +36,7 @@ tox==4.28.4 # tox-gh tox-gh==1.5.0 # via -r requirements/tox.in -typing-extensions==4.14.1 ; python_full_version < '3.11' +typing-extensions==4.15.0 ; python_full_version < '3.11' # via # tox # virtualenv From 45958c84828bef5f771b13f92c1f44f9ab4f889d Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 30 Aug 2025 16:38:33 -0400 Subject: [PATCH 03/29] refactor: on second thought, it should be a classmethod --- coverage/config.py | 18 +++++++++--------- coverage/control.py | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/coverage/config.py b/coverage/config.py index 82e56fd3b..eb33c26af 100644 --- a/coverage/config.py +++ b/coverage/config.py @@ -568,7 +568,7 @@ def debug_info(self) -> list[tuple[str, Any]]: return human_sorted_items((k, v) for k, v in self.__dict__.items() if not k.startswith("_")) def serialize(self) -> str: - """Convert to a string that can be ingested with `deserialize_config`. + """Convert to a string that can be ingested with `deserialize`. File paths used by `coverage run` are made absolute to ensure the deserialized config will refer to the same files. @@ -584,6 +584,14 @@ def serialize(self) -> str: data[k] = v return base64.b64encode(json.dumps(data).encode()).decode() + @classmethod + def deserialize(cls, config_str: str) -> CoverageConfig: + """Take a string from `serialize`, and make a CoverageConfig.""" + data = json.loads(base64.b64decode(config_str.encode()).decode()) + config = cls() + config.__dict__.update(data) + return config + def process_file_value(path: str) -> str: """Make adjustments to a file path to make it usable.""" @@ -707,11 +715,3 @@ def read_coverage_config( config.post_process() return config - - -def deserialize_config(config_str: str) -> CoverageConfig: - """Take a string from CoverageConfig.serialize, and make a CoverageConfig.""" - data = json.loads(base64.b64decode(config_str.encode()).decode()) - config = CoverageConfig() - config.__dict__.update(data) - return config diff --git a/coverage/control.py b/coverage/control.py index fece86c0f..791c52505 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -24,7 +24,7 @@ from coverage import env from coverage.annotate import AnnotateReporter from coverage.collector import Collector -from coverage.config import CoverageConfig, deserialize_config, read_coverage_config +from coverage.config import CoverageConfig, read_coverage_config from coverage.context import combine_context_switchers, should_start_context_test_function from coverage.core import CTRACER_FILE, Core from coverage.data import CoverageData, combine_parallel_data @@ -320,7 +320,7 @@ def __init__( # pylint: disable=too-many-arguments # Build our configuration from a number of sources. if isinstance(config_file, str) and config_file.startswith(CONFIG_DATA_PREFIX): - self.config = deserialize_config(config_file[len(CONFIG_DATA_PREFIX) :]) + self.config = CoverageConfig.deserialize(config_file[len(CONFIG_DATA_PREFIX) :]) else: if not isinstance(config_file, bool): config_file = os.fspath(config_file) From 8000b0aaf76c74598b61ae84e598fa7e17a71aa3 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 31 Aug 2025 08:17:10 -0400 Subject: [PATCH 04/29] test: test CoverageConfig.serialize unitly --- tests/test_config.py | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/tests/test_config.py b/tests/test_config.py index e74758ed3..300ecb3c4 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -6,14 +6,16 @@ from __future__ import annotations import os +import tempfile +from pathlib import Path from unittest import mock import pytest import coverage from coverage import Coverage, env -from coverage.config import HandyConfigParser +from coverage.config import CoverageConfig, HandyConfigParser from coverage.exceptions import ConfigError, CoverageWarning from coverage.tomlconfig import TomlConfigParser from coverage.types import FilePathClasses, FilePathType @@ -1021,3 +1023,39 @@ def test_exceptions_from_missing_toml_things(self) -> None: config.get("xyzzy", "foo") with pytest.raises(ConfigError, match="No option 'foo' in section: 'tool.coverage.run'"): config.get("run", "foo") + + +class SerializeConfigTest(CoverageTest): + """Tests of serializing the configuration for subprocesses.""" + + def test_them(self) -> None: + tmpsrc = str(Path(tempfile.gettempdir()) / "more_source") + self.make_file( + ".coveragerc", + f"""\ + [run] + timid = True + data_file = somewhere/the_data.db + debug_file = somewhere/debug.out + source = my_src, their_src + source_dirs = my_src, their_src, {tmpsrc} + debug = this_thing, that_thing + """, + ) + self.make_file("my_src/__init__.py") + self.make_file("that_thing/__init__.py") + cov = coverage.Coverage() + config2 = CoverageConfig.deserialize(cov.config.serialize()) + assert config2.timid + assert config2.data_file == os.path.abspath("somewhere/the_data.db") + assert config2.debug_file == os.path.abspath("somewhere/debug.out") + assert config2.source == [ + os.path.abspath("my_src"), + "their_src", + ] + assert config2.source_dirs == [ + os.path.abspath("my_src"), + os.path.abspath("their_src"), + tmpsrc, + ] + assert config2.debug == ["this_thing", "that_thing"] From 1ed78a9ae73a0bffb420ae02d3825a09339744af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 31 Aug 2025 10:33:27 -0400 Subject: [PATCH 05/29] chore: bump the action-dependencies group with 2 updates (#2042) Bumps the action-dependencies group with 2 updates: [actions/dependency-review-action](https://github.com/actions/dependency-review-action) and [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance). Updates `actions/dependency-review-action` from 4.7.2 to 4.7.3 - [Release notes](https://github.com/actions/dependency-review-action/releases) - [Commits](https://github.com/actions/dependency-review-action/compare/bc41886e18ea39df68b1b1245f4184881938e050...595b5aeba73380359d98a5e087f648dbb0edce1b) Updates `actions/attest-build-provenance` from 2.4.0 to 3.0.0 - [Release notes](https://github.com/actions/attest-build-provenance/releases) - [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md) - [Commits](https://github.com/actions/attest-build-provenance/compare/e8998f949152b193b063cb0ec769d69d929409be...977bb373ede98d70efdf65b84cb5f73e068dcc2a) --- updated-dependencies: - dependency-name: actions/dependency-review-action dependency-version: 4.7.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: action-dependencies - dependency-name: actions/attest-build-provenance dependency-version: 3.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: action-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/dependency-review.yml | 2 +- .github/workflows/publish.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 312c23b13..d250d84ff 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -29,7 +29,7 @@ jobs: persist-credentials: false - name: 'Dependency Review' - uses: actions/dependency-review-action@bc41886e18ea39df68b1b1245f4184881938e050 # v4.7.2 + uses: actions/dependency-review-action@595b5aeba73380359d98a5e087f648dbb0edce1b # v4.7.3 with: base-ref: ${{ github.event.pull_request.base.sha || 'master' }} head-ref: ${{ github.event.pull_request.head.sha || github.ref }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9bcf63947..0d071697f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -81,7 +81,7 @@ jobs: files=$(ls dist 2>/dev/null | wc -l) && [ "$files" -eq $EXPECTED ] || exit 1 - name: "Generate attestations" - uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2.4.0 + uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0 with: subject-path: "dist/*" @@ -121,7 +121,7 @@ jobs: files=$(ls dist 2>/dev/null | wc -l) && [ "$files" -eq $EXPECTED ] || exit 1 - name: "Generate attestations" - uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2.4.0 + uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0 with: subject-path: "dist/*" From 504c481ed61b745935affaf4dc7c574ce8f9a332 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 31 Aug 2025 10:57:30 -0400 Subject: [PATCH 06/29] test: exclude 'not env.METACOV' more forcefully --- metacov.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metacov.ini b/metacov.ini index 2039b733e..f64854720 100644 --- a/metacov.ini +++ b/metacov.ini @@ -55,7 +55,7 @@ exclude_lines = # Lines that we can't run during metacov. pytest.mark.skipif\(env.METACOV - if not env.METACOV: + if(.* and)? not env.METACOV: # These lines only happen if tests fail. raise AssertionError From 2b793baa29ff311d8cc3df44039ab6037f1f3892 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 5 Sep 2025 08:09:56 -0400 Subject: [PATCH 07/29] docs: restructure commands into their own pages --- Makefile | 2 +- doc/cmd.rst | 1111 -------------------------------- doc/commands/cmd_annotate.rst | 77 +++ doc/commands/cmd_combine.rst | 118 ++++ doc/commands/cmd_debug.rst | 124 ++++ doc/commands/cmd_erase.rst | 41 ++ doc/commands/cmd_html.rst | 111 ++++ doc/commands/cmd_json.rst | 63 ++ doc/commands/cmd_lcov.rst | 55 ++ doc/commands/cmd_report.rst | 140 ++++ doc/commands/cmd_reporting.rst | 42 ++ doc/commands/cmd_run.rst | 290 +++++++++ doc/commands/cmd_xml.rst | 98 +++ doc/commands/index.rst | 89 +++ doc/index.rst | 2 +- 15 files changed, 1250 insertions(+), 1113 deletions(-) delete mode 100644 doc/cmd.rst create mode 100644 doc/commands/cmd_annotate.rst create mode 100644 doc/commands/cmd_combine.rst create mode 100644 doc/commands/cmd_debug.rst create mode 100644 doc/commands/cmd_erase.rst create mode 100644 doc/commands/cmd_html.rst create mode 100644 doc/commands/cmd_json.rst create mode 100644 doc/commands/cmd_lcov.rst create mode 100644 doc/commands/cmd_report.rst create mode 100644 doc/commands/cmd_reporting.rst create mode 100644 doc/commands/cmd_run.rst create mode 100644 doc/commands/cmd_xml.rst create mode 100644 doc/commands/index.rst diff --git a/Makefile b/Makefile index fb86e1806..f1c03dd2d 100644 --- a/Makefile +++ b/Makefile @@ -262,7 +262,7 @@ $(DOCBIN): tox -q -e doc --notest cogdoc: $(DOCBIN) ## Run docs through cog. - $(DOCBIN)/python -m cogapp -crP --verbosity=1 doc/*.rst + $(DOCBIN)/python -m cogapp -crP --verbosity=1 doc/*.rst doc/*/*.rst dochtml: cogdoc $(DOCBIN) ## Build the docs HTML output. $(SPHINXBUILD) -b html doc doc/_build/html diff --git a/doc/cmd.rst b/doc/cmd.rst deleted file mode 100644 index 68ec406ad..000000000 --- a/doc/cmd.rst +++ /dev/null @@ -1,1111 +0,0 @@ -.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 -.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt - -.. This file is processed with cog to insert the latest command - help into the docs. If it's out of date, the quality checks will fail. - Running "make prebuild" will bring it up to date. - -.. [[[cog - from cog_helpers import show_configs, show_help -.. ]]] -.. [[[end]]] (sum: 1B2M2Y8Asg) - -.. _cmd: - -================== -Command line usage -================== - -.. highlight:: console - -When you install coverage.py, a command-line script called ``coverage`` is -placed on your path. To help with multi-version installs, it will also create -a ``coverage3`` alias, and a ``coverage-X.Y`` alias, depending on the version -of Python you're using. For example, when installing on Python 3.10, you will -be able to use ``coverage``, ``coverage3``, or ``coverage-3.10`` on the command -line. - -Coverage.py has a number of commands: - -* **run** -- :ref:`Run a Python program and collect execution data `. - -* **combine** -- :ref:`Combine together a number of data files `. - -* **erase** -- :ref:`Erase previously collected coverage data `. - -* **report** -- :ref:`Report coverage results `. - -* **html** -- - :ref:`Produce annotated HTML listings with coverage results `. - -* **xml** -- :ref:`Produce an XML report with coverage results `. - -* **json** -- :ref:`Produce a JSON report with coverage results `. - -* **lcov** -- :ref:`Produce an LCOV report with coverage results `. - -* **annotate** -- - :ref:`Annotate source files with coverage results `. - -* **debug** -- :ref:`Get diagnostic information `. - -Help is available with the **help** command, or with the ``--help`` switch on -any other command:: - - $ coverage help - $ coverage help run - $ coverage run --help - -Version information for coverage.py can be displayed with -``coverage --version``: - -.. parsed-literal:: - - $ coverage --version - Coverage.py, version |release| with C extension - Documentation at |doc-url| - -Any command can use a configuration file by specifying it with the -``--rcfile=FILE`` command-line switch. Any option you can set on the command -line can also be set in the configuration file. This can be a better way to -control coverage.py since the configuration file can be checked into source -control, and can provide options that other invocation techniques (like test -runner plugins) may not offer. See :ref:`config` for more details. - - -.. _cmd_run: - -Execution: ``coverage run`` ---------------------------- - -You collect execution data by running your Python program with the **run** -command:: - - $ coverage run my_program.py arg1 arg2 - blah blah ..your program's output.. blah blah - -Your program runs just as if it had been invoked with the Python command line. -Arguments after your file name are passed to your program as usual in -``sys.argv``. Rather than providing a file name, you can use the ``-m`` switch -and specify an importable module name instead, just as you can with the -Python ``-m`` switch:: - - $ coverage run -m packagename.modulename arg1 arg2 - blah blah ..your program's output.. blah blah - -.. note:: - - In most cases, the program to use here is a test runner, not your program - you are trying to measure. The test runner will run your tests and coverage - will measure the coverage of your code along the way. - -There are many options: - -.. [[[cog show_help("run") ]]] -.. code:: - - $ coverage run --help - Usage: coverage run [options] [program options] - - Run a Python program, measuring code execution. - - Options: - -a, --append Append data to the data file. Otherwise it starts - clean each time. - --branch Measure branch coverage in addition to statement - coverage. - --concurrency=LIBS Properly measure code using a concurrency library. - Valid values are: eventlet, gevent, greenlet, - multiprocessing, thread, or a comma-list of them. - --context=LABEL The context label to record for this coverage run. - --data-file=OUTFILE Write the recorded coverage data to this file. - Defaults to '.coverage'. [env: COVERAGE_FILE] - --include=PAT1,PAT2,... - Include only files whose paths match one of these - patterns. Accepts shell-style wildcards, which must be - quoted. - -m, --module is an importable Python module, not a script - path, to be run as 'python -m' would run it. - --omit=PAT1,PAT2,... Omit files whose paths match one of these patterns. - Accepts shell-style wildcards, which must be quoted. - -L, --pylib Measure coverage even inside the Python installed - library, which isn't done by default. - -p, --parallel-mode Append a unique suffix to the data file name to - collect separate data from multiple processes. - --save-signal=SIGNAL Specify a signal that will trigger coverage to write - its collected data. Supported values are: USR1, USR2. - Not available on Windows. - --source=SRC1,SRC2,... - A list of directories or importable names of code to - measure. - --timid Use the slower Python trace function core. - --debug=OPTS Debug options, separated by commas. [env: - COVERAGE_DEBUG] - -h, --help Get help on this command. - --rcfile=RCFILE Specify configuration file. By default '.coveragerc', - 'setup.cfg', 'tox.ini', and 'pyproject.toml' are - tried. [env: COVERAGE_RCFILE] -.. [[[end]]] (sum: YwMI03MDmQ) - -Many of these options can also be set in the :ref:`config_run` section of your -configuration file. Remember to put options for run after "run", but before -the program invocation:: - - $ coverage run --source=dir1,dir2 my_program.py arg1 arg2 - $ coverage run --source=dir1,dir2 -m packagename.modulename arg1 arg2 - -If you want :ref:`branch coverage ` measurement, use the ``--branch`` -flag. Otherwise only statement coverage is measured. - -You can specify the code to measure with the ``--source``, ``--include``, and -``--omit`` switches. See :ref:`Specifying source files ` for -details of their interpretation. - -.. note:: - - Specifying ``--source`` on the ``coverage run`` command line won't affect - subsequent reporting commands like ``coverage xml``. Use the :ref:`source - ` setting in the configuration file to apply the setting - uniformly to all commands. - -Coverage.py can measure multi-threaded programs by default. If you are using -more other concurrency support, with the `multiprocessing`_, `greenlet`_, -`eventlet`_, or `gevent`_ libraries, then coverage.py can get confused. Use the -``--concurrency`` switch to properly measure programs using these libraries. -Give it a value of ``multiprocessing``, ``thread``, ``greenlet``, ``eventlet``, -or ``gevent``. Values other than ``thread`` require the :ref:`C extension -`. - -You can combine multiple values for ``--concurrency``, separated with commas. -You can specify ``thread`` and also one of ``eventlet``, ``gevent``, or -``greenlet``. - -If you are using ``--concurrency=multiprocessing``, you must set your other -options in the configuration file. Options on the command line will not be -passed to the processes that multiprocessing creates. Best practice is to use -the configuration file for all options. - -.. _multiprocessing: https://docs.python.org/3/library/multiprocessing.html -.. _greenlet: https://greenlet.readthedocs.io/ -.. _gevent: https://www.gevent.org/ -.. _eventlet: https://eventlet.readthedocs.io/ - -If you are measuring coverage in a multi-process program, or across a number of -machines, you'll want the ``--parallel-mode`` switch to keep the data separate -during measurement. See :ref:`cmd_combine` below. - -You can specify a :ref:`static context ` for a coverage run with -``--context``. This can be any label you want, and will be recorded with the -data. See :ref:`contexts` for more information. - -By default, coverage.py does not measure code installed with the Python -interpreter, for example, the standard library. If you want to measure that -code as well as your own, add the ``-L`` (or ``--pylib``) flag. - -If your coverage results seem to be overlooking code that you know has been -executed, try running coverage.py again with the ``--timid`` flag. This uses a -simpler but slower trace method, and might be needed in rare cases. - -If you are specifying ``--save-signal``, please make sure that your program -doesn't intercept this signal. If it does, coverage won't receive the signal -and the data file will not be written. - -.. versionadded:: 7.10 ``--save-signal`` - -Coverage.py sets an environment variable, ``COVERAGE_RUN`` to indicate that -your code is running under coverage measurement. The value is not relevant, -and may change in the future. - - -.. _cmd_warnings: - -Warnings -........ - -During execution, coverage.py may warn you about conditions it detects that -could affect the measurement process. The possible warnings include: - -Couldn't parse Python file XXX (couldnt-parse) - During reporting, a file was thought to be Python, but it couldn't be parsed - as Python. - -Trace function changed, data is likely wrong: XXX (trace-changed) - Coverage measurement depends on a Python setting called the trace function. - Other Python code in your product might change that function, which will - disrupt coverage.py's measurement. This warning indicates that has happened. - The XXX in the message is the new trace function value, which might provide - a clue to the cause. - -Module XXX has no Python source (module-not-python) - You asked coverage.py to measure module XXX, but once it was imported, it - turned out not to have a corresponding .py file. Without a .py file, - coverage.py can't report on missing lines. - -Module XXX was never imported (module-not-imported) - You asked coverage.py to measure module XXX, but it was never imported by - your program. - -No data was collected (no-data-collected) - Coverage.py ran your program, but didn't measure any lines as executed. - This could be because you asked to measure only modules that never ran, - or for other reasons. - - To debug this problem, try using ``run --debug=trace`` to see the tracing - decision made for each file. - -Module XXX was previously imported, but not measured (module-not-measured) - You asked coverage.py to measure module XXX, but it had already been imported - when coverage started. This meant coverage.py couldn't monitor its - execution. - -Already imported a file that will be measured: XXX (already-imported) - File XXX had already been imported when coverage.py started measurement. Your - setting for ``--source`` or ``--include`` indicates that you wanted to - measure that file. Lines will be missing from the coverage report since the - execution during import hadn't been measured. - -\-\-include is ignored because \-\-source is set (include-ignored) - Both ``--include`` and ``--source`` were specified while running code. Both - are meant to focus measurement on a particular part of your source code, so - ``--include`` is ignored in favor of ``--source``. - -Conflicting dynamic contexts (dynamic-conflict) - The ``[run] dynamic_context`` option is set in the configuration file, but - something (probably a test runner plugin) is also calling the - :meth:`.Coverage.switch_context` function to change the context. Only one of - these mechanisms should be in use at a time. - -Couldn't import C tracer (no-ctracer) - The core tracer implemented in C should have been used, but couldn't be - imported. The reason is included in the warning message. The Python tracer - will be used instead. - -sys.monitoring isn't available in this version, using default core (no-sysmon) - You requested to use the sys.monitoring measurement core, but are running on - Python 3.11 or lower where it isn't available. A default core will be used - instead. - -sys.monitoring can't measure branches in this version, using default core (no-sysmon) - You requested the sys.monitoring measurement core and also branch coverage. - This isn't supported until the later alphas of Python 3.14. A default core - will be used instead. - -sys.monitoring doesn't yet support dynamic contexts, using default core (no-sysmon) - You requested the sys.monitoring measurement core and also dynamic contexts. - This isn't supported by coverage.py yet. A default core will be used - instead. - -Individual warnings can be disabled with the :ref:`disable_warnings -` configuration setting. It is a list of the -short parenthetical nicknames in the warning messages. For example, to silence -"No data was collected (no-data-collected)", add this to your configuration -file: - -.. [[[cog - show_configs( - ini=r""" - [run] - disable_warnings = no-data-collected - """, - toml=r""" - [tool.coverage.run] - disable_warnings = ["no-data-collected"] - """, - ) -.. ]]] - -.. tabs:: - - .. code-tab:: ini - :caption: .coveragerc - - [run] - disable_warnings = no-data-collected - - .. code-tab:: toml - :caption: pyproject.toml - - [tool.coverage.run] - disable_warnings = ["no-data-collected"] - - .. code-tab:: ini - :caption: setup.cfg or tox.ini - - [coverage:run] - disable_warnings = no-data-collected - -.. [[[end]]] (sum: SJKFvPoXO2) - - -.. _cmd_datafile: - -Data file -......... - -Coverage.py collects execution data in a file called ".coverage". If need be, -you can set a new file name with the ``COVERAGE_FILE`` environment variable. -This can include a path to another directory. - -By default, each run of your program starts with an empty data set. If you need -to run your program multiple times to get complete data (for example, because -you need to supply different options), you can accumulate data across runs with -the ``--append`` flag on the **run** command. - - -.. _cmd_combine: - -Combining data files: ``coverage combine`` ------------------------------------------- - -Often test suites are run under different conditions, for example, with -different versions of Python, or dependencies, or on different operating -systems. In these cases, you can collect coverage data for each test run, and -then combine all the separate data files into one combined file for reporting. - -The **combine** command reads a number of separate data files, matches the data -by source file name, and writes a combined data file with all of the data. - -Coverage normally writes data to a filed named ".coverage". The ``run ---parallel-mode`` switch (or ``[run] parallel=True`` configuration option) -tells coverage to expand the file name to include machine name, process id, and -a random number so that every data file is distinct:: - - .coverage.Neds-MacBook-Pro.local.88335.316857 - .coverage.Geometer.8044.799674 - -You can also define a new data file name with the ``[run] data_file`` option. - -Once you have created a number of these files, you can copy them all to a -single directory, and use the **combine** command to combine them into one -.coverage data file:: - - $ coverage combine - -You can also name directories or files to be combined on the command line:: - - $ coverage combine data1.dat windows_data_files/ - -Coverage.py will collect the data from those places and combine them. The -current directory isn't searched if you use command-line arguments. If you -also want data from the current directory, name it explicitly on the command -line. - -When coverage.py combines data files, it looks for files named the same as the -data file (defaulting to ".coverage"), with a dotted suffix. Here are some -examples of data files that can be combined:: - - .coverage.machine1 - .coverage.20120807T212300 - .coverage.last_good_run.ok - -An existing combined data file is ignored and re-written. If you want to use -**combine** to accumulate results into the .coverage data file over a number of -runs, use the ``--append`` switch on the **combine** command. This behavior -was the default before version 4.2. - -If any of the data files can't be read, coverage.py will print a warning -indicating the file and the problem. - -The original input data files are deleted once they've been combined. If you -want to keep those files, use the ``--keep`` command-line option. - -.. [[[cog show_help("combine") ]]] -.. code:: - - $ coverage combine --help - Usage: coverage combine [options] ... - - Combine data from multiple coverage files. The combined results are written to - a single file representing the union of the data. The positional arguments are - data files or directories containing data files. If no paths are provided, - data files in the default data file's directory are combined. - - Options: - -a, --append Append data to the data file. Otherwise it starts - clean each time. - --data-file=DATAFILE Base name of the data files to operate on. Defaults to - '.coverage'. [env: COVERAGE_FILE] - --keep Keep original coverage files, otherwise they are - deleted. - -q, --quiet Don't print messages about what is happening. - --debug=OPTS Debug options, separated by commas. [env: - COVERAGE_DEBUG] - -h, --help Get help on this command. - --rcfile=RCFILE Specify configuration file. By default '.coveragerc', - 'setup.cfg', 'tox.ini', and 'pyproject.toml' are - tried. [env: COVERAGE_RCFILE] -.. [[[end]]] (sum: ARyg8KB1fE) - - -.. _cmd_combine_remapping: - -Re-mapping paths -................ - -To combine data for a source file, coverage has to find its data in each of the -data files. Different test runs may run the same source file from different -locations. For example, different operating systems will use different paths -for the same file, or perhaps each Python version is run from a different -subdirectory. Coverage needs to know that different file paths are actually -the same source file for reporting purposes. - -You can tell coverage.py how different source locations relate with a -``[paths]`` section in your configuration file (see :ref:`config_paths`). -It might be more convenient to use the ``[run] relative_files`` -setting to store relative file paths (see :ref:`relative_files -`). - -If data isn't combining properly, you can see details about the inner workings -with ``--debug=pathmap``. - - -.. _cmd_erase: - -Erase data: ``coverage erase`` ------------------------------- - -To erase the collected data, use the **erase** command: - -.. [[[cog show_help("erase") ]]] -.. code:: - - $ coverage erase --help - Usage: coverage erase [options] - - Erase previously collected coverage data. - - Options: - --data-file=DATAFILE Base name of the data files to operate on. Defaults to - '.coverage'. [env: COVERAGE_FILE] - --debug=OPTS Debug options, separated by commas. [env: - COVERAGE_DEBUG] - -h, --help Get help on this command. - --rcfile=RCFILE Specify configuration file. By default '.coveragerc', - 'setup.cfg', 'tox.ini', and 'pyproject.toml' are - tried. [env: COVERAGE_RCFILE] -.. [[[end]]] (sum: z+rvZs6NUV) - -If your configuration file indicates parallel data collection, **erase** will -remove all of the data files. - - -.. _cmd_reporting: - -Reporting ---------- - -Coverage.py provides a few styles of reporting, with the **report**, **html**, -**annotate**, **json**, **lcov**, and **xml** commands. They share a number -of common options. - -The command-line arguments are module or file names to report on, if you'd like -to report on a subset of the data collected. - -The ``--include`` and ``--omit`` flags specify lists of file name patterns. -They control which files to report on, and are described in more detail in -:ref:`source`. - -The ``-i`` or ``--ignore-errors`` switch tells coverage.py to ignore problems -encountered trying to find source files to report on. This can be useful if -some files are missing, or if your Python execution is tricky enough that file -names are synthesized without real source files. - -If you provide a ``--fail-under`` value, the total percentage covered will be -compared to that value. If it is less, the command will exit with a status -code of 2, indicating that the total coverage was less than your target. This -can be used as part of a pass/fail condition, for example in a continuous -integration server. This option isn't available for **annotate**. - -These options can also be set in your .coveragerc file. See -:ref:`Configuration: [report] `. - - -.. _cmd_report: - -Coverage summary: ``coverage report`` -------------------------------------- - -The simplest reporting is a textual summary produced with **report**:: - - $ coverage report - Name Stmts Miss Cover - --------------------------------------------- - my_program.py 20 4 80% - my_module.py 15 2 86% - my_other_module.py 56 6 89% - --------------------------------------------- - TOTAL 91 12 87% - -For each module executed, the report shows the count of executable statements, -the number of those statements missed, and the resulting coverage, expressed -as a percentage. - -.. [[[cog show_help("report") ]]] -.. code:: - - $ coverage report --help - Usage: coverage report [options] [modules] - - Report coverage statistics on modules. - - Options: - --contexts=REGEX1,REGEX2,... - Only display data from lines covered in the given - contexts. Accepts Python regexes, which must be - quoted. - --data-file=INFILE Read coverage data for report generation from this - file. Defaults to '.coverage'. [env: COVERAGE_FILE] - --fail-under=MIN Exit with a status of 2 if the total coverage is less - than MIN. - --format=FORMAT Output format, either text (default), markdown, or - total. - -i, --ignore-errors Ignore errors while reading source files. - --include=PAT1,PAT2,... - Include only files whose paths match one of these - patterns. Accepts shell-style wildcards, which must be - quoted. - --omit=PAT1,PAT2,... Omit files whose paths match one of these patterns. - Accepts shell-style wildcards, which must be quoted. - --precision=N Number of digits after the decimal point to display - for reported coverage percentages. - --sort=COLUMN Sort the report by the named column: name, stmts, - miss, branch, brpart, or cover. Default is name. - -m, --show-missing Show line numbers of statements in each module that - weren't executed. - --skip-covered Skip files with 100% coverage. - --no-skip-covered Disable --skip-covered. - --skip-empty Skip files with no code. - --debug=OPTS Debug options, separated by commas. [env: - COVERAGE_DEBUG] - -h, --help Get help on this command. - --rcfile=RCFILE Specify configuration file. By default '.coveragerc', - 'setup.cfg', 'tox.ini', and 'pyproject.toml' are - tried. [env: COVERAGE_RCFILE] -.. [[[end]]] (sum: FnJyop2efr) - -The ``-m`` flag also shows the line numbers of missing statements:: - - $ coverage report -m - Name Stmts Miss Cover Missing - ------------------------------------------------------- - my_program.py 20 4 80% 33-35, 39 - my_module.py 15 2 86% 8, 12 - my_other_module.py 56 6 89% 17-23 - ------------------------------------------------------- - TOTAL 91 12 87% - -If you are using branch coverage, then branch statistics will be reported in -the Branch and BrPart (for Partial Branch) columns, the Missing column will -detail the missed branches:: - - $ coverage report -m - Name Stmts Miss Branch BrPart Cover Missing - --------------------------------------------------------------------- - my_program.py 20 4 10 2 80% 33-35, 36->38, 39 - my_module.py 15 2 3 0 86% 8, 12 - my_other_module.py 56 6 5 1 89% 17-23, 40->45 - --------------------------------------------------------------------- - TOTAL 91 12 18 3 87% - -Ranges of lines are shown with a dash: "17-23" means all lines from 17 to 23 -inclusive are missing coverage. Missed branches are shown with an arrow: -"40->45" means the branch from line 40 to line 45 is missing. A branch can go -backwards in a file, so you might see a branch from a later line to an earlier -line, like "55->50". - -You can restrict the report to only certain files by naming them on the -command line:: - - $ coverage report -m my_program.py my_other_module.py - Name Stmts Miss Cover Missing - ------------------------------------------------------- - my_program.py 20 4 80% 33-35, 39 - my_other_module.py 56 6 89% 17-23 - ------------------------------------------------------- - TOTAL 76 10 87% - -The ``--skip-covered`` switch will skip any file with 100% coverage, letting -you focus on the files that still need attention. The ``--no-skip-covered`` -option can be used if needed to see all the files. The ``--skip-empty`` switch -will skip any file with no executable statements. - -If you have :ref:`recorded contexts `, the ``--contexts`` option lets -you choose which contexts to report on. See :ref:`context_reporting` for -details. - -The ``--precision`` option controls the number of digits displayed after the -decimal point in coverage percentages, defaulting to none. - -The ``--sort`` option is the name of a column to sort the report by. - -The ``--format`` option controls the style of the report. ``--format=text`` -creates plain text tables as shown above. ``--format=markdown`` creates -Markdown tables. ``--format=total`` writes out a single number, the total -coverage percentage as shown at the end of the tables, but without a percent -sign. - -Other common reporting options are described above in :ref:`cmd_reporting`. -These options can also be set in your .coveragerc file. See -:ref:`Configuration: [report] `. - - -.. _cmd_html: - -HTML reporting: ``coverage html`` ---------------------------------- - -Coverage.py can annotate your source code to show which lines were executed -and which were not. The **html** command creates an HTML report similar to the -**report** summary, but as an HTML file. Each module name links to the source -file decorated to show the status of each line. - -Here's a `sample report`__. - -__ https://nedbatchelder.com/files/sample_coverage_html/index.html - -Lines are highlighted: green for executed, red for missing, and gray for -excluded. If you've used branch coverage, partial branches are yellow. The -colored counts at the top of the file are buttons to turn on and off the -highlighting. - -A number of keyboard shortcuts are available for navigating the report. -Click the keyboard icon in the upper right to see the complete list. - -.. [[[cog show_help("html") ]]] -.. code:: - - $ coverage html --help - Usage: coverage html [options] [modules] - - Create an HTML report of the coverage of the files. Each file gets its own - page, with the source decorated to show executed, excluded, and missed lines. - - Options: - --contexts=REGEX1,REGEX2,... - Only display data from lines covered in the given - contexts. Accepts Python regexes, which must be - quoted. - -d DIR, --directory=DIR - Write the output files to DIR. - --data-file=INFILE Read coverage data for report generation from this - file. Defaults to '.coverage'. [env: COVERAGE_FILE] - --fail-under=MIN Exit with a status of 2 if the total coverage is less - than MIN. - -i, --ignore-errors Ignore errors while reading source files. - --include=PAT1,PAT2,... - Include only files whose paths match one of these - patterns. Accepts shell-style wildcards, which must be - quoted. - --omit=PAT1,PAT2,... Omit files whose paths match one of these patterns. - Accepts shell-style wildcards, which must be quoted. - --precision=N Number of digits after the decimal point to display - for reported coverage percentages. - -q, --quiet Don't print messages about what is happening. - --show-contexts Show contexts for covered lines. - --skip-covered Skip files with 100% coverage. - --no-skip-covered Disable --skip-covered. - --skip-empty Skip files with no code. - --title=TITLE A text string to use as the title on the HTML. - --debug=OPTS Debug options, separated by commas. [env: - COVERAGE_DEBUG] - -h, --help Get help on this command. - --rcfile=RCFILE Specify configuration file. By default '.coveragerc', - 'setup.cfg', 'tox.ini', and 'pyproject.toml' are - tried. [env: COVERAGE_RCFILE] -.. [[[end]]] (sum: DwG6DxRZIf) - -The title of the report can be set with the ``title`` setting in the -``[html]`` section of the configuration file, or the ``--title`` switch on -the command line. - -If you prefer a different style for your HTML report, you can provide your -own CSS file to apply, by specifying a CSS file in the ``[html]`` section of -the configuration file. See :ref:`config_html_extra_css` for details. - -The ``-d`` argument specifies an output directory, defaulting to "htmlcov":: - - $ coverage html -d coverage_html - -Other common reporting options are described above in :ref:`cmd_reporting`. - -Generating the HTML report can be time-consuming. Stored with the HTML report -is a data file that is used to speed up reporting the next time. If you -generate a new report into the same directory, coverage.py will skip -generating unchanged pages, making the process faster. - -The ``--skip-covered`` switch will skip any file with 100% coverage, letting -you focus on the files that still need attention. The ``--skip-empty`` switch -will skip any file with no executable statements. - -The ``--precision`` option controls the number of digits displayed after the -decimal point in coverage percentages, defaulting to none. - -If you have :ref:`recorded contexts `, the ``--contexts`` option lets -you choose which contexts to report on, and the ``--show-contexts`` option will -annotate lines with the contexts that ran them. See :ref:`context_reporting` -for details. - -These options can also be set in your .coveragerc file. See -:ref:`Configuration: [html] `. - - -.. _cmd_xml: - -XML reporting: ``coverage xml`` -------------------------------- - -The **xml** command writes coverage data to a "coverage.xml" file in a format -compatible with `Cobertura`_. - -.. _Cobertura: http://cobertura.github.io/cobertura/ - -.. [[[cog show_help("xml") ]]] -.. code:: - - $ coverage xml --help - Usage: coverage xml [options] [modules] - - Generate an XML report of coverage results. - - Options: - --data-file=INFILE Read coverage data for report generation from this - file. Defaults to '.coverage'. [env: COVERAGE_FILE] - --fail-under=MIN Exit with a status of 2 if the total coverage is less - than MIN. - -i, --ignore-errors Ignore errors while reading source files. - --include=PAT1,PAT2,... - Include only files whose paths match one of these - patterns. Accepts shell-style wildcards, which must be - quoted. - --omit=PAT1,PAT2,... Omit files whose paths match one of these patterns. - Accepts shell-style wildcards, which must be quoted. - -o OUTFILE Write the XML report to this file. Defaults to - 'coverage.xml' - -q, --quiet Don't print messages about what is happening. - --skip-empty Skip files with no code. - --debug=OPTS Debug options, separated by commas. [env: - COVERAGE_DEBUG] - -h, --help Get help on this command. - --rcfile=RCFILE Specify configuration file. By default '.coveragerc', - 'setup.cfg', 'tox.ini', and 'pyproject.toml' are - tried. [env: COVERAGE_RCFILE] -.. [[[end]]] (sum: iyOdiVNL4L) - -You can specify the name of the output file with the ``-o`` switch. - -Other common reporting options are described above in :ref:`cmd_reporting`. - -To include complete file paths in the output file, rather than just -the file name, use [include] vs [source] in your ".coveragerc" file. - -For example, use this: - -.. code:: ini - - [run] - include = - foo/* - bar/* - - -which will result in - -.. code:: xml - - - - - -in place of this: - -.. code:: ini - - [run] - source = - foo - bar - -which may result in - -.. code:: xml - - - - -These options can also be set in your .coveragerc file. See -:ref:`Configuration: [xml] `. - - -.. _cmd_json: - -JSON reporting: ``coverage json`` ---------------------------------- - -The **json** command writes coverage data to a "coverage.json" file. - -.. [[[cog show_help("json") ]]] -.. code:: - - $ coverage json --help - Usage: coverage json [options] [modules] - - Generate a JSON report of coverage results. - - Options: - --contexts=REGEX1,REGEX2,... - Only display data from lines covered in the given - contexts. Accepts Python regexes, which must be - quoted. - --data-file=INFILE Read coverage data for report generation from this - file. Defaults to '.coverage'. [env: COVERAGE_FILE] - --fail-under=MIN Exit with a status of 2 if the total coverage is less - than MIN. - -i, --ignore-errors Ignore errors while reading source files. - --include=PAT1,PAT2,... - Include only files whose paths match one of these - patterns. Accepts shell-style wildcards, which must be - quoted. - --omit=PAT1,PAT2,... Omit files whose paths match one of these patterns. - Accepts shell-style wildcards, which must be quoted. - -o OUTFILE Write the JSON report to this file. Defaults to - 'coverage.json' - --pretty-print Format the JSON for human readers. - -q, --quiet Don't print messages about what is happening. - --show-contexts Show contexts for covered lines. - --debug=OPTS Debug options, separated by commas. [env: - COVERAGE_DEBUG] - -h, --help Get help on this command. - --rcfile=RCFILE Specify configuration file. By default '.coveragerc', - 'setup.cfg', 'tox.ini', and 'pyproject.toml' are - tried. [env: COVERAGE_RCFILE] -.. [[[end]]] (sum: 5T5gy2XZcc) - -You can specify the name of the output file with the ``-o`` switch. The JSON -can be nicely formatted by specifying the ``--pretty-print`` switch. - -Other common reporting options are described above in :ref:`cmd_reporting`. -These options can also be set in your .coveragerc file. See -:ref:`Configuration: [json] `. - - -.. _cmd_lcov: - -LCOV reporting: ``coverage lcov`` ---------------------------------- - -The **lcov** command writes coverage data to a "coverage.lcov" file. - -.. [[[cog show_help("lcov") ]]] -.. code:: - - $ coverage lcov --help - Usage: coverage lcov [options] [modules] - - Generate an LCOV report of coverage results. - - Options: - --data-file=INFILE Read coverage data for report generation from this - file. Defaults to '.coverage'. [env: COVERAGE_FILE] - --fail-under=MIN Exit with a status of 2 if the total coverage is less - than MIN. - -i, --ignore-errors Ignore errors while reading source files. - --include=PAT1,PAT2,... - Include only files whose paths match one of these - patterns. Accepts shell-style wildcards, which must be - quoted. - -o OUTFILE Write the LCOV report to this file. Defaults to - 'coverage.lcov' - --omit=PAT1,PAT2,... Omit files whose paths match one of these patterns. - Accepts shell-style wildcards, which must be quoted. - -q, --quiet Don't print messages about what is happening. - --debug=OPTS Debug options, separated by commas. [env: - COVERAGE_DEBUG] - -h, --help Get help on this command. - --rcfile=RCFILE Specify configuration file. By default '.coveragerc', - 'setup.cfg', 'tox.ini', and 'pyproject.toml' are - tried. [env: COVERAGE_RCFILE] -.. [[[end]]] (sum: Fqz7roAR0u) - -Common reporting options are described above in :ref:`cmd_reporting`. -Also see :ref:`Configuration: [lcov] `. - -.. versionadded:: 6.3 - - -.. _cmd_annotate: - -Text annotation: ``coverage annotate`` --------------------------------------- - -.. note:: - - The **annotate** command has been obsoleted by more modern reporting tools, - including the **html** command. - -The **annotate** command produces a text annotation of your source code. With -a ``-d`` argument specifying an output directory, each Python file becomes a -text file in that directory. Without ``-d``, the files are written into the -same directories as the original Python files. - -Coverage status for each line of source is indicated with a character prefix:: - - > executed - ! missing (not executed) - - excluded - -For example:: - - # A simple function, never called with x==1 - - > def h(x): - """Silly function.""" - - if 0: # pragma: no cover - - pass - > if x == 1: - ! a = 1 - > else: - > a = 2 - -.. [[[cog show_help("annotate") ]]] -.. code:: - - $ coverage annotate --help - Usage: coverage annotate [options] [modules] - - Make annotated copies of the given files, marking statements that are executed - with > and statements that are missed with !. - - Options: - -d DIR, --directory=DIR - Write the output files to DIR. - --data-file=INFILE Read coverage data for report generation from this - file. Defaults to '.coverage'. [env: COVERAGE_FILE] - -i, --ignore-errors Ignore errors while reading source files. - --include=PAT1,PAT2,... - Include only files whose paths match one of these - patterns. Accepts shell-style wildcards, which must be - quoted. - --omit=PAT1,PAT2,... Omit files whose paths match one of these patterns. - Accepts shell-style wildcards, which must be quoted. - --debug=OPTS Debug options, separated by commas. [env: - COVERAGE_DEBUG] - -h, --help Get help on this command. - --rcfile=RCFILE Specify configuration file. By default '.coveragerc', - 'setup.cfg', 'tox.ini', and 'pyproject.toml' are - tried. [env: COVERAGE_RCFILE] -.. [[[end]]] (sum: /X2PvS3W4k) - -Other common reporting options are described above in :ref:`cmd_reporting`. - - -.. _cmd_debug: - -Diagnostics: ``coverage debug`` -------------------------------- - -The **debug** command shows internal information to help diagnose problems. -If you are reporting a bug about coverage.py, including the output of this -command can often help:: - - $ coverage debug sys > please_attach_to_bug_report.txt - -A few types of information are available: - -* ``config``: show coverage's configuration -* ``sys``: show system configuration -* ``data``: show a summary of the collected coverage data -* ``premain``: show the call stack invoking coverage -* ``pybehave``: show internal flags describing Python behavior - -.. [[[cog show_help("debug") ]]] -.. code:: - - $ coverage debug --help - Usage: coverage debug - - Display information about the internals of coverage.py, for diagnosing - problems. Topics are: 'data' to show a summary of the collected data; 'sys' to - show installation information; 'config' to show the configuration; 'premain' - to show what is calling coverage; 'pybehave' to show internal flags describing - Python behavior. - - Options: - --debug=OPTS Debug options, separated by commas. [env: COVERAGE_DEBUG] - -h, --help Get help on this command. - --rcfile=RCFILE Specify configuration file. By default '.coveragerc', - 'setup.cfg', 'tox.ini', and 'pyproject.toml' are tried. - [env: COVERAGE_RCFILE] -.. [[[end]]] (sum: ybjftkTaNE) - -.. _cmd_run_debug: - -``--debug`` -........... - -The ``--debug`` option is also available on all commands. It instructs -coverage.py to log internal details of its operation, to help with diagnosing -problems. It takes a comma-separated list of options, each indicating a facet -of operation to log: - -* ``callers``: annotate each debug message with a stack trace of the callers - to that point. - -* ``config``: before starting, dump all the :ref:`configuration ` - values. - -* ``dataio``: log when reading or writing any data file. - -* ``dataop``: log a summary of data being added to CoverageData objects. - -* ``dataop2``: when used with ``debug=dataop``, log the actual data being added - to CoverageData objects. - -* ``lock``: log operations acquiring locks in the data layer. - -* ``multiproc``: log the start and stop of multiprocessing processes. - -* ``patch``: log when patches are applied and when they are executed. See - :ref:`config_run_patch`. - -* ``pathmap``: log the remapping of paths that happens during ``coverage - combine``. See :ref:`config_paths`. - -* ``pid``: annotate all warnings and debug output with the process and thread - ids. - -* ``plugin``: print information about plugin operations. - -* ``process``: show process creation information, and changes in the current - directory. This also writes a time stamp and command arguments into the data - file. - -* ``pybehave``: show the values of `internal flags `_ describing the - behavior of the current version of Python. - -* ``pytest``: indicate the name of the current pytest test when it changes. - -* ``self``: annotate each debug message with the object printing the message. - -* ``sql``: log the SQL statements used for recording data. - -* ``sqldata``: when used with ``debug=sql``, also log the full data being used - in SQL statements. - -* ``sys``: before starting, dump all the system and environment information, - as with :ref:`coverage debug sys `. - -* ``trace``: print every decision about whether to trace a file or not. For - files not being traced, the reason is also given. - -.. _env.py: https://github.com/nedbat/coveragepy/blob/master/coverage/env.py - -Debug options can also be set with the ``COVERAGE_DEBUG`` environment variable, -a comma-separated list of these options, or in the :ref:`config_run_debug` -section of the .coveragerc file. - -The debug output goes to stderr, unless the :ref:`config_run_debug_file` -setting or the ``COVERAGE_DEBUG_FILE`` environment variable names a different -file, which will be appended to. This can be useful because many test runners -capture output, which could hide important details. ``COVERAGE_DEBUG_FILE`` -accepts the special names ``stdout`` and ``stderr`` to write to those -destinations. diff --git a/doc/commands/cmd_annotate.rst b/doc/commands/cmd_annotate.rst new file mode 100644 index 000000000..549e75e38 --- /dev/null +++ b/doc/commands/cmd_annotate.rst @@ -0,0 +1,77 @@ +.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt + +.. This file is processed with cog to insert the latest command help into the + docs. If it's out of date, the quality checks will fail. Running "make + prebuild" will bring it up to date. + +.. [[[cog + from cog_helpers import show_configs, show_help +.. ]]] +.. [[[end]]] (sum: 1B2M2Y8Asg) + + +.. _cmd_annotate: + +Text annotation: ``coverage annotate`` +-------------------------------------- + +.. note:: + + The **annotate** command has been obsoleted by more modern reporting tools, + including the **html** command. + +The **annotate** command produces a text annotation of your source code. With +a ``-d`` argument specifying an output directory, each Python file becomes a +text file in that directory. Without ``-d``, the files are written into the +same directories as the original Python files. + +Coverage status for each line of source is indicated with a character prefix:: + + > executed + ! missing (not executed) + - excluded + +For example:: + + # A simple function, never called with x==1 + + > def h(x): + """Silly function.""" + - if 0: # pragma: no cover + - pass + > if x == 1: + ! a = 1 + > else: + > a = 2 + +.. [[[cog show_help("annotate") ]]] +.. code:: + + $ coverage annotate --help + Usage: coverage annotate [options] [modules] + + Make annotated copies of the given files, marking statements that are executed + with > and statements that are missed with !. + + Options: + -d DIR, --directory=DIR + Write the output files to DIR. + --data-file=INFILE Read coverage data for report generation from this + file. Defaults to '.coverage'. [env: COVERAGE_FILE] + -i, --ignore-errors Ignore errors while reading source files. + --include=PAT1,PAT2,... + Include only files whose paths match one of these + patterns. Accepts shell-style wildcards, which must be + quoted. + --omit=PAT1,PAT2,... Omit files whose paths match one of these patterns. + Accepts shell-style wildcards, which must be quoted. + --debug=OPTS Debug options, separated by commas. [env: + COVERAGE_DEBUG] + -h, --help Get help on this command. + --rcfile=RCFILE Specify configuration file. By default '.coveragerc', + 'setup.cfg', 'tox.ini', and 'pyproject.toml' are + tried. [env: COVERAGE_RCFILE] +.. [[[end]]] (sum: /X2PvS3W4k) + +Other common reporting options are described above in :ref:`cmd_reporting`. diff --git a/doc/commands/cmd_combine.rst b/doc/commands/cmd_combine.rst new file mode 100644 index 000000000..3637d7eb0 --- /dev/null +++ b/doc/commands/cmd_combine.rst @@ -0,0 +1,118 @@ +.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt + +.. This file is processed with cog to insert the latest command help into the + docs. If it's out of date, the quality checks will fail. Running "make + prebuild" will bring it up to date. + +.. [[[cog + from cog_helpers import show_configs, show_help +.. ]]] +.. [[[end]]] (sum: 1B2M2Y8Asg) + + +.. _cmd_combine: + +Combining data files: ``coverage combine`` +------------------------------------------ + +Often test suites are run under different conditions, for example, with +different versions of Python, or dependencies, or on different operating +systems. In these cases, you can collect coverage data for each test run, and +then combine all the separate data files into one combined file for reporting. + +The **combine** command reads a number of separate data files, matches the data +by source file name, and writes a combined data file with all of the data. + +Coverage normally writes data to a filed named ".coverage". The ``run +--parallel-mode`` switch (or ``[run] parallel=True`` configuration option) +tells coverage to expand the file name to include machine name, process id, and +a random number so that every data file is distinct:: + + .coverage.Neds-MacBook-Pro.local.88335.316857 + .coverage.Geometer.8044.799674 + +You can also define a new data file name with the ``[run] data_file`` option. + +Once you have created a number of these files, you can copy them all to a +single directory, and use the **combine** command to combine them into one +.coverage data file:: + + $ coverage combine + +You can also name directories or files to be combined on the command line:: + + $ coverage combine data1.dat windows_data_files/ + +Coverage.py will collect the data from those places and combine them. The +current directory isn't searched if you use command-line arguments. If you +also want data from the current directory, name it explicitly on the command +line. + +When coverage.py combines data files, it looks for files named the same as the +data file (defaulting to ".coverage"), with a dotted suffix. Here are some +examples of data files that can be combined:: + + .coverage.machine1 + .coverage.20120807T212300 + .coverage.last_good_run.ok + +An existing combined data file is ignored and re-written. If you want to use +**combine** to accumulate results into the .coverage data file over a number of +runs, use the ``--append`` switch on the **combine** command. This behavior +was the default before version 4.2. + +If any of the data files can't be read, coverage.py will print a warning +indicating the file and the problem. + +The original input data files are deleted once they've been combined. If you +want to keep those files, use the ``--keep`` command-line option. + +.. [[[cog show_help("combine") ]]] +.. code:: + + $ coverage combine --help + Usage: coverage combine [options] ... + + Combine data from multiple coverage files. The combined results are written to + a single file representing the union of the data. The positional arguments are + data files or directories containing data files. If no paths are provided, + data files in the default data file's directory are combined. + + Options: + -a, --append Append data to the data file. Otherwise it starts + clean each time. + --data-file=DATAFILE Base name of the data files to operate on. Defaults to + '.coverage'. [env: COVERAGE_FILE] + --keep Keep original coverage files, otherwise they are + deleted. + -q, --quiet Don't print messages about what is happening. + --debug=OPTS Debug options, separated by commas. [env: + COVERAGE_DEBUG] + -h, --help Get help on this command. + --rcfile=RCFILE Specify configuration file. By default '.coveragerc', + 'setup.cfg', 'tox.ini', and 'pyproject.toml' are + tried. [env: COVERAGE_RCFILE] +.. [[[end]]] (sum: ARyg8KB1fE) + + +.. _cmd_combine_remapping: + +Re-mapping paths +................ + +To combine data for a source file, coverage has to find its data in each of the +data files. Different test runs may run the same source file from different +locations. For example, different operating systems will use different paths +for the same file, or perhaps each Python version is run from a different +subdirectory. Coverage needs to know that different file paths are actually +the same source file for reporting purposes. + +You can tell coverage.py how different source locations relate with a +``[paths]`` section in your configuration file (see :ref:`config_paths`). +It might be more convenient to use the ``[run] relative_files`` +setting to store relative file paths (see :ref:`relative_files +`). + +If data isn't combining properly, you can see details about the inner workings +with ``--debug=pathmap``. diff --git a/doc/commands/cmd_debug.rst b/doc/commands/cmd_debug.rst new file mode 100644 index 000000000..41dd4e1bd --- /dev/null +++ b/doc/commands/cmd_debug.rst @@ -0,0 +1,124 @@ +.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt + +.. This file is processed with cog to insert the latest command help into the + docs. If it's out of date, the quality checks will fail. Running "make + prebuild" will bring it up to date. + +.. [[[cog + from cog_helpers import show_configs, show_help +.. ]]] +.. [[[end]]] (sum: 1B2M2Y8Asg) + + +.. _cmd_debug: + +Diagnostics: ``coverage debug`` +------------------------------- + +The **debug** command shows internal information to help diagnose problems. +If you are reporting a bug about coverage.py, including the output of this +command can often help:: + + $ coverage debug sys > please_attach_to_bug_report.txt + +A few types of information are available: + +* ``config``: show coverage's configuration +* ``sys``: show system configuration +* ``data``: show a summary of the collected coverage data +* ``premain``: show the call stack invoking coverage +* ``pybehave``: show internal flags describing Python behavior + +.. [[[cog show_help("debug") ]]] +.. code:: + + $ coverage debug --help + Usage: coverage debug + + Display information about the internals of coverage.py, for diagnosing + problems. Topics are: 'data' to show a summary of the collected data; 'sys' to + show installation information; 'config' to show the configuration; 'premain' + to show what is calling coverage; 'pybehave' to show internal flags describing + Python behavior. + + Options: + --debug=OPTS Debug options, separated by commas. [env: COVERAGE_DEBUG] + -h, --help Get help on this command. + --rcfile=RCFILE Specify configuration file. By default '.coveragerc', + 'setup.cfg', 'tox.ini', and 'pyproject.toml' are tried. + [env: COVERAGE_RCFILE] +.. [[[end]]] (sum: ybjftkTaNE) + +.. _cmd_run_debug: + +``--debug`` +........... + +The ``--debug`` option is also available on all commands. It instructs +coverage.py to log internal details of its operation, to help with diagnosing +problems. It takes a comma-separated list of options, each indicating a facet +of operation to log: + +* ``callers``: annotate each debug message with a stack trace of the callers + to that point. + +* ``config``: before starting, dump all the :ref:`configuration ` + values. + +* ``dataio``: log when reading or writing any data file. + +* ``dataop``: log a summary of data being added to CoverageData objects. + +* ``dataop2``: when used with ``debug=dataop``, log the actual data being added + to CoverageData objects. + +* ``lock``: log operations acquiring locks in the data layer. + +* ``multiproc``: log the start and stop of multiprocessing processes. + +* ``patch``: log when patches are applied and when they are executed. See + :ref:`config_run_patch`. + +* ``pathmap``: log the remapping of paths that happens during ``coverage + combine``. See :ref:`config_paths`. + +* ``pid``: annotate all warnings and debug output with the process and thread + ids. + +* ``plugin``: print information about plugin operations. + +* ``process``: show process creation information, and changes in the current + directory. This also writes a time stamp and command arguments into the data + file. + +* ``pybehave``: show the values of `internal flags `_ describing the + behavior of the current version of Python. + +* ``pytest``: indicate the name of the current pytest test when it changes. + +* ``self``: annotate each debug message with the object printing the message. + +* ``sql``: log the SQL statements used for recording data. + +* ``sqldata``: when used with ``debug=sql``, also log the full data being used + in SQL statements. + +* ``sys``: before starting, dump all the system and environment information, + as with :ref:`coverage debug sys `. + +* ``trace``: print every decision about whether to trace a file or not. For + files not being traced, the reason is also given. + +.. _env.py: https://github.com/nedbat/coveragepy/blob/master/coverage/env.py + +Debug options can also be set with the ``COVERAGE_DEBUG`` environment variable, +a comma-separated list of these options, or in the :ref:`config_run_debug` +section of the .coveragerc file. + +The debug output goes to stderr, unless the :ref:`config_run_debug_file` +setting or the ``COVERAGE_DEBUG_FILE`` environment variable names a different +file, which will be appended to. This can be useful because many test runners +capture output, which could hide important details. ``COVERAGE_DEBUG_FILE`` +accepts the special names ``stdout`` and ``stderr`` to write to those +destinations. diff --git a/doc/commands/cmd_erase.rst b/doc/commands/cmd_erase.rst new file mode 100644 index 000000000..deb9f59cc --- /dev/null +++ b/doc/commands/cmd_erase.rst @@ -0,0 +1,41 @@ +.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt + +.. This file is processed with cog to insert the latest command help into the + docs. If it's out of date, the quality checks will fail. Running "make + prebuild" will bring it up to date. + +.. [[[cog + from cog_helpers import show_configs, show_help +.. ]]] +.. [[[end]]] (sum: 1B2M2Y8Asg) + + +.. _cmd_erase: + +Erase data: ``coverage erase`` +------------------------------ + +To erase the collected data, use the **erase** command: + +.. [[[cog show_help("erase") ]]] +.. code:: + + $ coverage erase --help + Usage: coverage erase [options] + + Erase previously collected coverage data. + + Options: + --data-file=DATAFILE Base name of the data files to operate on. Defaults to + '.coverage'. [env: COVERAGE_FILE] + --debug=OPTS Debug options, separated by commas. [env: + COVERAGE_DEBUG] + -h, --help Get help on this command. + --rcfile=RCFILE Specify configuration file. By default '.coveragerc', + 'setup.cfg', 'tox.ini', and 'pyproject.toml' are + tried. [env: COVERAGE_RCFILE] +.. [[[end]]] (sum: z+rvZs6NUV) + +If your configuration file indicates parallel data collection, **erase** will +remove all of the data files. diff --git a/doc/commands/cmd_html.rst b/doc/commands/cmd_html.rst new file mode 100644 index 000000000..7dbfb214e --- /dev/null +++ b/doc/commands/cmd_html.rst @@ -0,0 +1,111 @@ +.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt + +.. This file is processed with cog to insert the latest command help into the + docs. If it's out of date, the quality checks will fail. Running "make + prebuild" will bring it up to date. + +.. [[[cog + from cog_helpers import show_configs, show_help +.. ]]] +.. [[[end]]] (sum: 1B2M2Y8Asg) + + +.. _cmd_html: + +HTML reporting: ``coverage html`` +--------------------------------- + +Coverage.py can annotate your source code to show which lines were executed +and which were not. The **html** command creates an HTML report similar to the +**report** summary, but as an HTML file. Each module name links to the source +file decorated to show the status of each line. + +Here's a `sample report`__. + +__ https://nedbatchelder.com/files/sample_coverage_html/index.html + +Lines are highlighted: green for executed, red for missing, and gray for +excluded. If you've used branch coverage, partial branches are yellow. The +colored counts at the top of the file are buttons to turn on and off the +highlighting. + +A number of keyboard shortcuts are available for navigating the report. +Click the keyboard icon in the upper right to see the complete list. + +.. [[[cog show_help("html") ]]] +.. code:: + + $ coverage html --help + Usage: coverage html [options] [modules] + + Create an HTML report of the coverage of the files. Each file gets its own + page, with the source decorated to show executed, excluded, and missed lines. + + Options: + --contexts=REGEX1,REGEX2,... + Only display data from lines covered in the given + contexts. Accepts Python regexes, which must be + quoted. + -d DIR, --directory=DIR + Write the output files to DIR. + --data-file=INFILE Read coverage data for report generation from this + file. Defaults to '.coverage'. [env: COVERAGE_FILE] + --fail-under=MIN Exit with a status of 2 if the total coverage is less + than MIN. + -i, --ignore-errors Ignore errors while reading source files. + --include=PAT1,PAT2,... + Include only files whose paths match one of these + patterns. Accepts shell-style wildcards, which must be + quoted. + --omit=PAT1,PAT2,... Omit files whose paths match one of these patterns. + Accepts shell-style wildcards, which must be quoted. + --precision=N Number of digits after the decimal point to display + for reported coverage percentages. + -q, --quiet Don't print messages about what is happening. + --show-contexts Show contexts for covered lines. + --skip-covered Skip files with 100% coverage. + --no-skip-covered Disable --skip-covered. + --skip-empty Skip files with no code. + --title=TITLE A text string to use as the title on the HTML. + --debug=OPTS Debug options, separated by commas. [env: + COVERAGE_DEBUG] + -h, --help Get help on this command. + --rcfile=RCFILE Specify configuration file. By default '.coveragerc', + 'setup.cfg', 'tox.ini', and 'pyproject.toml' are + tried. [env: COVERAGE_RCFILE] +.. [[[end]]] (sum: DwG6DxRZIf) + +The title of the report can be set with the ``title`` setting in the +``[html]`` section of the configuration file, or the ``--title`` switch on +the command line. + +If you prefer a different style for your HTML report, you can provide your +own CSS file to apply, by specifying a CSS file in the ``[html]`` section of +the configuration file. See :ref:`config_html_extra_css` for details. + +The ``-d`` argument specifies an output directory, defaulting to "htmlcov":: + + $ coverage html -d coverage_html + +Other common reporting options are described above in :ref:`cmd_reporting`. + +Generating the HTML report can be time-consuming. Stored with the HTML report +is a data file that is used to speed up reporting the next time. If you +generate a new report into the same directory, coverage.py will skip +generating unchanged pages, making the process faster. + +The ``--skip-covered`` switch will skip any file with 100% coverage, letting +you focus on the files that still need attention. The ``--skip-empty`` switch +will skip any file with no executable statements. + +The ``--precision`` option controls the number of digits displayed after the +decimal point in coverage percentages, defaulting to none. + +If you have :ref:`recorded contexts `, the ``--contexts`` option lets +you choose which contexts to report on, and the ``--show-contexts`` option will +annotate lines with the contexts that ran them. See :ref:`context_reporting` +for details. + +These options can also be set in your .coveragerc file. See +:ref:`Configuration: [html] `. diff --git a/doc/commands/cmd_json.rst b/doc/commands/cmd_json.rst new file mode 100644 index 000000000..a15a1cd00 --- /dev/null +++ b/doc/commands/cmd_json.rst @@ -0,0 +1,63 @@ +.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt + +.. This file is processed with cog to insert the latest command help into the + docs. If it's out of date, the quality checks will fail. Running "make + prebuild" will bring it up to date. + +.. [[[cog + from cog_helpers import show_configs, show_help +.. ]]] +.. [[[end]]] (sum: 1B2M2Y8Asg) + + +.. _cmd_json: + +JSON reporting: ``coverage json`` +--------------------------------- + +The **json** command writes coverage data to a "coverage.json" file. + +.. [[[cog show_help("json") ]]] +.. code:: + + $ coverage json --help + Usage: coverage json [options] [modules] + + Generate a JSON report of coverage results. + + Options: + --contexts=REGEX1,REGEX2,... + Only display data from lines covered in the given + contexts. Accepts Python regexes, which must be + quoted. + --data-file=INFILE Read coverage data for report generation from this + file. Defaults to '.coverage'. [env: COVERAGE_FILE] + --fail-under=MIN Exit with a status of 2 if the total coverage is less + than MIN. + -i, --ignore-errors Ignore errors while reading source files. + --include=PAT1,PAT2,... + Include only files whose paths match one of these + patterns. Accepts shell-style wildcards, which must be + quoted. + --omit=PAT1,PAT2,... Omit files whose paths match one of these patterns. + Accepts shell-style wildcards, which must be quoted. + -o OUTFILE Write the JSON report to this file. Defaults to + 'coverage.json' + --pretty-print Format the JSON for human readers. + -q, --quiet Don't print messages about what is happening. + --show-contexts Show contexts for covered lines. + --debug=OPTS Debug options, separated by commas. [env: + COVERAGE_DEBUG] + -h, --help Get help on this command. + --rcfile=RCFILE Specify configuration file. By default '.coveragerc', + 'setup.cfg', 'tox.ini', and 'pyproject.toml' are + tried. [env: COVERAGE_RCFILE] +.. [[[end]]] (sum: 5T5gy2XZcc) + +You can specify the name of the output file with the ``-o`` switch. The JSON +can be nicely formatted by specifying the ``--pretty-print`` switch. + +Other common reporting options are described above in :ref:`cmd_reporting`. +These options can also be set in your .coveragerc file. See +:ref:`Configuration: [json] `. diff --git a/doc/commands/cmd_lcov.rst b/doc/commands/cmd_lcov.rst new file mode 100644 index 000000000..610fee5dd --- /dev/null +++ b/doc/commands/cmd_lcov.rst @@ -0,0 +1,55 @@ +.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt + +.. This file is processed with cog to insert the latest command help into the + docs. If it's out of date, the quality checks will fail. Running "make + prebuild" will bring it up to date. + +.. [[[cog + from cog_helpers import show_configs, show_help +.. ]]] +.. [[[end]]] (sum: 1B2M2Y8Asg) + + +.. _cmd_lcov: + +LCOV reporting: ``coverage lcov`` +--------------------------------- + +The **lcov** command writes coverage data to a "coverage.lcov" file. + +.. [[[cog show_help("lcov") ]]] +.. code:: + + $ coverage lcov --help + Usage: coverage lcov [options] [modules] + + Generate an LCOV report of coverage results. + + Options: + --data-file=INFILE Read coverage data for report generation from this + file. Defaults to '.coverage'. [env: COVERAGE_FILE] + --fail-under=MIN Exit with a status of 2 if the total coverage is less + than MIN. + -i, --ignore-errors Ignore errors while reading source files. + --include=PAT1,PAT2,... + Include only files whose paths match one of these + patterns. Accepts shell-style wildcards, which must be + quoted. + -o OUTFILE Write the LCOV report to this file. Defaults to + 'coverage.lcov' + --omit=PAT1,PAT2,... Omit files whose paths match one of these patterns. + Accepts shell-style wildcards, which must be quoted. + -q, --quiet Don't print messages about what is happening. + --debug=OPTS Debug options, separated by commas. [env: + COVERAGE_DEBUG] + -h, --help Get help on this command. + --rcfile=RCFILE Specify configuration file. By default '.coveragerc', + 'setup.cfg', 'tox.ini', and 'pyproject.toml' are + tried. [env: COVERAGE_RCFILE] +.. [[[end]]] (sum: Fqz7roAR0u) + +Common reporting options are described above in :ref:`cmd_reporting`. +Also see :ref:`Configuration: [lcov] `. + +.. versionadded:: 6.3 diff --git a/doc/commands/cmd_report.rst b/doc/commands/cmd_report.rst new file mode 100644 index 000000000..b90301e9e --- /dev/null +++ b/doc/commands/cmd_report.rst @@ -0,0 +1,140 @@ +.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt + +.. This file is processed with cog to insert the latest command help into the + docs. If it's out of date, the quality checks will fail. Running "make + prebuild" will bring it up to date. + +.. [[[cog + from cog_helpers import show_configs, show_help +.. ]]] +.. [[[end]]] (sum: 1B2M2Y8Asg) + + +.. _cmd_report: + +Coverage summary: ``coverage report`` +------------------------------------- + +The simplest reporting is a textual summary produced with **report**:: + + $ coverage report + Name Stmts Miss Cover + --------------------------------------------- + my_program.py 20 4 80% + my_module.py 15 2 86% + my_other_module.py 56 6 89% + --------------------------------------------- + TOTAL 91 12 87% + +For each module executed, the report shows the count of executable statements, +the number of those statements missed, and the resulting coverage, expressed +as a percentage. + +.. [[[cog show_help("report") ]]] +.. code:: + + $ coverage report --help + Usage: coverage report [options] [modules] + + Report coverage statistics on modules. + + Options: + --contexts=REGEX1,REGEX2,... + Only display data from lines covered in the given + contexts. Accepts Python regexes, which must be + quoted. + --data-file=INFILE Read coverage data for report generation from this + file. Defaults to '.coverage'. [env: COVERAGE_FILE] + --fail-under=MIN Exit with a status of 2 if the total coverage is less + than MIN. + --format=FORMAT Output format, either text (default), markdown, or + total. + -i, --ignore-errors Ignore errors while reading source files. + --include=PAT1,PAT2,... + Include only files whose paths match one of these + patterns. Accepts shell-style wildcards, which must be + quoted. + --omit=PAT1,PAT2,... Omit files whose paths match one of these patterns. + Accepts shell-style wildcards, which must be quoted. + --precision=N Number of digits after the decimal point to display + for reported coverage percentages. + --sort=COLUMN Sort the report by the named column: name, stmts, + miss, branch, brpart, or cover. Default is name. + -m, --show-missing Show line numbers of statements in each module that + weren't executed. + --skip-covered Skip files with 100% coverage. + --no-skip-covered Disable --skip-covered. + --skip-empty Skip files with no code. + --debug=OPTS Debug options, separated by commas. [env: + COVERAGE_DEBUG] + -h, --help Get help on this command. + --rcfile=RCFILE Specify configuration file. By default '.coveragerc', + 'setup.cfg', 'tox.ini', and 'pyproject.toml' are + tried. [env: COVERAGE_RCFILE] +.. [[[end]]] (sum: FnJyop2efr) + +The ``-m`` flag also shows the line numbers of missing statements:: + + $ coverage report -m + Name Stmts Miss Cover Missing + ------------------------------------------------------- + my_program.py 20 4 80% 33-35, 39 + my_module.py 15 2 86% 8, 12 + my_other_module.py 56 6 89% 17-23 + ------------------------------------------------------- + TOTAL 91 12 87% + +If you are using branch coverage, then branch statistics will be reported in +the Branch and BrPart (for Partial Branch) columns, the Missing column will +detail the missed branches:: + + $ coverage report -m + Name Stmts Miss Branch BrPart Cover Missing + --------------------------------------------------------------------- + my_program.py 20 4 10 2 80% 33-35, 36->38, 39 + my_module.py 15 2 3 0 86% 8, 12 + my_other_module.py 56 6 5 1 89% 17-23, 40->45 + --------------------------------------------------------------------- + TOTAL 91 12 18 3 87% + +Ranges of lines are shown with a dash: "17-23" means all lines from 17 to 23 +inclusive are missing coverage. Missed branches are shown with an arrow: +"40->45" means the branch from line 40 to line 45 is missing. A branch can go +backwards in a file, so you might see a branch from a later line to an earlier +line, like "55->50". + +You can restrict the report to only certain files by naming them on the +command line:: + + $ coverage report -m my_program.py my_other_module.py + Name Stmts Miss Cover Missing + ------------------------------------------------------- + my_program.py 20 4 80% 33-35, 39 + my_other_module.py 56 6 89% 17-23 + ------------------------------------------------------- + TOTAL 76 10 87% + +The ``--skip-covered`` switch will skip any file with 100% coverage, letting +you focus on the files that still need attention. The ``--no-skip-covered`` +option can be used if needed to see all the files. The ``--skip-empty`` switch +will skip any file with no executable statements. + +If you have :ref:`recorded contexts `, the ``--contexts`` option lets +you choose which contexts to report on. See :ref:`context_reporting` for +details. + +The ``--precision`` option controls the number of digits displayed after the +decimal point in coverage percentages, defaulting to none. + +The ``--sort`` option is the name of a column to sort the report by. + +The ``--format`` option controls the style of the report. ``--format=text`` +creates plain text tables as shown above. ``--format=markdown`` creates +Markdown tables. ``--format=total`` writes out a single number, the total +coverage percentage as shown at the end of the tables, but without a percent +sign. + +Other common reporting options are described above in :ref:`cmd_reporting`. +These options can also be set in your .coveragerc file. See +:ref:`Configuration: [report] `. diff --git a/doc/commands/cmd_reporting.rst b/doc/commands/cmd_reporting.rst new file mode 100644 index 000000000..cd17d6b19 --- /dev/null +++ b/doc/commands/cmd_reporting.rst @@ -0,0 +1,42 @@ +.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt + +.. This file is processed with cog to insert the latest command help into the + docs. If it's out of date, the quality checks will fail. Running "make + prebuild" will bring it up to date. + +.. [[[cog + from cog_helpers import show_configs, show_help +.. ]]] +.. [[[end]]] (sum: 1B2M2Y8Asg) + + +.. _cmd_reporting: + +Reporting +--------- + +Coverage.py provides a few styles of reporting, with the **report**, **html**, +**annotate**, **json**, **lcov**, and **xml** commands. They share a number +of common options. + +The command-line arguments are module or file names to report on, if you'd like +to report on a subset of the data collected. + +The ``--include`` and ``--omit`` flags specify lists of file name patterns. +They control which files to report on, and are described in more detail in +:ref:`source`. + +The ``-i`` or ``--ignore-errors`` switch tells coverage.py to ignore problems +encountered trying to find source files to report on. This can be useful if +some files are missing, or if your Python execution is tricky enough that file +names are synthesized without real source files. + +If you provide a ``--fail-under`` value, the total percentage covered will be +compared to that value. If it is less, the command will exit with a status +code of 2, indicating that the total coverage was less than your target. This +can be used as part of a pass/fail condition, for example in a continuous +integration server. This option isn't available for **annotate**. + +These options can also be set in your .coveragerc file. See +:ref:`Configuration: [report] `. diff --git a/doc/commands/cmd_run.rst b/doc/commands/cmd_run.rst new file mode 100644 index 000000000..d3a2142d9 --- /dev/null +++ b/doc/commands/cmd_run.rst @@ -0,0 +1,290 @@ +.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt + +.. This file is processed with cog to insert the latest command help into the + docs. If it's out of date, the quality checks will fail. Running "make + prebuild" will bring it up to date. + +.. [[[cog + from cog_helpers import show_configs, show_help +.. ]]] +.. [[[end]]] (sum: 1B2M2Y8Asg) + + +.. _cmd_run: + +Execution: ``coverage run`` +--------------------------- + +You collect execution data by running your Python program with the **run** +command:: + + $ coverage run my_program.py arg1 arg2 + blah blah ..your program's output.. blah blah + +Your program runs just as if it had been invoked with the Python command line. +Arguments after your file name are passed to your program as usual in +``sys.argv``. Rather than providing a file name, you can use the ``-m`` switch +and specify an importable module name instead, just as you can with the +Python ``-m`` switch:: + + $ coverage run -m packagename.modulename arg1 arg2 + blah blah ..your program's output.. blah blah + +.. note:: + + In most cases, the program to use here is a test runner, not your program + you are trying to measure. The test runner will run your tests and coverage + will measure the coverage of your code along the way. + +There are many options: + +.. [[[cog show_help("run") ]]] +.. code:: + + $ coverage run --help + Usage: coverage run [options] [program options] + + Run a Python program, measuring code execution. + + Options: + -a, --append Append data to the data file. Otherwise it starts + clean each time. + --branch Measure branch coverage in addition to statement + coverage. + --concurrency=LIBS Properly measure code using a concurrency library. + Valid values are: eventlet, gevent, greenlet, + multiprocessing, thread, or a comma-list of them. + --context=LABEL The context label to record for this coverage run. + --data-file=OUTFILE Write the recorded coverage data to this file. + Defaults to '.coverage'. [env: COVERAGE_FILE] + --include=PAT1,PAT2,... + Include only files whose paths match one of these + patterns. Accepts shell-style wildcards, which must be + quoted. + -m, --module is an importable Python module, not a script + path, to be run as 'python -m' would run it. + --omit=PAT1,PAT2,... Omit files whose paths match one of these patterns. + Accepts shell-style wildcards, which must be quoted. + -L, --pylib Measure coverage even inside the Python installed + library, which isn't done by default. + -p, --parallel-mode Append a unique suffix to the data file name to + collect separate data from multiple processes. + --save-signal=SIGNAL Specify a signal that will trigger coverage to write + its collected data. Supported values are: USR1, USR2. + Not available on Windows. + --source=SRC1,SRC2,... + A list of directories or importable names of code to + measure. + --timid Use the slower Python trace function core. + --debug=OPTS Debug options, separated by commas. [env: + COVERAGE_DEBUG] + -h, --help Get help on this command. + --rcfile=RCFILE Specify configuration file. By default '.coveragerc', + 'setup.cfg', 'tox.ini', and 'pyproject.toml' are + tried. [env: COVERAGE_RCFILE] +.. [[[end]]] (sum: YwMI03MDmQ) + +Many of these options can also be set in the :ref:`config_run` section of your +configuration file. Remember to put options for run after "run", but before +the program invocation:: + + $ coverage run --source=dir1,dir2 my_program.py arg1 arg2 + $ coverage run --source=dir1,dir2 -m packagename.modulename arg1 arg2 + +If you want :ref:`branch coverage ` measurement, use the ``--branch`` +flag. Otherwise only statement coverage is measured. + +You can specify the code to measure with the ``--source``, ``--include``, and +``--omit`` switches. See :ref:`Specifying source files ` for +details of their interpretation. + +.. note:: + + Specifying ``--source`` on the ``coverage run`` command line won't affect + subsequent reporting commands like ``coverage xml``. Use the :ref:`source + ` setting in the configuration file to apply the setting + uniformly to all commands. + +Coverage.py can measure multi-threaded programs by default. If you are using +more other concurrency support, with the `multiprocessing`_, `greenlet`_, +`eventlet`_, or `gevent`_ libraries, then coverage.py can get confused. Use the +``--concurrency`` switch to properly measure programs using these libraries. +Give it a value of ``multiprocessing``, ``thread``, ``greenlet``, ``eventlet``, +or ``gevent``. Values other than ``thread`` require the :ref:`C extension +`. + +You can combine multiple values for ``--concurrency``, separated with commas. +You can specify ``thread`` and also one of ``eventlet``, ``gevent``, or +``greenlet``. + +If you are using ``--concurrency=multiprocessing``, you must set your other +options in the configuration file. Options on the command line will not be +passed to the processes that multiprocessing creates. Best practice is to use +the configuration file for all options. + +.. _multiprocessing: https://docs.python.org/3/library/multiprocessing.html +.. _greenlet: https://greenlet.readthedocs.io/ +.. _gevent: https://www.gevent.org/ +.. _eventlet: https://eventlet.readthedocs.io/ + +If you are measuring coverage in a multi-process program, or across a number of +machines, you'll want the ``--parallel-mode`` switch to keep the data separate +during measurement. See :ref:`cmd_combine` below. + +You can specify a :ref:`static context ` for a coverage run with +``--context``. This can be any label you want, and will be recorded with the +data. See :ref:`contexts` for more information. + +By default, coverage.py does not measure code installed with the Python +interpreter, for example, the standard library. If you want to measure that +code as well as your own, add the ``-L`` (or ``--pylib``) flag. + +If your coverage results seem to be overlooking code that you know has been +executed, try running coverage.py again with the ``--timid`` flag. This uses a +simpler but slower trace method, and might be needed in rare cases. + +If you are specifying ``--save-signal``, please make sure that your program +doesn't intercept this signal. If it does, coverage won't receive the signal +and the data file will not be written. + +.. versionadded:: 7.10 ``--save-signal`` + +Coverage.py sets an environment variable, ``COVERAGE_RUN`` to indicate that +your code is running under coverage measurement. The value is not relevant, +and may change in the future. + + +.. _cmd_warnings: + +Warnings +........ + +During execution, coverage.py may warn you about conditions it detects that +could affect the measurement process. The possible warnings include: + +Couldn't parse Python file XXX (couldnt-parse) + During reporting, a file was thought to be Python, but it couldn't be parsed + as Python. + +Trace function changed, data is likely wrong: XXX (trace-changed) + Coverage measurement depends on a Python setting called the trace function. + Other Python code in your product might change that function, which will + disrupt coverage.py's measurement. This warning indicates that has happened. + The XXX in the message is the new trace function value, which might provide + a clue to the cause. + +Module XXX has no Python source (module-not-python) + You asked coverage.py to measure module XXX, but once it was imported, it + turned out not to have a corresponding .py file. Without a .py file, + coverage.py can't report on missing lines. + +Module XXX was never imported (module-not-imported) + You asked coverage.py to measure module XXX, but it was never imported by + your program. + +No data was collected (no-data-collected) + Coverage.py ran your program, but didn't measure any lines as executed. + This could be because you asked to measure only modules that never ran, + or for other reasons. + + To debug this problem, try using ``run --debug=trace`` to see the tracing + decision made for each file. + +Module XXX was previously imported, but not measured (module-not-measured) + You asked coverage.py to measure module XXX, but it had already been imported + when coverage started. This meant coverage.py couldn't monitor its + execution. + +Already imported a file that will be measured: XXX (already-imported) + File XXX had already been imported when coverage.py started measurement. Your + setting for ``--source`` or ``--include`` indicates that you wanted to + measure that file. Lines will be missing from the coverage report since the + execution during import hadn't been measured. + +\-\-include is ignored because \-\-source is set (include-ignored) + Both ``--include`` and ``--source`` were specified while running code. Both + are meant to focus measurement on a particular part of your source code, so + ``--include`` is ignored in favor of ``--source``. + +Conflicting dynamic contexts (dynamic-conflict) + The ``[run] dynamic_context`` option is set in the configuration file, but + something (probably a test runner plugin) is also calling the + :meth:`.Coverage.switch_context` function to change the context. Only one of + these mechanisms should be in use at a time. + +Couldn't import C tracer (no-ctracer) + The core tracer implemented in C should have been used, but couldn't be + imported. The reason is included in the warning message. The Python tracer + will be used instead. + +sys.monitoring isn't available in this version, using default core (no-sysmon) + You requested to use the sys.monitoring measurement core, but are running on + Python 3.11 or lower where it isn't available. A default core will be used + instead. + +sys.monitoring can't measure branches in this version, using default core (no-sysmon) + You requested the sys.monitoring measurement core and also branch coverage. + This isn't supported until the later alphas of Python 3.14. A default core + will be used instead. + +sys.monitoring doesn't yet support dynamic contexts, using default core (no-sysmon) + You requested the sys.monitoring measurement core and also dynamic contexts. + This isn't supported by coverage.py yet. A default core will be used + instead. + +Individual warnings can be disabled with the :ref:`disable_warnings +` configuration setting. It is a list of the +short parenthetical nicknames in the warning messages. For example, to silence +"No data was collected (no-data-collected)", add this to your configuration +file: + +.. [[[cog + show_configs( + ini=r""" + [run] + disable_warnings = no-data-collected + """, + toml=r""" + [tool.coverage.run] + disable_warnings = ["no-data-collected"] + """, + ) +.. ]]] + +.. tabs:: + + .. code-tab:: ini + :caption: .coveragerc + + [run] + disable_warnings = no-data-collected + + .. code-tab:: toml + :caption: pyproject.toml + + [tool.coverage.run] + disable_warnings = ["no-data-collected"] + + .. code-tab:: ini + :caption: setup.cfg or tox.ini + + [coverage:run] + disable_warnings = no-data-collected + +.. [[[end]]] (sum: SJKFvPoXO2) + + +.. _cmd_datafile: + +Data file +......... + +Coverage.py collects execution data in a file called ".coverage". If need be, +you can set a new file name with the ``COVERAGE_FILE`` environment variable. +This can include a path to another directory. + +By default, each run of your program starts with an empty data set. If you need +to run your program multiple times to get complete data (for example, because +you need to supply different options), you can accumulate data across runs with +the ``--append`` flag on the **run** command. diff --git a/doc/commands/cmd_xml.rst b/doc/commands/cmd_xml.rst new file mode 100644 index 000000000..9d82476e1 --- /dev/null +++ b/doc/commands/cmd_xml.rst @@ -0,0 +1,98 @@ +.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt + +.. This file is processed with cog to insert the latest command help into the + docs. If it's out of date, the quality checks will fail. Running "make + prebuild" will bring it up to date. + +.. [[[cog + from cog_helpers import show_configs, show_help +.. ]]] +.. [[[end]]] (sum: 1B2M2Y8Asg) + + +.. _cmd_xml: + +XML reporting: ``coverage xml`` +------------------------------- + +The **xml** command writes coverage data to a "coverage.xml" file in a format +compatible with `Cobertura`_. + +.. _Cobertura: http://cobertura.github.io/cobertura/ + +.. [[[cog show_help("xml") ]]] +.. code:: + + $ coverage xml --help + Usage: coverage xml [options] [modules] + + Generate an XML report of coverage results. + + Options: + --data-file=INFILE Read coverage data for report generation from this + file. Defaults to '.coverage'. [env: COVERAGE_FILE] + --fail-under=MIN Exit with a status of 2 if the total coverage is less + than MIN. + -i, --ignore-errors Ignore errors while reading source files. + --include=PAT1,PAT2,... + Include only files whose paths match one of these + patterns. Accepts shell-style wildcards, which must be + quoted. + --omit=PAT1,PAT2,... Omit files whose paths match one of these patterns. + Accepts shell-style wildcards, which must be quoted. + -o OUTFILE Write the XML report to this file. Defaults to + 'coverage.xml' + -q, --quiet Don't print messages about what is happening. + --skip-empty Skip files with no code. + --debug=OPTS Debug options, separated by commas. [env: + COVERAGE_DEBUG] + -h, --help Get help on this command. + --rcfile=RCFILE Specify configuration file. By default '.coveragerc', + 'setup.cfg', 'tox.ini', and 'pyproject.toml' are + tried. [env: COVERAGE_RCFILE] +.. [[[end]]] (sum: iyOdiVNL4L) + +You can specify the name of the output file with the ``-o`` switch. + +Other common reporting options are described above in :ref:`cmd_reporting`. + +To include complete file paths in the output file, rather than just +the file name, use [include] vs [source] in your ".coveragerc" file. + +For example, use this: + +.. code:: ini + + [run] + include = + foo/* + bar/* + + +which will result in + +.. code:: xml + + + + + +in place of this: + +.. code:: ini + + [run] + source = + foo + bar + +which may result in + +.. code:: xml + + + + +These options can also be set in your .coveragerc file. See +:ref:`Configuration: [xml] `. diff --git a/doc/commands/index.rst b/doc/commands/index.rst new file mode 100644 index 000000000..0aaca3e8b --- /dev/null +++ b/doc/commands/index.rst @@ -0,0 +1,89 @@ +.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt + +.. _cmd: + +======== +Commands +======== + +.. highlight:: console + +When you install coverage.py, a command-line script called ``coverage`` is +placed on your path. To help with multi-version installs, it will also create +a ``coverage3`` alias, and a ``coverage-X.Y`` alias, depending on the version +of Python you're using. For example, when installing on Python 3.10, you will +be able to use ``coverage``, ``coverage3``, or ``coverage-3.10`` on the command +line. + +Coverage.py has a number of commands: + +* **run** -- :ref:`Run a Python program and collect execution data `. + +* **combine** -- :ref:`Combine together a number of data files `. + +* **erase** -- :ref:`Erase previously collected coverage data `. + +* **report** -- :ref:`Report coverage results `. + +* **html** -- + :ref:`Produce annotated HTML listings with coverage results `. + +* **xml** -- :ref:`Produce an XML report with coverage results `. + +* **json** -- :ref:`Produce a JSON report with coverage results `. + +* **lcov** -- :ref:`Produce an LCOV report with coverage results `. + +* **annotate** -- + :ref:`Annotate source files with coverage results `. + +* **debug** -- :ref:`Get diagnostic information `. + + +Global options +-------------- + +Help is available with the **help** command, or with the ``--help`` switch on +any other command:: + + $ coverage help + $ coverage help run + $ coverage run --help + +Version information for coverage.py can be displayed with +``coverage --version``: + +.. parsed-literal:: + + $ coverage --version + Coverage.py, version |release| with C extension + Documentation at |doc-url| + +Any command can use a configuration file by specifying it with the +``--rcfile=FILE`` command-line switch. Any option you can set on the command +line can also be set in the configuration file. This can be a better way to +control coverage.py since the configuration file can be checked into source +control, and can provide options that other invocation techniques (like test +runner plugins) may not offer. See :ref:`config` for more details. + + +Commands +-------- + +The details of each command are on these pages: + +.. toctree:: + :maxdepth: 1 + + cmd_run + cmd_combine + cmd_erase + cmd_reporting + cmd_report + cmd_html + cmd_xml + cmd_json + cmd_lcov + cmd_annotate + cmd_debug diff --git a/doc/index.rst b/doc/index.rst index fa67acbc7..bb619e702 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -231,7 +231,7 @@ More information install For enterprise - cmd + commands/index config source excluding From 6d8df0c212367cad926f08942cc0a6f73fde1687 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 5 Sep 2025 08:43:04 -0400 Subject: [PATCH 08/29] docs: --debug is common --- doc/commands/cmd_debug.rst | 11 ++++++----- doc/commands/cmd_reporting.rst | 15 ++++++++++----- doc/commands/index.rst | 3 +++ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/doc/commands/cmd_debug.rst b/doc/commands/cmd_debug.rst index 41dd4e1bd..1f2d86452 100644 --- a/doc/commands/cmd_debug.rst +++ b/doc/commands/cmd_debug.rst @@ -50,15 +50,16 @@ A few types of information are available: [env: COVERAGE_RCFILE] .. [[[end]]] (sum: ybjftkTaNE) + .. _cmd_run_debug: -``--debug`` -........... +``--debug`` option +.................. -The ``--debug`` option is also available on all commands. It instructs -coverage.py to log internal details of its operation, to help with diagnosing +The ``--debug`` option is available on all commands. It instructs +coverage.py to log internal details of its operation to help with diagnosing problems. It takes a comma-separated list of options, each indicating a facet -of operation to log: +of activity to log: * ``callers``: annotate each debug message with a stack trace of the callers to that point. diff --git a/doc/commands/cmd_reporting.rst b/doc/commands/cmd_reporting.rst index cd17d6b19..69d533292 100644 --- a/doc/commands/cmd_reporting.rst +++ b/doc/commands/cmd_reporting.rst @@ -16,11 +16,16 @@ Reporting --------- -Coverage.py provides a few styles of reporting, with the **report**, **html**, -**annotate**, **json**, **lcov**, and **xml** commands. They share a number -of common options. - -The command-line arguments are module or file names to report on, if you'd like +Coverage.py provides a few styles of reporting, with the +:ref:`report `, +:ref:`html `, +:ref:`json `, +:ref:`lcov `, +:ref:`xml `, +and :ref:`annotate ` +commands. They share a number of common options. + +The command-line arguments are module or file names to report on if you'd like to report on a subset of the data collected. The ``--include`` and ``--omit`` flags specify lists of file name patterns. diff --git a/doc/commands/index.rst b/doc/commands/index.rst index 0aaca3e8b..f6321ea3d 100644 --- a/doc/commands/index.rst +++ b/doc/commands/index.rst @@ -67,6 +67,9 @@ control coverage.py since the configuration file can be checked into source control, and can provide options that other invocation techniques (like test runner plugins) may not offer. See :ref:`config` for more details. +For diagnosing problems, commands accept a ``--debug`` option. See +:ref:`cmd_run_debug`. + Commands -------- From e32f20c114b03b36a0e4064cdb96276c26416638 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 5 Sep 2025 08:49:56 -0400 Subject: [PATCH 09/29] docs: don't import cog helpers we don't use --- doc/commands/cmd_annotate.rst | 2 +- doc/commands/cmd_combine.rst | 2 +- doc/commands/cmd_debug.rst | 2 +- doc/commands/cmd_erase.rst | 2 +- doc/commands/cmd_html.rst | 2 +- doc/commands/cmd_json.rst | 2 +- doc/commands/cmd_lcov.rst | 2 +- doc/commands/cmd_report.rst | 2 +- doc/commands/cmd_reporting.rst | 2 +- doc/commands/cmd_run.rst | 2 +- doc/commands/cmd_xml.rst | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/doc/commands/cmd_annotate.rst b/doc/commands/cmd_annotate.rst index 549e75e38..ff90d458f 100644 --- a/doc/commands/cmd_annotate.rst +++ b/doc/commands/cmd_annotate.rst @@ -6,7 +6,7 @@ prebuild" will bring it up to date. .. [[[cog - from cog_helpers import show_configs, show_help + from cog_helpers import show_help .. ]]] .. [[[end]]] (sum: 1B2M2Y8Asg) diff --git a/doc/commands/cmd_combine.rst b/doc/commands/cmd_combine.rst index 3637d7eb0..23d2a64fa 100644 --- a/doc/commands/cmd_combine.rst +++ b/doc/commands/cmd_combine.rst @@ -6,7 +6,7 @@ prebuild" will bring it up to date. .. [[[cog - from cog_helpers import show_configs, show_help + from cog_helpers import show_help .. ]]] .. [[[end]]] (sum: 1B2M2Y8Asg) diff --git a/doc/commands/cmd_debug.rst b/doc/commands/cmd_debug.rst index 1f2d86452..8f25c3ae8 100644 --- a/doc/commands/cmd_debug.rst +++ b/doc/commands/cmd_debug.rst @@ -6,7 +6,7 @@ prebuild" will bring it up to date. .. [[[cog - from cog_helpers import show_configs, show_help + from cog_helpers import show_help .. ]]] .. [[[end]]] (sum: 1B2M2Y8Asg) diff --git a/doc/commands/cmd_erase.rst b/doc/commands/cmd_erase.rst index deb9f59cc..232a32b65 100644 --- a/doc/commands/cmd_erase.rst +++ b/doc/commands/cmd_erase.rst @@ -6,7 +6,7 @@ prebuild" will bring it up to date. .. [[[cog - from cog_helpers import show_configs, show_help + from cog_helpers import show_help .. ]]] .. [[[end]]] (sum: 1B2M2Y8Asg) diff --git a/doc/commands/cmd_html.rst b/doc/commands/cmd_html.rst index 7dbfb214e..9bfa8be42 100644 --- a/doc/commands/cmd_html.rst +++ b/doc/commands/cmd_html.rst @@ -6,7 +6,7 @@ prebuild" will bring it up to date. .. [[[cog - from cog_helpers import show_configs, show_help + from cog_helpers import show_help .. ]]] .. [[[end]]] (sum: 1B2M2Y8Asg) diff --git a/doc/commands/cmd_json.rst b/doc/commands/cmd_json.rst index a15a1cd00..31d233edc 100644 --- a/doc/commands/cmd_json.rst +++ b/doc/commands/cmd_json.rst @@ -6,7 +6,7 @@ prebuild" will bring it up to date. .. [[[cog - from cog_helpers import show_configs, show_help + from cog_helpers import show_help .. ]]] .. [[[end]]] (sum: 1B2M2Y8Asg) diff --git a/doc/commands/cmd_lcov.rst b/doc/commands/cmd_lcov.rst index 610fee5dd..166f367c6 100644 --- a/doc/commands/cmd_lcov.rst +++ b/doc/commands/cmd_lcov.rst @@ -6,7 +6,7 @@ prebuild" will bring it up to date. .. [[[cog - from cog_helpers import show_configs, show_help + from cog_helpers import show_help .. ]]] .. [[[end]]] (sum: 1B2M2Y8Asg) diff --git a/doc/commands/cmd_report.rst b/doc/commands/cmd_report.rst index b90301e9e..abea66879 100644 --- a/doc/commands/cmd_report.rst +++ b/doc/commands/cmd_report.rst @@ -6,7 +6,7 @@ prebuild" will bring it up to date. .. [[[cog - from cog_helpers import show_configs, show_help + from cog_helpers import show_help .. ]]] .. [[[end]]] (sum: 1B2M2Y8Asg) diff --git a/doc/commands/cmd_reporting.rst b/doc/commands/cmd_reporting.rst index 69d533292..8f157182c 100644 --- a/doc/commands/cmd_reporting.rst +++ b/doc/commands/cmd_reporting.rst @@ -6,7 +6,7 @@ prebuild" will bring it up to date. .. [[[cog - from cog_helpers import show_configs, show_help + from cog_helpers import show_help .. ]]] .. [[[end]]] (sum: 1B2M2Y8Asg) diff --git a/doc/commands/cmd_run.rst b/doc/commands/cmd_run.rst index d3a2142d9..7b74ab196 100644 --- a/doc/commands/cmd_run.rst +++ b/doc/commands/cmd_run.rst @@ -6,7 +6,7 @@ prebuild" will bring it up to date. .. [[[cog - from cog_helpers import show_configs, show_help + from cog_helpers import show_help .. ]]] .. [[[end]]] (sum: 1B2M2Y8Asg) diff --git a/doc/commands/cmd_xml.rst b/doc/commands/cmd_xml.rst index 9d82476e1..124932b67 100644 --- a/doc/commands/cmd_xml.rst +++ b/doc/commands/cmd_xml.rst @@ -6,7 +6,7 @@ prebuild" will bring it up to date. .. [[[cog - from cog_helpers import show_configs, show_help + from cog_helpers import show_help .. ]]] .. [[[end]]] (sum: 1B2M2Y8Asg) From a9983a67ea5ba594cc61a2a5f371df2f03007c8c Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 5 Sep 2025 08:55:14 -0400 Subject: [PATCH 10/29] docs: split out warnings to their own messages page --- doc/commands/cmd_run.rst | 120 --------------------------------- doc/index.rst | 1 + doc/messages.rst | 142 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 120 deletions(-) create mode 100644 doc/messages.rst diff --git a/doc/commands/cmd_run.rst b/doc/commands/cmd_run.rst index 7b74ab196..5c9a5c44e 100644 --- a/doc/commands/cmd_run.rst +++ b/doc/commands/cmd_run.rst @@ -155,126 +155,6 @@ your code is running under coverage measurement. The value is not relevant, and may change in the future. -.. _cmd_warnings: - -Warnings -........ - -During execution, coverage.py may warn you about conditions it detects that -could affect the measurement process. The possible warnings include: - -Couldn't parse Python file XXX (couldnt-parse) - During reporting, a file was thought to be Python, but it couldn't be parsed - as Python. - -Trace function changed, data is likely wrong: XXX (trace-changed) - Coverage measurement depends on a Python setting called the trace function. - Other Python code in your product might change that function, which will - disrupt coverage.py's measurement. This warning indicates that has happened. - The XXX in the message is the new trace function value, which might provide - a clue to the cause. - -Module XXX has no Python source (module-not-python) - You asked coverage.py to measure module XXX, but once it was imported, it - turned out not to have a corresponding .py file. Without a .py file, - coverage.py can't report on missing lines. - -Module XXX was never imported (module-not-imported) - You asked coverage.py to measure module XXX, but it was never imported by - your program. - -No data was collected (no-data-collected) - Coverage.py ran your program, but didn't measure any lines as executed. - This could be because you asked to measure only modules that never ran, - or for other reasons. - - To debug this problem, try using ``run --debug=trace`` to see the tracing - decision made for each file. - -Module XXX was previously imported, but not measured (module-not-measured) - You asked coverage.py to measure module XXX, but it had already been imported - when coverage started. This meant coverage.py couldn't monitor its - execution. - -Already imported a file that will be measured: XXX (already-imported) - File XXX had already been imported when coverage.py started measurement. Your - setting for ``--source`` or ``--include`` indicates that you wanted to - measure that file. Lines will be missing from the coverage report since the - execution during import hadn't been measured. - -\-\-include is ignored because \-\-source is set (include-ignored) - Both ``--include`` and ``--source`` were specified while running code. Both - are meant to focus measurement on a particular part of your source code, so - ``--include`` is ignored in favor of ``--source``. - -Conflicting dynamic contexts (dynamic-conflict) - The ``[run] dynamic_context`` option is set in the configuration file, but - something (probably a test runner plugin) is also calling the - :meth:`.Coverage.switch_context` function to change the context. Only one of - these mechanisms should be in use at a time. - -Couldn't import C tracer (no-ctracer) - The core tracer implemented in C should have been used, but couldn't be - imported. The reason is included in the warning message. The Python tracer - will be used instead. - -sys.monitoring isn't available in this version, using default core (no-sysmon) - You requested to use the sys.monitoring measurement core, but are running on - Python 3.11 or lower where it isn't available. A default core will be used - instead. - -sys.monitoring can't measure branches in this version, using default core (no-sysmon) - You requested the sys.monitoring measurement core and also branch coverage. - This isn't supported until the later alphas of Python 3.14. A default core - will be used instead. - -sys.monitoring doesn't yet support dynamic contexts, using default core (no-sysmon) - You requested the sys.monitoring measurement core and also dynamic contexts. - This isn't supported by coverage.py yet. A default core will be used - instead. - -Individual warnings can be disabled with the :ref:`disable_warnings -` configuration setting. It is a list of the -short parenthetical nicknames in the warning messages. For example, to silence -"No data was collected (no-data-collected)", add this to your configuration -file: - -.. [[[cog - show_configs( - ini=r""" - [run] - disable_warnings = no-data-collected - """, - toml=r""" - [tool.coverage.run] - disable_warnings = ["no-data-collected"] - """, - ) -.. ]]] - -.. tabs:: - - .. code-tab:: ini - :caption: .coveragerc - - [run] - disable_warnings = no-data-collected - - .. code-tab:: toml - :caption: pyproject.toml - - [tool.coverage.run] - disable_warnings = ["no-data-collected"] - - .. code-tab:: ini - :caption: setup.cfg or tox.ini - - [coverage:run] - disable_warnings = no-data-collected - -.. [[[end]]] (sum: SJKFvPoXO2) - - .. _cmd_datafile: Data file diff --git a/doc/index.rst b/doc/index.rst index bb619e702..32b25403f 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -233,6 +233,7 @@ More information For enterprise commands/index config + messages source excluding branch diff --git a/doc/messages.rst b/doc/messages.rst new file mode 100644 index 000000000..f097e7f48 --- /dev/null +++ b/doc/messages.rst @@ -0,0 +1,142 @@ +.. Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 +.. For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt + +.. This file is processed with cog to insert the latest command help into the + docs. If it's out of date, the quality checks will fail. Running "make + prebuild" will bring it up to date. + +.. [[[cog + from cog_helpers import show_configs +.. ]]] +.. [[[end]]] (sum: 1B2M2Y8Asg) + +.. _messages: + +======== +Messages +======== + +Coverage.py has a number of messages for conditions that could affect +measurement or reporting. + + +.. _cmd_warnings: +.. _warnings: + +Warnings +........ + +Warnings are issued for possible problems but don't stop the measurement or +reporting. See below for the details of each warning, and how to suppress +warnings you don't need to see. + +Couldn't parse Python file XXX (couldnt-parse) + During reporting, a file was thought to be Python, but it couldn't be parsed + as Python. + +Trace function changed, data is likely wrong: XXX (trace-changed) + Coverage measurement depends on a Python setting called the trace function. + Other Python code in your product might change that function, which will + disrupt coverage.py's measurement. This warning indicates that has happened. + The XXX in the message is the new trace function value, which might provide + a clue to the cause. + +Module XXX has no Python source (module-not-python) + You asked coverage.py to measure module XXX, but once it was imported, it + turned out not to have a corresponding .py file. Without a .py file, + coverage.py can't report on missing lines. + +Module XXX was never imported (module-not-imported) + You asked coverage.py to measure module XXX, but it was never imported by + your program. + +No data was collected (no-data-collected) + Coverage.py ran your program, but didn't measure any lines as executed. + This could be because you asked to measure only modules that never ran, + or for other reasons. + + To debug this problem, try using ``run --debug=trace`` to see the tracing + decision made for each file. + +Module XXX was previously imported, but not measured (module-not-measured) + You asked coverage.py to measure module XXX, but it had already been imported + when coverage started. This meant coverage.py couldn't monitor its + execution. + +Already imported a file that will be measured: XXX (already-imported) + File XXX had already been imported when coverage.py started measurement. Your + setting for ``--source`` or ``--include`` indicates that you wanted to + measure that file. Lines will be missing from the coverage report since the + execution during import hadn't been measured. + +\-\-include is ignored because \-\-source is set (include-ignored) + Both ``--include`` and ``--source`` were specified while running code. Both + are meant to focus measurement on a particular part of your source code, so + ``--include`` is ignored in favor of ``--source``. + +Conflicting dynamic contexts (dynamic-conflict) + The ``[run] dynamic_context`` option is set in the configuration file, but + something (probably a test runner plugin) is also calling the + :meth:`.Coverage.switch_context` function to change the context. Only one of + these mechanisms should be in use at a time. + +Couldn't import C tracer (no-ctracer) + The core tracer implemented in C should have been used, but couldn't be + imported. The reason is included in the warning message. The Python tracer + will be used instead. + +sys.monitoring isn't available in this version, using default core (no-sysmon) + You requested to use the sys.monitoring measurement core, but are running on + Python 3.11 or lower where it isn't available. A default core will be used + instead. + +sys.monitoring can't measure branches in this version, using default core (no-sysmon) + You requested the sys.monitoring measurement core and also branch coverage. + This isn't supported until the later alphas of Python 3.14. A default core + will be used instead. + +sys.monitoring doesn't yet support dynamic contexts, using default core (no-sysmon) + You requested the sys.monitoring measurement core and also dynamic contexts. + This isn't supported by coverage.py yet. A default core will be used + instead. + +Individual warnings can be disabled with the :ref:`disable_warnings +` configuration setting. It is a list of the +short parenthetical nicknames in the warning messages. For example, to silence +"No data was collected (no-data-collected)", add this to your configuration +file: + +.. [[[cog + show_configs( + ini=r""" + [run] + disable_warnings = no-data-collected + """, + toml=r""" + [tool.coverage.run] + disable_warnings = ["no-data-collected"] + """, + ) +.. ]]] + +.. tabs:: + + .. code-tab:: ini + :caption: .coveragerc + + [run] + disable_warnings = no-data-collected + + .. code-tab:: toml + :caption: pyproject.toml + + [tool.coverage.run] + disable_warnings = ["no-data-collected"] + + .. code-tab:: ini + :caption: setup.cfg or tox.ini + + [coverage:run] + disable_warnings = no-data-collected + +.. [[[end]]] (sum: SJKFvPoXO2) From 1737cac80cf92bd92e9ce06d83f10c792239fbd1 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 5 Sep 2025 13:31:38 -0400 Subject: [PATCH 11/29] docs: warnings now link to docs about the warning --- coverage/control.py | 8 +++++++- doc/messages.rst | 28 +++++++++++++++++++++++++++- tests/helpers.py | 15 +++++++++++---- tests/test_api.py | 30 ++++++++++++++++++++++++++++-- tests/test_process.py | 3 ++- 5 files changed, 75 insertions(+), 9 deletions(-) diff --git a/coverage/control.py b/coverage/control.py index 791c52505..e12f5896c 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -470,6 +470,8 @@ def _warn(self, msg: str, slug: str | None = None, once: bool = False) -> None: slug.) """ + import coverage as covmod + if not self._no_warn_slugs: self._no_warn_slugs = set(self.config.disable_warnings) @@ -479,7 +481,11 @@ def _warn(self, msg: str, slug: str | None = None, once: bool = False) -> None: self._warnings.append(msg) if slug: - msg = f"{msg} ({slug})" + url = ( + f"https://coverage.readthedocs.io/en/{covmod.__version__}" + + f"/messages.html#warning-{slug}" + ) + msg = f"{msg} ({slug}); see {url}" if self._debug.should("pid"): msg = f"[{os.getpid()}] {msg}" warnings.warn(msg, category=CoverageWarning, stacklevel=2) diff --git a/doc/messages.rst b/doc/messages.rst index f097e7f48..77d2cf99b 100644 --- a/doc/messages.rst +++ b/doc/messages.rst @@ -24,16 +24,20 @@ measurement or reporting. .. _warnings: Warnings -........ +-------- Warnings are issued for possible problems but don't stop the measurement or reporting. See below for the details of each warning, and how to suppress warnings you don't need to see. +.. _warning_couldnt_parse: + Couldn't parse Python file XXX (couldnt-parse) During reporting, a file was thought to be Python, but it couldn't be parsed as Python. +.. _warning_trace_changed: + Trace function changed, data is likely wrong: XXX (trace-changed) Coverage measurement depends on a Python setting called the trace function. Other Python code in your product might change that function, which will @@ -41,15 +45,21 @@ Trace function changed, data is likely wrong: XXX (trace-changed) The XXX in the message is the new trace function value, which might provide a clue to the cause. +.. _warning_module_not_python: + Module XXX has no Python source (module-not-python) You asked coverage.py to measure module XXX, but once it was imported, it turned out not to have a corresponding .py file. Without a .py file, coverage.py can't report on missing lines. +.. _warning_module_not_imported: + Module XXX was never imported (module-not-imported) You asked coverage.py to measure module XXX, but it was never imported by your program. +.. _warning_no_data_collected: + No data was collected (no-data-collected) Coverage.py ran your program, but didn't measure any lines as executed. This could be because you asked to measure only modules that never ran, @@ -58,33 +68,45 @@ No data was collected (no-data-collected) To debug this problem, try using ``run --debug=trace`` to see the tracing decision made for each file. +.. _warning_module_not_measured: + Module XXX was previously imported, but not measured (module-not-measured) You asked coverage.py to measure module XXX, but it had already been imported when coverage started. This meant coverage.py couldn't monitor its execution. +.. _warning_already_imported: + Already imported a file that will be measured: XXX (already-imported) File XXX had already been imported when coverage.py started measurement. Your setting for ``--source`` or ``--include`` indicates that you wanted to measure that file. Lines will be missing from the coverage report since the execution during import hadn't been measured. +.. _warning_include_ignored: + \-\-include is ignored because \-\-source is set (include-ignored) Both ``--include`` and ``--source`` were specified while running code. Both are meant to focus measurement on a particular part of your source code, so ``--include`` is ignored in favor of ``--source``. +.. _warning_dynamic_conflict: + Conflicting dynamic contexts (dynamic-conflict) The ``[run] dynamic_context`` option is set in the configuration file, but something (probably a test runner plugin) is also calling the :meth:`.Coverage.switch_context` function to change the context. Only one of these mechanisms should be in use at a time. +.. _warning_no_ctracer: + Couldn't import C tracer (no-ctracer) The core tracer implemented in C should have been used, but couldn't be imported. The reason is included in the warning message. The Python tracer will be used instead. +.. _warning_no_sysmon: + sys.monitoring isn't available in this version, using default core (no-sysmon) You requested to use the sys.monitoring measurement core, but are running on Python 3.11 or lower where it isn't available. A default core will be used @@ -100,6 +122,10 @@ sys.monitoring doesn't yet support dynamic contexts, using default core (no-sysm This isn't supported by coverage.py yet. A default core will be used instead. + +Disabling warnings +------------------ + Individual warnings can be disabled with the :ref:`disable_warnings ` configuration setting. It is a list of the short parenthetical nicknames in the warning messages. For example, to silence diff --git a/tests/helpers.py b/tests/helpers.py index 29291a10d..27ba296d4 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -313,6 +313,13 @@ def assert_count_equal( assert collections.Counter(list(a)) == collections.Counter(list(b)) +def get_coverage_warnings(warns: Iterable[warnings.WarningMessage]) -> list[str]: + """Extract the text of CoverageWarnings.""" + warns = [w for w in warns if issubclass(w.category, CoverageWarning)] + texts = [cast(Warning, w.message).args[0] for w in warns] + return texts + + def assert_coverage_warnings( warns: Iterable[warnings.WarningMessage], *msgs: str | re.Pattern[str], @@ -323,15 +330,15 @@ def assert_coverage_warnings( Each msg can be a string compared for equality, or a compiled regex used to search the text. """ + actuals = get_coverage_warnings(warns) assert msgs # don't call this without some messages. - warns = [w for w in warns if issubclass(w.category, CoverageWarning)] - actuals = [cast(Warning, w.message).args[0] for w in warns] assert len(msgs) == len(actuals) - for expected, actual in zip(msgs, actuals): + for actual, expected in zip(actuals, msgs): if hasattr(expected, "search"): assert expected.search(actual), f"{actual!r} didn't match {expected!r}" else: - assert expected == actual + actual = actual.partition("; see ")[0] + assert actual == expected @contextlib.contextmanager diff --git a/tests/test_api.py b/tests/test_api.py index 9c6816158..6a13390a7 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -30,8 +30,14 @@ from tests import testenv from tests.coveragetest import CoverageTest, TESTS_DIR, UsingModulesMixin -from tests.helpers import assert_count_equal, assert_coverage_warnings -from tests.helpers import change_dir, nice_file, os_sep +from tests.helpers import ( + assert_count_equal, + assert_coverage_warnings, + change_dir, + get_coverage_warnings, + nice_file, + os_sep, +) BAD_SQLITE_REGEX = r"file( is encrypted or)? is not a database" @@ -619,6 +625,26 @@ def test_warn_once(self) -> None: assert_coverage_warnings(warns, "Warning, warning 1! (bot)") # No "Warning, warning 2!" in warns + assert len(warns) == 1 + + def test_warnings_with_urls(self) -> None: + with pytest.warns(Warning) as warns: + cov = coverage.Coverage() + cov.load() + cov._warn("Warning Will Robinson", slug="will-rob") + cov._warn("Warning, warning 2!", slug="second-one") + warnings = get_coverage_warnings(warns) + + def url(slug: str) -> str: + return ( + f"https://coverage.readthedocs.io/en/{coverage.__version__}" + + f"/messages.html#warning-{slug}" + ) + + assert warnings == [ + f"Warning Will Robinson (will-rob); see {url('will-rob')}", + f"Warning, warning 2! (second-one); see {url('second-one')}", + ] def test_source_and_include_dont_conflict(self) -> None: # A bad fix made this case fail: https://github.com/nedbat/coveragepy/issues/541 diff --git a/tests/test_process.py b/tests/test_process.py index 74bc2f50e..e7e0fa6b2 100644 --- a/tests/test_process.py +++ b/tests/test_process.py @@ -762,13 +762,14 @@ def foo(): # Remove the file location and source line from the warning. out = re.sub(r"(?m)^[\\/\w.:~_-]+:\d+: CoverageWarning: ", "f:d: CoverageWarning: ", out) out = re.sub(r"(?m)^\s+self.warn.*$\n", "", out) + out = re.sub(r"; see https://.*$", "", out) expected = ( "Run 1\n" + "Run 2\n" + "f:d: CoverageWarning: Module foo was previously imported, but not measured " + "(module-not-measured)\n" ) - assert expected == out + assert out == expected def test_module_name(self) -> None: # https://github.com/nedbat/coveragepy/issues/478 From c18ec8e7c510e253becab720d3eed328f03adbef Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 5 Sep 2025 16:58:27 -0400 Subject: [PATCH 12/29] test: somehow unclosed db warnings are in the list sometimes? --- tests/test_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_api.py b/tests/test_api.py index 6a13390a7..66890280a 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -625,7 +625,7 @@ def test_warn_once(self) -> None: assert_coverage_warnings(warns, "Warning, warning 1! (bot)") # No "Warning, warning 2!" in warns - assert len(warns) == 1 + assert len(get_coverage_warnings(warns)) == 1 def test_warnings_with_urls(self) -> None: with pytest.warns(Warning) as warns: From 0dd48b6d81e4393a5262799cdcdf8da8a5d7b31f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 7 Sep 2025 08:54:33 -0400 Subject: [PATCH 13/29] chore: bump the action-dependencies group with 4 updates (#2045) Bumps the action-dependencies group with 4 updates: [github/codeql-action](https://github.com/github/codeql-action), [actions/setup-python](https://github.com/actions/setup-python), [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) and [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv). Updates `github/codeql-action` from 3.29.11 to 3.30.1 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/3c3833e0f8c1c83d449a7478aa59c036a9165498...f1f6e5f6af878fb37288ce1c627459e94dbf7d01) Updates `actions/setup-python` from 5.6.0 to 6.0.0 - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/a26af69be951a213d495a4c3e4e4022e16d87065...e797f83bcb11b83ae66e0230d6156d7c80228e7c) Updates `pypa/gh-action-pypi-publish` from 1.12.4 to 1.13.0 - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/76f52bc884231f62b9a034ebfe128415bbaabdfc...ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e) Updates `astral-sh/setup-uv` from 6.6.0 to 6.6.1 - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](https://github.com/astral-sh/setup-uv/compare/4959332f0f014c5280e7eac8b70c90cb574c9f9b...557e51de59eb14aaaba2ed9621916900a91d50c6) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.30.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: action-dependencies - dependency-name: actions/setup-python dependency-version: 6.0.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: action-dependencies - dependency-name: pypa/gh-action-pypi-publish dependency-version: 1.13.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: action-dependencies - dependency-name: astral-sh/setup-uv dependency-version: 6.6.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: action-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/coverage.yml | 4 ++-- .github/workflows/kit.yml | 4 ++-- .github/workflows/publish.yml | 4 ++-- .github/workflows/quality.yml | 8 ++++---- .github/workflows/testsuite.yml | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 0c0d2bd8c..cd7580661 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -51,7 +51,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3 + uses: github/codeql-action/init@f1f6e5f6af878fb37288ce1c627459e94dbf7d01 # v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -62,7 +62,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3 + uses: github/codeql-action/autobuild@f1f6e5f6af878fb37288ce1c627459e94dbf7d01 # v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -76,4 +76,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3 + uses: github/codeql-action/analyze@f1f6e5f6af878fb37288ce1c627459e94dbf7d01 # v3 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 2841c55d7..3b308e4a9 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -128,7 +128,7 @@ jobs: persist-credentials: false - name: "Set up Python" - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: "${{ matrix.python-version }}" allow-prereleases: true @@ -189,7 +189,7 @@ jobs: persist-credentials: false - name: "Set up Python" - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: "3.9" # Minimum of PYVERSIONS # At a certain point, installing dependencies failed on pypy 3.9 and diff --git a/.github/workflows/kit.yml b/.github/workflows/kit.yml index a271fb1c6..0532e1e14 100644 --- a/.github/workflows/kit.yml +++ b/.github/workflows/kit.yml @@ -172,7 +172,7 @@ jobs: persist-credentials: false - name: "Install Python" - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: "${{ matrix.minpy || '3.11' }}" # PYVERSIONS needed by cibuildwheel cache: pip @@ -229,7 +229,7 @@ jobs: persist-credentials: false - name: "Install Python" - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: "3.11" # PYVERSIONS: the kit-building version cache: pip diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0d071697f..5334a79b6 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -86,7 +86,7 @@ jobs: subject-path: "dist/*" - name: "Publish dists to Test PyPI" - uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 + uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 with: repository-url: https://test.pypi.org/legacy/ @@ -126,4 +126,4 @@ jobs: subject-path: "dist/*" - name: "Publish dists to PyPI" - uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 + uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 94e4243e0..74e25c6ae 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -78,7 +78,7 @@ jobs: persist-credentials: false - name: "Install Python" - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: "3.10" # Minimum of PYVERSIONS cache: pip @@ -106,7 +106,7 @@ jobs: persist-credentials: false - name: "Install Python" - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: "3.9" # Minimum of PYVERSIONS cache: pip @@ -134,7 +134,7 @@ jobs: persist-credentials: false - name: "Install Python" - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: "3.11" # Doc version from PYVERSIONS cache: pip @@ -173,7 +173,7 @@ jobs: persist-credentials: false - name: Install the latest version of uv - uses: astral-sh/setup-uv@4959332f0f014c5280e7eac8b70c90cb574c9f9b #v6.6.0 + uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 #v6.6.1 with: enable-cache: false diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index b53364ecc..0fe438f83 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -114,7 +114,7 @@ jobs: persist-credentials: false - name: "Set up Python" - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 with: python-version: "${{ matrix.python-version }}" allow-prereleases: true From 32785ddb267d4fbe7769bf3600295dfdf453118d Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 7 Sep 2025 16:44:00 -0400 Subject: [PATCH 14/29] refactor: use __url__ and don't make a big production of it --- coverage/control.py | 9 ++------- coverage/version.py | 16 +--------------- tests/test_version.py | 12 +----------- 3 files changed, 4 insertions(+), 33 deletions(-) diff --git a/coverage/control.py b/coverage/control.py index e12f5896c..608abb632 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -67,6 +67,7 @@ TLineNo, TMorf, ) +from coverage.version import __url__ from coverage.xmlreport import XmlReporter os = isolate_module(os) @@ -470,8 +471,6 @@ def _warn(self, msg: str, slug: str | None = None, once: bool = False) -> None: slug.) """ - import coverage as covmod - if not self._no_warn_slugs: self._no_warn_slugs = set(self.config.disable_warnings) @@ -481,11 +480,7 @@ def _warn(self, msg: str, slug: str | None = None, once: bool = False) -> None: self._warnings.append(msg) if slug: - url = ( - f"https://coverage.readthedocs.io/en/{covmod.__version__}" - + f"/messages.html#warning-{slug}" - ) - msg = f"{msg} ({slug}); see {url}" + msg = f"{msg} ({slug}); see {__url__}/messages.html#warning-{slug}" if self._debug.should("pid"): msg = f"[{os.getpid()}] {msg}" warnings.warn(msg, category=CoverageWarning, stacklevel=2) diff --git a/coverage/version.py b/coverage/version.py index 5d309fbb0..3d5d37a00 100644 --- a/coverage/version.py +++ b/coverage/version.py @@ -31,19 +31,5 @@ def _make_version( return version -def _make_url( - major: int, - minor: int, - micro: int, - releaselevel: str, - serial: int = 0, - dev: int = 0, -) -> str: - """Make the URL people should start at for this version of coverage.py.""" - return "https://coverage.readthedocs.io/en/" + _make_version( - major, minor, micro, releaselevel, serial, dev - ) - - __version__ = _make_version(*version_info, _dev) -__url__ = _make_url(*version_info, _dev) +__url__ = f"https://coverage.readthedocs.io/en/{__version__}" diff --git a/tests/test_version.py b/tests/test_version.py index ebfea4a40..e38e36a66 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -6,7 +6,7 @@ from __future__ import annotations import coverage -from coverage.version import _make_url, _make_version +from coverage.version import _make_version from tests.coveragetest import CoverageTest @@ -31,13 +31,3 @@ def test_make_version(self) -> None: assert _make_version(4, 1, 2) == "4.1.2" assert _make_version(5, 10, 2, "candidate", 7) == "5.10.2rc7" assert _make_version(5, 10, 2, "candidate", 7, 3) == "5.10.2rc7.dev3" - - def test_make_url(self) -> None: - expected = "https://coverage.readthedocs.io/en/4.1.2" - assert _make_url(4, 1, 2, "final") == expected - expected = "https://coverage.readthedocs.io/en/4.1.2b3" - assert _make_url(4, 1, 2, "beta", 3) == expected - expected = "https://coverage.readthedocs.io/en/4.1.2b3.dev17" - assert _make_url(4, 1, 2, "beta", 3, 17) == expected - expected = "https://coverage.readthedocs.io/en/4.1.2.dev17" - assert _make_url(4, 1, 2, "final", 0, 17) == expected From dfdef057915e573d9090530cab38b801e72ca36d Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 7 Sep 2025 16:45:41 -0400 Subject: [PATCH 15/29] docs: provide an explanation for No Source. #1921 --- CHANGES.rst | 6 +++++- coverage/cmdline.py | 2 ++ coverage/exceptions.py | 6 +++++- coverage/python.py | 2 +- doc/messages.rst | 25 +++++++++++++++++++++++++ tests/test_process.py | 6 +++++- 6 files changed, 43 insertions(+), 4 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 6f347a16d..9da0e6fcc 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -23,7 +23,11 @@ upgrading your version of coverage.py. Unreleased ---------- -Nothing yet. +- Most warnings and one error now have a link to a page in the docs explaining + the specific message. Closes `issue 1921`_. + +.. _issue 1921: https://github.com/nedbat/coveragepy/issues/1921 + .. start-releases diff --git a/coverage/cmdline.py b/coverage/cmdline.py index fc4da70c2..c45c0078e 100644 --- a/coverage/cmdline.py +++ b/coverage/cmdline.py @@ -1142,6 +1142,8 @@ def main(argv: list[str] | None = None) -> int | None: except _BaseCoverageException as err: # A controlled error inside coverage.py: print the message to the user. msg = err.args[0] + if err.slug: + msg = f"{msg.rstrip('.')}; see {__url__}/messages.html#error-{err.slug}" print(msg) status = ERR except SystemExit as err: diff --git a/coverage/exceptions.py b/coverage/exceptions.py index 27c823686..f9d1ef33a 100644 --- a/coverage/exceptions.py +++ b/coverage/exceptions.py @@ -5,11 +5,15 @@ from __future__ import annotations +from typing import Any + class _BaseCoverageException(Exception): """The base-base of all Coverage exceptions.""" - pass + def __init__(self, *args: Any, slug: str | None = None) -> None: + super().__init__(*args) + self.slug = slug class CoverageException(_BaseCoverageException): diff --git a/coverage/python.py b/coverage/python.py index cbc23e25f..f91f59cfd 100644 --- a/coverage/python.py +++ b/coverage/python.py @@ -61,7 +61,7 @@ def get_python_source(filename: str) -> str: break else: # Couldn't find source. - raise NoSource(f"No source for code: '{filename}'.") + raise NoSource(f"No source for code: '{filename}'.", slug="no-source") # Replace \f because of http://bugs.python.org/issue19035 source_bytes = source_bytes.replace(b"\f", b" ") diff --git a/doc/messages.rst b/doc/messages.rst index 77d2cf99b..f9ad28b12 100644 --- a/doc/messages.rst +++ b/doc/messages.rst @@ -20,6 +20,31 @@ Coverage.py has a number of messages for conditions that could affect measurement or reporting. +.. _errors: + +Errors +------ + +.. _error_no_source: + +No source for code: 'filename.py' + A source file was traced during execution, but was not found when trying to + produce a report. Often this is due to libraries creating temporary source + files which are deleted after execution. + + You can add :ref:`configuration settings ` to avoid the error: + + - Use ":ref:`[run] source=. `" to prevent measurement of + code outside of your project. + + - Use ":ref:`[report] omit=$TMPDIR/* `" to explicitly + skip reporting the temporary directory where the files were created. The + appropriate environment variable will depend on your system. + + - Use ":ref:`[report] ignore_errors = true `" + to treat the error as a warning. + + .. _cmd_warnings: .. _warnings: diff --git a/tests/test_process.py b/tests/test_process.py index e7e0fa6b2..123ff68ad 100644 --- a/tests/test_process.py +++ b/tests/test_process.py @@ -322,7 +322,11 @@ def test_missing_source_file(self) -> None: self.run_command("coverage run fleeting") os.remove("fleeting") out = self.run_command("coverage html -d htmlcov", status=1) - assert re.search("No source for code: '.*fleeting'", out) + assert re.search(r"No source for code: '.*fleeting'", out) + assert re.search( + r"; see https://coverage.readthedocs.io/en/[^/]+/messages.html#error-no-source", + out, + ) assert "Traceback" not in out def test_running_missing_file(self) -> None: From 8ee107cee7501d2c9ff3258b4532bdfd98c0ee15 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 7 Sep 2025 17:12:54 -0400 Subject: [PATCH 16/29] build: a 'qual' marker for tox --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 3224d624a..47c49c1e8 100644 --- a/tox.ini +++ b/tox.ini @@ -7,6 +7,7 @@ envlist = py3{9-14}, py3{13-14}t, pypy3, anypy, doc, lint, mypy labels = py = py3{9-14}, py3{13-14}t, pypy3 + qual = lint, mypy skip_missing_interpreters = {env:COVERAGE_SKIP_MISSING_INTERPRETERS:True} toxworkdir = {env:TOXWORKDIR:.tox} From 01a71f05dcc6cf4385691dd9d4410831cd8bb078 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 7 Sep 2025 17:13:15 -0400 Subject: [PATCH 17/29] docs: one more error gets an explanation --- CHANGES.rst | 4 ++-- coverage/sqldata.py | 8 ++++++-- doc/messages.rst | 8 ++++++++ 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 9da0e6fcc..8df701fc1 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -23,8 +23,8 @@ upgrading your version of coverage.py. Unreleased ---------- -- Most warnings and one error now have a link to a page in the docs explaining - the specific message. Closes `issue 1921`_. +- Most warnings and a few errors now have links to a page in the docs + explaining the specific message. Closes `issue 1921`_. .. _issue 1921: https://github.com/nedbat/coveragepy/issues/1921 diff --git a/coverage/sqldata.py b/coverage/sqldata.py index 693b6e14c..3c958f405 100644 --- a/coverage/sqldata.py +++ b/coverage/sqldata.py @@ -706,9 +706,13 @@ def update( ) ) if self._has_lines and other_data._has_arcs: - raise DataError("Can't combine branch coverage data with statement data") + raise DataError( + "Can't combine branch coverage data with statement data", slug="cant-combine" + ) if self._has_arcs and other_data._has_lines: - raise DataError("Can't combine statement coverage data with branch data") + raise DataError( + "Can't combine statement coverage data with branch data", slug="cant-combine" + ) map_path = map_path or (lambda p: p) diff --git a/doc/messages.rst b/doc/messages.rst index f9ad28b12..b90aa951b 100644 --- a/doc/messages.rst +++ b/doc/messages.rst @@ -44,6 +44,14 @@ No source for code: 'filename.py' - Use ":ref:`[report] ignore_errors = true `" to treat the error as a warning. +.. _error_cant_combine: + +Can't combine (branch or statement) coverage data with (statement or branch) data + You have some data files that measured branch coverage and some data files + that didn't. They cannot be combined because the two types of data are + incompatible. You'll need to ensure that all of your data files are + collected with the same settings. + .. _cmd_warnings: .. _warnings: From 2b003ee7b2ae778626b10e90c4b32135526a1a69 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Tue, 9 Sep 2025 07:23:27 -0400 Subject: [PATCH 18/29] chore: make upgrade --- .pre-commit-config.yaml | 2 +- doc/requirements.pip | 2 +- requirements/dev.pip | 18 +++++++++--------- requirements/kit.pip | 10 +++++----- requirements/light-threads.pip | 10 ++++++---- requirements/mypy.pip | 4 ++-- requirements/pytest.pip | 4 ++-- requirements/tox.pip | 2 +- 8 files changed, 27 insertions(+), 25 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3c7cd7db2..4e44ebfe2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: exclude: "stress_phystoken|\\.py,cover$" - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.12.11 + rev: v0.12.12 hooks: - id: ruff-format diff --git a/doc/requirements.pip b/doc/requirements.pip index 7942b2d40..96c52ef16 100644 --- a/doc/requirements.pip +++ b/doc/requirements.pip @@ -66,7 +66,7 @@ pygments==2.19.2 # via # doc8 # sphinx -regex==2025.7.34 +regex==2025.9.1 # via sphinx-lint requests==2.32.5 # via diff --git a/requirements/dev.pip b/requirements/dev.pip index b4cc26a0f..100ff8f68 100644 --- a/requirements/dev.pip +++ b/requirements/dev.pip @@ -14,7 +14,7 @@ cachetools==6.2.0 # via tox certifi==2025.8.3 # via requests -cffi==1.17.1 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_python_implementation != 'PyPy' and sys_platform == 'linux' +cffi==2.0.0 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_python_implementation != 'PyPy' and sys_platform == 'linux' # via cryptography chardet==5.2.0 # via tox @@ -43,7 +43,7 @@ colorama==0.4.6 # pylint # pytest # tox -cryptography==45.0.6 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and sys_platform == 'linux' +cryptography==45.0.7 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and sys_platform == 'linux' # via secretstorage dill==0.4.0 # via pylint @@ -65,7 +65,7 @@ flaky==3.8.1 # via -r requirements/pytest.in greenlet==3.2.4 # via -r requirements/dev.in -hypothesis==6.138.7 +hypothesis==6.138.15 # via -r requirements/pytest.in id==1.5.0 # via twine @@ -112,7 +112,7 @@ mccabe==0.7.0 # via pylint mdurl==0.1.2 # via markdown-it-py -more-itertools==10.7.0 ; platform_machine != 'ppc64le' and platform_machine != 's390x' +more-itertools==10.8.0 ; platform_machine != 'ppc64le' and platform_machine != 's390x' # via # jaraco-classes # jaraco-functools @@ -141,7 +141,7 @@ pluggy==1.6.0 # tox pudb==2025.1 # via -r requirements/dev.in -pycparser==2.22 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_python_implementation != 'PyPy' and sys_platform == 'linux' +pycparser==2.22 ; implementation_name != 'PyPy' and platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_python_implementation != 'PyPy' and sys_platform == 'linux' # via cffi pygments==2.19.2 # via @@ -156,7 +156,7 @@ pyproject-api==1.9.1 # via tox pyproject-hooks==1.2.0 # via build -pytest==8.4.1 +pytest==8.4.2 # via # -r requirements/pytest.in # pytest-xdist @@ -181,7 +181,7 @@ rfc3986==2.0.0 # via twine rich==14.1.0 # via twine -ruff==0.12.11 +ruff==0.12.12 # via -r requirements/dev.in scriv==1.7.0 # via -r requirements/dev.in @@ -205,13 +205,13 @@ tomli==2.2.1 ; python_full_version < '3.11' # tox tomlkit==0.13.3 # via pylint -tox==4.28.4 +tox==4.30.2 # via # -r requirements/tox.in # tox-gh tox-gh==1.5.0 # via -r requirements/tox.in -twine==6.1.0 +twine==6.2.0 # via -r requirements/dev.in typing-extensions==4.15.0 ; python_full_version < '3.11' # via diff --git a/requirements/kit.pip b/requirements/kit.pip index 18c0ac449..8576edcad 100644 --- a/requirements/kit.pip +++ b/requirements/kit.pip @@ -16,7 +16,7 @@ certifi==2025.8.3 # via # cibuildwheel # requests -cffi==1.17.1 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_python_implementation != 'PyPy' and sys_platform == 'linux' +cffi==2.0.0 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_python_implementation != 'PyPy' and sys_platform == 'linux' # via cryptography charset-normalizer==3.4.3 # via requests @@ -26,7 +26,7 @@ colorama==0.4.6 # via # -r requirements/kit.in # build -cryptography==45.0.6 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and sys_platform == 'linux' +cryptography==45.0.7 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and sys_platform == 'linux' # via secretstorage dependency-groups==1.3.1 # via cibuildwheel @@ -58,7 +58,7 @@ markdown-it-py==4.0.0 # via rich mdurl==0.1.2 # via markdown-it-py -more-itertools==10.7.0 ; platform_machine != 'ppc64le' and platform_machine != 's390x' +more-itertools==10.8.0 ; platform_machine != 'ppc64le' and platform_machine != 's390x' # via # jaraco-classes # jaraco-functools @@ -75,7 +75,7 @@ patchelf==0.17.2.4 ; (platform_machine == 'aarch64' and sys_platform == 'darwin' # via cibuildwheel platformdirs==4.4.0 # via cibuildwheel -pycparser==2.22 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_python_implementation != 'PyPy' and sys_platform == 'linux' +pycparser==2.22 ; implementation_name != 'PyPy' and platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_python_implementation != 'PyPy' and sys_platform == 'linux' # via cffi pyelftools==0.32 # via @@ -106,7 +106,7 @@ secretstorage==3.3.3 ; platform_machine != 'ppc64le' and platform_machine != 's3 # via keyring setuptools==80.9.0 # via -r requirements/kit.in -twine==6.1.0 +twine==6.2.0 # via -r requirements/kit.in urllib3==2.5.0 # via diff --git a/requirements/light-threads.pip b/requirements/light-threads.pip index 7c2fcb7a8..bf2de2228 100644 --- a/requirements/light-threads.pip +++ b/requirements/light-threads.pip @@ -1,21 +1,23 @@ # This file was autogenerated by uv via the following command: # make upgrade -cffi==1.17.1 +cffi==2.0.0 # via # -r requirements/light-threads.in # gevent -dnspython==2.7.0 +dnspython==2.7.0 ; python_full_version < '3.10' + # via eventlet +dnspython==2.8.0 ; python_full_version >= '3.10' # via eventlet eventlet==0.40.3 # via -r requirements/light-threads.in -gevent==25.8.1 +gevent==25.8.2 # via -r requirements/light-threads.in greenlet==3.2.4 # via # -r requirements/light-threads.in # eventlet # gevent -pycparser==2.22 +pycparser==2.22 ; implementation_name != 'PyPy' # via cffi setuptools==80.9.0 # via diff --git a/requirements/mypy.pip b/requirements/mypy.pip index 9e3b5ea76..9fda69892 100644 --- a/requirements/mypy.pip +++ b/requirements/mypy.pip @@ -14,7 +14,7 @@ execnet==2.1.1 # via pytest-xdist flaky==3.8.1 # via -r requirements/pytest.in -hypothesis==6.138.7 +hypothesis==6.138.15 # via -r requirements/pytest.in iniconfig==2.1.0 # via pytest @@ -32,7 +32,7 @@ pygments==2.19.2 # via # -r requirements/pytest.in # pytest -pytest==8.4.1 +pytest==8.4.2 # via # -r requirements/pytest.in # pytest-xdist diff --git a/requirements/pytest.pip b/requirements/pytest.pip index e345f445d..239033749 100644 --- a/requirements/pytest.pip +++ b/requirements/pytest.pip @@ -14,7 +14,7 @@ execnet==2.1.1 # via pytest-xdist flaky==3.8.1 # via -r requirements/pytest.in -hypothesis==6.138.7 +hypothesis==6.138.15 # via -r requirements/pytest.in iniconfig==2.1.0 # via pytest @@ -26,7 +26,7 @@ pygments==2.19.2 # via # -r requirements/pytest.in # pytest -pytest==8.4.1 +pytest==8.4.2 # via # -r requirements/pytest.in # pytest-xdist diff --git a/requirements/tox.pip b/requirements/tox.pip index 8f631577c..05427b971 100644 --- a/requirements/tox.pip +++ b/requirements/tox.pip @@ -30,7 +30,7 @@ tomli==2.2.1 ; python_full_version < '3.11' # via # pyproject-api # tox -tox==4.28.4 +tox==4.30.2 # via # -r requirements/tox.in # tox-gh From d8011e72e3ccdb20fddcc37afbfcb5d06aeb6731 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 13 Sep 2025 12:49:08 -0400 Subject: [PATCH 19/29] build: adjust permissions and comment unusual ones Needed for zizmor 1.13.0 --- .github/workflows/codeql-analysis.yml | 4 ++-- .github/workflows/coverage.yml | 2 +- .github/workflows/kit.yml | 2 +- .github/workflows/publish.yml | 8 ++++---- .github/workflows/quality.yml | 3 +-- .github/workflows/testsuite.yml | 2 +- 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index cd7580661..af974a0fa 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -30,9 +30,9 @@ jobs: name: Analyze runs-on: ubuntu-latest permissions: - actions: read + actions: read # CodeQL wrote this action. contents: read - security-events: write + security-events: write # CodeQL wrote this action. strategy: fail-fast: false diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 3b308e4a9..0a950f3af 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -33,7 +33,7 @@ jobs: name: "Check changed files" runs-on: ubuntu-latest permissions: - pull-requests: read + pull-requests: read # Needed for this check to run on pull requests outputs: run_coverage: ${{ steps.filter.outputs.run_coverage }} workflow: ${{ steps.filter.outputs.workflow }} diff --git a/.github/workflows/kit.yml b/.github/workflows/kit.yml index 0532e1e14..e84e6d9bb 100644 --- a/.github/workflows/kit.yml +++ b/.github/workflows/kit.yml @@ -269,7 +269,7 @@ jobs: - non-binary runs-on: ubuntu-latest permissions: - id-token: write + id-token: write # Needed for signing artifacts steps: - name: "Download artifacts" uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5334a79b6..6b841104c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -54,8 +54,8 @@ jobs: name: "Publish to Test PyPI" if: ${{ github.event.action == 'publish-testpypi' }} permissions: - id-token: write - attestations: write + id-token: write # needed for actions/attest-build-provenance + attestations: write # needed for actions/attest-build-provenance runs-on: "ubuntu-latest" environment: name: "testpypi" @@ -94,8 +94,8 @@ jobs: name: "Publish to PyPI" if: ${{ github.event.action == 'publish-pypi' }} permissions: - id-token: write - attestations: write + id-token: write # needed for actions/attest-build-provenance + attestations: write # needed for actions/attest-build-provenance runs-on: "ubuntu-latest" environment: name: "pypi" diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 74e25c6ae..f049d50d4 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -30,7 +30,7 @@ jobs: name: "Check changed files" runs-on: ubuntu-latest permissions: - pull-requests: read + pull-requests: read # Needed for this check to run on pull requests outputs: python: ${{ steps.filter.outputs.python }} docs: ${{ steps.filter.outputs.docs }} @@ -161,7 +161,6 @@ jobs: runs-on: ubuntu-latest permissions: contents: read - actions: read needs: changed if: ${{ needs.changed.outputs.actions == 'true' || needs.changed.outputs.workflow == 'true' }} diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index 0fe438f83..1175f21f6 100644 --- a/.github/workflows/testsuite.yml +++ b/.github/workflows/testsuite.yml @@ -32,7 +32,7 @@ jobs: name: "Check changed files" runs-on: ubuntu-latest permissions: - pull-requests: read + pull-requests: read # Needed for this check to run on pull requests outputs: run_tests: ${{ steps.filter.outputs.run_tests }} steps: From a868ed9269ca474748130f5c6360cd2aae66ffc8 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 19 Sep 2025 17:22:56 -0400 Subject: [PATCH 20/29] docs: mention Python Discord on the index page --- doc/index.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/index.rst b/doc/index.rst index 32b25403f..4e08073f6 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -199,10 +199,12 @@ Getting help ------------ If the :ref:`FAQ ` doesn't answer your question, you can discuss -coverage.py or get help using it on the `Python discussion forums`_. If you -ping me (``@nedbat``), there's a higher chance I'll see the post. +coverage.py or get help using it on the `Python discussion forums`_ or in the +`Python Discord`_. If you ping me (``@nedbat``), there's a higher chance I'll +see the post. .. _Python discussion forums: https://discuss.python.org/ +.. _Python Discord: https://discord.com/channels/267624335836053506/1253355750684753950 Bug reports are gladly accepted at the `GitHub issue tracker`_. GitHub also hosts the `code repository`_. From 0d5a112fc54c1d5a07f3f2ec451779808902c9af Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Wed, 17 Sep 2025 12:36:55 -0400 Subject: [PATCH 21/29] perf: bulk narrowing to avoid N**2. #2048 --- CHANGES.rst | 7 ++- coverage/html.py | 16 +++-- coverage/jsonreport.py | 19 ++++-- coverage/lcovreport.py | 7 ++- coverage/results.py | 139 +++++++++++++++++++++++++++++------------ 5 files changed, 136 insertions(+), 52 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 8df701fc1..e2c00740f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -23,11 +23,16 @@ upgrading your version of coverage.py. Unreleased ---------- +- Performance: with branch coverage in large files, generating HTML, JSON, or + LCOV reports could take far too long due to some quadratic behavior. This is + now fixed, closing `issue 2048`_. Thanks to Daniel Diniz for help diagnosing + the problem. + - Most warnings and a few errors now have links to a page in the docs explaining the specific message. Closes `issue 1921`_. .. _issue 1921: https://github.com/nedbat/coveragepy/issues/1921 - +.. _issue 2048: https://github.com/nedbat/coveragepy/issues/2048 .. start-releases diff --git a/coverage/html.py b/coverage/html.py index bacd572d1..f2fe3205f 100644 --- a/coverage/html.py +++ b/coverage/html.py @@ -32,7 +32,7 @@ stdout_link, ) from coverage.report_core import get_analysis_to_report -from coverage.results import Analysis, Numbers +from coverage.results import Analysis, AnalysisNarrower, Numbers from coverage.templite import Templite from coverage.types import TLineNo, TMorf from coverage.version import __url__ @@ -582,13 +582,21 @@ def write_region_index_pages(self, files_to_report: Iterable[FileToReport]) -> N for noun in region_nouns: page_data = self.index_pages[noun] - outside_lines = set(range(1, num_lines + 1)) + outside_lines = set(range(1, num_lines + 1)) for region in regions: if region.kind != noun: continue outside_lines -= region.lines - analysis = ftr.analysis.narrow(region.lines) + + narrower = AnalysisNarrower(ftr.analysis) + narrower.add_regions(r.lines for r in regions if r.kind == noun) + narrower.add_regions([outside_lines]) + + for region in regions: + if region.kind != noun: + continue + analysis = narrower.narrow(region.lines) if not self.should_report(analysis, page_data): continue sorting_name = region.name.rpartition(".")[-1].lstrip("_") @@ -605,7 +613,7 @@ def write_region_index_pages(self, files_to_report: Iterable[FileToReport]) -> N ) ) - analysis = ftr.analysis.narrow(outside_lines) + analysis = narrower.narrow(outside_lines) if self.should_report(analysis, page_data): page_data.summaries.append( IndexItem( diff --git a/coverage/jsonreport.py b/coverage/jsonreport.py index 85d7a973c..b10c1a366 100644 --- a/coverage/jsonreport.py +++ b/coverage/jsonreport.py @@ -13,7 +13,7 @@ from coverage import __version__ from coverage.report_core import get_analysis_to_report -from coverage.results import Analysis, Numbers +from coverage.results import Analysis, AnalysisNarrower, Numbers from coverage.types import TLineNo, TMorf if TYPE_CHECKING: @@ -128,21 +128,30 @@ def report_one_file( ) num_lines = len(file_reporter.source().splitlines()) + regions = file_reporter.code_regions() for noun, plural in file_reporter.code_region_kinds(): - reported_file[plural] = region_data = {} outside_lines = set(range(1, num_lines + 1)) - for region in file_reporter.code_regions(): + for region in regions: if region.kind != noun: continue outside_lines -= region.lines + + narrower = AnalysisNarrower(analysis) + narrower.add_regions(r.lines for r in regions if r.kind == noun) + narrower.add_regions([outside_lines]) + + reported_file[plural] = region_data = {} + for region in regions: + if region.kind != noun: + continue region_data[region.name] = self.make_region_data( coverage_data, - analysis.narrow(region.lines), + narrower.narrow(region.lines), ) region_data[""] = self.make_region_data( coverage_data, - analysis.narrow(outside_lines), + narrower.narrow(outside_lines), ) return reported_file diff --git a/coverage/lcovreport.py b/coverage/lcovreport.py index 247c892a0..aacbed73d 100644 --- a/coverage/lcovreport.py +++ b/coverage/lcovreport.py @@ -13,7 +13,7 @@ from coverage.plugin import FileReporter from coverage.report_core import get_analysis_to_report -from coverage.results import Analysis, Numbers +from coverage.results import Analysis, AnalysisNarrower, Numbers from coverage.types import TMorf if TYPE_CHECKING: @@ -81,12 +81,15 @@ def lcov_functions( if not functions: return + narrower = AnalysisNarrower(file_analysis) + narrower.add_regions(r.lines for _, _, r in functions) + functions.sort() functions_hit = 0 for first_line, last_line, region in functions: # A function counts as having been executed if any of it has been # executed. - analysis = file_analysis.narrow(region.lines) + analysis = narrower.narrow(region.lines) hit = int(analysis.numbers.n_executed > 0) functions_hit += hit diff --git a/coverage/results.py b/coverage/results.py index 86f6fcc15..0145971a3 100644 --- a/coverage/results.py +++ b/coverage/results.py @@ -7,7 +7,7 @@ import collections import dataclasses -from collections.abc import Container, Iterable +from collections.abc import Iterable from typing import TYPE_CHECKING from coverage.exceptions import ConfigError @@ -113,45 +113,6 @@ def __post_init__(self) -> None: n_missing_branches=n_missing_branches, ) - def narrow(self, lines: Container[TLineNo]) -> Analysis: - """Create a narrowed Analysis. - - The current analysis is copied to make a new one that only considers - the lines in `lines`. - """ - - statements = {lno for lno in self.statements if lno in lines} - excluded = {lno for lno in self.excluded if lno in lines} - executed = {lno for lno in self.executed if lno in lines} - - if self.has_arcs: - arc_possibilities_set = { - (a, b) for a, b in self.arc_possibilities_set if a in lines or b in lines - } - arcs_executed_set = { - (a, b) for a, b in self.arcs_executed_set if a in lines or b in lines - } - exit_counts = {lno: num for lno, num in self.exit_counts.items() if lno in lines} - no_branch = {lno for lno in self.no_branch if lno in lines} - else: - arc_possibilities_set = set() - arcs_executed_set = set() - exit_counts = {} - no_branch = set() - - return Analysis( - precision=self.precision, - filename=self.filename, - has_arcs=self.has_arcs, - statements=statements, - excluded=excluded, - executed=executed, - arc_possibilities_set=arc_possibilities_set, - arcs_executed_set=arcs_executed_set, - exit_counts=exit_counts, - no_branch=no_branch, - ) - def missing_formatted(self, branches: bool = False) -> str: """The missing line numbers, formatted nicely. @@ -236,6 +197,104 @@ def branch_stats(self) -> dict[TLineNo, tuple[int, int]]: return stats +TRegionLines = frozenset[TLineNo] + + +class AnalysisNarrower: + """ + For reducing an `Analysis` to a subset of its lines. + + Originally this was a simpler method on Analysis, but that led to quadratic + behavior. This class does the bulk of the work up-front to provide the + same results in linear time. + + Create an AnalysisNarrower from an Analysis, bulk-add region lines to it + with `add_regions`, then individually request new narrowed Analysis objects + for each region with `narrow`. Doing most of the work in limited calls to + `add_regions` lets us avoid poor performance. + """ + + # In this class, regions are represented by a frozenset of their lines. + + def __init__(self, analysis: Analysis) -> None: + self.analysis = analysis + self.region2arc_possibilities: dict[TRegionLines, set[TArc]] = collections.defaultdict(set) + self.region2arc_executed: dict[TRegionLines, set[TArc]] = collections.defaultdict(set) + self.region2exit_counts: dict[TRegionLines, dict[TLineNo, int]] = collections.defaultdict( + dict + ) + + def add_regions(self, liness: Iterable[set[TLineNo]]) -> None: + """ + Pre-process a number of sets of line numbers. Later calls to `narrow` + with one of these sets will provide a narrowed Analysis. + """ + if self.analysis.has_arcs: + line2region: dict[TLineNo, TRegionLines] = {} + + for lines in liness: + fzlines = frozenset(lines) + for line in lines: + line2region[line] = fzlines + + def collect_arcs( + arc_set: set[TArc], + region2arcs: dict[TRegionLines, set[TArc]], + ) -> None: + for a, b in arc_set: + if r := line2region.get(a): + region2arcs[r].add((a, b)) + if r := line2region.get(b): + region2arcs[r].add((a, b)) + + collect_arcs(self.analysis.arc_possibilities_set, self.region2arc_possibilities) + collect_arcs(self.analysis.arcs_executed_set, self.region2arc_executed) + + for lno, num in self.analysis.exit_counts.items(): + if r := line2region.get(lno): + self.region2exit_counts[r][lno] = num + + def narrow(self, lines: set[TLineNo]) -> Analysis: + """Create a narrowed Analysis. + + The current analysis is copied to make a new one that only considers + the lines in `lines`. + """ + + # Technically, the set intersections in this method are still O(N**2) + # since this method is called N times, but they're very fast and moving + # them to `add_regions` won't avoid the quadratic time. + + statements = self.analysis.statements & lines + excluded = self.analysis.excluded & lines + executed = self.analysis.executed & lines + + if self.analysis.has_arcs: + fzlines = frozenset(lines) + arc_possibilities_set = self.region2arc_possibilities[fzlines] + arcs_executed_set = self.region2arc_executed[fzlines] + exit_counts = self.region2exit_counts[fzlines] + no_branch = self.analysis.no_branch & lines + else: + arc_possibilities_set = set() + arcs_executed_set = set() + exit_counts = {} + no_branch = set() + + return Analysis( + precision=self.analysis.precision, + filename=self.analysis.filename, + has_arcs=self.analysis.has_arcs, + statements=statements, + excluded=excluded, + executed=executed, + arc_possibilities_set=arc_possibilities_set, + arcs_executed_set=arcs_executed_set, + exit_counts=exit_counts, + no_branch=no_branch, + ) + + @dataclasses.dataclass class Numbers: """The numerical results of measuring coverage. From 0744b73b6b503ccf2cb75aba095c023672b921a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Sep 2025 08:04:09 -0400 Subject: [PATCH 22/29] chore: bump the action-dependencies group across 1 directory with 2 updates (#2057) Bumps the action-dependencies group with 2 updates in the / directory: [github/codeql-action](https://github.com/github/codeql-action) and [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv). Updates `github/codeql-action` from 3.30.1 to 3.30.3 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/f1f6e5f6af878fb37288ce1c627459e94dbf7d01...192325c86100d080feab897ff886c34abd4c83a3) Updates `astral-sh/setup-uv` from 6.6.1 to 6.7.0 - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](https://github.com/astral-sh/setup-uv/compare/557e51de59eb14aaaba2ed9621916900a91d50c6...b75a909f75acd358c2196fb9a5f1299a9a8868a4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.30.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: action-dependencies - dependency-name: astral-sh/setup-uv dependency-version: 6.7.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: action-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/quality.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index af974a0fa..a7331e07c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -51,7 +51,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@f1f6e5f6af878fb37288ce1c627459e94dbf7d01 # v3 + uses: github/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3 # v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -62,7 +62,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@f1f6e5f6af878fb37288ce1c627459e94dbf7d01 # v3 + uses: github/codeql-action/autobuild@192325c86100d080feab897ff886c34abd4c83a3 # v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -76,4 +76,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@f1f6e5f6af878fb37288ce1c627459e94dbf7d01 # v3 + uses: github/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3 # v3 diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index f049d50d4..2646f00f3 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -172,7 +172,7 @@ jobs: persist-credentials: false - name: Install the latest version of uv - uses: astral-sh/setup-uv@557e51de59eb14aaaba2ed9621916900a91d50c6 #v6.6.1 + uses: astral-sh/setup-uv@b75a909f75acd358c2196fb9a5f1299a9a8868a4 #v6.7.0 with: enable-cache: false From 33c4ba196f49e0ee86ab0ff473c0876c0bacd5fa Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 21 Sep 2025 11:55:09 -0400 Subject: [PATCH 23/29] chore: make upgrade --- .pre-commit-config.yaml | 2 +- doc/requirements.pip | 12 ++++++------ requirements/dev.pip | 21 ++++++++++++--------- requirements/kit.pip | 8 ++++---- requirements/light-threads.pip | 8 ++++---- requirements/mypy.pip | 6 +++--- requirements/pytest.pip | 2 +- 7 files changed, 31 insertions(+), 28 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4e44ebfe2..12ea0c286 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: exclude: "stress_phystoken|\\.py,cover$" - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.12.12 + rev: v0.13.1 hooks: - id: ruff-format diff --git a/doc/requirements.pip b/doc/requirements.pip index 96c52ef16..9c82a573e 100644 --- a/doc/requirements.pip +++ b/doc/requirements.pip @@ -14,14 +14,14 @@ certifi==2025.8.3 # via requests charset-normalizer==3.4.3 # via requests -click==8.2.1 +click==8.3.0 # via # click-log # scriv # uvicorn click-log==0.4.0 # via scriv -cogapp==3.5.1 +cogapp==3.6.0 # via -r doc/requirements.in colorama==0.4.6 # via @@ -58,7 +58,7 @@ packaging==25.0 # via sphinx polib==1.2.0 # via sphinx-lint -pyenchant==3.2.2 +pyenchant==3.3.0 # via # -r doc/requirements.in # sphinxcontrib-spelling @@ -66,7 +66,7 @@ pygments==2.19.2 # via # doc8 # sphinx -regex==2025.9.1 +regex==2025.9.18 # via sphinx-lint requests==2.32.5 # via @@ -118,7 +118,7 @@ sphinxcontrib-serializinghtml==2.0.0 # via sphinx sphinxcontrib-spelling==8.0.1 # via -r doc/requirements.in -starlette==0.47.3 +starlette==0.48.0 # via sphinx-autobuild stevedore==5.5.0 # via doc8 @@ -128,7 +128,7 @@ typing-extensions==4.15.0 ; python_full_version < '3.13' # starlette urllib3==2.5.0 # via requests -uvicorn==0.35.0 +uvicorn==0.36.0 # via sphinx-autobuild watchfiles==1.1.0 # via sphinx-autobuild diff --git a/requirements/dev.pip b/requirements/dev.pip index 100ff8f68..47a68fe97 100644 --- a/requirements/dev.pip +++ b/requirements/dev.pip @@ -26,13 +26,13 @@ click==8.1.8 ; python_full_version < '3.10' # via # click-log # scriv -click==8.2.1 ; python_full_version >= '3.10' +click==8.3.0 ; python_full_version >= '3.10' # via # click-log # scriv click-log==0.4.0 # via scriv -cogapp==3.5.1 +cogapp==3.6.0 # via -r requirements/dev.in colorama==0.4.6 # via @@ -43,13 +43,13 @@ colorama==0.4.6 # pylint # pytest # tox -cryptography==45.0.7 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and sys_platform == 'linux' +cryptography==46.0.1 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and sys_platform == 'linux' # via secretstorage dill==0.4.0 # via pylint distlib==0.4.0 # via virtualenv -docutils==0.22 +docutils==0.22.2 # via readme-renderer exceptiongroup==1.3.0 ; python_full_version < '3.11' # via @@ -65,7 +65,7 @@ flaky==3.8.1 # via -r requirements/pytest.in greenlet==3.2.4 # via -r requirements/dev.in -hypothesis==6.138.15 +hypothesis==6.139.2 # via -r requirements/pytest.in id==1.5.0 # via twine @@ -141,7 +141,7 @@ pluggy==1.6.0 # tox pudb==2025.1 # via -r requirements/dev.in -pycparser==2.22 ; implementation_name != 'PyPy' and platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_python_implementation != 'PyPy' and sys_platform == 'linux' +pycparser==2.23 ; implementation_name != 'PyPy' and platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_python_implementation != 'PyPy' and sys_platform == 'linux' # via cffi pygments==2.19.2 # via @@ -181,11 +181,13 @@ rfc3986==2.0.0 # via twine rich==14.1.0 # via twine -ruff==0.12.12 +ruff==0.13.1 # via -r requirements/dev.in scriv==1.7.0 # via -r requirements/dev.in -secretstorage==3.3.3 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and sys_platform == 'linux' +secretstorage==3.3.3 ; python_full_version < '3.10' and platform_machine != 'ppc64le' and platform_machine != 's390x' and sys_platform == 'linux' + # via keyring +secretstorage==3.4.0 ; python_full_version >= '3.10' and platform_machine != 'ppc64le' and platform_machine != 's390x' and sys_platform == 'linux' # via keyring setuptools==80.9.0 # via @@ -216,6 +218,7 @@ twine==6.2.0 typing-extensions==4.15.0 ; python_full_version < '3.11' # via # astroid + # cryptography # exceptiongroup # pylint # tox @@ -224,7 +227,7 @@ urllib3==2.5.0 # via # requests # twine -urwid==3.0.2 +urwid==3.0.3 # via # pudb # urwid-readline diff --git a/requirements/kit.pip b/requirements/kit.pip index 8576edcad..989558ac0 100644 --- a/requirements/kit.pip +++ b/requirements/kit.pip @@ -26,11 +26,11 @@ colorama==0.4.6 # via # -r requirements/kit.in # build -cryptography==45.0.7 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and sys_platform == 'linux' +cryptography==46.0.1 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and sys_platform == 'linux' # via secretstorage dependency-groups==1.3.1 # via cibuildwheel -docutils==0.22 +docutils==0.22.2 # via readme-renderer filelock==3.19.1 # via cibuildwheel @@ -75,7 +75,7 @@ patchelf==0.17.2.4 ; (platform_machine == 'aarch64' and sys_platform == 'darwin' # via cibuildwheel platformdirs==4.4.0 # via cibuildwheel -pycparser==2.22 ; implementation_name != 'PyPy' and platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_python_implementation != 'PyPy' and sys_platform == 'linux' +pycparser==2.23 ; implementation_name != 'PyPy' and platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_python_implementation != 'PyPy' and sys_platform == 'linux' # via cffi pyelftools==0.32 # via @@ -102,7 +102,7 @@ rfc3986==2.0.0 # via twine rich==14.1.0 # via twine -secretstorage==3.3.3 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and sys_platform == 'linux' +secretstorage==3.4.0 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and sys_platform == 'linux' # via keyring setuptools==80.9.0 # via -r requirements/kit.in diff --git a/requirements/light-threads.pip b/requirements/light-threads.pip index bf2de2228..e3b475ff3 100644 --- a/requirements/light-threads.pip +++ b/requirements/light-threads.pip @@ -10,20 +10,20 @@ dnspython==2.8.0 ; python_full_version >= '3.10' # via eventlet eventlet==0.40.3 # via -r requirements/light-threads.in -gevent==25.8.2 +gevent==25.9.1 # via -r requirements/light-threads.in greenlet==3.2.4 # via # -r requirements/light-threads.in # eventlet # gevent -pycparser==2.22 ; implementation_name != 'PyPy' +pycparser==2.23 ; implementation_name != 'PyPy' # via cffi setuptools==80.9.0 # via # zope-event # zope-interface -zope-event==5.1.1 +zope-event==6.0 # via gevent -zope-interface==7.2 +zope-interface==8.0 # via gevent diff --git a/requirements/mypy.pip b/requirements/mypy.pip index 9fda69892..c5d92b2fd 100644 --- a/requirements/mypy.pip +++ b/requirements/mypy.pip @@ -14,11 +14,11 @@ execnet==2.1.1 # via pytest-xdist flaky==3.8.1 # via -r requirements/pytest.in -hypothesis==6.138.15 +hypothesis==6.139.2 # via -r requirements/pytest.in iniconfig==2.1.0 # via pytest -mypy==1.17.1 +mypy==1.18.2 # via -r requirements/mypy.in mypy-extensions==1.1.0 # via mypy @@ -44,7 +44,7 @@ tomli==2.2.1 ; python_full_version < '3.11' # via # mypy # pytest -types-requests==2.32.4.20250809 +types-requests==2.32.4.20250913 # via -r requirements/mypy.in types-tabulate==0.9.0.20241207 # via -r requirements/mypy.in diff --git a/requirements/pytest.pip b/requirements/pytest.pip index 239033749..9a457cb3a 100644 --- a/requirements/pytest.pip +++ b/requirements/pytest.pip @@ -14,7 +14,7 @@ execnet==2.1.1 # via pytest-xdist flaky==3.8.1 # via -r requirements/pytest.in -hypothesis==6.138.15 +hypothesis==6.139.2 # via -r requirements/pytest.in iniconfig==2.1.0 # via pytest From c181b9315f59a81667da47cf3d760d0253872238 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 21 Sep 2025 13:07:43 -0400 Subject: [PATCH 24/29] build: use cog --check-fail-msg to instruct devs --- doc/cog_helpers.py | 6 +++++- tox.ini | 11 ++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/doc/cog_helpers.py b/doc/cog_helpers.py index 4a3f7de0f..948221383 100644 --- a/doc/cog_helpers.py +++ b/doc/cog_helpers.py @@ -82,7 +82,11 @@ def show_configs(ini, toml): toml, toml_vals = _read_config(toml, "covrc.toml") for key, val in ini_vals.items(): if val != toml_vals[key]: - cog.error(f"Mismatch! {key}:\nini: {val!r}\ntoml: {toml_vals[key]!r}") + cog.error( + f"Mismatch between configuration tabs in docs for {key = }:\n" + + f" ini: {val!r}\n" + + f" toml: {toml_vals[key]!r}" + ) ini2 = re.sub(r"(?m)^\[", "[coverage:", ini) print() diff --git a/tox.ini b/tox.ini index 47c49c1e8..6d9c7379f 100644 --- a/tox.ini +++ b/tox.ini @@ -81,8 +81,8 @@ deps = allowlist_externals = make commands = - # If this command fails, see the comment at the top of doc/cmd.rst - python -m cogapp -cP --check --verbosity=1 doc/*.rst + # If cog fails, it means the docs need to be updated. Run `make prebuild`. + python -m cogapp -cP --check --check-fail-msg='run `make prebuild`' --verbosity=1 doc/*.rst doc/*/*.rst doc8 -q --ignore-path 'doc/_*' doc CHANGES.rst README.rst sphinx-lint doc CHANGES.rst README.rst sphinx-build -b html -aEnqW doc doc/_build/html @@ -102,9 +102,10 @@ setenv = commands = python -m tabnanny {env:LINTABLE} - # If this command fails, see the comment at the top of doc/cmd.rst - python -m cogapp -cP --check --verbosity=1 doc/*.rst - python -m cogapp -cP --check --verbosity=1 .github/workflows/*.yml + # If cog fails, it means the docs need to be updated. Run `make prebuild`. + python -m cogapp -cP --check --check-fail-msg='run `make prebuild`' --verbosity=1 doc/*.rst doc/*/*.rst + # If cog fails, it means the workflow files need to be updated. Run `make workflows`. + python -m cogapp -cP --check --check-fail-msg='run `make workflows`' --verbosity=1 .github/workflows/*.yml ruff format --check python -m pylint -j 0 --notes= --ignore-paths 'doc/_build/.*' {env:LINTABLE} check-manifest --ignore 'doc/sample_html/*' From 04bbc3acfd914fdd99ffec9873bc03bdc7329357 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 21 Sep 2025 13:16:22 -0400 Subject: [PATCH 25/29] docs: discuss cog in the contributing docs --- doc/contributing.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/doc/contributing.rst b/doc/contributing.rst index 9dc968dd0..22cde031f 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -242,6 +242,25 @@ Many people love auto-formatting with `black`_ or `ruff`_, but I would prefer not to on coverage.py. +Cog +--- + +Parts of the documentation and GitHub actions are kept up-to-date with `cog`_. +There are checks to make sure that files are correct and not being incorrectly +edited. + +If a check fails, it will show you what command to run to update the files. +If you edit parts of a file that should be generated, you will see a message +like:: + + Output has been edited! Delete old checksum to unprotect. + +Probably you should revert the edits and run the command to generate the +output. The top of the file you edited will have instructions. + +.. _cog: https://cog.readthedocs.io + + Continuous integration ---------------------- From 5daff8d38786aa540ff9bec622eb3389f117f911 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 21 Sep 2025 13:21:31 -0400 Subject: [PATCH 26/29] docs: now source is formatted with ruff --- doc/contributing.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/contributing.rst b/doc/contributing.rst index 22cde031f..2ff4f2903 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -232,15 +232,15 @@ keep you from sending patches. I can clean them up. Lines should be kept to a 100-character maximum length. I recommend an `editorconfig.org`_ plugin for your editor of choice, which will also help with -indentation, line endings and so on. +indentation, line endings and so on. Source files are formatted with `ruff`_. + +I use `pre-commit`_ to run checks and formatting when making commits, you +should also. Other style questions are best answered by looking at the existing code. Formatting of docstrings, comments, long lines, and so on, should match the code that already exists. -Many people love auto-formatting with `black`_ or `ruff`_, but I would prefer -not to on coverage.py. - Cog --- @@ -319,8 +319,8 @@ fixes. If you need help writing tests, please ask. .. _fork the repo: https://docs.github.com/en/get-started/quickstart/fork-a-repo .. _editorconfig.org: http://editorconfig.org .. _tox: https://tox.readthedocs.io/ -.. _black: https://pypi.org/project/black/ .. _ruff: https://pypi.org/project/ruff/ +.. _pre-commit: https://pre-commit.com/ .. _set_env.py: https://nedbatchelder.com/blog/201907/set_envpy.html .. _pytest test selectors: https://doc.pytest.org/en/stable/usage.html#specifying-which-tests-to-run .. _sys.monitoring: https://docs.python.org/3/library/sys.monitoring.html From a301761e69da97b27662f395974d26f78fa8b2b5 Mon Sep 17 00:00:00 2001 From: ffgan <114909534+ffgan@users.noreply.github.com> Date: Mon, 22 Sep 2025 03:11:15 +0800 Subject: [PATCH 27/29] build: riscv64 wheels (#2055) * build wheel for riscv64 Co-authored by: nijincheng@iscas.ac.cn; * update `EXPECTED` number for riscv64 Co-authored by: nijincheng@iscas.ac.cn; * pin `setup-qemu-action` version for CI Co-authored by: nijincheng@iscas.ac.cn; --- .github/workflows/kit.yml | 16 ++++++++++++---- .github/workflows/publish.yml | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/.github/workflows/kit.yml b/.github/workflows/kit.yml index e84e6d9bb..2acfe9c60 100644 --- a/.github/workflows/kit.yml +++ b/.github/workflows/kit.yml @@ -36,7 +36,7 @@ env: PIP_DISABLE_PIP_VERSION_CHECK: 1 # PYVERSIONS: changing the list of versions will change the number of # expected distributions. This must match the same number in publish.yml. - EXPECTED: 88 + EXPECTED: 104 permissions: contents: read @@ -73,7 +73,7 @@ jobs: # # # For each OS, what arch to use with cibuildwheel: # os_archs = { - # "ubuntu": ["x86_64", "i686", "aarch64"], + # "ubuntu": ["x86_64", "i686", "aarch64", "riscv64"], # "macos": ["arm64", "x86_64"], # "windows": ["x86", "AMD64", "ARM64"], # } @@ -128,6 +128,12 @@ jobs: - {"os": "ubuntu", "py": "cp312", "arch": "aarch64", "os-version": "22.04-arm"} - {"os": "ubuntu", "py": "cp313", "arch": "aarch64", "os-version": "22.04-arm"} - {"os": "ubuntu", "py": "cp314", "arch": "aarch64", "os-version": "22.04-arm"} + - {"os": "ubuntu", "py": "cp39", "arch": "riscv64"} + - {"os": "ubuntu", "py": "cp310", "arch": "riscv64"} + - {"os": "ubuntu", "py": "cp311", "arch": "riscv64"} + - {"os": "ubuntu", "py": "cp312", "arch": "riscv64"} + - {"os": "ubuntu", "py": "cp313", "arch": "riscv64"} + - {"os": "ubuntu", "py": "cp314", "arch": "riscv64"} - {"os": "macos", "py": "cp39", "arch": "arm64", "os-version": "13"} - {"os": "macos", "py": "cp310", "arch": "arm64", "os-version": "13"} - {"os": "macos", "py": "cp311", "arch": "arm64", "os-version": "13"} @@ -156,7 +162,7 @@ jobs: - {"os": "windows", "py": "cp312", "arch": "ARM64", "os-version": "11-arm"} - {"os": "windows", "py": "cp313", "arch": "ARM64", "os-version": "11-arm"} - {"os": "windows", "py": "cp314", "arch": "ARM64", "os-version": "11-arm"} - # [[[end]]] (sum: 7BoHzaIHKR) + # [[[end]]] (sum: 0tXzcJPEQS) # ^^^^^^^^^^^^^^^ # If a check fails and points to this checksum line, it means you edited # the matrix directly instead of editing the Python code in the comment @@ -193,7 +199,9 @@ jobs: - name: "Install tools" run: | python -m pip install -r requirements/kit.pip - + - name: Set up QEMU + if: ${{ matrix.arch == 'riscv64' }} + uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 - name: "Build binary wheels" env: CIBW_BUILD: ${{ matrix.py }}*-* diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 6b841104c..fc7bd1733 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -17,7 +17,7 @@ defaults: env: # PYVERSIONS: changing the list of versions will change the number of # expected distributions. This must match the same number in kit.yml. - EXPECTED: 88 + EXPECTED: 104 permissions: contents: read From 952afdaca658d5e1acdd533c727448a0b218caf0 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 21 Sep 2025 15:20:02 -0400 Subject: [PATCH 28/29] docs: prep for 7.10.7 --- CHANGES.rst | 16 +++++++++------- coverage/version.py | 4 ++-- doc/conf.py | 6 +++--- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index e2c00740f..5b406d440 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -20,13 +20,17 @@ upgrading your version of coverage.py. .. Version 9.8.1 — 2027-07-27 .. -------------------------- -Unreleased ----------- +.. start-releases + +.. _changes_7-10-7: + +Version 7.10.7 — 2025-09-21 +--------------------------- - Performance: with branch coverage in large files, generating HTML, JSON, or - LCOV reports could take far too long due to some quadratic behavior. This is - now fixed, closing `issue 2048`_. Thanks to Daniel Diniz for help diagnosing - the problem. + LCOV reports could take far too long due to some quadratic behavior when + creating the function and class index pages. This is now fixed, closing + `issue 2048`_. Thanks to Daniel Diniz for help diagnosing the problem. - Most warnings and a few errors now have links to a page in the docs explaining the specific message. Closes `issue 1921`_. @@ -35,8 +39,6 @@ Unreleased .. _issue 2048: https://github.com/nedbat/coveragepy/issues/2048 -.. start-releases - .. _changes_7-10-6: Version 7.10.6 — 2025-08-29 diff --git a/coverage/version.py b/coverage/version.py index 3d5d37a00..69b7c374f 100644 --- a/coverage/version.py +++ b/coverage/version.py @@ -8,8 +8,8 @@ # version_info: same semantics as sys.version_info. # _dev: the .devN suffix if any. -version_info = (7, 10, 7, "alpha", 0) -_dev = 1 +version_info = (7, 10, 7, "final", 0) +_dev = 0 def _make_version( diff --git a/doc/conf.py b/doc/conf.py index b1fa60438..9cf8f9c41 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -67,11 +67,11 @@ # @@@ editable copyright = "2009–2025, Ned Batchelder" # pylint: disable=redefined-builtin # The short X.Y.Z version. -version = "7.10.6" +version = "7.10.7" # The full version, including alpha/beta/rc tags. -release = "7.10.6" +release = "7.10.7" # The date of release, in "monthname day, year" format. -release_date = "August 29, 2025" +release_date = "September 21, 2025" # @@@ end rst_epilog = f""" From 92a2af54e6bc948a9c536bd9b12bab70fb055904 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 21 Sep 2025 15:20:18 -0400 Subject: [PATCH 29/29] docs: sample HTML for 7.10.7 --- doc/sample_html/class_index.html | 104 +- doc/sample_html/function_index.html | 326 ++-- doc/sample_html/index.html | 32 +- doc/sample_html/status.json | 2 +- .../z_7b071bdc2a35fa80___init___py.html | 8 +- .../z_7b071bdc2a35fa80___main___py.html | 8 +- .../z_7b071bdc2a35fa80_cogapp_py.html | 1564 +++++++++-------- .../z_7b071bdc2a35fa80_hashhandler_py.html | 8 +- .../z_7b071bdc2a35fa80_makefiles_py.html | 8 +- .../z_7b071bdc2a35fa80_test_cogapp_py.html | 1140 ++++++------ .../z_7b071bdc2a35fa80_test_makefiles_py.html | 8 +- ...z_7b071bdc2a35fa80_test_whiteutils_py.html | 8 +- .../z_7b071bdc2a35fa80_utils_py.html | 8 +- .../z_7b071bdc2a35fa80_whiteutils_py.html | 8 +- 14 files changed, 1636 insertions(+), 1596 deletions(-) diff --git a/doc/sample_html/class_index.html b/doc/sample_html/class_index.html index 277ef81f7..f6dca10d3 100644 --- a/doc/sample_html/class_index.html +++ b/doc/sample_html/class_index.html @@ -11,7 +11,7 @@

Cog coverage: - 37.79% + 37.59%

@@ -97,8 +97,8 @@

0.00% - cogapp/cogapp.py - CogError + cogapp/cogapp.py + CogError 3 0 0 @@ -107,8 +107,8 @@

100.00% - cogapp/cogapp.py - CogUsageError + cogapp/cogapp.py + CogUsageError 0 0 0 @@ -117,8 +117,8 @@

100.00% - cogapp/cogapp.py - CogInternalError + cogapp/cogapp.py + CogInternalError 0 0 0 @@ -127,8 +127,8 @@

100.00% - cogapp/cogapp.py - CogGeneratedError + cogapp/cogapp.py + CogGeneratedError 0 0 0 @@ -137,8 +137,8 @@

100.00% - cogapp/cogapp.py - CogUserException + cogapp/cogapp.py + CogUserException 0 0 0 @@ -147,8 +147,8 @@

100.00% - cogapp/cogapp.py - CogCheckFailed + cogapp/cogapp.py + CogCheckFailed 0 0 0 @@ -157,8 +157,8 @@

100.00% - cogapp/cogapp.py - CogGenerator + cogapp/cogapp.py + CogGenerator 58 6 0 @@ -167,24 +167,24 @@

88.46% - cogapp/cogapp.py - CogOptions - 85 - 62 + cogapp/cogapp.py + CogOptions + 88 + 64 1 - 48 + 50 0 - 17.29% + 17.39% - cogapp/cogapp.py - Cog - 256 - 159 + cogapp/cogapp.py + Cog + 259 + 162 0 - 114 + 116 21 - 35.68% + 35.20% cogapp/cogapp.py @@ -369,16 +369,16 @@

cogapp/test_cogapp.py CheckTests - 56 - 56 + 61 + 61 0 6 0 - 0.00% + 0.00% - cogapp/test_cogapp.py - WritabilityTests + cogapp/test_cogapp.py + WritabilityTests 19 19 0 @@ -387,8 +387,8 @@

0.00% - cogapp/test_cogapp.py - ChecksumTests + cogapp/test_cogapp.py + ChecksumTests 34 34 0 @@ -397,8 +397,8 @@

0.00% - cogapp/test_cogapp.py - CustomMarkerTests + cogapp/test_cogapp.py + CustomMarkerTests 12 12 0 @@ -407,8 +407,8 @@

0.00% - cogapp/test_cogapp.py - BlakeTests + cogapp/test_cogapp.py + BlakeTests 15 15 0 @@ -417,8 +417,8 @@

0.00% - cogapp/test_cogapp.py - ErrorCallTests + cogapp/test_cogapp.py + ErrorCallTests 12 12 0 @@ -427,8 +427,8 @@

0.00% - cogapp/test_cogapp.py - HashHandlerTests + cogapp/test_cogapp.py + HashHandlerTests 10 10 0 @@ -439,12 +439,12 @@

cogapp/test_cogapp.py (no class) - 196 + 197 2 0 2 1 - 98.48% + 98.49% cogapp/test_makefiles.py @@ -551,12 +551,12 @@

Total   - 1701 - 1044 + 1713 + 1054 3 - 294 + 298 35 - 37.79% + 37.59% @@ -567,8 +567,8 @@

- coverage.py v7.10.6, - created at 2025-08-29 10:20 -0400 + coverage.py v7.10.7, + created at 2025-09-21 15:20 -0400

- 490 statements   - - + 496 statements   + +

@@ -66,8 +66,8 @@

^ index     » next       - coverage.py v7.10.6, - created at 2025-08-29 10:20 -0400 + coverage.py v7.10.7, + created at 2025-09-21 15:20 -0400

diff --git a/doc/sample_html/z_7b071bdc2a35fa80_makefiles_py.html b/doc/sample_html/z_7b071bdc2a35fa80_makefiles_py.html index 01eabeb42..5407e3704 100644 --- a/doc/sample_html/z_7b071bdc2a35fa80_makefiles_py.html +++ b/doc/sample_html/z_7b071bdc2a35fa80_makefiles_py.html @@ -66,8 +66,8 @@

^ index     » next       - coverage.py v7.10.6, - created at 2025-08-29 10:20 -0400 + coverage.py v7.10.7, + created at 2025-09-21 15:20 -0400

- 905 statements   - - + 911 statements   + +

@@ -66,8 +66,8 @@

^ index     » next       - coverage.py v7.10.6, - created at 2025-08-29 10:20 -0400 + coverage.py v7.10.7, + created at 2025-09-21 15:20 -0400