Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: pytest-dev/pytest-cov
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v6.0.0
Choose a base ref
...
head repository: pytest-dev/pytest-cov
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v6.1.0
Choose a head ref

Commits on Nov 24, 2024

  1. Update CHANGELOG.rst to highlight when python 3.7 and 3.6 where dropped

    mikemanger authored Nov 24, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    8916e6e View commit details

Commits on Nov 25, 2024

  1. Merge pull request #668 from mikemanger/patch-1

    webknjaz authored Nov 25, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    4732d50 View commit details

Commits on Mar 20, 2025

  1. fix failing link

    tsvikas committed Mar 20, 2025
    Copy the full SHA
    b02c9a0 View commit details
  2. Configure Sphinx to ignore GitHub comment anchors

    Add linkcheck_anchors_ignore setting to skip GitHub's dynamic comment
    anchors during documentation link validation. This prevents false
    positives in CI when checking links to GitHub issue comments, as GitHub
    injects these anchors dynamically in the web interface.
    tsvikas committed Mar 20, 2025
    Copy the full SHA
    2da9bb7 View commit details
  3. ignore anchors for github

    tsvikas authored Mar 20, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    72af9ef View commit details

Commits on Mar 23, 2025

  1. add ^ to regex

    Co-authored-by: 🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко) <wk.cvs.github@sydorenko.org.ua>
    tsvikas and webknjaz authored Mar 23, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    cc7eca2 View commit details
  2. Merge pull request #680 from tsvikas/fix-doc-links

    fix failing tests related to broken links
    webknjaz authored Mar 23, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    84b660c View commit details

Commits on Mar 24, 2025

  1. switch line to _

    tsvikas authored and ionelmc committed Mar 24, 2025
    Copy the full SHA
    1e54540 View commit details
  2. add heading to coverage reports

    tsvikas authored and ionelmc committed Mar 24, 2025
    Copy the full SHA
    102fdc7 View commit details
  3. use TerminalReporter for first heading

    tsvikas authored and ionelmc committed Mar 24, 2025
    Copy the full SHA
    a131cf2 View commit details
  4. use correct width

    tsvikas authored and ionelmc committed Mar 24, 2025
    Copy the full SHA
    43fda5e View commit details
  5. fix tests for _

    tsvikas authored and ionelmc committed Mar 24, 2025
    Copy the full SHA
    82d5ea0 View commit details
  6. move heading to pytest_terminal_summary

    tsvikas authored and ionelmc committed Mar 24, 2025
    Copy the full SHA
    22c4ae5 View commit details
  7. Copy the full SHA
    034faf2 View commit details
  8. made wrote_headings private

    tsvikas authored and ionelmc committed Mar 24, 2025
    Copy the full SHA
    2a1fed6 View commit details
  9. move section between functions

    tsvikas authored and ionelmc committed Mar 24, 2025
    Copy the full SHA
    362a359 View commit details
  10. add reference to code source

    tsvikas authored and ionelmc committed Mar 24, 2025
    Copy the full SHA
    2de0c6c View commit details
  11. Copy the full SHA
    ab2cd26 View commit details
  12. Upgrade ruff.

    ionelmc committed Mar 24, 2025
    Copy the full SHA
    089e7bb View commit details
  13. Update changelog.

    ionelmc committed Mar 24, 2025
    Copy the full SHA
    204af14 View commit details

Commits on Apr 1, 2025

  1. Remove unnecessary CovFailUnderWarning. Closes #675.

    ionelmc committed Apr 1, 2025
    Copy the full SHA
    44540e1 View commit details
  2. Copy the full SHA
    e760099 View commit details
  3. Refactor a bit the internals to be a bit less boilerplatey and have m…

    …ore clarity. The config/nodeid were never meant to be optional for this internal class - the intention there was to signal that they can be None values.
    ionelmc committed Apr 1, 2025
    Copy the full SHA
    aa57aed View commit details
  4. Update changelog.

    ionelmc committed Apr 1, 2025
    Copy the full SHA
    10b14af View commit details
  5. Bump version: 6.0.0 → 6.1.0

    ionelmc committed Apr 1, 2025
    Copy the full SHA
    10f8cde View commit details
