diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 0c0d2bd8c..a7331e07c 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 @@ -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@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@3c3833e0f8c1c83d449a7478aa59c036a9165498 # 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@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3 + uses: github/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3 # v3 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 2841c55d7..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 }} @@ -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/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/kit.yml b/.github/workflows/kit.yml index a271fb1c6..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 @@ -172,7 +178,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 @@ -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 }}*-* @@ -229,7 +237,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 @@ -269,7 +277,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 9bcf63947..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 @@ -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" @@ -81,12 +81,12 @@ 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/*" - 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/ @@ -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" @@ -121,9 +121,9 @@ 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/*" - 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..2646f00f3 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 }} @@ -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 @@ -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' }} @@ -173,7 +172,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@b75a909f75acd358c2196fb9a5f1299a9a8868a4 #v6.7.0 with: enable-cache: false diff --git a/.github/workflows/testsuite.yml b/.github/workflows/testsuite.yml index b53364ecc..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: @@ -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 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 94c00e595..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.10 + rev: v0.13.1 hooks: - id: ruff-format diff --git a/CHANGES.rst b/CHANGES.rst index a115f44cb..5b406d440 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -22,6 +22,23 @@ upgrading your version of coverage.py. .. 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 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`_. + +.. _issue 1921: https://github.com/nedbat/coveragepy/issues/1921 +.. _issue 2048: https://github.com/nedbat/coveragepy/issues/2048 + + .. _changes_7-10-6: Version 7.10.6 — 2025-08-29 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/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/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..608abb632 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 @@ -67,6 +67,7 @@ TLineNo, TMorf, ) +from coverage.version import __url__ from coverage.xmlreport import XmlReporter os = isolate_module(os) @@ -320,7 +321,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) @@ -479,7 +480,7 @@ def _warn(self, msg: str, slug: str | None = None, once: bool = False) -> None: self._warnings.append(msg) if slug: - msg = f"{msg} ({slug})" + 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/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/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/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/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. 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/coverage/version.py b/coverage/version.py index a9d77c510..69b7c374f 100644 --- a/coverage/version.py +++ b/coverage/version.py @@ -8,7 +8,7 @@ # version_info: same semantics as sys.version_info. # _dev: the .devN suffix if any. -version_info = (7, 10, 6, "final", 0) +version_info = (7, 10, 7, "final", 0) _dev = 0 @@ -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/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/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/doc/commands/cmd_annotate.rst b/doc/commands/cmd_annotate.rst new file mode 100644 index 000000000..ff90d458f --- /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_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..23d2a64fa --- /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_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..8f25c3ae8 --- /dev/null +++ b/doc/commands/cmd_debug.rst @@ -0,0 +1,125 @@ +.. 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_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`` option +.................. + +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 activity 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..232a32b65 --- /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_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..9bfa8be42 --- /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_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..31d233edc --- /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_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..166f367c6 --- /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_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..abea66879 --- /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_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..8f157182c --- /dev/null +++ b/doc/commands/cmd_reporting.rst @@ -0,0 +1,47 @@ +.. 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_help +.. ]]] +.. [[[end]]] (sum: 1B2M2Y8Asg) + + +.. _cmd_reporting: + +Reporting +--------- + +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. +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..5c9a5c44e --- /dev/null +++ b/doc/commands/cmd_run.rst @@ -0,0 +1,170 @@ +.. 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_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_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..124932b67 --- /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_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..f6321ea3d --- /dev/null +++ b/doc/commands/index.rst @@ -0,0 +1,92 @@ +.. 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. + +For diagnosing problems, commands accept a ``--debug`` option. See +:ref:`cmd_run_debug`. + + +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/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""" diff --git a/doc/contributing.rst b/doc/contributing.rst index 9dc968dd0..2ff4f2903 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -232,14 +232,33 @@ 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 +--- + +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 @@ -300,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 diff --git a/doc/index.rst b/doc/index.rst index fa67acbc7..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`_. @@ -231,8 +233,9 @@ More information install For enterprise - cmd + commands/index config + messages source excluding branch diff --git a/doc/messages.rst b/doc/messages.rst new file mode 100644 index 000000000..b90aa951b --- /dev/null +++ b/doc/messages.rst @@ -0,0 +1,201 @@ +.. 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. + + +.. _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. + +.. _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: + +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 + 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. + +.. _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, + or for other reasons. + + 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 + 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. + + +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 +"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) diff --git a/doc/requirements.pip b/doc/requirements.pip index 1250bbaa6..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 @@ -56,11 +56,9 @@ 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 +pyenchant==3.3.0 # via # -r doc/requirements.in # sphinxcontrib-spelling @@ -68,7 +66,7 @@ pygments==2.19.2 # via # doc8 # sphinx -regex==2025.7.34 +regex==2025.9.18 # via sphinx-lint requests==2.32.5 # via @@ -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,17 +118,17 @@ sphinxcontrib-serializinghtml==2.0.0 # via sphinx sphinxcontrib-spelling==8.0.1 # via -r doc/requirements.in -starlette==0.47.2 +starlette==0.48.0 # 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 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/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