Showing with 223 additions and 130 deletions.
  1. +1 −1 .bumpversion.cfg
  2. +1 −1 .cookiecutterrc
  3. +54 −36 .github/workflows/test.yml
  4. +2 −2 .pre-commit-config.yaml
  5. +2 −1 AUTHORS.rst
  6. +11 −0 CHANGELOG.rst
  7. +2 −2 README.rst
  8. +5 −1 docs/conf.py
  9. +2 −2 setup.py
  10. +1 −7 src/pytest_cov/__init__.py
  11. +45 −16 src/pytest_cov/engine.py
  12. +20 −19 src/pytest_cov/plugin.py
  13. +72 −39 tests/test_pytest_cov.py
  14. +5 −3 tox.ini
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 6.0.0
current_version = 6.1.0
commit = True
tag = True

2 changes: 1 addition & 1 deletion .cookiecutterrc
Original file line number Diff line number Diff line change
@@ -40,7 +40,7 @@ default_context:
sphinx_doctest: 'no'
sphinx_theme: sphinx-py3doc-enhanced-theme
test_matrix_separate_coverage: 'no'
version: 6.0.0
version: 6.1.0
version_manager: bump2version
website: http://blog.ionelmc.ro
year_from: '2010'
90 changes: 54 additions & 36 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -60,113 +60,131 @@ jobs:
toxpython: 'python3.11'
tox_env: 'docs'
os: 'ubuntu-latest'
- name: 'py39-pytest83-xdist36-coverage76 (ubuntu)'
- name: 'py39-pytest83-xdist36-coverage77 (ubuntu)'
python: '3.9'
toxpython: 'python3.9'
python_arch: 'x64'
tox_env: 'py39-pytest83-xdist36-coverage76'
tox_env: 'py39-pytest83-xdist36-coverage77'
os: 'ubuntu-latest'
- name: 'py39-pytest83-xdist36-coverage76 (windows)'
- name: 'py39-pytest83-xdist36-coverage77 (windows)'
python: '3.9'
toxpython: 'python3.9'
python_arch: 'x64'
tox_env: 'py39-pytest83-xdist36-coverage76'
tox_env: 'py39-pytest83-xdist36-coverage77'
os: 'windows-latest'
- name: 'py39-pytest83-xdist36-coverage76 (macos)'
- name: 'py39-pytest83-xdist36-coverage77 (macos)'
python: '3.9'
toxpython: 'python3.9'
python_arch: 'arm64'
tox_env: 'py39-pytest83-xdist36-coverage76'
tox_env: 'py39-pytest83-xdist36-coverage77'
os: 'macos-latest'
- name: 'py310-pytest83-xdist36-coverage76 (ubuntu)'
- name: 'py310-pytest83-xdist36-coverage77 (ubuntu)'
python: '3.10'
toxpython: 'python3.10'
python_arch: 'x64'
tox_env: 'py310-pytest83-xdist36-coverage76'
tox_env: 'py310-pytest83-xdist36-coverage77'
os: 'ubuntu-latest'
- name: 'py310-pytest83-xdist36-coverage76 (windows)'
- name: 'py310-pytest83-xdist36-coverage77 (windows)'
python: '3.10'
toxpython: 'python3.10'
python_arch: 'x64'
tox_env: 'py310-pytest83-xdist36-coverage76'
tox_env: 'py310-pytest83-xdist36-coverage77'
os: 'windows-latest'
- name: 'py310-pytest83-xdist36-coverage76 (macos)'
- name: 'py310-pytest83-xdist36-coverage77 (macos)'
python: '3.10'
toxpython: 'python3.10'
python_arch: 'arm64'
tox_env: 'py310-pytest83-xdist36-coverage76'
tox_env: 'py310-pytest83-xdist36-coverage77'
os: 'macos-latest'
- name: 'py311-pytest83-xdist36-coverage76 (ubuntu)'
- name: 'py311-pytest83-xdist36-coverage77 (ubuntu)'
python: '3.11'
toxpython: 'python3.11'
python_arch: 'x64'
tox_env: 'py311-pytest83-xdist36-coverage76'
tox_env: 'py311-pytest83-xdist36-coverage77'
os: 'ubuntu-latest'
- name: 'py311-pytest83-xdist36-coverage76 (windows)'
- name: 'py311-pytest83-xdist36-coverage77 (windows)'
python: '3.11'
toxpython: 'python3.11'
python_arch: 'x64'
tox_env: 'py311-pytest83-xdist36-coverage76'
tox_env: 'py311-pytest83-xdist36-coverage77'
os: 'windows-latest'
- name: 'py311-pytest83-xdist36-coverage76 (macos)'
- name: 'py311-pytest83-xdist36-coverage77 (macos)'
python: '3.11'
toxpython: 'python3.11'
python_arch: 'arm64'
tox_env: 'py311-pytest83-xdist36-coverage76'
tox_env: 'py311-pytest83-xdist36-coverage77'
os: 'macos-latest'
- name: 'py312-pytest83-xdist36-coverage76 (ubuntu)'
- name: 'py312-pytest83-xdist36-coverage77 (ubuntu)'
python: '3.12'
toxpython: 'python3.12'
python_arch: 'x64'
tox_env: 'py312-pytest83-xdist36-coverage76'
tox_env: 'py312-pytest83-xdist36-coverage77'
os: 'ubuntu-latest'
- name: 'py312-pytest83-xdist36-coverage76 (windows)'
- name: 'py312-pytest83-xdist36-coverage77 (windows)'
python: '3.12'
toxpython: 'python3.12'
python_arch: 'x64'
tox_env: 'py312-pytest83-xdist36-coverage76'
tox_env: 'py312-pytest83-xdist36-coverage77'
os: 'windows-latest'
- name: 'py312-pytest83-xdist36-coverage76 (macos)'
- name: 'py312-pytest83-xdist36-coverage77 (macos)'
python: '3.12'
toxpython: 'python3.12'
python_arch: 'arm64'
tox_env: 'py312-pytest83-xdist36-coverage76'
tox_env: 'py312-pytest83-xdist36-coverage77'
os: 'macos-latest'
- name: 'pypy39-pytest83-xdist36-coverage76 (ubuntu)'
- name: 'py313-pytest83-xdist36-coverage77 (ubuntu)'
python: '3.13'
toxpython: 'python3.13'
python_arch: 'x64'
tox_env: 'py313-pytest83-xdist36-coverage77'
os: 'ubuntu-latest'
- name: 'py313-pytest83-xdist36-coverage77 (windows)'
python: '3.13'
toxpython: 'python3.13'
python_arch: 'x64'
tox_env: 'py313-pytest83-xdist36-coverage77'
os: 'windows-latest'
- name: 'py313-pytest83-xdist36-coverage77 (macos)'
python: '3.13'
toxpython: 'python3.13'
python_arch: 'arm64'
tox_env: 'py313-pytest83-xdist36-coverage77'
os: 'macos-latest'
- name: 'pypy39-pytest83-xdist36-coverage77 (ubuntu)'
python: 'pypy-3.9'
toxpython: 'pypy3.9'
python_arch: 'x64'
tox_env: 'pypy39-pytest83-xdist36-coverage76'
tox_env: 'pypy39-pytest83-xdist36-coverage77'
os: 'ubuntu-latest'
- name: 'pypy39-pytest83-xdist36-coverage76 (windows)'
- name: 'pypy39-pytest83-xdist36-coverage77 (windows)'
python: 'pypy-3.9'
toxpython: 'pypy3.9'
python_arch: 'x64'
tox_env: 'pypy39-pytest83-xdist36-coverage76'
tox_env: 'pypy39-pytest83-xdist36-coverage77'
os: 'windows-latest'
- name: 'pypy39-pytest83-xdist36-coverage76 (macos)'
- name: 'pypy39-pytest83-xdist36-coverage77 (macos)'
python: 'pypy-3.9'
toxpython: 'pypy3.9'
python_arch: 'arm64'
tox_env: 'pypy39-pytest83-xdist36-coverage76'
tox_env: 'pypy39-pytest83-xdist36-coverage77'
os: 'macos-latest'
- name: 'pypy310-pytest83-xdist36-coverage76 (ubuntu)'
- name: 'pypy310-pytest83-xdist36-coverage77 (ubuntu)'
python: 'pypy-3.10'
toxpython: 'pypy3.10'
python_arch: 'x64'
tox_env: 'pypy310-pytest83-xdist36-coverage76'
tox_env: 'pypy310-pytest83-xdist36-coverage77'
os: 'ubuntu-latest'
- name: 'pypy310-pytest83-xdist36-coverage76 (windows)'
- name: 'pypy310-pytest83-xdist36-coverage77 (windows)'
python: 'pypy-3.10'
toxpython: 'pypy3.10'
python_arch: 'x64'
tox_env: 'pypy310-pytest83-xdist36-coverage76'
tox_env: 'pypy310-pytest83-xdist36-coverage77'
os: 'windows-latest'
- name: 'pypy310-pytest83-xdist36-coverage76 (macos)'
- name: 'pypy310-pytest83-xdist36-coverage77 (macos)'
python: 'pypy-3.10'
toxpython: 'pypy3.10'
python_arch: 'arm64'
tox_env: 'pypy310-pytest83-xdist36-coverage76'
tox_env: 'pypy310-pytest83-xdist36-coverage77'
os: 'macos-latest'
steps:
- uses: actions/checkout@v4
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -6,13 +6,13 @@ exclude: '^(\.tox|ci/templates|\.bumpversion\.cfg)(/|$)'
# Note the order is intentional to avoid multiple passes of the hooks
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.5
rev: v0.11.2
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix, --show-fixes, --unsafe-fixes]
- id: ruff-format
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
3 changes: 2 additions & 1 deletion AUTHORS.rst
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ Authors
* Marc Abramowitz - \http://marc-abramowitz.com
* Thomas Kluyver - https://github.com/takluyver
* Guillaume Ayoub - http://www.yabz.fr
* Federico Ceratto - http://firelet.net
* Federico Ceratto - \http://firelet.net
* Josh Kalderimis - \http://blog.cookiestack.com
* Ionel Cristian Mărieș - https://blog.ionelmc.ro
* Christian Ledermann - https://github.com/cleder
@@ -62,3 +62,4 @@ Authors
* Matthew Gamble - https://github.com/mwgamble
* Christian Clauss - https://github.com/cclauss
* Dawn James - https://github.com/dawngerpony
* Tsvika Shapira - https://github.com/tsvikas
11 changes: 11 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -2,6 +2,15 @@
Changelog
=========

6.1.0 (2025-04-01)
------------------

* Change terminal output to use full width lines for the coverage header.
Contributed by Tsvika Shapira in `#678 <https://github.com/pytest-dev/pytest-cov/pull/678>`_.
* Removed unnecessary CovFailUnderWarning. Fixes `#675 <https://github.com/pytest-dev/pytest-cov/issues/675>`_.
* Fixed the term report not using the precision specified via ``--cov-precision``.


6.0.0 (2024-10-29)
------------------

@@ -27,6 +36,7 @@ Changelog
Contributed by Dawn James in `#626 <https://github.com/pytest-dev/pytest-cov/pull/626>`_.
* Modernized project's pre-commit hooks to use ruff. Initial POC contributed by
Christian Clauss in `#584 <https://github.com/pytest-dev/pytest-cov/pull/584>`_.
* Dropped support for Python 3.7.

4.1.0 (2023-05-24)
------------------
@@ -43,6 +53,7 @@ Changelog
Contributed by Mark Mayo in `#572 <https://github.com/pytest-dev/pytest-cov/pull/572>`_.
* Fixed a skip in the test suite for some old xdist.
Contributed by a bunch of people in `#565 <https://github.com/pytest-dev/pytest-cov/pull/565>`_.
* Dropped support for Python 3.6.


4.0.0 (2022-09-28)
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
@@ -39,9 +39,9 @@ Overview
:alt: Supported implementations
:target: https://pypi.org/project/pytest-cov

.. |commits-since| image:: https://img.shields.io/github/commits-since/pytest-dev/pytest-cov/v6.0.0.svg
.. |commits-since| image:: https://img.shields.io/github/commits-since/pytest-dev/pytest-cov/v6.1.0.svg
:alt: Commits since latest release
:target: https://github.com/pytest-dev/pytest-cov/compare/v6.0.0...master
:target: https://github.com/pytest-dev/pytest-cov/compare/v6.1.0...master

.. end-badges
6 changes: 5 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@
year = '2010-2024'
author = 'pytest-cov contributors'
copyright = f'{year}, {author}'
version = release = '6.0.0'
version = release = '6.1.0'

pygments_style = 'trac'
templates_path = ['.']
@@ -39,6 +39,10 @@
html_split_index = False
html_short_title = f'{project}-{version}'

linkcheck_anchors_ignore_for_url = [
r'^https?://(www\.)?github\.com/.*',
]

napoleon_use_ivar = True
napoleon_use_rtype = False
napoleon_use_param = False
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
@@ -72,12 +72,12 @@ def finalize_options(self):
def run(self):
with Path(__file__).parent.joinpath('src', 'pytest-cov.pth').open('w') as fh:
with Path(__file__).parent.joinpath('src', 'pytest-cov.embed').open() as sh:
fh.write(f"import os, sys;exec({sh.read().replace(' ', ' ')!r})")
fh.write(f'import os, sys;exec({sh.read().replace(" ", " ")!r})')


setup(
name='pytest-cov',
version='6.0.0',
version='6.1.0',
license='MIT',
description='Pytest plugin for measuring coverage.',
long_description='{}\n{}'.format(read('README.rst'), re.sub(':[a-z]+:`~?(.*?)`', r'``\1``', read('CHANGELOG.rst'))),
8 changes: 1 addition & 7 deletions src/pytest_cov/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""pytest-cov: avoid already-imported warning: PYTEST_DONT_REWRITE."""

__version__ = '6.0.0'
__version__ = '6.1.0'

import pytest

@@ -27,12 +27,6 @@ class CovReportWarning(PytestCovWarning):
"""


class CovFailUnderWarning(PytestCovWarning):
"""
Indicates that we failed to generate a report.
"""


class CentralCovContextWarning(PytestCovWarning):
"""
Indicates that dynamic_context was set to test_function instead of using the builtin --cov-context.
61 changes: 45 additions & 16 deletions src/pytest_cov/engine.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
"""Coverage controllers for use by pytest-cov and nose-cov."""

import argparse
import contextlib
import copy
import functools
import os
import random
import shutil
import socket
import sys
import warnings
from io import StringIO
from pathlib import Path
from typing import Union

import coverage
from coverage.data import CoverageData
@@ -66,13 +69,14 @@ def _data_suffix(name):
class CovController:
"""Base class for different plugin implementations."""

def __init__(self, cov_source, cov_report, cov_config, cov_append, cov_branch, config=None, nodeid=None):
def __init__(self, options: argparse.Namespace, config: Union[None, object], nodeid: Union[None, str]):
"""Get some common config used by multiple derived classes."""
self.cov_source = cov_source
self.cov_report = cov_report
self.cov_config = cov_config
self.cov_append = cov_append
self.cov_branch = cov_branch
self.cov_source = options.cov_source
self.cov_report = options.cov_report
self.cov_config = options.cov_config
self.cov_append = options.cov_append
self.cov_branch = options.cov_branch
self.cov_precision = options.cov_precision
self.config = config
self.nodeid = nodeid

@@ -134,15 +138,39 @@ def get_node_desc(platform, version_info):
return 'platform {}, python {}'.format(platform, '{}.{}.{}-{}-{}'.format(*version_info[:5]))

@staticmethod
def sep(stream, s, txt):
def get_width():
# taken from https://github.com/pytest-dev/pytest/blob/33c7b05a/src/_pytest/_io/terminalwriter.py#L26
width, _ = shutil.get_terminal_size(fallback=(80, 24))
# The Windows get_terminal_size may be bogus, let's sanify a bit.
if width < 40:
width = 80
return width

def sep(self, stream, s, txt):
if hasattr(stream, 'sep'):
stream.sep(s, txt)
else:
sep_total = max((70 - 2 - len(txt)), 2)
sep_len = sep_total // 2
sep_extra = sep_total % 2
out = f'{s * sep_len} {txt} {s * (sep_len + sep_extra)}\n'
stream.write(out)
fullwidth = self.get_width()
# taken from https://github.com/pytest-dev/pytest/blob/33c7b05a/src/_pytest/_io/terminalwriter.py#L126
# The goal is to have the line be as long as possible
# under the condition that len(line) <= fullwidth.
if sys.platform == 'win32':
# If we print in the last column on windows we are on a
# new line but there is no way to verify/neutralize this
# (we may not know the exact line width).
# So let's be defensive to avoid empty lines in the output.
fullwidth -= 1
N = max((fullwidth - len(txt) - 2) // (2 * len(s)), 1)
fill = s * N
line = f'{fill} {txt} {fill}'
# In some situations there is room for an extra sepchar at the right,
# in particular if we consider that with a sepchar like "_ " the
# trailing space is not important at the end of the line.
if len(line) + len(s.rstrip()) <= fullwidth:
line += s.rstrip()
# (end of terminalwriter borrowed code)
line += '\n\n'
stream.write(line)

@_ensure_topdir
def summary(self, stream):
@@ -155,15 +183,15 @@ def summary(self, stream):

# Output coverage section header.
if len(self.node_descs) == 1:
self.sep(stream, '-', f"coverage: {''.join(self.node_descs)}")
self.sep(stream, '_', f'coverage: {"".join(self.node_descs)}')
else:
self.sep(stream, '-', 'coverage')
self.sep(stream, '_', 'coverage')
for node_desc in sorted(self.node_descs):
self.sep(stream, ' ', f'{node_desc}')

# Report on any failed workers.
if self.failed_workers:
self.sep(stream, '-', 'coverage: failed workers')
self.sep(stream, '_', 'coverage: failed workers')
stream.write('The following workers failed to return coverage data, ensure that pytest-cov is installed on these workers.\n')
for node in self.failed_workers:
stream.write(f'{node.gateway.id}\n')
@@ -174,6 +202,7 @@ def summary(self, stream):
'show_missing': ('term-missing' in self.cov_report) or None,
'ignore_errors': True,
'file': stream,
'precision': self.cov_precision,
}
skip_covered = isinstance(self.cov_report, dict) and 'skip-covered' in self.cov_report.values()
options.update({'skip_covered': skip_covered or None})
@@ -341,7 +370,7 @@ def testnodedown(self, node, error):
# If worker is not collocated then we must save the data file
# that it returns to us.
if 'cov_worker_data' in output:
data_suffix = '%s.%s.%06d.%s' % (
data_suffix = '%s.%s.%06d.%s' % ( # noqa: UP031
socket.gethostname(),
os.getpid(),
random.randint(0, 999999), # noqa: S311
39 changes: 20 additions & 19 deletions src/pytest_cov/plugin.py
Original file line number Diff line number Diff line change
@@ -5,18 +5,21 @@
import warnings
from io import StringIO
from pathlib import Path
from typing import TYPE_CHECKING

import coverage
import pytest
from coverage.results import display_covered
from coverage.results import should_fail_under

from . import CovDisabledWarning
from . import CovFailUnderWarning
from . import CovReportWarning
from . import compat
from . import embed

if TYPE_CHECKING:
from .engine import CovController


def validate_report(arg):
file_choices = ['annotate', 'html', 'xml', 'json', 'lcov']
@@ -56,8 +59,7 @@ def validate_fail_under(num_str):
raise argparse.ArgumentTypeError('An integer or float value is required.') from None
if value > 100:
raise argparse.ArgumentTypeError(
'Your desire for over-achievement is admirable but misplaced. '
'The maximum value is 100. Perhaps write more integration tests?'
'Your desire for over-achievement is admirable but misplaced. The maximum value is 100. Perhaps write more integration tests?'
)
return value

@@ -199,7 +201,7 @@ class CovPlugin:
distributed worker.
"""

def __init__(self, options, pluginmanager, start=True, no_cov_should_warn=False):
def __init__(self, options: argparse.Namespace, pluginmanager, start=True, no_cov_should_warn=False):
"""Creates a coverage pytest plugin.
We read the rc file that coverage uses to get the data file
@@ -217,6 +219,7 @@ def __init__(self, options, pluginmanager, start=True, no_cov_should_warn=False)
self._start_path = None
self._disabled = False
self.options = options
self._wrote_heading = False

is_dist = getattr(options, 'numprocesses', False) or getattr(options, 'distload', False) or getattr(options, 'dist', 'no') != 'no'
if getattr(options, 'no_cov', False):
@@ -240,23 +243,15 @@ def __init__(self, options, pluginmanager, start=True, no_cov_should_warn=False)

# worker is started in pytest hook

def start(self, controller_cls, config=None, nodeid=None):
def start(self, controller_cls: type['CovController'], config=None, nodeid=None):
if config is None:
# fake config option for engine
class Config:
option = self.options

config = Config()

self.cov_controller = controller_cls(
self.options.cov_source,
self.options.cov_report,
self.options.cov_config,
self.options.cov_append,
self.options.cov_branch,
config,
nodeid,
)
self.cov_controller = controller_cls(self.options, config, nodeid)
self.cov_controller.start()
self._started = True
self._start_path = Path.cwd()
@@ -352,13 +347,18 @@ def pytest_runtestloop(self, session):
p=cov_precision,
)
session.config.pluginmanager.getplugin('terminalreporter').write(f'\nERROR: {message}\n', red=True, bold=True)
warnings.warn(CovFailUnderWarning(message), stacklevel=1)
# make sure we get the EXIT_TESTSFAILED exit code
compat_session.testsfailed += 1

def write_heading(self, terminalreporter):
if not self._wrote_heading:
terminalreporter.write_sep('=', 'tests coverage')
self._wrote_heading = True

def pytest_terminal_summary(self, terminalreporter):
if self._disabled:
if self.options.no_cov_should_warn:
self.write_heading(terminalreporter)
message = 'Coverage disabled via --no-cov switch!'
terminalreporter.write(f'WARNING: {message}\n', red=True, bold=True)
warnings.warn(CovDisabledWarning(message), stacklevel=1)
@@ -372,14 +372,15 @@ def pytest_terminal_summary(self, terminalreporter):

report = self.cov_report.getvalue()

# Avoid undesirable new lines when output is disabled with "--cov-report=".
if report:
terminalreporter.write('\n' + report + '\n')
self.write_heading(terminalreporter)
terminalreporter.write(report)

if self.options.cov_fail_under is not None and self.options.cov_fail_under > 0:
self.write_heading(terminalreporter)
failed = self.cov_total < self.options.cov_fail_under
markup = {'red': True, 'bold': True} if failed else {'green': True}
message = '{fail}Required test coverage of {required}% {reached}. ' 'Total coverage: {actual:.2f}%\n'.format(
message = '{fail}Required test coverage of {required}% {reached}. Total coverage: {actual:.2f}%\n'.format(
required=self.options.cov_fail_under,
actual=self.cov_total,
fail='FAIL ' if failed else '',
@@ -426,7 +427,7 @@ def switch_context(self, item, when):


@pytest.fixture
def no_cover(): # noqa: PT004
def no_cover():
"""A pytest fixture to disable coverage."""


111 changes: 72 additions & 39 deletions tests/test_pytest_cov.py

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ envlist =
clean,
check,
docs,
{py39,py310,py311,py312,pypy39,pypy310}-{pytest83}-{xdist36}-{coverage76},
{py39,py310,py311,py312,py313,pypy39,pypy310}-{pytest83}-{xdist36}-{coverage77},
report
ignore_basepython_conflict = true

@@ -28,6 +28,7 @@ basepython =
py310: {env:TOXPYTHON:python3.10}
py311: {env:TOXPYTHON:python3.11}
py312: {env:TOXPYTHON:python3.12}
py313: {env:TOXPYTHON:python3.13}
{bootstrap,clean,check,report,docs}: {env:TOXPYTHON:python3}
extras = testing
setenv =
@@ -38,7 +39,7 @@ setenv =
pytest80: _DEP_PYTEST=pytest==8.0.2
pytest81: _DEP_PYTEST=pytest==8.1.1
pytest82: _DEP_PYTEST=pytest==8.2.2
pytest83: _DEP_PYTEST=pytest==8.3.3
pytest83: _DEP_PYTEST=pytest==8.3.5

xdist32: _DEP_PYTESTXDIST=pytest-xdist==3.2.0
xdist33: _DEP_PYTESTXDIST=pytest-xdist==3.3.1
@@ -51,7 +52,8 @@ setenv =
coverage73: _DEP_COVERAGE=coverage==7.3.4
coverage74: _DEP_COVERAGE=coverage==7.4.4
coverage75: _DEP_COVERAGE=coverage==7.5.4
coverage76: _DEP_COVERAGE=coverage==7.6.4
coverage76: _DEP_COVERAGE=coverage==7.6.12
coverage77: _DEP_COVERAGE=coverage==7.7.1
# For testing against a coverage.py working tree.
coveragedev: _DEP_COVERAGE=-e{env:COVERAGE_HOME}
passenv =