From fb2c67ea0832ef75259db9209908508eab1e0a8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danilo=20=C5=A0egan?= Date: Thu, 25 Mar 2021 15:53:16 +0100 Subject: [PATCH 001/117] Implement --cov-reset option that resets accumulated --cov directories to an empty list. --- docs/config.rst | 2 ++ src/pytest_cov/plugin.py | 2 ++ tests/test_pytest_cov.py | 27 +++++++++++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/docs/config.rst b/docs/config.rst index 0dfb17a0..6aa0e411 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -66,6 +66,8 @@ The complete list of command line options is: False --no-cov Disable coverage report completely (useful for debuggers). Default: False + --cov-reset Reset cov sources accumulated in options so far. + Mostly useful for scripts and configuration files. --cov-fail-under=MIN Fail if the total coverage is less than MIN. --cov-append Do not delete coverage but append to current. Default: False diff --git a/src/pytest_cov/plugin.py b/src/pytest_cov/plugin.py index b875f409..3662cfbb 100644 --- a/src/pytest_cov/plugin.py +++ b/src/pytest_cov/plugin.py @@ -70,6 +70,8 @@ def pytest_addoption(parser): nargs='?', const=True, dest='cov_source', help='Path or package name to measure during execution (multi-allowed). ' 'Use --cov= to not do any source filtering and record everything.') + group.addoption('--cov-reset', action='store_const', const=[], dest='cov_source', + help='Reset cov sources accumulated in options so far. ') group.addoption('--cov-report', action=StoreReport, default={}, metavar='TYPE', type=validate_report, help='Type of report to generate: term, term-missing, ' diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 0d1b5a23..2647edfd 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -1999,6 +1999,33 @@ def test_double_cov2(testdir): assert result.ret == 0 +def test_cov_reset(testdir): + script = testdir.makepyfile(SCRIPT_SIMPLE) + result = testdir.runpytest('-v', + '--assert=plain', + '--cov=%s' % script.dirpath(), + '--cov-reset', + script) + + assert 'coverage: platform' not in result.stdout.str() + + +def test_cov_reset_then_set(testdir): + script = testdir.makepyfile(SCRIPT_SIMPLE) + result = testdir.runpytest('-v', + '--assert=plain', + '--cov=%s' % script.dirpath(), + '--cov-reset', + '--cov=%s' % script.dirpath(), + script) + + result.stdout.fnmatch_lines([ + '*- coverage: platform *, python * -*', + 'test_cov_reset_then_set* %s*' % SCRIPT_SIMPLE_RESULT, + '*1 passed*' + ]) + + @pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"') def test_cov_and_no_cov(testdir): script = testdir.makepyfile(SCRIPT_SIMPLE) From 297965e05a7712e50c2f8a4dc26cd189132aba22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 14 May 2021 14:37:02 +0300 Subject: [PATCH 002/117] Update appveyor envs. Workaround pypy install problem. --- .appveyor.yml | 23 ++++++++++++++++++++++- ci/templates/.appveyor.yml | 23 ++++++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index fde3a6aa..6ee159e9 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,6 +1,9 @@ # NOTE: this file is auto-generated via ci/bootstrap.py (ci/templates/.appveyor.yml). version: '{branch}-{build}' build: off +image: + - Visual Studio 2015 + - Visual Studio 2019 environment: matrix: - TOXENV: check @@ -8,8 +11,26 @@ environment: - TOXENV: 'py35-pytest46-xdist127-coverage55' - TOXENV: 'py36-pytest46-xdist127-coverage55,py36-pytest46-xdist133-coverage55,py36-pytest54-xdist133-coverage55,py36-pytest62-xdist202-coverage55' - TOXENV: 'py37-pytest46-xdist127-coverage55,py37-pytest46-xdist133-coverage55,py37-pytest54-xdist133-coverage55,py37-pytest62-xdist202-coverage55' + - TOXENV: 'py38-pytest46-xdist133-coverage55,py38-pytest54-xdist133-coverage55,py38-pytest62-xdist202-coverage55' + - TOXENV: 'py39-pytest62-xdist202-coverage55' - TOXENV: 'pypy-pytest46-xdist127-coverage55' - + - TOXENV: 'pypy3-pytest46-xdist127-coverage55,pypy3-pytest46-xdist133-coverage55,pypy3-pytest54-xdist133-coverage55,pypy3-pytest62-xdist202-coverage55' +matrix: + exclude: + - image: Visual Studio 2015 + TOXENV: 'py27-pytest46-xdist127-coverage55' + - image: Visual Studio 2019 + TOXENV: 'py36-pytest46-xdist127-coverage55,py36-pytest46-xdist133-coverage55,py36-pytest54-xdist133-coverage55,py36-pytest62-xdist202-coverage55' + - image: Visual Studio 2019 + TOXENV: 'py37-pytest46-xdist127-coverage55,py37-pytest46-xdist133-coverage55,py37-pytest54-xdist133-coverage55,py37-pytest62-xdist202-coverage55' + - image: Visual Studio 2019 + TOXENV: 'py38-pytest46-xdist133-coverage55,py38-pytest54-xdist133-coverage55,py38-pytest62-xdist202-coverage55' + - image: Visual Studio 2019 + TOXENV: 'py39-pytest62-xdist202-coverage55' + - image: Visual Studio 2019 + TOXENV: 'pypy-pytest46-xdist127-coverage55' + - image: Visual Studio 2019 + TOXENV: 'pypy3-pytest46-xdist127-coverage55,pypy3-pytest46-xdist133-coverage55,pypy3-pytest54-xdist133-coverage55,pypy3-pytest62-xdist202-coverage55' init: - ps: echo $env:TOXENV - ps: ls C:\Python* diff --git a/ci/templates/.appveyor.yml b/ci/templates/.appveyor.yml index 92630e15..01ca3493 100644 --- a/ci/templates/.appveyor.yml +++ b/ci/templates/.appveyor.yml @@ -1,5 +1,8 @@ version: '{branch}-{build}' build: off +image: + - Visual Studio 2015 + - Visual Studio 2019 environment: matrix: - TOXENV: check @@ -7,8 +10,26 @@ environment: - TOXENV: '{{ py35_environments|join(",") }}' - TOXENV: '{{ py36_environments|join(",") }}' - TOXENV: '{{ py37_environments|join(",") }}' + - TOXENV: '{{ py38_environments|join(",") }}' + - TOXENV: '{{ py39_environments|join(",") }}' - TOXENV: '{{ pypy_environments|join(",") }}' - + - TOXENV: '{{ pypy3_environments|join(",") }}' +matrix: + exclude: + - image: Visual Studio 2015 + TOXENV: '{{ py27_environments|join(",") }}' + - image: Visual Studio 2019 + TOXENV: '{{ py36_environments|join(",") }}' + - image: Visual Studio 2019 + TOXENV: '{{ py37_environments|join(",") }}' + - image: Visual Studio 2019 + TOXENV: '{{ py38_environments|join(",") }}' + - image: Visual Studio 2019 + TOXENV: '{{ py39_environments|join(",") }}' + - image: Visual Studio 2019 + TOXENV: '{{ pypy_environments|join(",") }}' + - image: Visual Studio 2019 + TOXENV: '{{ pypy3_environments|join(",") }}' init: - ps: echo $env:TOXENV - ps: ls C:\Python* From edc60bb9e560d7ea53056590a64a4e5c81b5e62c Mon Sep 17 00:00:00 2001 From: Brian Rutledge Date: Tue, 1 Jun 2021 05:47:05 -0400 Subject: [PATCH 003/117] Document `show_contexts` (#473) --- docs/contexts.rst | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/contexts.rst b/docs/contexts.rst index e5256fcc..c502c1d6 100644 --- a/docs/contexts.rst +++ b/docs/contexts.rst @@ -2,10 +2,12 @@ Contexts ======== -Coverage.py 5.0 can record separate coverage data for different contexts during +Coverage.py 5.0 can record separate coverage data for `different contexts`_ during one run of a test suite. Pytest-cov can use this feature to record coverage data for each test individually, with the ``--cov-context=test`` option. +.. _`different_contexts`: https://coverage.readthedocs.io/en/stable/contexts.html + The context name recorded in the coverage.py database is the pytest test id, and the phase of execution, one of "setup", "run", or "teardown". These two are separated with a pipe symbol. You might see contexts like:: @@ -16,3 +18,12 @@ are separated with a pipe symbol. You might see contexts like:: Note that parameterized tests include the values of the parameters in the test id, and each set of parameter values is recorded as a separate test. + +To view contexts when using ``--cov-report=html``, add this to your ``.coveragerc``:: + + [html] + show_contexts = True + +The HTML report will include an annotation on each covered line, indicating the +number of contexts that executed the line. Clicking the annotation displays a +list of the contexts. From 34edfa1cf80f8d722dec4f587b10d2783f89a0f0 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Tue, 1 Jun 2021 10:51:23 +0100 Subject: [PATCH 004/117] Avoid using toml as extra (#472) Avoid pip-compile bug which forces it to produce invalid constraints files. Installing toml as a direct dependency is enough to avoid this bug. Related: https://github.com/jazzband/pip-tools/issues/1300 --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ca77760c..ebb7e9b0 100755 --- a/setup.py +++ b/setup.py @@ -124,7 +124,8 @@ def run(self): ], install_requires=[ 'pytest>=4.6', - 'coverage[toml]>=5.2.1' + 'coverage>=5.2.1', + 'toml' ], python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*', extras_require={ From ac7cc916268266807d43e9d7f0bcb1ae4277010c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 1 Jun 2021 13:00:56 +0300 Subject: [PATCH 005/117] Update changelog. --- AUTHORS.rst | 2 ++ CHANGELOG.rst | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/AUTHORS.rst b/AUTHORS.rst index d3d01d11..14cb7781 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -46,3 +46,5 @@ Authors * Pamela McA'Nulty - https://github.com/PamelaM * Christian Riedel - https://github.com/Cielquan * Chris Sreesangkom - https://github.com/csreesan +* Sorin Sbarnea - https://github.com/ssbarnea +* Brian Rutledge - https://github.com/bhrutledge diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 23e9a3b9..ef5ad741 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,14 @@ Changelog ========= +2.12.1 (2021-06-01) +------------------- + +* Changed the `toml` requirement to be always be directly required (instead of being required through a coverage extra). + This fixes issues with pip-compile (`pip-tools#1300 `_). + Contributed by Sorin Sbarnea in `#472 `_. +* Documented ``show_contexts``. + Contributed by Brian Rutledge in `#473 `_. 2.12.0 (2021-05-14) ------------------- From 17252e0896ac18d7b15284d8dc4e2ecb16b050c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 1 Jun 2021 13:02:22 +0300 Subject: [PATCH 006/117] Remove unnecessary pin. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ebb7e9b0..d3f9f133 100755 --- a/setup.py +++ b/setup.py @@ -132,7 +132,7 @@ def run(self): 'testing': [ 'fields', 'hunter', - 'process-tests==2.0.2', + 'process-tests', 'six', 'pytest-xdist', 'virtualenv', From df20c973a5a34c4e085362dcf41df88fc3ee6297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 1 Jun 2021 14:13:02 +0300 Subject: [PATCH 007/117] Looks like I got the images wrong. --- .appveyor.yml | 12 ++++++------ ci/templates/.appveyor.yml | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 6ee159e9..111eb3c5 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -17,19 +17,19 @@ environment: - TOXENV: 'pypy3-pytest46-xdist127-coverage55,pypy3-pytest46-xdist133-coverage55,pypy3-pytest54-xdist133-coverage55,pypy3-pytest62-xdist202-coverage55' matrix: exclude: - - image: Visual Studio 2015 - TOXENV: 'py27-pytest46-xdist127-coverage55' - image: Visual Studio 2019 + TOXENV: 'py27-pytest46-xdist127-coverage55' + - image: Visual Studio 2015 TOXENV: 'py36-pytest46-xdist127-coverage55,py36-pytest46-xdist133-coverage55,py36-pytest54-xdist133-coverage55,py36-pytest62-xdist202-coverage55' - - image: Visual Studio 2019 + - image: Visual Studio 2015 TOXENV: 'py37-pytest46-xdist127-coverage55,py37-pytest46-xdist133-coverage55,py37-pytest54-xdist133-coverage55,py37-pytest62-xdist202-coverage55' - - image: Visual Studio 2019 + - image: Visual Studio 2015 TOXENV: 'py38-pytest46-xdist133-coverage55,py38-pytest54-xdist133-coverage55,py38-pytest62-xdist202-coverage55' - - image: Visual Studio 2019 + - image: Visual Studio 2015 TOXENV: 'py39-pytest62-xdist202-coverage55' - image: Visual Studio 2019 TOXENV: 'pypy-pytest46-xdist127-coverage55' - - image: Visual Studio 2019 + - image: Visual Studio 2015 TOXENV: 'pypy3-pytest46-xdist127-coverage55,pypy3-pytest46-xdist133-coverage55,pypy3-pytest54-xdist133-coverage55,pypy3-pytest62-xdist202-coverage55' init: - ps: echo $env:TOXENV diff --git a/ci/templates/.appveyor.yml b/ci/templates/.appveyor.yml index 01ca3493..6358a6d1 100644 --- a/ci/templates/.appveyor.yml +++ b/ci/templates/.appveyor.yml @@ -16,19 +16,19 @@ environment: - TOXENV: '{{ pypy3_environments|join(",") }}' matrix: exclude: - - image: Visual Studio 2015 - TOXENV: '{{ py27_environments|join(",") }}' - image: Visual Studio 2019 + TOXENV: '{{ py27_environments|join(",") }}' + - image: Visual Studio 2015 TOXENV: '{{ py36_environments|join(",") }}' - - image: Visual Studio 2019 + - image: Visual Studio 2015 TOXENV: '{{ py37_environments|join(",") }}' - - image: Visual Studio 2019 + - image: Visual Studio 2015 TOXENV: '{{ py38_environments|join(",") }}' - - image: Visual Studio 2019 + - image: Visual Studio 2015 TOXENV: '{{ py39_environments|join(",") }}' - image: Visual Studio 2019 TOXENV: '{{ pypy_environments|join(",") }}' - - image: Visual Studio 2019 + - image: Visual Studio 2015 TOXENV: '{{ pypy3_environments|join(",") }}' init: - ps: echo $env:TOXENV From 3685acbd61df9135e41b188374347d04d2d689db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 1 Jun 2021 14:45:47 +0300 Subject: [PATCH 008/117] Use 2019 image for pypy. --- .appveyor.yml | 2 +- ci/templates/.appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 111eb3c5..a0ffc795 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -27,7 +27,7 @@ matrix: TOXENV: 'py38-pytest46-xdist133-coverage55,py38-pytest54-xdist133-coverage55,py38-pytest62-xdist202-coverage55' - image: Visual Studio 2015 TOXENV: 'py39-pytest62-xdist202-coverage55' - - image: Visual Studio 2019 + - image: Visual Studio 2015 TOXENV: 'pypy-pytest46-xdist127-coverage55' - image: Visual Studio 2015 TOXENV: 'pypy3-pytest46-xdist127-coverage55,pypy3-pytest46-xdist133-coverage55,pypy3-pytest54-xdist133-coverage55,pypy3-pytest62-xdist202-coverage55' diff --git a/ci/templates/.appveyor.yml b/ci/templates/.appveyor.yml index 6358a6d1..2b7e611c 100644 --- a/ci/templates/.appveyor.yml +++ b/ci/templates/.appveyor.yml @@ -26,7 +26,7 @@ matrix: TOXENV: '{{ py38_environments|join(",") }}' - image: Visual Studio 2015 TOXENV: '{{ py39_environments|join(",") }}' - - image: Visual Studio 2019 + - image: Visual Studio 2015 TOXENV: '{{ pypy_environments|join(",") }}' - image: Visual Studio 2015 TOXENV: '{{ pypy3_environments|join(",") }}' From 67d49b9e06cbc1cace3fc2cdfe70b09f55d1726f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 1 Jun 2021 20:24:18 +0300 Subject: [PATCH 009/117] =?UTF-8?q?Bump=20version:=202.12.0=20=E2=86=92=20?= =?UTF-8?q?2.12.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- README.rst | 4 ++-- docs/conf.py | 2 +- setup.py | 2 +- src/pytest_cov/__init__.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 7e15ff94..a838695a 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.12.0 +current_version = 2.12.1 commit = True tag = True diff --git a/README.rst b/README.rst index a0c44a71..149594fd 100644 --- a/README.rst +++ b/README.rst @@ -38,9 +38,9 @@ Overview .. |conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pytest-cov.svg :target: https://anaconda.org/conda-forge/pytest-cov -.. |commits-since| image:: https://img.shields.io/github/commits-since/pytest-dev/pytest-cov/v2.12.0.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/pytest-dev/pytest-cov/v2.12.1.svg :alt: Commits since latest release - :target: https://github.com/pytest-dev/pytest-cov/compare/v2.12.0...master + :target: https://github.com/pytest-dev/pytest-cov/compare/v2.12.1...master .. |wheel| image:: https://img.shields.io/pypi/wheel/pytest-cov.svg :alt: PyPI Wheel diff --git a/docs/conf.py b/docs/conf.py index 1da75b8a..0668fdc2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -26,7 +26,7 @@ year = '2016' author = 'pytest-cov contributors' copyright = '{}, {}'.format(year, author) -version = release = '2.12.0' +version = release = '2.12.1' pygments_style = 'trac' templates_path = ['.'] diff --git a/setup.py b/setup.py index d3f9f133..143746ab 100755 --- a/setup.py +++ b/setup.py @@ -85,7 +85,7 @@ def run(self): setup( name='pytest-cov', - version='2.12.0', + version='2.12.1', license='MIT', description='Pytest plugin for measuring coverage.', long_description='%s\n%s' % (read('README.rst'), re.sub(':[a-z]+:`~?(.*?)`', r'``\1``', read('CHANGELOG.rst'))), diff --git a/src/pytest_cov/__init__.py b/src/pytest_cov/__init__.py index a9f1cb62..dad3b766 100644 --- a/src/pytest_cov/__init__.py +++ b/src/pytest_cov/__init__.py @@ -1,2 +1,2 @@ """pytest-cov: avoid already-imported warning: PYTEST_DONT_REWRITE.""" -__version__ = '2.12.0' +__version__ = '2.12.1' From d5d06841f66d01105a65f7ceb7e1b404b9a28e1b Mon Sep 17 00:00:00 2001 From: James Smith Date: Mon, 19 Jul 2021 21:15:48 +0300 Subject: [PATCH 010/117] Fix typo in contexts.rst (#478) The hyperlink wasn't rendering properly. This looks better. --- docs/contexts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contexts.rst b/docs/contexts.rst index c502c1d6..f651db5d 100644 --- a/docs/contexts.rst +++ b/docs/contexts.rst @@ -6,7 +6,7 @@ Coverage.py 5.0 can record separate coverage data for `different contexts`_ duri one run of a test suite. Pytest-cov can use this feature to record coverage data for each test individually, with the ``--cov-context=test`` option. -.. _`different_contexts`: https://coverage.readthedocs.io/en/stable/contexts.html +.. _different contexts: https://coverage.readthedocs.io/en/stable/contexts.html The context name recorded in the coverage.py database is the pytest test id, and the phase of execution, one of "setup", "run", or "teardown". These two From 2b6660ecd9ecea3a3d610bddde4915f4745a56a9 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Mon, 19 Jul 2021 19:19:43 +0100 Subject: [PATCH 011/117] Revert "Avoid using toml as extra (#472)" (#477) This reverts commit 34edfa1cf80f8d722dec4f587b10d2783f89a0f0. --- setup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 143746ab..8cb1162b 100755 --- a/setup.py +++ b/setup.py @@ -124,8 +124,7 @@ def run(self): ], install_requires=[ 'pytest>=4.6', - 'coverage>=5.2.1', - 'toml' + 'coverage[toml]>=5.2.1' ], python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*', extras_require={ From d810c6508ec0ae4de7131f3bafaa486cb53bbbf7 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Sun, 25 Jul 2021 13:20:36 +0100 Subject: [PATCH 012/117] warn specific classes (#361) * warn specific classes * Update src/pytest_cov/plugin.py * Update src/pytest_cov/compat.py * Update src/pytest_cov/compat.py * Update src/pytest_cov/compat.py * Update src/pytest_cov/plugin.py * Update src/pytest_cov/plugin.py --- src/pytest_cov/plugin.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/pytest_cov/plugin.py b/src/pytest_cov/plugin.py index b875f409..f5a00953 100644 --- a/src/pytest_cov/plugin.py +++ b/src/pytest_cov/plugin.py @@ -14,6 +14,20 @@ class CoverageError(Exception): """Indicates that our coverage is too low""" +class PytestCovWarning(pytest.PytestWarning): + """ + The base for all pytest-cov warnings, never raised directly + """ + + +class CovDisabledWarning(PytestCovWarning): + """Indicates that Coverage was manually disabled""" + + +class CovReportWarning(PytestCovWarning): + """Indicates that we failed to generate a report""" + + def validate_report(arg): file_choices = ['annotate', 'html', 'xml'] term_choices = ['term', 'term-missing'] @@ -282,7 +296,7 @@ def pytest_runtestloop(self, session): message = 'Failed to generate report: %s\n' % exc session.config.pluginmanager.getplugin("terminalreporter").write( 'WARNING: %s\n' % message, red=True, bold=True) - warnings.warn(pytest.PytestWarning(message)) + warnings.warn(CovReportWarning(message)) self.cov_total = 0 assert self.cov_total is not None, 'Test coverage should never be `None`' if self._failed_cov_total(): @@ -294,7 +308,7 @@ def pytest_terminal_summary(self, terminalreporter): if self.options.no_cov_should_warn: message = 'Coverage disabled via --no-cov switch!' terminalreporter.write('WARNING: %s\n' % message, red=True, bold=True) - warnings.warn(pytest.PytestWarning(message)) + warnings.warn(CovDisabledWarning(message)) return if self.cov_controller is None: return From d976cded4eacdaba5b4099bec8949f4e94c40735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 28 Jul 2021 20:21:48 +0300 Subject: [PATCH 013/117] Improve argument validation a bit. --- src/pytest_cov/plugin.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/pytest_cov/plugin.py b/src/pytest_cov/plugin.py index f5a00953..ffb87945 100644 --- a/src/pytest_cov/plugin.py +++ b/src/pytest_cov/plugin.py @@ -56,16 +56,23 @@ def validate_report(arg): def validate_fail_under(num_str): try: - return int(num_str) + value = int(num_str) except ValueError: - return float(num_str) + try: + value = float(num_str) + except ValueError: + raise argparse.ArgumentTypeError('An integer or float value is required.') + 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?') + return value def validate_context(arg): if coverage.version_info <= (5, 0): raise argparse.ArgumentTypeError('Contexts are only supported with coverage.py >= 5.x') if arg != "test": - raise argparse.ArgumentTypeError('--cov-context=test is the only supported value') + raise argparse.ArgumentTypeError('The only supported value is "test".') return arg From 88da65813635f1b85e0873d5d067de1dc1ce7332 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Sat, 14 Aug 2021 07:19:15 +0100 Subject: [PATCH 014/117] pre-commit autoupdate --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f9942328..36701b79 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,16 +5,16 @@ exclude: '^(src/.*\.pth|\.tox|ci/templates|\.bumpversion\.cfg)(/|$)' repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: master + rev: v4.0.1 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: debug-statements - repo: https://github.com/timothycrosley/isort - rev: master + rev: 5.9.3 hooks: - id: isort - repo: https://gitlab.com/pycqa/flake8 - rev: master + rev: 3.9.2 hooks: - id: flake8 From 9382ac5864362c02305ead9a58890f0daaa4f38d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 14 Aug 2021 06:19:34 +0000 Subject: [PATCH 015/117] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- docs/config.rst | 10 +++++----- docs/contexts.rst | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index 0dfb17a0..38ff0c1b 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -23,13 +23,13 @@ For full details refer to the `coverage config file`_ documentation. .. note:: Important Note - This plugin overrides the ``data_file`` and ``parallel`` options of coverage. Unless you also run coverage without + This plugin overrides the ``data_file`` and ``parallel`` options of coverage. Unless you also run coverage without pytest-cov it's pointless to set those options in your ``.coveragerc``. - - If you use the ``--cov=something`` option (with a value) then coverage's ``source`` option will also get overriden. - If you have multiple sources it might be easier to set those in ``.coveragerc`` and always use ``--cov`` (wihout a value) + + If you use the ``--cov=something`` option (with a value) then coverage's ``source`` option will also get overriden. + If you have multiple sources it might be easier to set those in ``.coveragerc`` and always use ``--cov`` (wihout a value) instead of having a long command line with ``--cov=pkg1 --cov=pkg2 --cov=pkg3 ...``. - + If you use the ``--cov-branch`` option then coverage's ``branch`` option will also get overriden. If you wish to always add pytest-cov with pytest, you can use ``addopts`` under ``pytest`` or ``tool:pytest`` section. diff --git a/docs/contexts.rst b/docs/contexts.rst index f651db5d..cde920d5 100644 --- a/docs/contexts.rst +++ b/docs/contexts.rst @@ -22,8 +22,8 @@ id, and each set of parameter values is recorded as a separate test. To view contexts when using ``--cov-report=html``, add this to your ``.coveragerc``:: [html] - show_contexts = True - + show_contexts = True + The HTML report will include an annotation on each covered line, indicating the -number of contexts that executed the line. Clicking the annotation displays a +number of contexts that executed the line. Clicking the annotation displays a list of the contexts. From 02e0e6045a8382ae678f51742939b88fc67a09b5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 16 Aug 2021 18:46:23 +0000 Subject: [PATCH 016/117] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - https://github.com/timothycrosley/isort → https://github.com/PyCQA/isort - https://gitlab.com/pycqa/flake8 → https://github.com/PyCQA/flake8 --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 36701b79..67d55a68 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,11 +10,11 @@ repos: - id: trailing-whitespace - id: end-of-file-fixer - id: debug-statements - - repo: https://github.com/timothycrosley/isort + - repo: https://github.com/PyCQA/isort rev: 5.9.3 hooks: - id: isort - - repo: https://gitlab.com/pycqa/flake8 + - repo: https://github.com/PyCQA/flake8 rev: 3.9.2 hooks: - id: flake8 From 5ab02e686a30afa5ac7c9d28829fddb9137f5e64 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Fri, 13 Aug 2021 13:40:17 +0100 Subject: [PATCH 017/117] Update .pre-commit-config.yaml --- .pre-commit-config.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 67d55a68..98314202 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,3 +18,7 @@ repos: rev: 3.9.2 hooks: - id: flake8 + - repo: https://github.com/asottile/pyupgrade + rev: v2.23.3 + hooks: + - id: pyupgrade From 4a06aba59b50f3bdbe5e1fe3b23d3cab787c6823 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 13 Aug 2021 22:53:07 +0000 Subject: [PATCH 018/117] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ci/bootstrap.py | 8 ++++---- docs/conf.py | 2 +- examples/adhoc-layout/example/__init__.py | 1 - examples/src-layout/src/example/__init__.py | 1 - setup.py | 2 +- src/pytest_cov/engine.py | 4 ++-- tests/test_pytest_cov.py | 6 +++--- 7 files changed, 11 insertions(+), 13 deletions(-) diff --git a/ci/bootstrap.py b/ci/bootstrap.py index 61747a15..6e9e75f2 100755 --- a/ci/bootstrap.py +++ b/ci/bootstrap.py @@ -30,7 +30,7 @@ def exec_in_env(): if not exists(env_path): import subprocess - print("Making bootstrap env in: {0} ...".format(env_path)) + print("Making bootstrap env in: {} ...".format(env_path)) try: check_call([sys.executable, "-m", "venv", env_path]) except subprocess.CalledProcessError: @@ -44,7 +44,7 @@ def exec_in_env(): if not os.path.exists(python_executable): python_executable += '.exe' - print("Re-executing with: {0}".format(python_executable)) + print("Re-executing with: {}".format(python_executable)) print("+ exec", python_executable, __file__, "--no-env") os.execv(python_executable, [python_executable, __file__, "--no-env"]) @@ -52,7 +52,7 @@ def exec_in_env(): def main(): import jinja2 - print("Project path: {0}".format(base_path)) + print("Project path: {}".format(base_path)) jinja = jinja2.Environment( loader=jinja2.FileSystemLoader(join(base_path, "ci", "templates")), @@ -89,5 +89,5 @@ def main(): elif not args: exec_in_env() else: - print("Unexpected arguments {0}".format(args), file=sys.stderr) + print("Unexpected arguments {}".format(args), file=sys.stderr) sys.exit(1) diff --git a/docs/conf.py b/docs/conf.py index 0668fdc2..d3c31081 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -47,7 +47,7 @@ html_sidebars = { '**': ['searchbox.html', 'globaltoc.html', 'sourcelink.html'], } -html_short_title = '%s-%s' % (project, version) +html_short_title = '{}-{}'.format(project, version) napoleon_use_ivar = True napoleon_use_rtype = False diff --git a/examples/adhoc-layout/example/__init__.py b/examples/adhoc-layout/example/__init__.py index 18080ac5..8a6dfd5e 100644 --- a/examples/adhoc-layout/example/__init__.py +++ b/examples/adhoc-layout/example/__init__.py @@ -1,4 +1,3 @@ - import sys PY2 = sys.version_info[0] == 2 diff --git a/examples/src-layout/src/example/__init__.py b/examples/src-layout/src/example/__init__.py index 18080ac5..8a6dfd5e 100644 --- a/examples/src-layout/src/example/__init__.py +++ b/examples/src-layout/src/example/__init__.py @@ -1,4 +1,3 @@ - import sys PY2 = sys.version_info[0] == 2 diff --git a/setup.py b/setup.py index 8cb1162b..631573ea 100755 --- a/setup.py +++ b/setup.py @@ -88,7 +88,7 @@ def run(self): version='2.12.1', license='MIT', description='Pytest plugin for measuring coverage.', - long_description='%s\n%s' % (read('README.rst'), re.sub(':[a-z]+:`~?(.*?)`', r'``\1``', read('CHANGELOG.rst'))), + long_description='{}\n{}'.format(read('README.rst'), re.sub(':[a-z]+:`~?(.*?)`', r'``\1``', read('CHANGELOG.rst'))), author='Marc Schlaich', author_email='marc.schlaich@gmail.com', url='https://github.com/pytest-dev/pytest-cov', diff --git a/src/pytest_cov/engine.py b/src/pytest_cov/engine.py index 084e92ea..ccdc99f1 100644 --- a/src/pytest_cov/engine.py +++ b/src/pytest_cov/engine.py @@ -116,7 +116,7 @@ def unset_env(): def get_node_desc(platform, version_info): """Return a description of this node.""" - return 'platform %s, python %s' % (platform, '%s.%s.%s-%s-%s' % version_info[:5]) + return 'platform {}, python {}'.format(platform, '%s.%s.%s-%s-%s' % version_info[:5]) @staticmethod def sep(stream, s, txt): @@ -126,7 +126,7 @@ def sep(stream, s, txt): sep_total = max((70 - 2 - len(txt)), 2) sep_len = sep_total // 2 sep_extra = sep_total % 2 - out = '%s %s %s\n' % (s * sep_len, txt, s * (sep_len + sep_extra)) + out = '{} {} {}\n'.format(s * sep_len, txt, s * (sep_len + sep_extra)) stream.write(out) @_ensure_topdir diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 0d1b5a23..b4b9f534 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -780,13 +780,13 @@ def test_dist_not_collocated_coveragerc_source(testdir, prop): dir2 = testdir.mkdir('dir2') testdir.tmpdir.join('.coveragerc').write(''' [run] -%s -source = %s +{} +source = {} [paths] source = . dir1 - dir2''' % (prop.conf, script.dirpath())) + dir2'''.format(prop.conf, script.dirpath())) result = testdir.runpytest('-v', '--cov', From 3f9576caa68416a32e1b1458aa6a7e1f5b758663 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Fri, 13 Aug 2021 23:55:42 +0100 Subject: [PATCH 019/117] remove py2 compatibility syntax --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 98314202..e07f891b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,3 +22,4 @@ repos: rev: v2.23.3 hooks: - id: pyupgrade + args: [--py36-plus] From b3a41c38f9bb5678a7c0ff8939f781ddaa8c8b5d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 13 Aug 2021 22:55:49 +0000 Subject: [PATCH 020/117] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- ci/bootstrap.py | 14 +++++--------- docs/conf.py | 7 ++----- setup.py | 5 +---- src/pytest_cov/compat.py | 2 +- src/pytest_cov/engine.py | 6 +++--- src/pytest_cov/plugin.py | 10 +++++----- tests/test_pytest_cov.py | 4 ++-- 7 files changed, 19 insertions(+), 29 deletions(-) diff --git a/ci/bootstrap.py b/ci/bootstrap.py index 6e9e75f2..77daad5b 100755 --- a/ci/bootstrap.py +++ b/ci/bootstrap.py @@ -1,8 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import print_function -from __future__ import unicode_literals import os import subprocess @@ -30,7 +26,7 @@ def exec_in_env(): if not exists(env_path): import subprocess - print("Making bootstrap env in: {} ...".format(env_path)) + print(f"Making bootstrap env in: {env_path} ...") try: check_call([sys.executable, "-m", "venv", env_path]) except subprocess.CalledProcessError: @@ -44,7 +40,7 @@ def exec_in_env(): if not os.path.exists(python_executable): python_executable += '.exe' - print("Re-executing with: {}".format(python_executable)) + print(f"Re-executing with: {python_executable}") print("+ exec", python_executable, __file__, "--no-env") os.execv(python_executable, [python_executable, __file__, "--no-env"]) @@ -52,7 +48,7 @@ def exec_in_env(): def main(): import jinja2 - print("Project path: {}".format(base_path)) + print(f"Project path: {base_path}") jinja = jinja2.Environment( loader=jinja2.FileSystemLoader(join(base_path, "ci", "templates")), @@ -78,7 +74,7 @@ def main(): with open(join(base_path, name), "w") as fh: fh.write('# NOTE: this file is auto-generated via ci/bootstrap.py (ci/templates/%s).\n' % name) fh.write(jinja.get_template(name).render(**template_vars)) - print("Wrote {}".format(name)) + print(f"Wrote {name}") print("DONE.") @@ -89,5 +85,5 @@ def main(): elif not args: exec_in_env() else: - print("Unexpected arguments {}".format(args), file=sys.stderr) + print(f"Unexpected arguments {args}", file=sys.stderr) sys.exit(1) diff --git a/docs/conf.py b/docs/conf.py index d3c31081..45da656c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - import os import sphinx_py3doc_enhanced_theme @@ -25,7 +22,7 @@ project = 'pytest-cov' year = '2016' author = 'pytest-cov contributors' -copyright = '{}, {}'.format(year, author) +copyright = f'{year}, {author}' version = release = '2.12.1' pygments_style = 'trac' @@ -47,7 +44,7 @@ html_sidebars = { '**': ['searchbox.html', 'globaltoc.html', 'sourcelink.html'], } -html_short_title = '{}-{}'.format(project, version) +html_short_title = f'{project}-{version}' napoleon_use_ivar = True napoleon_use_rtype = False diff --git a/setup.py b/setup.py index 631573ea..0497db1d 100755 --- a/setup.py +++ b/setup.py @@ -1,7 +1,4 @@ #!/usr/bin/env python -# -*- encoding: utf-8 -*- -from __future__ import absolute_import -from __future__ import print_function import io import re @@ -22,7 +19,7 @@ def read(*names, **kwargs): - with io.open( + with open( join(dirname(__file__), *names), encoding=kwargs.get('encoding', 'utf8') ) as fh: diff --git a/src/pytest_cov/compat.py b/src/pytest_cov/compat.py index 5b4a0bfb..f422f25c 100644 --- a/src/pytest_cov/compat.py +++ b/src/pytest_cov/compat.py @@ -14,7 +14,7 @@ hookwrapper = pytest.mark.hookwrapper -class SessionWrapper(object): +class SessionWrapper: def __init__(self, session): self._session = session if hasattr(session, 'testsfailed'): diff --git a/src/pytest_cov/engine.py b/src/pytest_cov/engine.py index ccdc99f1..0303c2f1 100644 --- a/src/pytest_cov/engine.py +++ b/src/pytest_cov/engine.py @@ -14,7 +14,7 @@ from .embed import cleanup -class _NullFile(object): +class _NullFile: @staticmethod def write(v): pass @@ -49,7 +49,7 @@ def ensure_topdir_wrapper(self, *args, **kwargs): return ensure_topdir_wrapper -class CovController(object): +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): @@ -126,7 +126,7 @@ def sep(stream, s, txt): sep_total = max((70 - 2 - len(txt)), 2) sep_len = sep_total // 2 sep_extra = sep_total % 2 - out = '{} {} {}\n'.format(s * sep_len, txt, s * (sep_len + sep_extra)) + out = f'{s * sep_len} {txt} {s * (sep_len + sep_extra)}\n' stream.write(out) @_ensure_topdir diff --git a/src/pytest_cov/plugin.py b/src/pytest_cov/plugin.py index f5a00953..e214627c 100644 --- a/src/pytest_cov/plugin.py +++ b/src/pytest_cov/plugin.py @@ -36,7 +36,7 @@ def validate_report(arg): values = arg.split(":", 1) report_type = values[0] if report_type not in all_choices + ['']: - msg = 'invalid choice: "{}" (choose from "{}")'.format(arg, all_choices) + msg = f'invalid choice: "{arg}" (choose from "{all_choices}")' raise argparse.ArgumentTypeError(msg) if len(values) == 1: @@ -141,7 +141,7 @@ def pytest_load_initial_conftests(early_config, parser, args): early_config.pluginmanager.register(plugin, '_cov') -class CovPlugin(object): +class CovPlugin: """Use coverage package to produce code coverage reports. Delegates all work to a particular implementation based on whether @@ -196,7 +196,7 @@ def start(self, controller_cls, config=None, nodeid=None): if config is None: # fake config option for engine - class Config(object): + class Config: option = self.options config = Config() @@ -354,7 +354,7 @@ def pytest_runtest_call(self, item): yield -class TestContextPlugin(object): +class TestContextPlugin: def __init__(self, cov): self.cov = cov @@ -368,7 +368,7 @@ def pytest_runtest_call(self, item): self.switch_context(item, 'run') def switch_context(self, item, when): - context = "{item.nodeid}|{when}".format(item=item, when=when) + context = f"{item.nodeid}|{when}" self.cov.switch_context(context) os.environ['COV_CORE_CONTEXT'] = context diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index b4b9f534..670bfcdd 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -1964,7 +1964,7 @@ def bad_init(): monkeypatch.setattr(embed, 'init', bad_init) monkeypatch.setattr(sys, 'stderr', buff) monkeypatch.setitem(os.environ, 'COV_CORE_SOURCE', 'foobar') - exec_(payload) + exec(payload) assert buff.getvalue() == '''pytest-cov: Failed to setup subprocess coverage. Environ: {'COV_CORE_SOURCE': 'foobar'} Exception: SpecificError() ''' @@ -2087,7 +2087,7 @@ def test_contexts(testdir, opts): continue data.set_query_context(context) actual = set(data.lines(test_context_path)) - assert line_data[label] == actual, "Wrong lines for context {!r}".format(context) + assert line_data[label] == actual, f"Wrong lines for context {context!r}" @pytest.mark.skipif("coverage.version_info >= (5, 0)") From ce732093f16ca43e0fb8d7075e666bf0960146df Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Fri, 13 Aug 2021 23:57:38 +0100 Subject: [PATCH 021/117] remove unused io import from setup.py --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 0497db1d..9508c16a 100755 --- a/setup.py +++ b/setup.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -import io import re from distutils.command.build import build from glob import glob From fadb280413fc1a73ad83443a692f6aae086c1696 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Fri, 13 Aug 2021 23:59:28 +0100 Subject: [PATCH 022/117] remove unused six import --- tests/test_pytest_cov.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 670bfcdd..233058fd 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -16,7 +16,6 @@ from process_tests import TestProcess as _TestProcess from process_tests import dump_on_error from process_tests import wait_for_strings -from six import exec_ import pytest_cov.plugin From f17d8c524125497a5359842788ad9946a292f4ab Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 18 Aug 2021 08:26:03 +0100 Subject: [PATCH 023/117] only exclude directories that need exluding --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e07f891b..dc36fa2b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,13 +2,13 @@ # pre-commit install # To update the pre-commit hooks run: # pre-commit install-hooks -exclude: '^(src/.*\.pth|\.tox|ci/templates|\.bumpversion\.cfg)(/|$)' repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.0.1 hooks: - id: trailing-whitespace - id: end-of-file-fixer + exclude: '.*\.pth$' - id: debug-statements - repo: https://github.com/PyCQA/isort rev: 5.9.3 From c9f11ed7278ca2dc0a68df2010203ad363408e69 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 18 Aug 2021 08:27:44 +0100 Subject: [PATCH 024/117] remove unusupported python versions from ci/tox --- .appveyor.yml | 8 -------- tox.ini | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index a0ffc795..9f176ee5 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -7,18 +7,13 @@ image: environment: matrix: - TOXENV: check - - TOXENV: 'py27-pytest46-xdist127-coverage55' - - TOXENV: 'py35-pytest46-xdist127-coverage55' - TOXENV: 'py36-pytest46-xdist127-coverage55,py36-pytest46-xdist133-coverage55,py36-pytest54-xdist133-coverage55,py36-pytest62-xdist202-coverage55' - TOXENV: 'py37-pytest46-xdist127-coverage55,py37-pytest46-xdist133-coverage55,py37-pytest54-xdist133-coverage55,py37-pytest62-xdist202-coverage55' - TOXENV: 'py38-pytest46-xdist133-coverage55,py38-pytest54-xdist133-coverage55,py38-pytest62-xdist202-coverage55' - TOXENV: 'py39-pytest62-xdist202-coverage55' - - TOXENV: 'pypy-pytest46-xdist127-coverage55' - TOXENV: 'pypy3-pytest46-xdist127-coverage55,pypy3-pytest46-xdist133-coverage55,pypy3-pytest54-xdist133-coverage55,pypy3-pytest62-xdist202-coverage55' matrix: exclude: - - image: Visual Studio 2019 - TOXENV: 'py27-pytest46-xdist127-coverage55' - image: Visual Studio 2015 TOXENV: 'py36-pytest46-xdist127-coverage55,py36-pytest46-xdist133-coverage55,py36-pytest54-xdist133-coverage55,py36-pytest62-xdist202-coverage55' - image: Visual Studio 2015 @@ -27,15 +22,12 @@ matrix: TOXENV: 'py38-pytest46-xdist133-coverage55,py38-pytest54-xdist133-coverage55,py38-pytest62-xdist202-coverage55' - image: Visual Studio 2015 TOXENV: 'py39-pytest62-xdist202-coverage55' - - image: Visual Studio 2015 - TOXENV: 'pypy-pytest46-xdist127-coverage55' - image: Visual Studio 2015 TOXENV: 'pypy3-pytest46-xdist127-coverage55,pypy3-pytest46-xdist133-coverage55,pypy3-pytest54-xdist133-coverage55,pypy3-pytest62-xdist202-coverage55' init: - ps: echo $env:TOXENV - ps: ls C:\Python* install: - - IF "%TOXENV:~0,5%" == "pypy-" choco install --no-progress python.pypy - IF "%TOXENV:~0,6%" == "pypy3-" choco install --no-progress pypy3 - SET PATH=C:\tools\pypy\pypy;%PATH% - C:\Python37\python -m pip install --progress-bar=off tox -rci/requirements.txt diff --git a/tox.ini b/tox.ini index 6be986e9..490a5201 100644 --- a/tox.ini +++ b/tox.ini @@ -13,7 +13,7 @@ passenv = [tox] envlist = check - py{27,35,36,37,py,py3}-pytest46-xdist127-coverage{55} + py{36,37,py,py3}-pytest46-xdist127-coverage{55} py{36,37,38,py3}-pytest{46,54}-xdist133-coverage{55} py{36,37,38,39,py3}-pytest{62}-xdist202-coverage{55} docs From 555fdcecc0da3d561b10a49a4b9333e4f8ec16b8 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 18 Aug 2021 12:57:16 +0100 Subject: [PATCH 025/117] update python_requires and trove classifiers --- setup.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 9508c16a..fa3e1a42 100755 --- a/setup.py +++ b/setup.py @@ -103,10 +103,8 @@ def run(self): 'Operating System :: POSIX', 'Operating System :: Unix', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3 :: Only', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', @@ -122,7 +120,7 @@ def run(self): 'pytest>=4.6', 'coverage[toml]>=5.2.1' ], - python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*', + python_requires='>=3.6', extras_require={ 'testing': [ 'fields', From 1f4277cd2733fa22702deb5aaca80db1a2747e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Bielawski?= Date: Thu, 26 Aug 2021 15:17:47 +0100 Subject: [PATCH 026/117] Add Python 3.9 to trove classifiers (#481) --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 8cb1162b..d4e0c6de 100755 --- a/setup.py +++ b/setup.py @@ -110,10 +110,10 @@ def run(self): 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Software Development :: Testing', From db5d7c9db1c12a93ba4e896c3cb15b77a65607de Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 20 Sep 2021 16:53:20 +0300 Subject: [PATCH 027/117] Replace Travis CI with GitHub Actions --- .github/workflows/examples.yml | 45 +++++++++++++++++ .github/workflows/lint.yml | 41 ++++++++++++++++ .github/workflows/test.yml | 68 ++++++++++++++++++++++++++ .travis.yml | 89 ---------------------------------- CONTRIBUTING.rst | 4 +- MANIFEST.in | 3 +- README.rst | 8 +-- ci/templates/.travis.yml | 61 ----------------------- docs/releasing.rst | 2 +- docs/reporting.rst | 2 +- 10 files changed, 164 insertions(+), 159 deletions(-) create mode 100644 .github/workflows/examples.yml create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/test.yml delete mode 100644 .travis.yml delete mode 100644 ci/templates/.travis.yml diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml new file mode 100644 index 00000000..5835c173 --- /dev/null +++ b/.github/workflows/examples.yml @@ -0,0 +1,45 @@ +name: Examples + +on: [push, pull_request, workflow_dispatch] + +env: + FORCE_COLOR: 1 + +jobs: + examples: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: [ + "src-layout", + "adhoc-layout", + ] + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + + - name: Cache + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: + examples-v1-${{ hashFiles('**/tox.ini') }} + restore-keys: | + examples-v1- + + - name: Install dependencies + run: | + python -m pip install -U pip + python -m pip install -U wheel + python -m pip install --progress-bar=off tox -rci/requirements.txt + + - name: Examples + run: | + cd examples/${{ matrix.target }} + tox -v diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..001ccfee --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,41 @@ +name: Lint + +on: [push, pull_request, workflow_dispatch] + +env: + FORCE_COLOR: 1 + +jobs: + lint: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + toxenv: ["check", "docs"] + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + + - name: Cache + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: + lint-v1-${{ hashFiles('**/tox.ini') }} + restore-keys: | + lint-v1- + + - name: Install dependencies + run: | + python -m pip install -U pip + python -m pip install -U wheel + python -m pip install --progress-bar=off tox -rci/requirements.txt + + - name: Lint ${{ matrix.toxenv }} + run: | + tox -v -e ${{ matrix.toxenv }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..b11e9f02 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,68 @@ +name: Test + +on: [push, pull_request, workflow_dispatch] + +env: + FORCE_COLOR: 1 + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["pypy3", "3.6", "3.7", "3.8", "3.9"] + tox-extra-versions: [ + "pytest46-xdist133-coverage55", + "pytest54-xdist133-coverage55", + "pytest62-xdist202-coverage55", + ] + include: + # Add new helper variables to existing jobs + - {python-version: "pypy3", tox-python-version: "pypy3"} + - {python-version: "3.6", tox-python-version: "py36"} + - {python-version: "3.7", tox-python-version: "py37"} + - {python-version: "3.8", tox-python-version: "py38"} + - {python-version: "3.9", tox-python-version: "py39"} + # Add extra jobs to the matrix + - {tox-extra-versions: "pytest46-xdist127", python-version: "pypy-2.7", tox-python-version: "pypy"} + - {tox-extra-versions: "pytest46-xdist127", python-version: "pypy3", tox-python-version: "pypy3"} + - {tox-extra-versions: "pytest46-xdist127", python-version: "2.7", tox-python-version: "py27"} + - {tox-extra-versions: "pytest46-xdist127", python-version: "3.5", tox-python-version: "py35"} + - {tox-extra-versions: "pytest46-xdist127", python-version: "3.6", tox-python-version: "py36"} + - {tox-extra-versions: "pytest46-xdist127", python-version: "3.7", tox-python-version: "py37"} + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" + + - name: Cache + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: + test-${{ matrix.python-version }}-v1-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + test-${{ matrix.python-version }}-v1- + + - name: Install dependencies + run: | + python -m pip install -U pip + python -m pip install -U wheel + python -m pip install --progress-bar=off tox -rci/requirements.txt + virtualenv --version + pip --version + tox --version + + - name: Tox tests + run: | + tox -v -e ${{ matrix.tox-python-version }}-${{ matrix.tox-extra-versions }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index a9cafa4e..00000000 --- a/.travis.yml +++ /dev/null @@ -1,89 +0,0 @@ -# NOTE: this file is auto-generated via ci/bootstrap.py (ci/templates/.travis.yml). -dist: xenial -language: python -cache: false -env: - global: - - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so - - SEGFAULT_SIGNALS=all -stages: - - lint - - examples - - tests -jobs: - fast_finish: true - allow_failures: - - python: '3.8' - include: - - stage: lint - env: TOXENV=check - - env: TOXENV=docs - - - stage: tests - env: TOXENV=py27-pytest46-xdist127-coverage55 - python: '2.7' - - env: TOXENV=py35-pytest46-xdist127-coverage55 - python: '3.5' - - env: TOXENV=py36-pytest46-xdist127-coverage55 - python: '3.6' - - env: TOXENV=py37-pytest46-xdist127-coverage55 - python: '3.7' - - env: TOXENV=pypy-pytest46-xdist127-coverage55 - python: 'pypy' - - env: TOXENV=pypy3-pytest46-xdist127-coverage55 - python: 'pypy3' - - env: TOXENV=py36-pytest46-xdist133-coverage55 - python: '3.6' - - env: TOXENV=py36-pytest54-xdist133-coverage55 - python: '3.6' - - env: TOXENV=py37-pytest46-xdist133-coverage55 - python: '3.7' - - env: TOXENV=py37-pytest54-xdist133-coverage55 - python: '3.7' - - env: TOXENV=py38-pytest46-xdist133-coverage55 - python: '3.8' - - env: TOXENV=py38-pytest54-xdist133-coverage55 - python: '3.8' - - env: TOXENV=pypy3-pytest46-xdist133-coverage55 - python: 'pypy3' - - env: TOXENV=pypy3-pytest54-xdist133-coverage55 - python: 'pypy3' - - env: TOXENV=py36-pytest62-xdist202-coverage55 - python: '3.6' - - env: TOXENV=py37-pytest62-xdist202-coverage55 - python: '3.7' - - env: TOXENV=py38-pytest62-xdist202-coverage55 - python: '3.8' - - env: TOXENV=py39-pytest62-xdist202-coverage55 - python: '3.9' - - env: TOXENV=pypy3-pytest62-xdist202-coverage55 - python: 'pypy3' - - - stage: examples - python: '3.8' - script: cd $TARGET; tox -v - env: - - TARGET=examples/src-layout - - python: '3.8' - script: cd $TARGET; tox -v - env: - - TARGET=examples/adhoc-layout -before_install: - - python --version - - uname -a - - lsb_release -a -install: - - python -mpip install --progress-bar=off tox -rci/requirements.txt - - virtualenv --version - - easy_install --version - - pip --version - - tox --version -script: - - tox -v -after_failure: - - more .tox/log/* | cat - - more .tox/*/log/* | cat -notifications: - email: - on_success: never - on_failure: always diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index f02562eb..e742be47 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -73,8 +73,8 @@ For merging, you should: 3. Add a note to ``CHANGELOG.rst`` about the changes. 4. Add yourself to ``AUTHORS.rst``. -.. [1] If you don't have all the necessary python versions available locally you can rely on Travis - it will - `run the tests `_ +.. [1] If you don't have all the necessary Python versions available locally you can rely on GitHub Actions - it will + `run the tests `_ for each change you add in the pull request. It will be slower though ... diff --git a/MANIFEST.in b/MANIFEST.in index 6db46114..af1581be 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -6,6 +6,7 @@ prune examples/*/*/htmlcov prune examples/adhoc-layout/*.egg-info prune examples/src-layout/src/*.egg-info +graft .github/workflows graft src graft ci graft tests @@ -21,6 +22,6 @@ include CONTRIBUTING.rst include LICENSE include README.rst -include tox.ini .travis.yml .appveyor.yml .readthedocs.yml .pre-commit-config.yaml +include tox.ini .appveyor.yml .readthedocs.yml .pre-commit-config.yaml global-exclude *.py[cod] __pycache__/* *.so *.dylib .coverage .coverage.* diff --git a/README.rst b/README.rst index 149594fd..fa49179e 100644 --- a/README.rst +++ b/README.rst @@ -10,7 +10,7 @@ Overview * - docs - |docs| * - tests - - | |travis| |appveyor| |requires| + - | |github-actions| |appveyor| |requires| * - package - | |version| |conda-forge| |wheel| |supported-versions| |supported-implementations| | |commits-since| @@ -19,9 +19,9 @@ Overview :target: https://readthedocs.org/projects/pytest-cov :alt: Documentation Status -.. |travis| image:: https://api.travis-ci.com/pytest-dev/pytest-cov.svg?branch=master - :alt: Travis-CI Build Status - :target: https://travis-ci.com/github/pytest-dev/pytest-cov +.. |github-actions| image:: https://github.com/pytest-dev/pytest-cov/actions/workflows/test.yml/badge.svg + :alt: GitHub Actions Status + :target: https://github.com/pytest-dev/pytest-cov/actions .. |appveyor| image:: https://ci.appveyor.com/api/projects/status/github/pytest-dev/pytest-cov?branch=master&svg=true :alt: AppVeyor Build Status diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml deleted file mode 100644 index 8c7e3b93..00000000 --- a/ci/templates/.travis.yml +++ /dev/null @@ -1,61 +0,0 @@ -dist: xenial -language: python -cache: false -env: - global: - - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so - - SEGFAULT_SIGNALS=all -stages: - - lint - - examples - - tests -jobs: - fast_finish: true - allow_failures: - - python: '3.8' - include: - - stage: lint - env: TOXENV=check - - env: TOXENV=docs - - - stage: tests -{% for env in tox_environments %} - {%+ if not loop.first %}- {% else %} {% endif -%} - env: TOXENV={{ env }} - {% if env.startswith("pypy-") %} - python: 'pypy' - {% elif env.startswith("pypy3-") %} - python: 'pypy3' - {% else %} - python: '{{ "{0[2]}.{0[3]}".format(env) }}' - {% endif -%} -{% endfor %} - - - stage: examples -{%- for example in ['src', 'adhoc'] %}{{ '' }} - {%+ if not loop.first %}- {% else %} {% endif -%} - python: '3.8' - script: cd $TARGET; tox -v - env: - - TARGET=examples/{{ example }}-layout -{%- endfor %} - -before_install: - - python --version - - uname -a - - lsb_release -a -install: - - python -mpip install --progress-bar=off tox -rci/requirements.txt - - virtualenv --version - - easy_install --version - - pip --version - - tox --version -script: - - tox -v -after_failure: - - more .tox/log/* | cat - - more .tox/*/log/* | cat -notifications: - email: - on_success: never - on_failure: always diff --git a/docs/releasing.rst b/docs/releasing.rst index 245dca54..ae78228d 100644 --- a/docs/releasing.rst +++ b/docs/releasing.rst @@ -16,7 +16,7 @@ The process for releasing should follow these steps: git push git push --tags #. Wait for `AppVeyor `_ - and `Travis `_ to give the green builds. + and `GitHub Actions `_ to give the green builds. #. Check that the docs on `ReadTheDocs `_ are built. #. Make sure you have a clean checkout, run ``git status`` to verify. #. Manually clean temporary files (that are ignored and won't show up in ``git status``):: diff --git a/docs/reporting.rst b/docs/reporting.rst index e9e4b06b..eaa99ad3 100644 --- a/docs/reporting.rst +++ b/docs/reporting.rst @@ -71,4 +71,4 @@ The final report option can also suppress printing to the terminal:: This mode can be especially useful on continuous integration servers, where a coverage file is needed for subsequent processing, but no local report needs to be viewed. For example, -tests run on Travis-CI could produce a .coverage file for use with Coveralls. +tests run on GitHub Actions could produce a .coverage file for use with Coveralls. From 3490ccb725758abfbd1162abd53e8a6c454d1f95 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 20 Sep 2021 17:20:33 +0300 Subject: [PATCH 028/117] Python 2.7 and 3.5 support was already dropped --- .github/workflows/test.yml | 16 +++++----------- examples/adhoc-layout/example/__init__.py | 14 ++------------ examples/adhoc-layout/tox.ini | 6 +++--- examples/src-layout/src/example/__init__.py | 14 ++------------ examples/src-layout/tox.ini | 6 +++--- 5 files changed, 15 insertions(+), 41 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b11e9f02..272ade24 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,9 +13,10 @@ jobs: matrix: python-version: ["pypy3", "3.6", "3.7", "3.8", "3.9"] tox-extra-versions: [ - "pytest46-xdist133-coverage55", - "pytest54-xdist133-coverage55", - "pytest62-xdist202-coverage55", + "pytest46-xdist127", + "pytest46-xdist133", + "pytest54-xdist133", + "pytest62-xdist202", ] include: # Add new helper variables to existing jobs @@ -24,13 +25,6 @@ jobs: - {python-version: "3.7", tox-python-version: "py37"} - {python-version: "3.8", tox-python-version: "py38"} - {python-version: "3.9", tox-python-version: "py39"} - # Add extra jobs to the matrix - - {tox-extra-versions: "pytest46-xdist127", python-version: "pypy-2.7", tox-python-version: "pypy"} - - {tox-extra-versions: "pytest46-xdist127", python-version: "pypy3", tox-python-version: "pypy3"} - - {tox-extra-versions: "pytest46-xdist127", python-version: "2.7", tox-python-version: "py27"} - - {tox-extra-versions: "pytest46-xdist127", python-version: "3.5", tox-python-version: "py35"} - - {tox-extra-versions: "pytest46-xdist127", python-version: "3.6", tox-python-version: "py36"} - - {tox-extra-versions: "pytest46-xdist127", python-version: "3.7", tox-python-version: "py37"} steps: - uses: actions/checkout@v2 @@ -65,4 +59,4 @@ jobs: - name: Tox tests run: | - tox -v -e ${{ matrix.tox-python-version }}-${{ matrix.tox-extra-versions }} + tox -v -e ${{ matrix.tox-python-version }}-${{ matrix.tox-extra-versions }}-coverage55 diff --git a/examples/adhoc-layout/example/__init__.py b/examples/adhoc-layout/example/__init__.py index 8a6dfd5e..4693ad3c 100644 --- a/examples/adhoc-layout/example/__init__.py +++ b/examples/adhoc-layout/example/__init__.py @@ -1,12 +1,2 @@ -import sys - -PY2 = sys.version_info[0] == 2 - - -if PY2: - def add(a, b): - return b + a - -else: - def add(a, b): - return a + b +def add(a, b): + return a + b diff --git a/examples/adhoc-layout/tox.ini b/examples/adhoc-layout/tox.ini index 6e299f24..7b6f7c43 100644 --- a/examples/adhoc-layout/tox.ini +++ b/examples/adhoc-layout/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27,py38,report +envlist = py38,report [tool:pytest] addopts = @@ -7,7 +7,7 @@ addopts = [testenv] setenv = - py{27,38}: COVERAGE_FILE = .coverage.{envname} + py38: COVERAGE_FILE = .coverage.{envname} commands = pytest --cov --cov-config={toxinidir}/.coveragerc {posargs:-vv} deps = pytest @@ -19,7 +19,7 @@ deps = ../.. depends = - report: py27,py38 + report: py38 # note that this is necessary to prevent the tests importing the code from your badly laid project changedir = tests diff --git a/examples/src-layout/src/example/__init__.py b/examples/src-layout/src/example/__init__.py index 8a6dfd5e..4693ad3c 100644 --- a/examples/src-layout/src/example/__init__.py +++ b/examples/src-layout/src/example/__init__.py @@ -1,12 +1,2 @@ -import sys - -PY2 = sys.version_info[0] == 2 - - -if PY2: - def add(a, b): - return b + a - -else: - def add(a, b): - return a + b +def add(a, b): + return a + b diff --git a/examples/src-layout/tox.ini b/examples/src-layout/tox.ini index 6be8e73d..5a2083c9 100644 --- a/examples/src-layout/tox.ini +++ b/examples/src-layout/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27,py38,report +envlist = py38,report [tool:pytest] testpaths = tests @@ -8,7 +8,7 @@ addopts = [testenv] setenv = - py{27,38}: COVERAGE_FILE = .coverage.{envname} + py38: COVERAGE_FILE = .coverage.{envname} commands = pytest --cov {posargs:-vv} deps = pytest @@ -20,7 +20,7 @@ deps = ../.. depends = - report: py27,py38 + report: py38 [testenv:report] skip_install = true From f81aa0c90c8f423248d8596179dfcbd8627a9a0f Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 20 Sep 2021 17:46:45 +0300 Subject: [PATCH 029/117] Remove some jobs from the matrix --- .github/workflows/test.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 272ade24..e09414f4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,6 +25,12 @@ jobs: - {python-version: "3.7", tox-python-version: "py37"} - {python-version: "3.8", tox-python-version: "py38"} - {python-version: "3.9", tox-python-version: "py39"} + exclude: + # Remove some jobs from the matrix + - {tox-extra-versions: "pytest46-xdist127", python-version: "3.8"} + - {tox-extra-versions: "pytest46-xdist127", python-version: "3.9"} + - {tox-extra-versions: "pytest46-xdist133", python-version: "3.9"} + - {tox-extra-versions: "pytest54-xdist133", python-version: "3.9"} steps: - uses: actions/checkout@v2 From b25afc71a15ff52f2edd1e8e78b85cb8390cbf60 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 20 Sep 2021 19:34:12 +0300 Subject: [PATCH 030/117] Hello world to demo GHA --- .github/workflows/test.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..b92a0fa3 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,26 @@ +name: Test + +on: [push, pull_request, workflow_dispatch] + +env: + FORCE_COLOR: 1 + +jobs: + test: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.9"] + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Hello world + run: | + echo "hello world" From a948e899fa002fbc7f52c6c0f7e7dffdf7987ec8 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 21 Sep 2021 10:30:18 +0300 Subject: [PATCH 031/117] Test both PyPy3.6 and PyPy3.7 Co-authored-by: Thomas Grainger --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e09414f4..51a03bef 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["pypy3", "3.6", "3.7", "3.8", "3.9"] + python-version: ["pypy-3.6", "pypy-3.7", "3.6", "3.7", "3.8", "3.9"] tox-extra-versions: [ "pytest46-xdist127", "pytest46-xdist133", From f4a88d6187630295ce960010456def6b19ef01b2 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 21 Sep 2021 10:34:07 +0300 Subject: [PATCH 032/117] Test both PyPy3.6 and PyPy3.7 --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 51a03bef..967386af 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,7 +20,8 @@ jobs: ] include: # Add new helper variables to existing jobs - - {python-version: "pypy3", tox-python-version: "pypy3"} + - {python-version: "pypy-3.6", tox-python-version: "pypy3"} + - {python-version: "pypy-3.7", tox-python-version: "pypy3"} - {python-version: "3.6", tox-python-version: "py36"} - {python-version: "3.7", tox-python-version: "py37"} - {python-version: "3.8", tox-python-version: "py38"} From 6395ecec756433194c05d34af490315b35dddafa Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 21 Sep 2021 10:40:56 +0300 Subject: [PATCH 033/117] Test conditional collection on PyPy and CPython --- .github/workflows/examples.yml | 12 ++++++++---- examples/adhoc-layout/example/__init__.py | 13 +++++++++++-- examples/adhoc-layout/tox.ini | 6 +++--- examples/src-layout/src/example/__init__.py | 13 +++++++++++-- examples/src-layout/tox.ini | 6 +++--- 5 files changed, 36 insertions(+), 14 deletions(-) diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 5835c173..dbd62a6c 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -11,18 +11,22 @@ jobs: strategy: fail-fast: false matrix: + python-version: ["pypy-3.7", "3.9"] target: [ "src-layout", "adhoc-layout", ] - + include: + # Add new helper variables to existing jobs + - {python-version: "pypy-3.7", tox-python-version: "pypy3"} + - {python-version: "3.9", tox-python-version: "py39"} steps: - uses: actions/checkout@v2 - - name: Set up Python + - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: - python-version: 3.9 + python-version: ${{ matrix.python-version }} - name: Cache uses: actions/cache@v2 @@ -42,4 +46,4 @@ jobs: - name: Examples run: | cd examples/${{ matrix.target }} - tox -v + tox -v -e ${{ matrix.tox-python-version }} diff --git a/examples/adhoc-layout/example/__init__.py b/examples/adhoc-layout/example/__init__.py index 4693ad3c..684905a1 100644 --- a/examples/adhoc-layout/example/__init__.py +++ b/examples/adhoc-layout/example/__init__.py @@ -1,2 +1,11 @@ -def add(a, b): - return a + b +import platform + +# test merging multiple tox runs with a platform +# based branch +if platform.python_implementation() == "PyPy": + def add(a, b): + return a + b + +else: + def add(a, b): + return a + b diff --git a/examples/adhoc-layout/tox.ini b/examples/adhoc-layout/tox.ini index 7b6f7c43..e855c315 100644 --- a/examples/adhoc-layout/tox.ini +++ b/examples/adhoc-layout/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py38,report +envlist = pypy3,py39,report [tool:pytest] addopts = @@ -7,7 +7,7 @@ addopts = [testenv] setenv = - py38: COVERAGE_FILE = .coverage.{envname} + py{py3,39}: COVERAGE_FILE = .coverage.{envname} commands = pytest --cov --cov-config={toxinidir}/.coveragerc {posargs:-vv} deps = pytest @@ -19,7 +19,7 @@ deps = ../.. depends = - report: py38 + report: pypy3,py39 # note that this is necessary to prevent the tests importing the code from your badly laid project changedir = tests diff --git a/examples/src-layout/src/example/__init__.py b/examples/src-layout/src/example/__init__.py index 4693ad3c..684905a1 100644 --- a/examples/src-layout/src/example/__init__.py +++ b/examples/src-layout/src/example/__init__.py @@ -1,2 +1,11 @@ -def add(a, b): - return a + b +import platform + +# test merging multiple tox runs with a platform +# based branch +if platform.python_implementation() == "PyPy": + def add(a, b): + return a + b + +else: + def add(a, b): + return a + b diff --git a/examples/src-layout/tox.ini b/examples/src-layout/tox.ini index 5a2083c9..94b72730 100644 --- a/examples/src-layout/tox.ini +++ b/examples/src-layout/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py38,report +envlist = pypy3,py39,report [tool:pytest] testpaths = tests @@ -8,7 +8,7 @@ addopts = [testenv] setenv = - py38: COVERAGE_FILE = .coverage.{envname} + py{py3,39}: COVERAGE_FILE = .coverage.{envname} commands = pytest --cov {posargs:-vv} deps = pytest @@ -20,7 +20,7 @@ deps = ../.. depends = - report: py38 + report: pypy3,py39 [testenv:report] skip_install = true From 96f9aad28a35d9d0824add0ef2309e600052c531 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Tue, 21 Sep 2021 10:53:55 +0300 Subject: [PATCH 034/117] Add 'all good' job to be added as a required build --- .github/workflows/test.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 967386af..a60cbd96 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -67,3 +67,11 @@ jobs: - name: Tox tests run: | tox -v -e ${{ matrix.tox-python-version }}-${{ matrix.tox-extra-versions }}-coverage55 + + allgood: + needs: test + runs-on: ubuntu-latest + name: Test successful + steps: + - name: Success + run: echo Test successful From 37fbe31d8d1dfab7b9beb76812700fefd4a8e939 Mon Sep 17 00:00:00 2001 From: Greg Smethells <3286666+gsmethells@users.noreply.github.com> Date: Wed, 29 Sep 2021 16:21:12 -0500 Subject: [PATCH 035/117] Fix spelling error --- src/pytest_cov/embed.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pytest_cov/embed.py b/src/pytest_cov/embed.py index 3adecdba..fe0575e9 100644 --- a/src/pytest_cov/embed.py +++ b/src/pytest_cov/embed.py @@ -100,9 +100,9 @@ def cleanup(): _active_cov = None _cleanup_in_progress = False if _pending_signal: - pending_singal = _pending_signal + pending_signal = _pending_signal _pending_signal = None - _signal_cleanup_handler(*pending_singal) + _signal_cleanup_handler(*pending_signal) multiprocessing_finish = cleanup # in case someone dared to use this internal From 0bc997adfea8b6ab63029163044a2fc42fd7ecf1 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 3 Oct 2021 21:22:32 +0300 Subject: [PATCH 036/117] Add support for Python 3.10 --- .github/workflows/test.yml | 6 +++++- setup.py | 1 + tox.ini | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a60cbd96..b9f7c11c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["pypy-3.6", "pypy-3.7", "3.6", "3.7", "3.8", "3.9"] + python-version: ["pypy-3.6", "pypy-3.7", "3.6", "3.7", "3.8", "3.9", "3.10-dev"] tox-extra-versions: [ "pytest46-xdist127", "pytest46-xdist133", @@ -26,12 +26,16 @@ jobs: - {python-version: "3.7", tox-python-version: "py37"} - {python-version: "3.8", tox-python-version: "py38"} - {python-version: "3.9", tox-python-version: "py39"} + - {python-version: "3.10-dev", tox-python-version: "py310"} exclude: # Remove some jobs from the matrix - {tox-extra-versions: "pytest46-xdist127", python-version: "3.8"} - {tox-extra-versions: "pytest46-xdist127", python-version: "3.9"} - {tox-extra-versions: "pytest46-xdist133", python-version: "3.9"} - {tox-extra-versions: "pytest54-xdist133", python-version: "3.9"} + - {tox-extra-versions: "pytest46-xdist127", python-version: "3.10-dev"} + - {tox-extra-versions: "pytest46-xdist133", python-version: "3.10-dev"} + - {tox-extra-versions: "pytest54-xdist133", python-version: "3.10-dev"} steps: - uses: actions/checkout@v2 diff --git a/setup.py b/setup.py index 11e98a88..73370564 100755 --- a/setup.py +++ b/setup.py @@ -109,6 +109,7 @@ def run(self): 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Software Development :: Testing', diff --git a/tox.ini b/tox.ini index 490a5201..20a1561b 100644 --- a/tox.ini +++ b/tox.ini @@ -15,7 +15,7 @@ envlist = check py{36,37,py,py3}-pytest46-xdist127-coverage{55} py{36,37,38,py3}-pytest{46,54}-xdist133-coverage{55} - py{36,37,38,39,py3}-pytest{62}-xdist202-coverage{55} + py{36,37,38,39,310,py3}-pytest{62}-xdist202-coverage{55} docs [testenv] @@ -29,7 +29,7 @@ setenv = pytest54: _DEP_PYTEST=pytest==5.4.3 pytest60: _DEP_PYTEST=pytest==6.0.2 pytest61: _DEP_PYTEST=pytest==6.1.2 - pytest62: _DEP_PYTEST=pytest==6.2.2 + pytest62: _DEP_PYTEST=pytest==6.2.5 xdist127: _DEP_PYTESTXDIST=pytest-xdist==1.27.0 xdist129: _DEP_PYTESTXDIST=pytest-xdist==1.29.0 From 60a3cc1e796428f2373fb2024122f8dbc7f1c56b Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sun, 3 Oct 2021 21:24:03 +0300 Subject: [PATCH 037/117] No need to build universal wheels for Python 3-only --- setup.cfg | 3 --- 1 file changed, 3 deletions(-) diff --git a/setup.cfg b/setup.cfg index d6c36d12..360a416d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,3 @@ -[bdist_wheel] -universal = 1 - [flake8] max-line-length = 140 exclude = .tox,.eggs,ci/templates,build,dist From 5980fe2c22667891957e716e1047e4cd15e07898 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 27 Sep 2021 19:44:17 +0000 Subject: [PATCH 038/117] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.23.3 → v2.28.0](https://github.com/asottile/pyupgrade/compare/v2.23.3...v2.28.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dc36fa2b..6af3f081 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,7 +19,7 @@ repos: hooks: - id: flake8 - repo: https://github.com/asottile/pyupgrade - rev: v2.23.3 + rev: v2.28.0 hooks: - id: pyupgrade args: [--py36-plus] From 574b5b5d30e663050910b0ae07f31bc7e45d2527 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 27 Sep 2021 19:47:25 +0000 Subject: [PATCH 039/117] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- setup.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index 11e98a88..a43d0f07 100755 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ def read(*names, **kwargs): class BuildWithPTH(build): def run(self, *args, **kwargs): - build.run(self, *args, **kwargs) + super().run(*args, **kwargs) path = join(dirname(__file__), 'src', 'pytest-cov.pth') dest = join(self.build_lib, basename(path)) self.copy_file(path, dest) @@ -35,7 +35,7 @@ def run(self, *args, **kwargs): class EasyInstallWithPTH(easy_install): def run(self, *args, **kwargs): - easy_install.run(self, *args, **kwargs) + super().run(*args, **kwargs) path = join(dirname(__file__), 'src', 'pytest-cov.pth') dest = join(self.install_dir, basename(path)) self.copy_file(path, dest) @@ -43,19 +43,19 @@ def run(self, *args, **kwargs): class InstallLibWithPTH(install_lib): def run(self, *args, **kwargs): - install_lib.run(self, *args, **kwargs) + super().run(*args, **kwargs) path = join(dirname(__file__), 'src', 'pytest-cov.pth') dest = join(self.install_dir, basename(path)) self.copy_file(path, dest) self.outputs = [dest] def get_outputs(self): - return chain(install_lib.get_outputs(self), self.outputs) + return chain(super().get_outputs(), self.outputs) class DevelopWithPTH(develop): def run(self, *args, **kwargs): - develop.run(self, *args, **kwargs) + super().run(*args, **kwargs) path = join(dirname(__file__), 'src', 'pytest-cov.pth') dest = join(self.install_dir, basename(path)) self.copy_file(path, dest) From e988a6c48b45924433c9b38886f759f4f3be8a94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Mon, 4 Oct 2021 03:07:53 +0300 Subject: [PATCH 040/117] Update changelog. --- AUTHORS.rst | 2 ++ CHANGELOG.rst | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/AUTHORS.rst b/AUTHORS.rst index 14cb7781..6aa916a1 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -48,3 +48,5 @@ Authors * Chris Sreesangkom - https://github.com/csreesan * Sorin Sbarnea - https://github.com/ssbarnea * Brian Rutledge - https://github.com/bhrutledge +* Danilo Šegan - https://github.com/dsegan +* Michał Bielawski - https://github.com/D3X diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ef5ad741..e6e47d95 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,41 @@ Changelog ========= + +3.0.0 (2021-10-04) +------------------- + +**Note that this release drops support for Python 2.7 and Python 3.5.** + +* Added support for Python 3.10 and updated various test dependencies. + Contributed by Hugo van Kemenade in + `#500 `_. +* Switched from Travis CI to GitHub Actions. Contributed by Hugo van Kemenade in + `#494 `_ and + `#495 `_. +* Add a ``--cov-reset`` CLI option. + Contributed by Danilo Šegan in + `#459 `_. +* Improved validation of ``--cov-fail-under`` CLI option. + Contributed by ... Ronny Pfannschmidt's desire for skark in + `#480 `_. +* Dropped Python 2.7 support. + Contributed by Thomas Grainger in + `#488 `_. +* Updated trove classifiers. Contributed by Michał Bielawski in + `#481 `_. + + +2.13.0 (2021-06-01) +------------------- + +* Changed the `toml` requirement to be always be directly required (instead of being required through a coverage extra). + This fixes issues with pip-compile (`pip-tools#1300 `_). + Contributed by Sorin Sbarnea in `#472 `_. +* Documented ``show_contexts``. + Contributed by Brian Rutledge in `#473 `_. + + 2.12.1 (2021-06-01) ------------------- From 560b95575efe9e55874bc8bbc99de1dd2db80ba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Mon, 4 Oct 2021 04:48:36 +0300 Subject: [PATCH 041/117] =?UTF-8?q?Bump=20version:=202.12.1=20=E2=86=92=20?= =?UTF-8?q?3.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- README.rst | 4 ++-- docs/conf.py | 2 +- setup.py | 2 +- src/pytest_cov/__init__.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index a838695a..27a843fb 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 2.12.1 +current_version = 3.0.0 commit = True tag = True diff --git a/README.rst b/README.rst index fa49179e..508aff84 100644 --- a/README.rst +++ b/README.rst @@ -38,9 +38,9 @@ Overview .. |conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pytest-cov.svg :target: https://anaconda.org/conda-forge/pytest-cov -.. |commits-since| image:: https://img.shields.io/github/commits-since/pytest-dev/pytest-cov/v2.12.1.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/pytest-dev/pytest-cov/v3.0.0.svg :alt: Commits since latest release - :target: https://github.com/pytest-dev/pytest-cov/compare/v2.12.1...master + :target: https://github.com/pytest-dev/pytest-cov/compare/v3.0.0...master .. |wheel| image:: https://img.shields.io/pypi/wheel/pytest-cov.svg :alt: PyPI Wheel diff --git a/docs/conf.py b/docs/conf.py index 45da656c..5dd4e74b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,7 +23,7 @@ year = '2016' author = 'pytest-cov contributors' copyright = f'{year}, {author}' -version = release = '2.12.1' +version = release = '3.0.0' pygments_style = 'trac' templates_path = ['.'] diff --git a/setup.py b/setup.py index 73370564..503b9aac 100755 --- a/setup.py +++ b/setup.py @@ -81,7 +81,7 @@ def run(self): setup( name='pytest-cov', - version='2.12.1', + version='3.0.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'))), diff --git a/src/pytest_cov/__init__.py b/src/pytest_cov/__init__.py index dad3b766..fba1d219 100644 --- a/src/pytest_cov/__init__.py +++ b/src/pytest_cov/__init__.py @@ -1,2 +1,2 @@ """pytest-cov: avoid already-imported warning: PYTEST_DONT_REWRITE.""" -__version__ = '2.12.1' +__version__ = '3.0.0' From e764047dd6cd2bed504e54232fc1b48c58724882 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Oct 2021 19:49:32 +0000 Subject: [PATCH 042/117] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.28.0 → v2.29.0](https://github.com/asottile/pyupgrade/compare/v2.28.0...v2.29.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6af3f081..e57766d6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,7 +19,7 @@ repos: hooks: - id: flake8 - repo: https://github.com/asottile/pyupgrade - rev: v2.28.0 + rev: v2.29.0 hooks: - id: pyupgrade args: [--py36-plus] From e00d4a3ac5bc232a05ff909e51b0c6f510ff06a1 Mon Sep 17 00:00:00 2001 From: Arthur Rio Date: Thu, 7 Oct 2021 10:48:14 -0600 Subject: [PATCH 043/117] Update Changelog to reflect toml requirement change --- CHANGELOG.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e6e47d95..014b2c67 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -24,6 +24,9 @@ Changelog `#488 `_. * Updated trove classifiers. Contributed by Michał Bielawski in `#481 `_. +* Reverted change for `toml` requirement. + Contributed by Thomas Grainger in + `#477 `_. 2.13.0 (2021-06-01) From 125f5188632340e25908ae4163d1a9dfa3ab8fe9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Nov 2021 20:53:36 +0000 Subject: [PATCH 044/117] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/PyCQA/isort: 5.9.3 → 5.10.1](https://github.com/PyCQA/isort/compare/5.9.3...5.10.1) - [github.com/PyCQA/flake8: 3.9.2 → 4.0.1](https://github.com/PyCQA/flake8/compare/3.9.2...4.0.1) - [github.com/asottile/pyupgrade: v2.29.0 → v2.29.1](https://github.com/asottile/pyupgrade/compare/v2.29.0...v2.29.1) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e57766d6..a0c352fb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,15 +11,15 @@ repos: exclude: '.*\.pth$' - id: debug-statements - repo: https://github.com/PyCQA/isort - rev: 5.9.3 + rev: 5.10.1 hooks: - id: isort - repo: https://github.com/PyCQA/flake8 - rev: 3.9.2 + rev: 4.0.1 hooks: - id: flake8 - repo: https://github.com/asottile/pyupgrade - rev: v2.29.0 + rev: v2.29.1 hooks: - id: pyupgrade args: [--py36-plus] From 6925fcfbb2c96e01124840c46ebbda6719fbbbcd Mon Sep 17 00:00:00 2001 From: Zac-HD Date: Sun, 12 Dec 2021 00:59:38 +1100 Subject: [PATCH 045/117] --co disables --cov-fail-under --- AUTHORS.rst | 1 + CHANGELOG.rst | 8 ++++++++ src/pytest_cov/plugin.py | 2 +- tests/test_pytest_cov.py | 13 +++++++++++++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 6aa916a1..489903c2 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -50,3 +50,4 @@ Authors * Brian Rutledge - https://github.com/bhrutledge * Danilo Šegan - https://github.com/dsegan * Michał Bielawski - https://github.com/D3X +* Zac Hatfield-Dodds - https://github.com/Zac-HD diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e6e47d95..684720ff 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,14 @@ Changelog ========= +3.1.0 (future) +------------------- + +* `--cov-fail-under` no longer causes `pytest --collect-only` to fail + Contributed by Zac Hatfield-Dodds in + `#511 `_. + + 3.0.0 (2021-10-04) ------------------- diff --git a/src/pytest_cov/plugin.py b/src/pytest_cov/plugin.py index 94a1e494..e915246d 100644 --- a/src/pytest_cov/plugin.py +++ b/src/pytest_cov/plugin.py @@ -308,7 +308,7 @@ def pytest_runtestloop(self, session): warnings.warn(CovReportWarning(message)) self.cov_total = 0 assert self.cov_total is not None, 'Test coverage should never be `None`' - if self._failed_cov_total(): + if self._failed_cov_total() and not self.options.collectonly: # make sure we get the EXIT_TESTSFAILED exit code compat_session.testsfailed += 1 diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index e7f90668..f93d352f 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -389,6 +389,19 @@ def test_cov_min_100(testdir): ]) +def test_cov_min_100_passes_if_collectonly(testdir): + script = testdir.makepyfile(SCRIPT) + + result = testdir.runpytest('-v', + '--cov=%s' % script.dirpath(), + '--cov-report=term-missing', + '--cov-fail-under=100', + '--collect-only', + script) + + assert result.ret == 0 + + def test_cov_min_50(testdir): script = testdir.makepyfile(SCRIPT) From 2c23d3aa0761703617392a628e846afdad9e83cc Mon Sep 17 00:00:00 2001 From: "Kian-Meng, Ang" Date: Tue, 21 Dec 2021 13:13:09 +0800 Subject: [PATCH 046/117] Fix typos --- docs/config.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index 0ead66e8..e1e4c3d3 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -26,11 +26,11 @@ For full details refer to the `coverage config file`_ documentation. This plugin overrides the ``data_file`` and ``parallel`` options of coverage. Unless you also run coverage without pytest-cov it's pointless to set those options in your ``.coveragerc``. - If you use the ``--cov=something`` option (with a value) then coverage's ``source`` option will also get overriden. - If you have multiple sources it might be easier to set those in ``.coveragerc`` and always use ``--cov`` (wihout a value) + If you use the ``--cov=something`` option (with a value) then coverage's ``source`` option will also get overridden. + If you have multiple sources it might be easier to set those in ``.coveragerc`` and always use ``--cov`` (without a value) instead of having a long command line with ``--cov=pkg1 --cov=pkg2 --cov=pkg3 ...``. - If you use the ``--cov-branch`` option then coverage's ``branch`` option will also get overriden. + If you use the ``--cov-branch`` option then coverage's ``branch`` option will also get overridden. If you wish to always add pytest-cov with pytest, you can use ``addopts`` under ``pytest`` or ``tool:pytest`` section. For example: :: From 6d06678a76d9275beb48917d7653ea6a610c7717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20G=C3=B3rny?= Date: Sun, 23 Jan 2022 15:03:35 +0100 Subject: [PATCH 047/117] Update test_invalid_coverage_source for coverage-6.2 Update test_invalid_coverage_source to make the "No data was collected" less strict, as the output has changed in coverage-6.2. This solution was suggested by Tom Callaway (@spotrh) on the linked bug. Fixes #509 --- tests/test_pytest_cov.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index f93d352f..6d979495 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -981,7 +981,7 @@ def test_invalid_coverage_source(testdir): '*10 passed*' ]) result.stderr.fnmatch_lines([ - 'Coverage.py warning: No data was collected.*' + '*No data was collected.*' ]) result.stdout.fnmatch_lines([ '*Failed to generate report: No data to report.', From 4870f997615c1ac40a996e296079454c003f9e91 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 9 May 2022 20:57:16 +0000 Subject: [PATCH 048/117] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.0.1 → v4.2.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.0.1...v4.2.0) - [github.com/asottile/pyupgrade: v2.29.1 → v2.32.1](https://github.com/asottile/pyupgrade/compare/v2.29.1...v2.32.1) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a0c352fb..9bc7894f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ # pre-commit install-hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.0.1 + rev: v4.2.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -19,7 +19,7 @@ repos: hooks: - id: flake8 - repo: https://github.com/asottile/pyupgrade - rev: v2.29.1 + rev: v2.32.1 hooks: - id: pyupgrade args: [--py36-plus] From e31f3e93802aa084d2e601667fa36be603d844d5 Mon Sep 17 00:00:00 2001 From: Colin O'Dell Date: Tue, 1 Mar 2022 17:14:23 -0500 Subject: [PATCH 049/117] Add sample error a user might encounter --- docs/debuggers.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/debuggers.rst b/docs/debuggers.rst index 15c83218..603119bb 100644 --- a/docs/debuggers.rst +++ b/docs/debuggers.rst @@ -7,7 +7,13 @@ Debuggers and PyCharm When it comes to TDD one obviously would like to debug tests. Debuggers in Python use mostly the sys.settrace function to gain access to context. Coverage uses the same technique to get access to the lines executed. Coverage does not play well with other tracers simultaneously running. This manifests itself in behaviour that PyCharm might not hit a -breakpoint no matter what the user does. Since it is common practice to have coverage configuration in the pytest.ini +breakpoint no matter what the user does, or encountering an error like this:: + + PYDEV DEBUGGER WARNING: + sys.settrace() should not be used when the debugger is being used. + This may cause the debugger to stop working correctly. + +Since it is common practice to have coverage configuration in the pytest.ini file and pytest does not support removeopts or similar the `--no-cov` flag can disable coverage completely. At the reporting part a warning message will show on screen:: From 6da2d308d1f6279c7016164e3b22842e2fe669f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 2 Feb 2022 18:49:23 +0200 Subject: [PATCH 050/117] Add extra envs. --- tox.ini | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 20a1561b..99e1fff6 100644 --- a/tox.ini +++ b/tox.ini @@ -15,7 +15,7 @@ envlist = check py{36,37,py,py3}-pytest46-xdist127-coverage{55} py{36,37,38,py3}-pytest{46,54}-xdist133-coverage{55} - py{36,37,38,39,310,py3}-pytest{62}-xdist202-coverage{55} + py{36,37,38,39,310,py3}-pytest{62}-xdist250-coverage{55,60,61,62,63} docs [testenv] @@ -40,6 +40,7 @@ setenv = xdist200: _DEP_PYTESTXDIST=pytest-xdist==2.0.0 xdist201: _DEP_PYTESTXDIST=pytest-xdist==2.1.0 xdist202: _DEP_PYTESTXDIST=pytest-xdist==2.2.0 + xdist250: _DEP_PYTESTXDIST=pytest-xdist==2.5.0 xdistdev: _DEP_PYTESTXDIST=git+https://github.com/pytest-dev/pytest-xdist.git#egg=pytest-xdist coverage45: _DEP_COVERAGE=coverage==4.5.4 @@ -49,6 +50,10 @@ setenv = coverage53: _DEP_COVERAGE=coverage==5.3.1 coverage54: _DEP_COVERAGE=coverage==5.4 coverage55: _DEP_COVERAGE=coverage==5.5 + coverage60: _DEP_COVERAGE=coverage==6.0.2 + coverage61: _DEP_COVERAGE=coverage==6.1.2 + coverage62: _DEP_COVERAGE=coverage==6.2 + coverage63: _DEP_COVERAGE=coverage==6.3 # For testing against a coverage.py working tree. coveragedev: _DEP_COVERAGE=-e{env:COVERAGE_HOME} passenv = From 3858888f670c80edeace519234fa251d7c0448a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 2 Feb 2022 19:34:59 +0200 Subject: [PATCH 051/117] Skip this on newer xdist. --- tests/test_pytest_cov.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 6d979495..32804bc0 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -994,6 +994,8 @@ def test_invalid_coverage_source(testdir): @pytest.mark.skipif("'dev' in pytest.__version__") @pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"') +@pytest.mark.skipif('tuple(map(int, xdist.__version__.split("."))) >= (2, 3, 0)', + reason="Since pytest-xdist 2.3.0 the parent sys.path is copied in the child process") def test_dist_missing_data(testdir): """Test failure when using a worker without pytest-cov installed.""" venv_path = os.path.join(str(testdir.tmpdir), 'venv') From fa8ecf4ec198c3a29c979ae5be0f0b236c29bf4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 2 Feb 2022 19:36:42 +0200 Subject: [PATCH 052/117] Parametrize the spawn method. --- tests/test_pytest_cov.py | 60 ++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 32804bc0..e94d59be 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -157,6 +157,15 @@ def test_foo(cov): pytest.param('-n 1', marks=pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"')) ], ids=['nodist', 'xdist']) +skipif_multiprocessing_is_broken = pytest.mark.skipif( + 'sys.version_info[:2] >= (3, 8)', + reason="deadlocks on Python 3.8+, see: https://bugs.python.org/issue38227" +) +method_params = pytest.mark.parametrize('method', [ + pytest.param('fork', marks=skipif_multiprocessing_is_broken), + pytest.param('spawn', marks=skipif_multiprocessing_is_broken), +]) + @pytest.fixture(scope='session', autouse=True) def adjust_sys_path(): @@ -1025,7 +1034,7 @@ def test_dist_missing_data(testdir): '--dist=load', '--tx=popen//python=%s' % exe, max_worker_restart_0, - script) + str(script)) result.stdout.fnmatch_lines([ 'The following workers failed to return coverage data, ensure that pytest-cov is installed on these workers.' ]) @@ -1062,18 +1071,19 @@ def test_funcarg_not_active(testdir): @pytest.mark.skipif("sys.version_info[0] < 3", reason="no context manager api on Python 2") @pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows") @pytest.mark.skipif('platform.python_implementation() == "PyPy"', reason="often deadlocks on PyPy") -@pytest.mark.skipif('sys.version_info[:2] >= (3, 8)', reason="deadlocks on Python 3.8+, see: https://bugs.python.org/issue38227") -def test_multiprocessing_pool(testdir): +@method_params +def test_multiprocessing_pool(testdir, method): pytest.importorskip('multiprocessing.util') script = testdir.makepyfile(''' import multiprocessing def target_fn(a): - %sse: # pragma: nocover + {}se: # pragma: nocover return None def test_run_target(): + multiprocessing.set_start_method({!r}) from pytest_cov.embed import cleanup_on_sigterm cleanup_on_sigterm() @@ -1081,9 +1091,9 @@ def test_run_target(): with multiprocessing.Pool(3) as p: p.map(target_fn, [i * 3 + j for j in range(3)]) p.join() -''' % ''.join('''if a == %r: +'''.format(''.join('''if a == %r: return a - el''' % i for i in range(99))) + el''' % i for i in range(99)), method)) result = testdir.runpytest('-v', '--cov=%s' % script.dirpath(), @@ -1103,18 +1113,19 @@ def test_run_target(): @pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows") @pytest.mark.skipif('platform.python_implementation() == "PyPy"', reason="often deadlocks on PyPy") -@pytest.mark.skipif('sys.version_info[:2] >= (3, 8)', reason="deadlocks on Python 3.8, see: https://bugs.python.org/issue38227") -def test_multiprocessing_pool_terminate(testdir): +@method_params +def test_multiprocessing_pool_terminate(testdir, method): pytest.importorskip('multiprocessing.util') script = testdir.makepyfile(''' import multiprocessing def target_fn(a): - %sse: # pragma: nocover + {}se: # pragma: nocover return None def test_run_target(): + multiprocessing.set_start_method({!r}) from pytest_cov.embed import cleanup_on_sigterm cleanup_on_sigterm() @@ -1125,9 +1136,9 @@ def test_run_target(): finally: p.terminate() p.join() -''' % ''.join('''if a == %r: +'''.format(''.join('''if a == %r: return a - el''' % i for i in range(99))) + el''' % i for i in range(99)), method)) result = testdir.runpytest('-v', '--cov=%s' % script.dirpath(), @@ -1147,17 +1158,19 @@ def test_run_target(): @pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows") @pytest.mark.skipif('sys.version_info[0] > 2 and platform.python_implementation() == "PyPy"', reason="broken on PyPy3") -def test_multiprocessing_pool_close(testdir): +@method_params +def test_multiprocessing_pool_close(testdir, method): pytest.importorskip('multiprocessing.util') script = testdir.makepyfile(''' import multiprocessing def target_fn(a): - %sse: # pragma: nocover + {}se: # pragma: nocover return None def test_run_target(): + multiprocessing.set_start_method({!r}) for i in range(33): p = multiprocessing.Pool(3) try: @@ -1165,9 +1178,9 @@ def test_run_target(): finally: p.close() p.join() -''' % ''.join('''if a == %r: +'''.format(''.join('''if a == %r: return a - el''' % i for i in range(99))) + el''' % i for i in range(99)), method)) result = testdir.runpytest('-v', '--cov=%s' % script.dirpath(), @@ -1185,7 +1198,8 @@ def test_run_target(): @pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows") -def test_multiprocessing_process(testdir): +@method_params +def test_multiprocessing_process(testdir, method): pytest.importorskip('multiprocessing.util') script = testdir.makepyfile(''' @@ -1196,10 +1210,11 @@ def target_fn(): return a def test_run_target(): + multiprocessing.set_start_method({!r}) p = multiprocessing.Process(target=target_fn) p.start() p.join() -''') +'''.format(method)) result = testdir.runpytest('-v', '--cov=%s' % script.dirpath(), @@ -1215,7 +1230,8 @@ def test_run_target(): @pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows") -def test_multiprocessing_process_no_source(testdir): +@method_params +def test_multiprocessing_process_no_source(testdir, method): pytest.importorskip('multiprocessing.util') script = testdir.makepyfile(''' @@ -1245,7 +1261,8 @@ def test_run_target(): @pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows") -def test_multiprocessing_process_with_terminate(testdir): +@method_params +def test_multiprocessing_process_with_terminate(testdir, method): pytest.importorskip('multiprocessing.util') script = testdir.makepyfile(''' @@ -1783,6 +1800,7 @@ def test_not_started_plugin_does_not_fail(testdir): class ns: cov_source = [True] cov_report = '' + plugin = pytest_cov.plugin.CovPlugin(ns, None, start=False) plugin.pytest_runtestloop(None) plugin.pytest_terminal_summary(None) @@ -1900,7 +1918,7 @@ def test_append_coverage(testdir, opts, prop): result = testdir.runpytest('-v', '--cov=%s' % script.dirpath(), script, - *opts.split()+prop.args) + *opts.split() + prop.args) result.stdout.fnmatch_lines([ 'test_1* %s*' % prop.result, ]) @@ -1909,7 +1927,7 @@ def test_append_coverage(testdir, opts, prop): '--cov-append', '--cov=%s' % script2.dirpath(), script2, - *opts.split()+prop.args) + *opts.split() + prop.args) result.stdout.fnmatch_lines([ 'test_1* %s*' % prop.result, 'test_2* %s*' % prop.result2, From 47ad2d1da4516895bacb4c22a0f922f04457199c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 2 Feb 2022 19:45:01 +0200 Subject: [PATCH 053/117] Remove appveyor and update various skel things. --- .appveyor.yml | 46 -- .cookiecutterrc | 14 +- .github/workflows/test.yml | 864 ++++++++++++++++++++++-- MANIFEST.in | 9 +- README.rst | 3 +- ci/appveyor-with-compiler.cmd | 23 - ci/bootstrap.py | 34 +- ci/requirements.txt | 1 + ci/templates/.appveyor.yml | 53 -- ci/templates/.github/workflows/test.yml | 65 ++ setup.py | 5 + tox.ini | 1 - 12 files changed, 893 insertions(+), 225 deletions(-) delete mode 100644 .appveyor.yml delete mode 100644 ci/appveyor-with-compiler.cmd delete mode 100644 ci/templates/.appveyor.yml create mode 100644 ci/templates/.github/workflows/test.yml diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 9f176ee5..00000000 --- a/.appveyor.yml +++ /dev/null @@ -1,46 +0,0 @@ -# NOTE: this file is auto-generated via ci/bootstrap.py (ci/templates/.appveyor.yml). -version: '{branch}-{build}' -build: off -image: - - Visual Studio 2015 - - Visual Studio 2019 -environment: - matrix: - - TOXENV: check - - TOXENV: 'py36-pytest46-xdist127-coverage55,py36-pytest46-xdist133-coverage55,py36-pytest54-xdist133-coverage55,py36-pytest62-xdist202-coverage55' - - TOXENV: 'py37-pytest46-xdist127-coverage55,py37-pytest46-xdist133-coverage55,py37-pytest54-xdist133-coverage55,py37-pytest62-xdist202-coverage55' - - TOXENV: 'py38-pytest46-xdist133-coverage55,py38-pytest54-xdist133-coverage55,py38-pytest62-xdist202-coverage55' - - TOXENV: 'py39-pytest62-xdist202-coverage55' - - TOXENV: 'pypy3-pytest46-xdist127-coverage55,pypy3-pytest46-xdist133-coverage55,pypy3-pytest54-xdist133-coverage55,pypy3-pytest62-xdist202-coverage55' -matrix: - exclude: - - image: Visual Studio 2015 - TOXENV: 'py36-pytest46-xdist127-coverage55,py36-pytest46-xdist133-coverage55,py36-pytest54-xdist133-coverage55,py36-pytest62-xdist202-coverage55' - - image: Visual Studio 2015 - TOXENV: 'py37-pytest46-xdist127-coverage55,py37-pytest46-xdist133-coverage55,py37-pytest54-xdist133-coverage55,py37-pytest62-xdist202-coverage55' - - image: Visual Studio 2015 - TOXENV: 'py38-pytest46-xdist133-coverage55,py38-pytest54-xdist133-coverage55,py38-pytest62-xdist202-coverage55' - - image: Visual Studio 2015 - TOXENV: 'py39-pytest62-xdist202-coverage55' - - image: Visual Studio 2015 - TOXENV: 'pypy3-pytest46-xdist127-coverage55,pypy3-pytest46-xdist133-coverage55,pypy3-pytest54-xdist133-coverage55,pypy3-pytest62-xdist202-coverage55' -init: - - ps: echo $env:TOXENV - - ps: ls C:\Python* -install: - - IF "%TOXENV:~0,6%" == "pypy3-" choco install --no-progress pypy3 - - SET PATH=C:\tools\pypy\pypy;%PATH% - - C:\Python37\python -m pip install --progress-bar=off tox -rci/requirements.txt - -test_script: - - cmd /E:ON /V:ON /C .\ci\appveyor-with-compiler.cmd C:\Python37\python -m tox - -on_failure: - - ps: dir "env:" - - ps: get-content .tox\*\log\* -artifacts: - - path: dist\* - -### To enable remote debugging uncomment this (also, see: http://www.appveyor.com/docs/how-to/rdp-to-build-worker): -# on_finish: -# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/.cookiecutterrc b/.cookiecutterrc index 9cad1178..49e9880e 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -2,7 +2,7 @@ default_context: allow_tests_inside_package: no - appveyor: yes + appveyor: no c_extension_function: '-' c_extension_module: '-' c_extension_optional: no @@ -16,10 +16,10 @@ default_context: command_line_interface: no command_line_interface_bin_name: '-' coveralls: no - coveralls_token: '[Required for Appveyor, take it from https://coveralls.io/github/ionelmc/pytest-cov]' distribution_name: pytest-cov email: contact@ionelmc.ro full_name: Ionel Cristian Mărieș + github_actions: yes legacy_python: yes license: MIT license linter: flake8 @@ -29,9 +29,10 @@ default_context: project_short_description: This plugin produces coverage reports. It supports centralised testing and distributed testing in both load and each modes. It also supports coverage of subprocesses. pypi_badge: yes pypi_disable_upload: no - release_date: '2020-06-12' + release_date: '2021-10-04' repo_hosting: github.com repo_hosting_domain: github.com + repo_main_branch: master repo_name: pytest-cov repo_username: pytest-dev requiresio: yes @@ -45,9 +46,10 @@ default_context: test_matrix_configurator: no test_matrix_separate_coverage: no test_runner: pytest - travis: yes + travis: no travis_osx: no - version: 2.10.1 + version: 3.0.0 + version_manager: bump2version website: http://blog.ionelmc.ro year_from: '2010' - year_to: '2020' + year_to: '2022' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b9f7c11c..18299c41 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,81 +1,797 @@ -name: Test - -on: [push, pull_request, workflow_dispatch] - -env: - FORCE_COLOR: 1 - +name: build +on: [push, pull_request] jobs: test: - runs-on: ubuntu-latest + name: ${{ matrix.name }} + runs-on: ${{ matrix.os }} + timeout-minutes: 30 strategy: fail-fast: false matrix: - python-version: ["pypy-3.6", "pypy-3.7", "3.6", "3.7", "3.8", "3.9", "3.10-dev"] - tox-extra-versions: [ - "pytest46-xdist127", - "pytest46-xdist133", - "pytest54-xdist133", - "pytest62-xdist202", - ] include: - # Add new helper variables to existing jobs - - {python-version: "pypy-3.6", tox-python-version: "pypy3"} - - {python-version: "pypy-3.7", tox-python-version: "pypy3"} - - {python-version: "3.6", tox-python-version: "py36"} - - {python-version: "3.7", tox-python-version: "py37"} - - {python-version: "3.8", tox-python-version: "py38"} - - {python-version: "3.9", tox-python-version: "py39"} - - {python-version: "3.10-dev", tox-python-version: "py310"} - exclude: - # Remove some jobs from the matrix - - {tox-extra-versions: "pytest46-xdist127", python-version: "3.8"} - - {tox-extra-versions: "pytest46-xdist127", python-version: "3.9"} - - {tox-extra-versions: "pytest46-xdist133", python-version: "3.9"} - - {tox-extra-versions: "pytest54-xdist133", python-version: "3.9"} - - {tox-extra-versions: "pytest46-xdist127", python-version: "3.10-dev"} - - {tox-extra-versions: "pytest46-xdist133", python-version: "3.10-dev"} - - {tox-extra-versions: "pytest54-xdist133", python-version: "3.10-dev"} - + - name: 'check' + python: '3.9' + toxpython: 'python3.9' + tox_env: 'check' + os: 'ubuntu-latest' + - name: 'docs' + python: '3.9' + toxpython: 'python3.9' + tox_env: 'docs' + os: 'ubuntu-latest' + - name: 'py36-pytest46-xdist127-coverage55 (ubuntu)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest46-xdist127-coverage55' + os: 'ubuntu-latest' + - name: 'py36-pytest46-xdist127-coverage55 (windows)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest46-xdist127-coverage55' + os: 'windows-latest' + - name: 'py36-pytest46-xdist127-coverage55 (macos)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest46-xdist127-coverage55' + os: 'macos-latest' + - name: 'py37-pytest46-xdist127-coverage55 (ubuntu)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest46-xdist127-coverage55' + os: 'ubuntu-latest' + - name: 'py37-pytest46-xdist127-coverage55 (windows)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest46-xdist127-coverage55' + os: 'windows-latest' + - name: 'py37-pytest46-xdist127-coverage55 (macos)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest46-xdist127-coverage55' + os: 'macos-latest' + - name: 'pypy-pytest46-xdist127-coverage55 (ubuntu)' + python: 'pypy-.' + toxpython: 'pypy.' + python_arch: 'x64' + tox_env: 'pypy-pytest46-xdist127-coverage55' + os: 'ubuntu-latest' + - name: 'pypy-pytest46-xdist127-coverage55 (windows)' + python: 'pypy-.' + toxpython: 'pypy.' + python_arch: 'x64' + tox_env: 'pypy-pytest46-xdist127-coverage55' + os: 'windows-latest' + - name: 'pypy-pytest46-xdist127-coverage55 (macos)' + python: 'pypy-.' + toxpython: 'pypy.' + python_arch: 'x64' + tox_env: 'pypy-pytest46-xdist127-coverage55' + os: 'macos-latest' + - name: 'pypy3-pytest46-xdist127-coverage55 (ubuntu)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest46-xdist127-coverage55' + os: 'ubuntu-latest' + - name: 'pypy3-pytest46-xdist127-coverage55 (windows)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest46-xdist127-coverage55' + os: 'windows-latest' + - name: 'pypy3-pytest46-xdist127-coverage55 (macos)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest46-xdist127-coverage55' + os: 'macos-latest' + - name: 'py36-pytest46-xdist133-coverage55 (ubuntu)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest46-xdist133-coverage55' + os: 'ubuntu-latest' + - name: 'py36-pytest46-xdist133-coverage55 (windows)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest46-xdist133-coverage55' + os: 'windows-latest' + - name: 'py36-pytest46-xdist133-coverage55 (macos)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest46-xdist133-coverage55' + os: 'macos-latest' + - name: 'py36-pytest54-xdist133-coverage55 (ubuntu)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest54-xdist133-coverage55' + os: 'ubuntu-latest' + - name: 'py36-pytest54-xdist133-coverage55 (windows)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest54-xdist133-coverage55' + os: 'windows-latest' + - name: 'py36-pytest54-xdist133-coverage55 (macos)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest54-xdist133-coverage55' + os: 'macos-latest' + - name: 'py37-pytest46-xdist133-coverage55 (ubuntu)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest46-xdist133-coverage55' + os: 'ubuntu-latest' + - name: 'py37-pytest46-xdist133-coverage55 (windows)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest46-xdist133-coverage55' + os: 'windows-latest' + - name: 'py37-pytest46-xdist133-coverage55 (macos)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest46-xdist133-coverage55' + os: 'macos-latest' + - name: 'py37-pytest54-xdist133-coverage55 (ubuntu)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest54-xdist133-coverage55' + os: 'ubuntu-latest' + - name: 'py37-pytest54-xdist133-coverage55 (windows)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest54-xdist133-coverage55' + os: 'windows-latest' + - name: 'py37-pytest54-xdist133-coverage55 (macos)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest54-xdist133-coverage55' + os: 'macos-latest' + - name: 'py38-pytest46-xdist133-coverage55 (ubuntu)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest46-xdist133-coverage55' + os: 'ubuntu-latest' + - name: 'py38-pytest46-xdist133-coverage55 (windows)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest46-xdist133-coverage55' + os: 'windows-latest' + - name: 'py38-pytest46-xdist133-coverage55 (macos)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest46-xdist133-coverage55' + os: 'macos-latest' + - name: 'py38-pytest54-xdist133-coverage55 (ubuntu)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest54-xdist133-coverage55' + os: 'ubuntu-latest' + - name: 'py38-pytest54-xdist133-coverage55 (windows)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest54-xdist133-coverage55' + os: 'windows-latest' + - name: 'py38-pytest54-xdist133-coverage55 (macos)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest54-xdist133-coverage55' + os: 'macos-latest' + - name: 'pypy3-pytest46-xdist133-coverage55 (ubuntu)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest46-xdist133-coverage55' + os: 'ubuntu-latest' + - name: 'pypy3-pytest46-xdist133-coverage55 (windows)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest46-xdist133-coverage55' + os: 'windows-latest' + - name: 'pypy3-pytest46-xdist133-coverage55 (macos)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest46-xdist133-coverage55' + os: 'macos-latest' + - name: 'pypy3-pytest54-xdist133-coverage55 (ubuntu)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest54-xdist133-coverage55' + os: 'ubuntu-latest' + - name: 'pypy3-pytest54-xdist133-coverage55 (windows)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest54-xdist133-coverage55' + os: 'windows-latest' + - name: 'pypy3-pytest54-xdist133-coverage55 (macos)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest54-xdist133-coverage55' + os: 'macos-latest' + - name: 'py36-pytest62-xdist250-coverage55 (ubuntu)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest62-xdist250-coverage55' + os: 'ubuntu-latest' + - name: 'py36-pytest62-xdist250-coverage55 (windows)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest62-xdist250-coverage55' + os: 'windows-latest' + - name: 'py36-pytest62-xdist250-coverage55 (macos)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest62-xdist250-coverage55' + os: 'macos-latest' + - name: 'py36-pytest62-xdist250-coverage60 (ubuntu)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest62-xdist250-coverage60' + os: 'ubuntu-latest' + - name: 'py36-pytest62-xdist250-coverage60 (windows)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest62-xdist250-coverage60' + os: 'windows-latest' + - name: 'py36-pytest62-xdist250-coverage60 (macos)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest62-xdist250-coverage60' + os: 'macos-latest' + - name: 'py36-pytest62-xdist250-coverage61 (ubuntu)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest62-xdist250-coverage61' + os: 'ubuntu-latest' + - name: 'py36-pytest62-xdist250-coverage61 (windows)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest62-xdist250-coverage61' + os: 'windows-latest' + - name: 'py36-pytest62-xdist250-coverage61 (macos)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest62-xdist250-coverage61' + os: 'macos-latest' + - name: 'py36-pytest62-xdist250-coverage62 (ubuntu)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest62-xdist250-coverage62' + os: 'ubuntu-latest' + - name: 'py36-pytest62-xdist250-coverage62 (windows)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest62-xdist250-coverage62' + os: 'windows-latest' + - name: 'py36-pytest62-xdist250-coverage62 (macos)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest62-xdist250-coverage62' + os: 'macos-latest' + - name: 'py36-pytest62-xdist250-coverage63 (ubuntu)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest62-xdist250-coverage63' + os: 'ubuntu-latest' + - name: 'py36-pytest62-xdist250-coverage63 (windows)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest62-xdist250-coverage63' + os: 'windows-latest' + - name: 'py36-pytest62-xdist250-coverage63 (macos)' + python: '3.6' + toxpython: 'python3.6' + python_arch: 'x64' + tox_env: 'py36-pytest62-xdist250-coverage63' + os: 'macos-latest' + - name: 'py37-pytest62-xdist250-coverage55 (ubuntu)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest62-xdist250-coverage55' + os: 'ubuntu-latest' + - name: 'py37-pytest62-xdist250-coverage55 (windows)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest62-xdist250-coverage55' + os: 'windows-latest' + - name: 'py37-pytest62-xdist250-coverage55 (macos)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest62-xdist250-coverage55' + os: 'macos-latest' + - name: 'py37-pytest62-xdist250-coverage60 (ubuntu)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest62-xdist250-coverage60' + os: 'ubuntu-latest' + - name: 'py37-pytest62-xdist250-coverage60 (windows)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest62-xdist250-coverage60' + os: 'windows-latest' + - name: 'py37-pytest62-xdist250-coverage60 (macos)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest62-xdist250-coverage60' + os: 'macos-latest' + - name: 'py37-pytest62-xdist250-coverage61 (ubuntu)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest62-xdist250-coverage61' + os: 'ubuntu-latest' + - name: 'py37-pytest62-xdist250-coverage61 (windows)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest62-xdist250-coverage61' + os: 'windows-latest' + - name: 'py37-pytest62-xdist250-coverage61 (macos)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest62-xdist250-coverage61' + os: 'macos-latest' + - name: 'py37-pytest62-xdist250-coverage62 (ubuntu)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest62-xdist250-coverage62' + os: 'ubuntu-latest' + - name: 'py37-pytest62-xdist250-coverage62 (windows)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest62-xdist250-coverage62' + os: 'windows-latest' + - name: 'py37-pytest62-xdist250-coverage62 (macos)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest62-xdist250-coverage62' + os: 'macos-latest' + - name: 'py37-pytest62-xdist250-coverage63 (ubuntu)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest62-xdist250-coverage63' + os: 'ubuntu-latest' + - name: 'py37-pytest62-xdist250-coverage63 (windows)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest62-xdist250-coverage63' + os: 'windows-latest' + - name: 'py37-pytest62-xdist250-coverage63 (macos)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-pytest62-xdist250-coverage63' + os: 'macos-latest' + - name: 'py38-pytest62-xdist250-coverage55 (ubuntu)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest62-xdist250-coverage55' + os: 'ubuntu-latest' + - name: 'py38-pytest62-xdist250-coverage55 (windows)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest62-xdist250-coverage55' + os: 'windows-latest' + - name: 'py38-pytest62-xdist250-coverage55 (macos)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest62-xdist250-coverage55' + os: 'macos-latest' + - name: 'py38-pytest62-xdist250-coverage60 (ubuntu)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest62-xdist250-coverage60' + os: 'ubuntu-latest' + - name: 'py38-pytest62-xdist250-coverage60 (windows)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest62-xdist250-coverage60' + os: 'windows-latest' + - name: 'py38-pytest62-xdist250-coverage60 (macos)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest62-xdist250-coverage60' + os: 'macos-latest' + - name: 'py38-pytest62-xdist250-coverage61 (ubuntu)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest62-xdist250-coverage61' + os: 'ubuntu-latest' + - name: 'py38-pytest62-xdist250-coverage61 (windows)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest62-xdist250-coverage61' + os: 'windows-latest' + - name: 'py38-pytest62-xdist250-coverage61 (macos)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest62-xdist250-coverage61' + os: 'macos-latest' + - name: 'py38-pytest62-xdist250-coverage62 (ubuntu)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest62-xdist250-coverage62' + os: 'ubuntu-latest' + - name: 'py38-pytest62-xdist250-coverage62 (windows)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest62-xdist250-coverage62' + os: 'windows-latest' + - name: 'py38-pytest62-xdist250-coverage62 (macos)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest62-xdist250-coverage62' + os: 'macos-latest' + - name: 'py38-pytest62-xdist250-coverage63 (ubuntu)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest62-xdist250-coverage63' + os: 'ubuntu-latest' + - name: 'py38-pytest62-xdist250-coverage63 (windows)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest62-xdist250-coverage63' + os: 'windows-latest' + - name: 'py38-pytest62-xdist250-coverage63 (macos)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-pytest62-xdist250-coverage63' + os: 'macos-latest' + - name: 'py39-pytest62-xdist250-coverage55 (ubuntu)' + python: '3.9' + toxpython: 'python3.9' + python_arch: 'x64' + tox_env: 'py39-pytest62-xdist250-coverage55' + os: 'ubuntu-latest' + - name: 'py39-pytest62-xdist250-coverage55 (windows)' + python: '3.9' + toxpython: 'python3.9' + python_arch: 'x64' + tox_env: 'py39-pytest62-xdist250-coverage55' + os: 'windows-latest' + - name: 'py39-pytest62-xdist250-coverage55 (macos)' + python: '3.9' + toxpython: 'python3.9' + python_arch: 'x64' + tox_env: 'py39-pytest62-xdist250-coverage55' + os: 'macos-latest' + - name: 'py39-pytest62-xdist250-coverage60 (ubuntu)' + python: '3.9' + toxpython: 'python3.9' + python_arch: 'x64' + tox_env: 'py39-pytest62-xdist250-coverage60' + os: 'ubuntu-latest' + - name: 'py39-pytest62-xdist250-coverage60 (windows)' + python: '3.9' + toxpython: 'python3.9' + python_arch: 'x64' + tox_env: 'py39-pytest62-xdist250-coverage60' + os: 'windows-latest' + - name: 'py39-pytest62-xdist250-coverage60 (macos)' + python: '3.9' + toxpython: 'python3.9' + python_arch: 'x64' + tox_env: 'py39-pytest62-xdist250-coverage60' + os: 'macos-latest' + - name: 'py39-pytest62-xdist250-coverage61 (ubuntu)' + python: '3.9' + toxpython: 'python3.9' + python_arch: 'x64' + tox_env: 'py39-pytest62-xdist250-coverage61' + os: 'ubuntu-latest' + - name: 'py39-pytest62-xdist250-coverage61 (windows)' + python: '3.9' + toxpython: 'python3.9' + python_arch: 'x64' + tox_env: 'py39-pytest62-xdist250-coverage61' + os: 'windows-latest' + - name: 'py39-pytest62-xdist250-coverage61 (macos)' + python: '3.9' + toxpython: 'python3.9' + python_arch: 'x64' + tox_env: 'py39-pytest62-xdist250-coverage61' + os: 'macos-latest' + - name: 'py39-pytest62-xdist250-coverage62 (ubuntu)' + python: '3.9' + toxpython: 'python3.9' + python_arch: 'x64' + tox_env: 'py39-pytest62-xdist250-coverage62' + os: 'ubuntu-latest' + - name: 'py39-pytest62-xdist250-coverage62 (windows)' + python: '3.9' + toxpython: 'python3.9' + python_arch: 'x64' + tox_env: 'py39-pytest62-xdist250-coverage62' + os: 'windows-latest' + - name: 'py39-pytest62-xdist250-coverage62 (macos)' + python: '3.9' + toxpython: 'python3.9' + python_arch: 'x64' + tox_env: 'py39-pytest62-xdist250-coverage62' + os: 'macos-latest' + - name: 'py39-pytest62-xdist250-coverage63 (ubuntu)' + python: '3.9' + toxpython: 'python3.9' + python_arch: 'x64' + tox_env: 'py39-pytest62-xdist250-coverage63' + os: 'ubuntu-latest' + - name: 'py39-pytest62-xdist250-coverage63 (windows)' + python: '3.9' + toxpython: 'python3.9' + python_arch: 'x64' + tox_env: 'py39-pytest62-xdist250-coverage63' + os: 'windows-latest' + - name: 'py39-pytest62-xdist250-coverage63 (macos)' + python: '3.9' + toxpython: 'python3.9' + python_arch: 'x64' + tox_env: 'py39-pytest62-xdist250-coverage63' + os: 'macos-latest' + - name: 'py310-pytest62-xdist250-coverage55 (ubuntu)' + python: '3.10' + toxpython: 'python3.10' + python_arch: 'x64' + tox_env: 'py310-pytest62-xdist250-coverage55' + os: 'ubuntu-latest' + - name: 'py310-pytest62-xdist250-coverage55 (windows)' + python: '3.10' + toxpython: 'python3.10' + python_arch: 'x64' + tox_env: 'py310-pytest62-xdist250-coverage55' + os: 'windows-latest' + - name: 'py310-pytest62-xdist250-coverage55 (macos)' + python: '3.10' + toxpython: 'python3.10' + python_arch: 'x64' + tox_env: 'py310-pytest62-xdist250-coverage55' + os: 'macos-latest' + - name: 'py310-pytest62-xdist250-coverage60 (ubuntu)' + python: '3.10' + toxpython: 'python3.10' + python_arch: 'x64' + tox_env: 'py310-pytest62-xdist250-coverage60' + os: 'ubuntu-latest' + - name: 'py310-pytest62-xdist250-coverage60 (windows)' + python: '3.10' + toxpython: 'python3.10' + python_arch: 'x64' + tox_env: 'py310-pytest62-xdist250-coverage60' + os: 'windows-latest' + - name: 'py310-pytest62-xdist250-coverage60 (macos)' + python: '3.10' + toxpython: 'python3.10' + python_arch: 'x64' + tox_env: 'py310-pytest62-xdist250-coverage60' + os: 'macos-latest' + - name: 'py310-pytest62-xdist250-coverage61 (ubuntu)' + python: '3.10' + toxpython: 'python3.10' + python_arch: 'x64' + tox_env: 'py310-pytest62-xdist250-coverage61' + os: 'ubuntu-latest' + - name: 'py310-pytest62-xdist250-coverage61 (windows)' + python: '3.10' + toxpython: 'python3.10' + python_arch: 'x64' + tox_env: 'py310-pytest62-xdist250-coverage61' + os: 'windows-latest' + - name: 'py310-pytest62-xdist250-coverage61 (macos)' + python: '3.10' + toxpython: 'python3.10' + python_arch: 'x64' + tox_env: 'py310-pytest62-xdist250-coverage61' + os: 'macos-latest' + - name: 'py310-pytest62-xdist250-coverage62 (ubuntu)' + python: '3.10' + toxpython: 'python3.10' + python_arch: 'x64' + tox_env: 'py310-pytest62-xdist250-coverage62' + os: 'ubuntu-latest' + - name: 'py310-pytest62-xdist250-coverage62 (windows)' + python: '3.10' + toxpython: 'python3.10' + python_arch: 'x64' + tox_env: 'py310-pytest62-xdist250-coverage62' + os: 'windows-latest' + - name: 'py310-pytest62-xdist250-coverage62 (macos)' + python: '3.10' + toxpython: 'python3.10' + python_arch: 'x64' + tox_env: 'py310-pytest62-xdist250-coverage62' + os: 'macos-latest' + - name: 'py310-pytest62-xdist250-coverage63 (ubuntu)' + python: '3.10' + toxpython: 'python3.10' + python_arch: 'x64' + tox_env: 'py310-pytest62-xdist250-coverage63' + os: 'ubuntu-latest' + - name: 'py310-pytest62-xdist250-coverage63 (windows)' + python: '3.10' + toxpython: 'python3.10' + python_arch: 'x64' + tox_env: 'py310-pytest62-xdist250-coverage63' + os: 'windows-latest' + - name: 'py310-pytest62-xdist250-coverage63 (macos)' + python: '3.10' + toxpython: 'python3.10' + python_arch: 'x64' + tox_env: 'py310-pytest62-xdist250-coverage63' + os: 'macos-latest' + - name: 'pypy3-pytest62-xdist250-coverage55 (ubuntu)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest62-xdist250-coverage55' + os: 'ubuntu-latest' + - name: 'pypy3-pytest62-xdist250-coverage55 (windows)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest62-xdist250-coverage55' + os: 'windows-latest' + - name: 'pypy3-pytest62-xdist250-coverage55 (macos)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest62-xdist250-coverage55' + os: 'macos-latest' + - name: 'pypy3-pytest62-xdist250-coverage60 (ubuntu)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest62-xdist250-coverage60' + os: 'ubuntu-latest' + - name: 'pypy3-pytest62-xdist250-coverage60 (windows)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest62-xdist250-coverage60' + os: 'windows-latest' + - name: 'pypy3-pytest62-xdist250-coverage60 (macos)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest62-xdist250-coverage60' + os: 'macos-latest' + - name: 'pypy3-pytest62-xdist250-coverage61 (ubuntu)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest62-xdist250-coverage61' + os: 'ubuntu-latest' + - name: 'pypy3-pytest62-xdist250-coverage61 (windows)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest62-xdist250-coverage61' + os: 'windows-latest' + - name: 'pypy3-pytest62-xdist250-coverage61 (macos)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest62-xdist250-coverage61' + os: 'macos-latest' + - name: 'pypy3-pytest62-xdist250-coverage62 (ubuntu)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest62-xdist250-coverage62' + os: 'ubuntu-latest' + - name: 'pypy3-pytest62-xdist250-coverage62 (windows)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest62-xdist250-coverage62' + os: 'windows-latest' + - name: 'pypy3-pytest62-xdist250-coverage62 (macos)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest62-xdist250-coverage62' + os: 'macos-latest' + - name: 'pypy3-pytest62-xdist250-coverage63 (ubuntu)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest62-xdist250-coverage63' + os: 'ubuntu-latest' + - name: 'pypy3-pytest62-xdist250-coverage63 (windows)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest62-xdist250-coverage63' + os: 'windows-latest' + - name: 'pypy3-pytest62-xdist250-coverage63 (macos)' + python: 'pypy-3.' + toxpython: 'pypy3.' + python_arch: 'x64' + tox_env: 'pypy3-pytest62-xdist250-coverage63' + os: 'macos-latest' steps: - - uses: actions/checkout@v2 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - - name: Get pip cache dir - id: pip-cache - run: | - echo "::set-output name=dir::$(pip cache dir)" - - - name: Cache - uses: actions/cache@v2 - with: - path: ${{ steps.pip-cache.outputs.dir }} - key: - test-${{ matrix.python-version }}-v1-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - test-${{ matrix.python-version }}-v1- - - - name: Install dependencies - run: | - python -m pip install -U pip - python -m pip install -U wheel - python -m pip install --progress-bar=off tox -rci/requirements.txt - virtualenv --version - pip --version - tox --version - - - name: Tox tests - run: | - tox -v -e ${{ matrix.tox-python-version }}-${{ matrix.tox-extra-versions }}-coverage55 - - allgood: - needs: test - runs-on: ubuntu-latest - name: Test successful - steps: - - name: Success - run: echo Test successful + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + architecture: ${{ matrix.python_arch }} + - name: install dependencies + run: | + python -mpip install --progress-bar=off -r ci/requirements.txt + virtualenv --version + pip --version + tox --version + pip list --format=freeze + - name: test + env: + TOXPYTHON: '${{ matrix.toxpython }}' + run: > + tox -e ${{ matrix.tox_env }} -v diff --git a/MANIFEST.in b/MANIFEST.in index af1581be..cbb88f74 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -12,16 +12,17 @@ graft ci graft tests include .bumpversion.cfg -include .coveragerc include .cookiecutterrc +include .coveragerc include .editorconfig - +include tox.ini +include .readthedocs.yml +include .pre-commit-config.yaml include AUTHORS.rst include CHANGELOG.rst include CONTRIBUTING.rst include LICENSE include README.rst -include tox.ini .appveyor.yml .readthedocs.yml .pre-commit-config.yaml -global-exclude *.py[cod] __pycache__/* *.so *.dylib .coverage .coverage.* +global-exclude *.py[cod] __pycache__/* *.so *.dylib diff --git a/README.rst b/README.rst index 508aff84..2137c118 100644 --- a/README.rst +++ b/README.rst @@ -10,7 +10,8 @@ Overview * - docs - |docs| * - tests - - | |github-actions| |appveyor| |requires| + - | |github-actions| |requires| + | * - package - | |version| |conda-forge| |wheel| |supported-versions| |supported-implementations| | |commits-since| diff --git a/ci/appveyor-with-compiler.cmd b/ci/appveyor-with-compiler.cmd deleted file mode 100644 index 289585fc..00000000 --- a/ci/appveyor-with-compiler.cmd +++ /dev/null @@ -1,23 +0,0 @@ -:: Very simple setup: -:: - if WINDOWS_SDK_VERSION is set then activate the SDK. -:: - disable the WDK if it's around. - -SET COMMAND_TO_RUN=%* -SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows -SET WIN_WDK="c:\Program Files (x86)\Windows Kits\10\Include\wdf" -ECHO SDK: %WINDOWS_SDK_VERSION% ARCH: %PYTHON_ARCH% - -IF EXIST %WIN_WDK% ( - REM See: https://connect.microsoft.com/VisualStudio/feedback/details/1610302/ - REN %WIN_WDK% 0wdf -) -IF "%WINDOWS_SDK_VERSION%"=="" GOTO main - -SET DISTUTILS_USE_SDK=1 -SET MSSdk=1 -"%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% -CALL "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release - -:main -ECHO Executing: %COMMAND_TO_RUN% -CALL %COMMAND_TO_RUN% || EXIT 1 diff --git a/ci/bootstrap.py b/ci/bootstrap.py index 77daad5b..b0977495 100755 --- a/ci/bootstrap.py +++ b/ci/bootstrap.py @@ -3,13 +3,14 @@ import os import subprocess import sys -from collections import defaultdict from os.path import abspath from os.path import dirname from os.path import exists from os.path import join +from os.path import relpath base_path = dirname(dirname(abspath(__file__))) +templates_path = join(base_path, "ci", "templates") def check_call(args): @@ -51,7 +52,7 @@ def main(): print(f"Project path: {base_path}") jinja = jinja2.Environment( - loader=jinja2.FileSystemLoader(join(base_path, "ci", "templates")), + loader=jinja2.FileSystemLoader(templates_path), trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True @@ -59,22 +60,21 @@ def main(): tox_environments = [ line.strip() - # WARNING: 'tox' must be installed globally or in the project's virtualenv - for line in subprocess.check_output(['tox', '--listenvs'], universal_newlines=True).splitlines() + # 'tox' need not be installed globally, but must be importable + # by the Python that is running this script. + # This uses sys.executable the same way that the call in + # cookiecutter-pylibrary/hooks/post_gen_project.py + # invokes this bootstrap.py itself. + for line in subprocess.check_output([sys.executable, '-m', 'tox', '--listenvs'], universal_newlines=True).splitlines() ] - tox_environments = [line for line in tox_environments if line not in ['clean', 'report', 'docs', 'check']] - - template_vars = defaultdict(list) - template_vars['tox_environments'] = tox_environments - for env in tox_environments: - first, _ = env.split('-', 1) - template_vars['%s_environments' % first].append(env) - - for name in os.listdir(join("ci", "templates")): - with open(join(base_path, name), "w") as fh: - fh.write('# NOTE: this file is auto-generated via ci/bootstrap.py (ci/templates/%s).\n' % name) - fh.write(jinja.get_template(name).render(**template_vars)) - print(f"Wrote {name}") + tox_environments = [line for line in tox_environments if line.startswith('py')] + + for root, _, files in os.walk(templates_path): + for name in files: + relative = relpath(root, templates_path) + with open(join(base_path, relative, name), "w") as fh: + fh.write(jinja.get_template(join(relative, name)).render(tox_environments=tox_environments)) + print(f"Wrote {name}") print("DONE.") diff --git a/ci/requirements.txt b/ci/requirements.txt index d7f5177e..a0ef106f 100644 --- a/ci/requirements.txt +++ b/ci/requirements.txt @@ -2,3 +2,4 @@ virtualenv>=16.6.0 pip>=19.1.1 setuptools>=18.0.1 six>=1.14.0 +tox diff --git a/ci/templates/.appveyor.yml b/ci/templates/.appveyor.yml deleted file mode 100644 index 2b7e611c..00000000 --- a/ci/templates/.appveyor.yml +++ /dev/null @@ -1,53 +0,0 @@ -version: '{branch}-{build}' -build: off -image: - - Visual Studio 2015 - - Visual Studio 2019 -environment: - matrix: - - TOXENV: check - - TOXENV: '{{ py27_environments|join(",") }}' - - TOXENV: '{{ py35_environments|join(",") }}' - - TOXENV: '{{ py36_environments|join(",") }}' - - TOXENV: '{{ py37_environments|join(",") }}' - - TOXENV: '{{ py38_environments|join(",") }}' - - TOXENV: '{{ py39_environments|join(",") }}' - - TOXENV: '{{ pypy_environments|join(",") }}' - - TOXENV: '{{ pypy3_environments|join(",") }}' -matrix: - exclude: - - image: Visual Studio 2019 - TOXENV: '{{ py27_environments|join(",") }}' - - image: Visual Studio 2015 - TOXENV: '{{ py36_environments|join(",") }}' - - image: Visual Studio 2015 - TOXENV: '{{ py37_environments|join(",") }}' - - image: Visual Studio 2015 - TOXENV: '{{ py38_environments|join(",") }}' - - image: Visual Studio 2015 - TOXENV: '{{ py39_environments|join(",") }}' - - image: Visual Studio 2015 - TOXENV: '{{ pypy_environments|join(",") }}' - - image: Visual Studio 2015 - TOXENV: '{{ pypy3_environments|join(",") }}' -init: - - ps: echo $env:TOXENV - - ps: ls C:\Python* -install: - - IF "%TOXENV:~0,5%" == "pypy-" choco install --no-progress python.pypy - - IF "%TOXENV:~0,6%" == "pypy3-" choco install --no-progress pypy3 - - SET PATH=C:\tools\pypy\pypy;%PATH% - - C:\Python37\python -m pip install --progress-bar=off tox -rci/requirements.txt - -test_script: - - cmd /E:ON /V:ON /C .\ci\appveyor-with-compiler.cmd C:\Python37\python -m tox - -on_failure: - - ps: dir "env:" - - ps: get-content .tox\*\log\* -artifacts: - - path: dist\* - -### To enable remote debugging uncomment this (also, see: http://www.appveyor.com/docs/how-to/rdp-to-build-worker): -# on_finish: -# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/ci/templates/.github/workflows/test.yml b/ci/templates/.github/workflows/test.yml new file mode 100644 index 00000000..96d24801 --- /dev/null +++ b/ci/templates/.github/workflows/test.yml @@ -0,0 +1,65 @@ +name: build +on: [push, pull_request] +jobs: + test: + name: {{ '${{ matrix.name }}' }} + runs-on: {{ '${{ matrix.os }}' }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: + - name: 'check' + python: '3.9' + toxpython: 'python3.9' + tox_env: 'check' + os: 'ubuntu-latest' + - name: 'docs' + python: '3.9' + toxpython: 'python3.9' + tox_env: 'docs' + os: 'ubuntu-latest' +{% for env in tox_environments %} +{% set prefix = env.split('-')[0] -%} +{% if prefix.startswith('pypy') %} +{% set python %}pypy-{{ prefix[4] }}.{{ prefix[5] }}{% endset %} +{% set cpython %}pp{{ prefix[4:5] }}{% endset %} +{% set toxpython %}pypy{{ prefix[4] }}.{{ prefix[5] }}{% endset %} +{% else %} +{% set python %}{{ prefix[2] }}.{{ prefix[3:] }}{% endset %} +{% set cpython %}cp{{ prefix[2:] }}{% endset %} +{% set toxpython %}python{{ prefix[2] }}.{{ prefix[3:] }}{% endset %} +{% endif %} +{% for os, python_arch in [ + ['ubuntu', 'x64'], + ['windows', 'x64'], + ['macos', 'x64'], +] %} + - name: '{{ env }} ({{ os }})' + python: '{{ python }}' + toxpython: '{{ toxpython }}' + python_arch: '{{ python_arch }}' + tox_env: '{{ env }}' + os: '{{ os }}-latest' +{% endfor %} +{% endfor %} + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/setup-python@v2 + with: + python-version: {{ '${{ matrix.python }}' }} + architecture: {{ '${{ matrix.python_arch }}' }} + - name: install dependencies + run: | + python -mpip install --progress-bar=off -r ci/requirements.txt + virtualenv --version + pip --version + tox --version + pip list --format=freeze + - name: test + env: + TOXPYTHON: '{{ '${{ matrix.toxpython }}' }}' + run: > + tox -e {{ '${{ matrix.tox_env }}' }} -v diff --git a/setup.py b/setup.py index 0748d12e..21681887 100755 --- a/setup.py +++ b/setup.py @@ -115,6 +115,11 @@ def run(self): 'Topic :: Software Development :: Testing', 'Topic :: Utilities', ], + project_urls={ + 'Documentation': 'https://pytest-cov.readthedocs.io/', + 'Changelog': 'https://pytest-cov.readthedocs.io/en/latest/changelog.html', + 'Issue Tracker': 'https://github.com/pytest-dev/pytest-cov/issues', + }, keywords=[ 'cover', 'coverage', 'pytest', 'py.test', 'distributed', 'parallel', ], diff --git a/tox.ini b/tox.ini index 99e1fff6..6d7ab6eb 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,6 @@ [testenv:bootstrap] deps = jinja2 - matrix tox skip_install = true commands = From 3cf0631e771595838749f8005e4ff79e92879747 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 3 Feb 2022 15:22:10 +0200 Subject: [PATCH 054/117] Remove unnecessary parametrization. Fix statement count. --- tests/test_pytest_cov.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index e94d59be..33814ef4 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -1223,15 +1223,14 @@ def test_run_target(): result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'test_multiprocessing_process* 8 * 100%*', + 'test_multiprocessing_process* 9 * 100%*', '*1 passed*' ]) assert result.ret == 0 @pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows") -@method_params -def test_multiprocessing_process_no_source(testdir, method): +def test_multiprocessing_process_no_source(testdir): pytest.importorskip('multiprocessing.util') script = testdir.makepyfile(''' @@ -1261,8 +1260,7 @@ def test_run_target(): @pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows") -@method_params -def test_multiprocessing_process_with_terminate(testdir, method): +def test_multiprocessing_process_with_terminate(testdir): pytest.importorskip('multiprocessing.util') script = testdir.makepyfile(''' From 74f77d1c27ff6bfe71271cf647a9495807324b14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 21 Jul 2022 19:45:22 +0300 Subject: [PATCH 055/117] Bump deps and trim deps to strictly necessary. --- .github/workflows/test.yml | 720 ++----------------------------------- tox.ini | 10 +- 2 files changed, 42 insertions(+), 688 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 18299c41..0c182389 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,761 +19,113 @@ jobs: toxpython: 'python3.9' tox_env: 'docs' os: 'ubuntu-latest' - - name: 'py36-pytest46-xdist127-coverage55 (ubuntu)' + - name: 'py36-pytest70-xdist250-coverage62 (ubuntu)' python: '3.6' toxpython: 'python3.6' python_arch: 'x64' - tox_env: 'py36-pytest46-xdist127-coverage55' + tox_env: 'py36-pytest70-xdist250-coverage62' os: 'ubuntu-latest' - - name: 'py36-pytest46-xdist127-coverage55 (windows)' + - name: 'py36-pytest70-xdist250-coverage62 (windows)' python: '3.6' toxpython: 'python3.6' python_arch: 'x64' - tox_env: 'py36-pytest46-xdist127-coverage55' + tox_env: 'py36-pytest70-xdist250-coverage62' os: 'windows-latest' - - name: 'py36-pytest46-xdist127-coverage55 (macos)' + - name: 'py36-pytest70-xdist250-coverage62 (macos)' python: '3.6' toxpython: 'python3.6' python_arch: 'x64' - tox_env: 'py36-pytest46-xdist127-coverage55' + tox_env: 'py36-pytest70-xdist250-coverage62' os: 'macos-latest' - - name: 'py37-pytest46-xdist127-coverage55 (ubuntu)' + - name: 'py37-pytest71-xdist250-coverage64 (ubuntu)' python: '3.7' toxpython: 'python3.7' python_arch: 'x64' - tox_env: 'py37-pytest46-xdist127-coverage55' + tox_env: 'py37-pytest71-xdist250-coverage64' os: 'ubuntu-latest' - - name: 'py37-pytest46-xdist127-coverage55 (windows)' + - name: 'py37-pytest71-xdist250-coverage64 (windows)' python: '3.7' toxpython: 'python3.7' python_arch: 'x64' - tox_env: 'py37-pytest46-xdist127-coverage55' + tox_env: 'py37-pytest71-xdist250-coverage64' os: 'windows-latest' - - name: 'py37-pytest46-xdist127-coverage55 (macos)' + - name: 'py37-pytest71-xdist250-coverage64 (macos)' python: '3.7' toxpython: 'python3.7' python_arch: 'x64' - tox_env: 'py37-pytest46-xdist127-coverage55' + tox_env: 'py37-pytest71-xdist250-coverage64' os: 'macos-latest' - - name: 'pypy-pytest46-xdist127-coverage55 (ubuntu)' - python: 'pypy-.' - toxpython: 'pypy.' - python_arch: 'x64' - tox_env: 'pypy-pytest46-xdist127-coverage55' - os: 'ubuntu-latest' - - name: 'pypy-pytest46-xdist127-coverage55 (windows)' - python: 'pypy-.' - toxpython: 'pypy.' - python_arch: 'x64' - tox_env: 'pypy-pytest46-xdist127-coverage55' - os: 'windows-latest' - - name: 'pypy-pytest46-xdist127-coverage55 (macos)' - python: 'pypy-.' - toxpython: 'pypy.' - python_arch: 'x64' - tox_env: 'pypy-pytest46-xdist127-coverage55' - os: 'macos-latest' - - name: 'pypy3-pytest46-xdist127-coverage55 (ubuntu)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest46-xdist127-coverage55' - os: 'ubuntu-latest' - - name: 'pypy3-pytest46-xdist127-coverage55 (windows)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest46-xdist127-coverage55' - os: 'windows-latest' - - name: 'pypy3-pytest46-xdist127-coverage55 (macos)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest46-xdist127-coverage55' - os: 'macos-latest' - - name: 'py36-pytest46-xdist133-coverage55 (ubuntu)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest46-xdist133-coverage55' - os: 'ubuntu-latest' - - name: 'py36-pytest46-xdist133-coverage55 (windows)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest46-xdist133-coverage55' - os: 'windows-latest' - - name: 'py36-pytest46-xdist133-coverage55 (macos)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest46-xdist133-coverage55' - os: 'macos-latest' - - name: 'py36-pytest54-xdist133-coverage55 (ubuntu)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest54-xdist133-coverage55' - os: 'ubuntu-latest' - - name: 'py36-pytest54-xdist133-coverage55 (windows)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest54-xdist133-coverage55' - os: 'windows-latest' - - name: 'py36-pytest54-xdist133-coverage55 (macos)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest54-xdist133-coverage55' - os: 'macos-latest' - - name: 'py37-pytest46-xdist133-coverage55 (ubuntu)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest46-xdist133-coverage55' - os: 'ubuntu-latest' - - name: 'py37-pytest46-xdist133-coverage55 (windows)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest46-xdist133-coverage55' - os: 'windows-latest' - - name: 'py37-pytest46-xdist133-coverage55 (macos)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest46-xdist133-coverage55' - os: 'macos-latest' - - name: 'py37-pytest54-xdist133-coverage55 (ubuntu)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest54-xdist133-coverage55' - os: 'ubuntu-latest' - - name: 'py37-pytest54-xdist133-coverage55 (windows)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest54-xdist133-coverage55' - os: 'windows-latest' - - name: 'py37-pytest54-xdist133-coverage55 (macos)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest54-xdist133-coverage55' - os: 'macos-latest' - - name: 'py38-pytest46-xdist133-coverage55 (ubuntu)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-pytest46-xdist133-coverage55' - os: 'ubuntu-latest' - - name: 'py38-pytest46-xdist133-coverage55 (windows)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-pytest46-xdist133-coverage55' - os: 'windows-latest' - - name: 'py38-pytest46-xdist133-coverage55 (macos)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-pytest46-xdist133-coverage55' - os: 'macos-latest' - - name: 'py38-pytest54-xdist133-coverage55 (ubuntu)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-pytest54-xdist133-coverage55' - os: 'ubuntu-latest' - - name: 'py38-pytest54-xdist133-coverage55 (windows)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-pytest54-xdist133-coverage55' - os: 'windows-latest' - - name: 'py38-pytest54-xdist133-coverage55 (macos)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-pytest54-xdist133-coverage55' - os: 'macos-latest' - - name: 'pypy3-pytest46-xdist133-coverage55 (ubuntu)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest46-xdist133-coverage55' - os: 'ubuntu-latest' - - name: 'pypy3-pytest46-xdist133-coverage55 (windows)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest46-xdist133-coverage55' - os: 'windows-latest' - - name: 'pypy3-pytest46-xdist133-coverage55 (macos)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest46-xdist133-coverage55' - os: 'macos-latest' - - name: 'pypy3-pytest54-xdist133-coverage55 (ubuntu)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest54-xdist133-coverage55' - os: 'ubuntu-latest' - - name: 'pypy3-pytest54-xdist133-coverage55 (windows)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest54-xdist133-coverage55' - os: 'windows-latest' - - name: 'pypy3-pytest54-xdist133-coverage55 (macos)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest54-xdist133-coverage55' - os: 'macos-latest' - - name: 'py36-pytest62-xdist250-coverage55 (ubuntu)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest62-xdist250-coverage55' - os: 'ubuntu-latest' - - name: 'py36-pytest62-xdist250-coverage55 (windows)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest62-xdist250-coverage55' - os: 'windows-latest' - - name: 'py36-pytest62-xdist250-coverage55 (macos)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest62-xdist250-coverage55' - os: 'macos-latest' - - name: 'py36-pytest62-xdist250-coverage60 (ubuntu)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest62-xdist250-coverage60' - os: 'ubuntu-latest' - - name: 'py36-pytest62-xdist250-coverage60 (windows)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest62-xdist250-coverage60' - os: 'windows-latest' - - name: 'py36-pytest62-xdist250-coverage60 (macos)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest62-xdist250-coverage60' - os: 'macos-latest' - - name: 'py36-pytest62-xdist250-coverage61 (ubuntu)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest62-xdist250-coverage61' - os: 'ubuntu-latest' - - name: 'py36-pytest62-xdist250-coverage61 (windows)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest62-xdist250-coverage61' - os: 'windows-latest' - - name: 'py36-pytest62-xdist250-coverage61 (macos)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest62-xdist250-coverage61' - os: 'macos-latest' - - name: 'py36-pytest62-xdist250-coverage62 (ubuntu)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest62-xdist250-coverage62' - os: 'ubuntu-latest' - - name: 'py36-pytest62-xdist250-coverage62 (windows)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest62-xdist250-coverage62' - os: 'windows-latest' - - name: 'py36-pytest62-xdist250-coverage62 (macos)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest62-xdist250-coverage62' - os: 'macos-latest' - - name: 'py36-pytest62-xdist250-coverage63 (ubuntu)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest62-xdist250-coverage63' - os: 'ubuntu-latest' - - name: 'py36-pytest62-xdist250-coverage63 (windows)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest62-xdist250-coverage63' - os: 'windows-latest' - - name: 'py36-pytest62-xdist250-coverage63 (macos)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest62-xdist250-coverage63' - os: 'macos-latest' - - name: 'py37-pytest62-xdist250-coverage55 (ubuntu)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest62-xdist250-coverage55' - os: 'ubuntu-latest' - - name: 'py37-pytest62-xdist250-coverage55 (windows)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest62-xdist250-coverage55' - os: 'windows-latest' - - name: 'py37-pytest62-xdist250-coverage55 (macos)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest62-xdist250-coverage55' - os: 'macos-latest' - - name: 'py37-pytest62-xdist250-coverage60 (ubuntu)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest62-xdist250-coverage60' - os: 'ubuntu-latest' - - name: 'py37-pytest62-xdist250-coverage60 (windows)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest62-xdist250-coverage60' - os: 'windows-latest' - - name: 'py37-pytest62-xdist250-coverage60 (macos)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest62-xdist250-coverage60' - os: 'macos-latest' - - name: 'py37-pytest62-xdist250-coverage61 (ubuntu)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest62-xdist250-coverage61' - os: 'ubuntu-latest' - - name: 'py37-pytest62-xdist250-coverage61 (windows)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest62-xdist250-coverage61' - os: 'windows-latest' - - name: 'py37-pytest62-xdist250-coverage61 (macos)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest62-xdist250-coverage61' - os: 'macos-latest' - - name: 'py37-pytest62-xdist250-coverage62 (ubuntu)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest62-xdist250-coverage62' - os: 'ubuntu-latest' - - name: 'py37-pytest62-xdist250-coverage62 (windows)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest62-xdist250-coverage62' - os: 'windows-latest' - - name: 'py37-pytest62-xdist250-coverage62 (macos)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest62-xdist250-coverage62' - os: 'macos-latest' - - name: 'py37-pytest62-xdist250-coverage63 (ubuntu)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest62-xdist250-coverage63' - os: 'ubuntu-latest' - - name: 'py37-pytest62-xdist250-coverage63 (windows)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest62-xdist250-coverage63' - os: 'windows-latest' - - name: 'py37-pytest62-xdist250-coverage63 (macos)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest62-xdist250-coverage63' - os: 'macos-latest' - - name: 'py38-pytest62-xdist250-coverage55 (ubuntu)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-pytest62-xdist250-coverage55' - os: 'ubuntu-latest' - - name: 'py38-pytest62-xdist250-coverage55 (windows)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-pytest62-xdist250-coverage55' - os: 'windows-latest' - - name: 'py38-pytest62-xdist250-coverage55 (macos)' + - name: 'py38-pytest71-xdist250-coverage64 (ubuntu)' python: '3.8' toxpython: 'python3.8' python_arch: 'x64' - tox_env: 'py38-pytest62-xdist250-coverage55' - os: 'macos-latest' - - name: 'py38-pytest62-xdist250-coverage60 (ubuntu)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-pytest62-xdist250-coverage60' + tox_env: 'py38-pytest71-xdist250-coverage64' os: 'ubuntu-latest' - - name: 'py38-pytest62-xdist250-coverage60 (windows)' + - name: 'py38-pytest71-xdist250-coverage64 (windows)' python: '3.8' toxpython: 'python3.8' python_arch: 'x64' - tox_env: 'py38-pytest62-xdist250-coverage60' + tox_env: 'py38-pytest71-xdist250-coverage64' os: 'windows-latest' - - name: 'py38-pytest62-xdist250-coverage60 (macos)' + - name: 'py38-pytest71-xdist250-coverage64 (macos)' python: '3.8' toxpython: 'python3.8' python_arch: 'x64' - tox_env: 'py38-pytest62-xdist250-coverage60' + tox_env: 'py38-pytest71-xdist250-coverage64' os: 'macos-latest' - - name: 'py38-pytest62-xdist250-coverage61 (ubuntu)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-pytest62-xdist250-coverage61' - os: 'ubuntu-latest' - - name: 'py38-pytest62-xdist250-coverage61 (windows)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-pytest62-xdist250-coverage61' - os: 'windows-latest' - - name: 'py38-pytest62-xdist250-coverage61 (macos)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-pytest62-xdist250-coverage61' - os: 'macos-latest' - - name: 'py38-pytest62-xdist250-coverage62 (ubuntu)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-pytest62-xdist250-coverage62' - os: 'ubuntu-latest' - - name: 'py38-pytest62-xdist250-coverage62 (windows)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-pytest62-xdist250-coverage62' - os: 'windows-latest' - - name: 'py38-pytest62-xdist250-coverage62 (macos)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-pytest62-xdist250-coverage62' - os: 'macos-latest' - - name: 'py38-pytest62-xdist250-coverage63 (ubuntu)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-pytest62-xdist250-coverage63' - os: 'ubuntu-latest' - - name: 'py38-pytest62-xdist250-coverage63 (windows)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-pytest62-xdist250-coverage63' - os: 'windows-latest' - - name: 'py38-pytest62-xdist250-coverage63 (macos)' - python: '3.8' - toxpython: 'python3.8' - python_arch: 'x64' - tox_env: 'py38-pytest62-xdist250-coverage63' - os: 'macos-latest' - - name: 'py39-pytest62-xdist250-coverage55 (ubuntu)' - python: '3.9' - toxpython: 'python3.9' - python_arch: 'x64' - tox_env: 'py39-pytest62-xdist250-coverage55' - os: 'ubuntu-latest' - - name: 'py39-pytest62-xdist250-coverage55 (windows)' - python: '3.9' - toxpython: 'python3.9' - python_arch: 'x64' - tox_env: 'py39-pytest62-xdist250-coverage55' - os: 'windows-latest' - - name: 'py39-pytest62-xdist250-coverage55 (macos)' - python: '3.9' - toxpython: 'python3.9' - python_arch: 'x64' - tox_env: 'py39-pytest62-xdist250-coverage55' - os: 'macos-latest' - - name: 'py39-pytest62-xdist250-coverage60 (ubuntu)' + - name: 'py39-pytest71-xdist250-coverage64 (ubuntu)' python: '3.9' toxpython: 'python3.9' python_arch: 'x64' - tox_env: 'py39-pytest62-xdist250-coverage60' + tox_env: 'py39-pytest71-xdist250-coverage64' os: 'ubuntu-latest' - - name: 'py39-pytest62-xdist250-coverage60 (windows)' + - name: 'py39-pytest71-xdist250-coverage64 (windows)' python: '3.9' toxpython: 'python3.9' python_arch: 'x64' - tox_env: 'py39-pytest62-xdist250-coverage60' + tox_env: 'py39-pytest71-xdist250-coverage64' os: 'windows-latest' - - name: 'py39-pytest62-xdist250-coverage60 (macos)' + - name: 'py39-pytest71-xdist250-coverage64 (macos)' python: '3.9' toxpython: 'python3.9' python_arch: 'x64' - tox_env: 'py39-pytest62-xdist250-coverage60' + tox_env: 'py39-pytest71-xdist250-coverage64' os: 'macos-latest' - - name: 'py39-pytest62-xdist250-coverage61 (ubuntu)' - python: '3.9' - toxpython: 'python3.9' - python_arch: 'x64' - tox_env: 'py39-pytest62-xdist250-coverage61' - os: 'ubuntu-latest' - - name: 'py39-pytest62-xdist250-coverage61 (windows)' - python: '3.9' - toxpython: 'python3.9' - python_arch: 'x64' - tox_env: 'py39-pytest62-xdist250-coverage61' - os: 'windows-latest' - - name: 'py39-pytest62-xdist250-coverage61 (macos)' - python: '3.9' - toxpython: 'python3.9' - python_arch: 'x64' - tox_env: 'py39-pytest62-xdist250-coverage61' - os: 'macos-latest' - - name: 'py39-pytest62-xdist250-coverage62 (ubuntu)' - python: '3.9' - toxpython: 'python3.9' - python_arch: 'x64' - tox_env: 'py39-pytest62-xdist250-coverage62' - os: 'ubuntu-latest' - - name: 'py39-pytest62-xdist250-coverage62 (windows)' - python: '3.9' - toxpython: 'python3.9' - python_arch: 'x64' - tox_env: 'py39-pytest62-xdist250-coverage62' - os: 'windows-latest' - - name: 'py39-pytest62-xdist250-coverage62 (macos)' - python: '3.9' - toxpython: 'python3.9' - python_arch: 'x64' - tox_env: 'py39-pytest62-xdist250-coverage62' - os: 'macos-latest' - - name: 'py39-pytest62-xdist250-coverage63 (ubuntu)' - python: '3.9' - toxpython: 'python3.9' - python_arch: 'x64' - tox_env: 'py39-pytest62-xdist250-coverage63' - os: 'ubuntu-latest' - - name: 'py39-pytest62-xdist250-coverage63 (windows)' - python: '3.9' - toxpython: 'python3.9' - python_arch: 'x64' - tox_env: 'py39-pytest62-xdist250-coverage63' - os: 'windows-latest' - - name: 'py39-pytest62-xdist250-coverage63 (macos)' - python: '3.9' - toxpython: 'python3.9' - python_arch: 'x64' - tox_env: 'py39-pytest62-xdist250-coverage63' - os: 'macos-latest' - - name: 'py310-pytest62-xdist250-coverage55 (ubuntu)' - python: '3.10' - toxpython: 'python3.10' - python_arch: 'x64' - tox_env: 'py310-pytest62-xdist250-coverage55' - os: 'ubuntu-latest' - - name: 'py310-pytest62-xdist250-coverage55 (windows)' - python: '3.10' - toxpython: 'python3.10' - python_arch: 'x64' - tox_env: 'py310-pytest62-xdist250-coverage55' - os: 'windows-latest' - - name: 'py310-pytest62-xdist250-coverage55 (macos)' - python: '3.10' - toxpython: 'python3.10' - python_arch: 'x64' - tox_env: 'py310-pytest62-xdist250-coverage55' - os: 'macos-latest' - - name: 'py310-pytest62-xdist250-coverage60 (ubuntu)' - python: '3.10' - toxpython: 'python3.10' - python_arch: 'x64' - tox_env: 'py310-pytest62-xdist250-coverage60' - os: 'ubuntu-latest' - - name: 'py310-pytest62-xdist250-coverage60 (windows)' - python: '3.10' - toxpython: 'python3.10' - python_arch: 'x64' - tox_env: 'py310-pytest62-xdist250-coverage60' - os: 'windows-latest' - - name: 'py310-pytest62-xdist250-coverage60 (macos)' - python: '3.10' - toxpython: 'python3.10' - python_arch: 'x64' - tox_env: 'py310-pytest62-xdist250-coverage60' - os: 'macos-latest' - - name: 'py310-pytest62-xdist250-coverage61 (ubuntu)' - python: '3.10' - toxpython: 'python3.10' - python_arch: 'x64' - tox_env: 'py310-pytest62-xdist250-coverage61' - os: 'ubuntu-latest' - - name: 'py310-pytest62-xdist250-coverage61 (windows)' - python: '3.10' - toxpython: 'python3.10' - python_arch: 'x64' - tox_env: 'py310-pytest62-xdist250-coverage61' - os: 'windows-latest' - - name: 'py310-pytest62-xdist250-coverage61 (macos)' - python: '3.10' - toxpython: 'python3.10' - python_arch: 'x64' - tox_env: 'py310-pytest62-xdist250-coverage61' - os: 'macos-latest' - - name: 'py310-pytest62-xdist250-coverage62 (ubuntu)' - python: '3.10' - toxpython: 'python3.10' - python_arch: 'x64' - tox_env: 'py310-pytest62-xdist250-coverage62' - os: 'ubuntu-latest' - - name: 'py310-pytest62-xdist250-coverage62 (windows)' - python: '3.10' - toxpython: 'python3.10' - python_arch: 'x64' - tox_env: 'py310-pytest62-xdist250-coverage62' - os: 'windows-latest' - - name: 'py310-pytest62-xdist250-coverage62 (macos)' + - name: 'py310-pytest71-xdist250-coverage64 (ubuntu)' python: '3.10' toxpython: 'python3.10' python_arch: 'x64' - tox_env: 'py310-pytest62-xdist250-coverage62' - os: 'macos-latest' - - name: 'py310-pytest62-xdist250-coverage63 (ubuntu)' - python: '3.10' - toxpython: 'python3.10' - python_arch: 'x64' - tox_env: 'py310-pytest62-xdist250-coverage63' + tox_env: 'py310-pytest71-xdist250-coverage64' os: 'ubuntu-latest' - - name: 'py310-pytest62-xdist250-coverage63 (windows)' + - name: 'py310-pytest71-xdist250-coverage64 (windows)' python: '3.10' toxpython: 'python3.10' python_arch: 'x64' - tox_env: 'py310-pytest62-xdist250-coverage63' + tox_env: 'py310-pytest71-xdist250-coverage64' os: 'windows-latest' - - name: 'py310-pytest62-xdist250-coverage63 (macos)' + - name: 'py310-pytest71-xdist250-coverage64 (macos)' python: '3.10' toxpython: 'python3.10' python_arch: 'x64' - tox_env: 'py310-pytest62-xdist250-coverage63' - os: 'macos-latest' - - name: 'pypy3-pytest62-xdist250-coverage55 (ubuntu)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest62-xdist250-coverage55' - os: 'ubuntu-latest' - - name: 'pypy3-pytest62-xdist250-coverage55 (windows)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest62-xdist250-coverage55' - os: 'windows-latest' - - name: 'pypy3-pytest62-xdist250-coverage55 (macos)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest62-xdist250-coverage55' - os: 'macos-latest' - - name: 'pypy3-pytest62-xdist250-coverage60 (ubuntu)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest62-xdist250-coverage60' - os: 'ubuntu-latest' - - name: 'pypy3-pytest62-xdist250-coverage60 (windows)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest62-xdist250-coverage60' - os: 'windows-latest' - - name: 'pypy3-pytest62-xdist250-coverage60 (macos)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest62-xdist250-coverage60' - os: 'macos-latest' - - name: 'pypy3-pytest62-xdist250-coverage61 (ubuntu)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest62-xdist250-coverage61' - os: 'ubuntu-latest' - - name: 'pypy3-pytest62-xdist250-coverage61 (windows)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest62-xdist250-coverage61' - os: 'windows-latest' - - name: 'pypy3-pytest62-xdist250-coverage61 (macos)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest62-xdist250-coverage61' - os: 'macos-latest' - - name: 'pypy3-pytest62-xdist250-coverage62 (ubuntu)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest62-xdist250-coverage62' - os: 'ubuntu-latest' - - name: 'pypy3-pytest62-xdist250-coverage62 (windows)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest62-xdist250-coverage62' - os: 'windows-latest' - - name: 'pypy3-pytest62-xdist250-coverage62 (macos)' - python: 'pypy-3.' - toxpython: 'pypy3.' - python_arch: 'x64' - tox_env: 'pypy3-pytest62-xdist250-coverage62' + tox_env: 'py310-pytest71-xdist250-coverage64' os: 'macos-latest' - - name: 'pypy3-pytest62-xdist250-coverage63 (ubuntu)' + - name: 'pypy3-pytest71-xdist250-coverage64 (ubuntu)' python: 'pypy-3.' toxpython: 'pypy3.' python_arch: 'x64' - tox_env: 'pypy3-pytest62-xdist250-coverage63' + tox_env: 'pypy3-pytest71-xdist250-coverage64' os: 'ubuntu-latest' - - name: 'pypy3-pytest62-xdist250-coverage63 (windows)' + - name: 'pypy3-pytest71-xdist250-coverage64 (windows)' python: 'pypy-3.' toxpython: 'pypy3.' python_arch: 'x64' - tox_env: 'pypy3-pytest62-xdist250-coverage63' + tox_env: 'pypy3-pytest71-xdist250-coverage64' os: 'windows-latest' - - name: 'pypy3-pytest62-xdist250-coverage63 (macos)' + - name: 'pypy3-pytest71-xdist250-coverage64 (macos)' python: 'pypy-3.' toxpython: 'pypy3.' python_arch: 'x64' - tox_env: 'pypy3-pytest62-xdist250-coverage63' + tox_env: 'pypy3-pytest71-xdist250-coverage64' os: 'macos-latest' steps: - uses: actions/checkout@v2 diff --git a/tox.ini b/tox.ini index 6d7ab6eb..88a2b63a 100644 --- a/tox.ini +++ b/tox.ini @@ -12,9 +12,8 @@ passenv = [tox] envlist = check - py{36,37,py,py3}-pytest46-xdist127-coverage{55} - py{36,37,38,py3}-pytest{46,54}-xdist133-coverage{55} - py{36,37,38,39,310,py3}-pytest{62}-xdist250-coverage{55,60,61,62,63} + py{36}-pytest{70}-xdist250-coverage{62} + py{37,38,39,310,py3}-pytest{71}-xdist250-coverage{64} docs [testenv] @@ -29,6 +28,8 @@ setenv = pytest60: _DEP_PYTEST=pytest==6.0.2 pytest61: _DEP_PYTEST=pytest==6.1.2 pytest62: _DEP_PYTEST=pytest==6.2.5 + pytest70: _DEP_PYTEST=pytest==7.0.1 + pytest71: _DEP_PYTEST=pytest==7.1.2 xdist127: _DEP_PYTESTXDIST=pytest-xdist==1.27.0 xdist129: _DEP_PYTESTXDIST=pytest-xdist==1.29.0 @@ -52,7 +53,8 @@ setenv = coverage60: _DEP_COVERAGE=coverage==6.0.2 coverage61: _DEP_COVERAGE=coverage==6.1.2 coverage62: _DEP_COVERAGE=coverage==6.2 - coverage63: _DEP_COVERAGE=coverage==6.3 + coverage63: _DEP_COVERAGE=coverage==6.3.3 + coverage64: _DEP_COVERAGE=coverage==6.4.2 # For testing against a coverage.py working tree. coveragedev: _DEP_COVERAGE=-e{env:COVERAGE_HOME} passenv = From ef56546f0ae2add47bc9bed3734a096795fffcbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 21 Jul 2022 19:52:47 +0300 Subject: [PATCH 056/117] Cleanup what can only be an accidental fatfinger copypasta. --- CHANGELOG.rst | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3087426b..1ec62380 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -36,17 +36,6 @@ Changelog Contributed by Thomas Grainger in `#477 `_. - -2.13.0 (2021-06-01) -------------------- - -* Changed the `toml` requirement to be always be directly required (instead of being required through a coverage extra). - This fixes issues with pip-compile (`pip-tools#1300 `_). - Contributed by Sorin Sbarnea in `#472 `_. -* Documented ``show_contexts``. - Contributed by Brian Rutledge in `#473 `_. - - 2.12.1 (2021-06-01) ------------------- From f1ee0461b1dd7b5ea530fae17dcdc758bfa2d5b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 21 Jul 2022 19:53:22 +0300 Subject: [PATCH 057/117] Remove another dupe entry. --- CHANGELOG.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1ec62380..e559ebb4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -185,8 +185,6 @@ Changelog `#272 `_, `#271 `_ and `#269 `_. -* Improved documentation regarding subprocess and multiprocessing. - Contributed in `#265 `_. * Improved ``pytest_cov.embed.cleanup_on_sigterm`` to be reentrant (signal deliveries while signal handling is running won't break stuff). * Added ``pytest_cov.embed.cleanup_on_signal`` for customized cleanup. From a5b07425cafab44dd956f02f3c096023db1cd6e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 21 Jul 2022 19:56:58 +0300 Subject: [PATCH 058/117] Fix `cannot import name 'environmentfilter' from 'jinja2'` issue. --- docs/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/requirements.txt b/docs/requirements.txt index ccec79fd..6fdf26f9 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,5 @@ sphinx==3.0.3 sphinx-py3doc-enhanced-theme==2.4.0 docutils==0.16 +jinja2<3.1 -e . From 16601bb93612f1b087452577fd63b99f9a894766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 21 Jul 2022 23:34:07 +0300 Subject: [PATCH 059/117] Remove our crummy multiprocessing hooks and work on some tests with the builtin mp support in coverage. --- src/pytest-cov.pth | 2 +- src/pytest_cov/embed.py | 18 ------- tests/test_pytest_cov.py | 103 ++++++++++++++++++--------------------- 3 files changed, 49 insertions(+), 74 deletions(-) diff --git a/src/pytest-cov.pth b/src/pytest-cov.pth index 91f2b7c7..8ed1a516 100644 --- a/src/pytest-cov.pth +++ b/src/pytest-cov.pth @@ -1 +1 @@ -import os, sys;exec('if \'COV_CORE_SOURCE\' in os.environ:\n try:\n from pytest_cov.embed import init\n init()\n except Exception as exc:\n sys.stderr.write(\n "pytest-cov: Failed to setup subprocess coverage. "\n "Environ: {0!r} "\n "Exception: {1!r}\\n".format(\n dict((k, v) for k, v in os.environ.items() if k.startswith(\'COV_CORE\')),\n exc\n )\n )\n') \ No newline at end of file +import os, sys;exec('if \'COV_CORE_SOURCE\' in os.environ:\n try:\n from pytest_cov.embed import init\n init()\n except Exception as exc:\n sys.stderr.write(\n "pytest-cov: Failed to setup subprocess coverage. "\n "Environ: {0!r} "\n "Exception: {1!r}\\n".format(\n dict((k, v) for k, v in os.environ.items() if k.startswith(\'COV_CORE\')),\n exc\n )\n )\n') diff --git a/src/pytest_cov/embed.py b/src/pytest_cov/embed.py index fe0575e9..f8a2749f 100644 --- a/src/pytest_cov/embed.py +++ b/src/pytest_cov/embed.py @@ -20,22 +20,6 @@ _active_cov = None -def multiprocessing_start(_): - global _active_cov - cov = init() - if cov: - _active_cov = cov - multiprocessing.util.Finalize(None, cleanup, exitpriority=1000) - - -try: - import multiprocessing.util -except ImportError: - pass -else: - multiprocessing.util.register_after_fork(multiprocessing_start, multiprocessing_start) - - def init(): # Only continue if ancestor process has set everything needed in # the env. @@ -105,8 +89,6 @@ def cleanup(): _signal_cleanup_handler(*pending_signal) -multiprocessing_finish = cleanup # in case someone dared to use this internal - _previous_handlers = {} _pending_signal = None _cleanup_in_progress = False diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 33814ef4..edf0fe6e 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -157,15 +157,6 @@ def test_foo(cov): pytest.param('-n 1', marks=pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"')) ], ids=['nodist', 'xdist']) -skipif_multiprocessing_is_broken = pytest.mark.skipif( - 'sys.version_info[:2] >= (3, 8)', - reason="deadlocks on Python 3.8+, see: https://bugs.python.org/issue38227" -) -method_params = pytest.mark.parametrize('method', [ - pytest.param('fork', marks=skipif_multiprocessing_is_broken), - pytest.param('spawn', marks=skipif_multiprocessing_is_broken), -]) - @pytest.fixture(scope='session', autouse=True) def adjust_sys_path(): @@ -203,7 +194,7 @@ def prop(request): ) -def test_central(testdir, prop): +def test_central(pytester, testdir, prop): script = testdir.makepyfile(prop.code) testdir.tmpdir.join('.coveragerc').write(prop.fullconf) @@ -470,7 +461,7 @@ def test_cov_min_no_report(testdir): ]) -def test_central_nonspecific(testdir, prop): +def test_central_nonspecific(pytester, testdir, prop): script = testdir.makepyfile(prop.code) testdir.tmpdir.join('.coveragerc').write(prop.fullconf) result = testdir.runpytest('-v', @@ -505,7 +496,7 @@ def test_cov_min_from_coveragerc(testdir): assert result.ret != 0 -def test_central_coveragerc(testdir, prop): +def test_central_coveragerc(pytester, testdir, prop): script = testdir.makepyfile(prop.code) testdir.tmpdir.join('.coveragerc').write(COVERAGERC_SOURCE + prop.conf) @@ -523,7 +514,7 @@ def test_central_coveragerc(testdir, prop): @xdist_params -def test_central_with_path_aliasing(testdir, monkeypatch, opts, prop): +def test_central_with_path_aliasing(pytester, testdir, monkeypatch, opts, prop): mod1 = testdir.mkdir('src').join('mod.py') mod1.write(SCRIPT) mod2 = testdir.mkdir('aliased').join('mod.py') @@ -557,7 +548,7 @@ def test_central_with_path_aliasing(testdir, monkeypatch, opts, prop): @xdist_params -def test_borken_cwd(testdir, monkeypatch, opts): +def test_borken_cwd(pytester, testdir, monkeypatch, opts): testdir.makepyfile(mod=''' def foobar(a, b): return a + b @@ -596,7 +587,7 @@ def test_foobar(bad): assert result.ret == 0 -def test_subprocess_with_path_aliasing(testdir, monkeypatch): +def test_subprocess_with_path_aliasing(pytester, testdir, monkeypatch): src = testdir.mkdir('src') src.join('parent_script.py').write(SCRIPT_PARENT) src.join('child_script.py').write(SCRIPT_CHILD) @@ -632,7 +623,7 @@ def test_subprocess_with_path_aliasing(testdir, monkeypatch): assert result.ret == 0 -def test_show_missing_coveragerc(testdir, prop): +def test_show_missing_coveragerc(pytester, testdir, prop): script = testdir.makepyfile(prop.code) testdir.tmpdir.join('.coveragerc').write(""" [run] @@ -675,7 +666,7 @@ def test_fail(): result.stdout.fnmatch_lines(['*1 failed*']) -def test_no_cov(testdir, monkeypatch): +def test_no_cov(pytester, testdir, monkeypatch): script = testdir.makepyfile(SCRIPT) testdir.makeini(""" [pytest] @@ -742,7 +733,7 @@ def test_foo(foo): @pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"') -def test_dist_collocated(testdir, prop): +def test_dist_collocated(pytester, testdir, prop): script = testdir.makepyfile(prop.code) testdir.tmpdir.join('.coveragerc').write(prop.fullconf) result = testdir.runpytest('-v', @@ -762,7 +753,7 @@ def test_dist_collocated(testdir, prop): @pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"') -def test_dist_not_collocated(testdir, prop): +def test_dist_not_collocated(pytester, testdir, prop): script = testdir.makepyfile(prop.code) dir1 = testdir.mkdir('dir1') dir2 = testdir.mkdir('dir2') @@ -795,7 +786,7 @@ def test_dist_not_collocated(testdir, prop): @pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"') -def test_dist_not_collocated_coveragerc_source(testdir, prop): +def test_dist_not_collocated_coveragerc_source(pytester, testdir, prop): script = testdir.makepyfile(prop.code) dir1 = testdir.mkdir('dir1') dir2 = testdir.mkdir('dir2') @@ -870,7 +861,7 @@ def test_central_subprocess_change_cwd(testdir): assert result.ret == 0 -def test_central_subprocess_change_cwd_with_pythonpath(testdir, monkeypatch): +def test_central_subprocess_change_cwd_with_pythonpath(pytester, testdir, monkeypatch): stuff = testdir.mkdir('stuff') parent_script = stuff.join('parent_script.py') parent_script.write(SCRIPT_PARENT_CHANGE_CWD_IMPORT_CHILD) @@ -941,7 +932,7 @@ def test_dist_subprocess_collocated(testdir): @pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"') -def test_dist_subprocess_not_collocated(testdir, tmpdir): +def test_dist_subprocess_not_collocated(pytester, testdir, tmpdir): scripts = testdir.makepyfile(parent_script=SCRIPT_PARENT, child_script=SCRIPT_CHILD) parent_script = scripts.dirpath().join('parent_script.py') @@ -1071,10 +1062,11 @@ def test_funcarg_not_active(testdir): @pytest.mark.skipif("sys.version_info[0] < 3", reason="no context manager api on Python 2") @pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows") @pytest.mark.skipif('platform.python_implementation() == "PyPy"', reason="often deadlocks on PyPy") -@method_params -def test_multiprocessing_pool(testdir, method): +@pytest.mark.parametrize('method', ['fork', 'spawn']) +@pytest.mark.xfail +def test_multiprocessing_pool(pytester, testdir, method): pytest.importorskip('multiprocessing.util') - + pytester.path.joinpath(".coveragerc").write_text(MP_COVERAGERC) script = testdir.makepyfile(''' import multiprocessing @@ -1084,9 +1076,6 @@ def target_fn(a): def test_run_target(): multiprocessing.set_start_method({!r}) - from pytest_cov.embed import cleanup_on_sigterm - cleanup_on_sigterm() - for i in range(33): with multiprocessing.Pool(3) as p: p.map(target_fn, [i * 3 + j for j in range(3)]) @@ -1102,21 +1091,21 @@ def test_run_target(): assert "Doesn't seem to be a coverage.py data file" not in result.stdout.str() assert "Doesn't seem to be a coverage.py data file" not in result.stderr.str() - assert not testdir.tmpdir.listdir(".coverage.*") result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', 'test_multiprocessing_pool* 100%*', '*1 passed*' ]) + assert not testdir.tmpdir.listdir(".coverage.*") assert result.ret == 0 @pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows") @pytest.mark.skipif('platform.python_implementation() == "PyPy"', reason="often deadlocks on PyPy") -@method_params -def test_multiprocessing_pool_terminate(testdir, method): +@pytest.mark.parametrize('method', ['fork', 'spawn']) +def test_multiprocessing_pool_terminate(pytester, testdir, method): pytest.importorskip('multiprocessing.util') - + pytester.path.joinpath(".coveragerc").write_text(MP_COVERAGERC) script = testdir.makepyfile(''' import multiprocessing @@ -1126,8 +1115,6 @@ def target_fn(a): def test_run_target(): multiprocessing.set_start_method({!r}) - from pytest_cov.embed import cleanup_on_sigterm - cleanup_on_sigterm() for i in range(33): p = multiprocessing.Pool(3) @@ -1147,21 +1134,21 @@ def test_run_target(): assert "Doesn't seem to be a coverage.py data file" not in result.stdout.str() assert "Doesn't seem to be a coverage.py data file" not in result.stderr.str() - assert not testdir.tmpdir.listdir(".coverage.*") result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', 'test_multiprocessing_pool* 100%*', '*1 passed*' ]) + assert not testdir.tmpdir.listdir(".coverage.*") assert result.ret == 0 @pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows") @pytest.mark.skipif('sys.version_info[0] > 2 and platform.python_implementation() == "PyPy"', reason="broken on PyPy3") -@method_params -def test_multiprocessing_pool_close(testdir, method): +@pytest.mark.parametrize('method', ['fork', 'spawn']) +def test_multiprocessing_pool_close(pytester, testdir, method): pytest.importorskip('multiprocessing.util') - + pytester.path.joinpath(".coveragerc").write_text(MP_COVERAGERC) script = testdir.makepyfile(''' import multiprocessing @@ -1188,20 +1175,20 @@ def test_run_target(): script) assert "Doesn't seem to be a coverage.py data file" not in result.stdout.str() assert "Doesn't seem to be a coverage.py data file" not in result.stderr.str() - assert not testdir.tmpdir.listdir(".coverage.*") result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', 'test_multiprocessing_pool* 100%*', '*1 passed*' ]) + # assert not testdir.tmpdir.listdir(".coverage.*") assert result.ret == 0 @pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows") -@method_params -def test_multiprocessing_process(testdir, method): +@pytest.mark.parametrize('method', ['fork', 'spawn']) +def test_multiprocessing_process(pytester, testdir, method): pytest.importorskip('multiprocessing.util') - + pytester.path.joinpath(".coveragerc").write_text(MP_COVERAGERC) script = testdir.makepyfile(''' import multiprocessing @@ -1230,9 +1217,9 @@ def test_run_target(): @pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows") -def test_multiprocessing_process_no_source(testdir): +def test_multiprocessing_process_no_source(pytester, testdir): pytest.importorskip('multiprocessing.util') - + pytester.path.joinpath(".coveragerc").write_text(MP_COVERAGERC) script = testdir.makepyfile(''' import multiprocessing @@ -1260,9 +1247,9 @@ def test_run_target(): @pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows") -def test_multiprocessing_process_with_terminate(testdir): +def test_multiprocessing_process_with_terminate(pytester, testdir): pytest.importorskip('multiprocessing.util') - + pytester.path.joinpath(".coveragerc").write_text(MP_COVERAGERC) script = testdir.makepyfile(''' import multiprocessing import time @@ -1349,7 +1336,7 @@ def test_run(): ('cleanup_on_signal(signal.SIGBREAK)', '87% 21-22'), ('cleanup()', '73% 19-22'), ]) -def test_cleanup_on_sigterm_sig_break(testdir, setup): +def test_cleanup_on_sigterm_sig_break(pytester, testdir, setup): # worth a read: https://stefan.sofa-rockers.org/2013/08/15/handling-sub-process-hierarchies-python-linux-os-x/ script = testdir.makepyfile(''' import os, signal, subprocess, sys, time @@ -1395,7 +1382,7 @@ def test_run(): ('cleanup_on_sigterm()', '88% 18-19'), ('cleanup()', '75% 16-19'), ]) -def test_cleanup_on_sigterm_sig_dfl(testdir, setup): +def test_cleanup_on_sigterm_sig_dfl(pytester, testdir, setup): script = testdir.makepyfile(''' import os, signal, subprocess, sys, time @@ -1551,7 +1538,7 @@ def test_cover_conftest(testdir): @pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"') -def test_cover_looponfail(testdir, monkeypatch): +def test_cover_looponfail(pytester, testdir, monkeypatch): testdir.makepyfile(mod=MODULE) testdir.makeconftest(CONFTEST) script = testdir.makepyfile(BASIC_TEST) @@ -1559,9 +1546,9 @@ def test_cover_looponfail(testdir, monkeypatch): def mock_run(*args, **kwargs): return _TestProcess(*map(str, args)) - monkeypatch.setattr(testdir, 'run', mock_run) + monkeypatch.setattr(pytester, testdir, 'run', mock_run) assert testdir.run is mock_run - if hasattr(testdir, '_pytester'): + if hasattr(pytester, testdir, '_pytester'): monkeypatch.setattr(testdir._pytester, 'run', mock_run) assert testdir._pytester.run is mock_run with testdir.runpytest('-v', @@ -1637,7 +1624,13 @@ def test_basic(no_cover): # Regexes for lines to exclude from consideration exclude_lines = raise NotImplementedError +''' +MP_COVERAGERC = ''' +[run] +concurrency = multiprocessing +parallel = true +sigterm = True ''' EXCLUDED_TEST = ''' @@ -1704,7 +1697,7 @@ def test_basic(): @pytest.mark.parametrize('report_option', [ 'term-missing:skip-covered', 'term:skip-covered']) -def test_skip_covered_cli(testdir, report_option): +def test_skip_covered_cli(pytester, testdir, report_option): testdir.makefile('', coveragerc=SKIP_COVERED_COVERAGERC) script = testdir.makepyfile(SKIP_COVERED_TEST) result = testdir.runpytest('-v', @@ -1910,7 +1903,7 @@ def test_external_data_file_negative(testdir): @xdist_params -def test_append_coverage(testdir, opts, prop): +def test_append_coverage(pytester, testdir, opts, prop): script = testdir.makepyfile(test_1=prop.code) testdir.tmpdir.join('.coveragerc').write(prop.fullconf) result = testdir.runpytest('-v', @@ -1933,7 +1926,7 @@ def test_append_coverage(testdir, opts, prop): @xdist_params -def test_do_not_append_coverage(testdir, opts, prop): +def test_do_not_append_coverage(pytester, testdir, opts, prop): script = testdir.makepyfile(test_1=prop.code) testdir.tmpdir.join('.coveragerc').write(prop.fullconf) result = testdir.runpytest('-v', @@ -2115,7 +2108,7 @@ def find_labels(text, pattern): @pytest.mark.skipif("coverage.version_info < (5, 0)") @xdist_params -def test_contexts(testdir, opts): +def test_contexts(pytester, testdir, opts): with open(os.path.join(os.path.dirname(__file__), "contextful.py")) as f: contextful_tests = f.read() script = testdir.makepyfile(contextful_tests) From 0c176298ffc44d8a64befc466e61dc74f2ac0745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 21 Jul 2022 23:34:45 +0300 Subject: [PATCH 060/117] Reveert this change that wastes me time typing up full paths. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 88a2b63a..e283165c 100644 --- a/tox.ini +++ b/tox.ini @@ -65,7 +65,7 @@ deps = {env:_DEP_COVERAGE:coverage} pip_pre = true commands = - pytest {posargs:-vv} + {posargs:pytest -vv} [testenv:spell] setenv = From b0329585c6b0186715feb41f58071eefa36cdf0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 21 Jul 2022 23:36:52 +0300 Subject: [PATCH 061/117] Throwing in the towel. I don't care anymore. I don't see any workarounds for https://github.com/python/cpython/issues/82408. --- tests/test_pytest_cov.py | 226 --------------------------------------- 1 file changed, 226 deletions(-) diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index edf0fe6e..1cb68577 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -1059,232 +1059,6 @@ def test_funcarg_not_active(testdir): assert result.ret == 0 -@pytest.mark.skipif("sys.version_info[0] < 3", reason="no context manager api on Python 2") -@pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows") -@pytest.mark.skipif('platform.python_implementation() == "PyPy"', reason="often deadlocks on PyPy") -@pytest.mark.parametrize('method', ['fork', 'spawn']) -@pytest.mark.xfail -def test_multiprocessing_pool(pytester, testdir, method): - pytest.importorskip('multiprocessing.util') - pytester.path.joinpath(".coveragerc").write_text(MP_COVERAGERC) - script = testdir.makepyfile(''' -import multiprocessing - -def target_fn(a): - {}se: # pragma: nocover - return None - -def test_run_target(): - multiprocessing.set_start_method({!r}) - for i in range(33): - with multiprocessing.Pool(3) as p: - p.map(target_fn, [i * 3 + j for j in range(3)]) - p.join() -'''.format(''.join('''if a == %r: - return a - el''' % i for i in range(99)), method)) - - result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), - '--cov-report=term-missing', - script) - - assert "Doesn't seem to be a coverage.py data file" not in result.stdout.str() - assert "Doesn't seem to be a coverage.py data file" not in result.stderr.str() - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'test_multiprocessing_pool* 100%*', - '*1 passed*' - ]) - assert not testdir.tmpdir.listdir(".coverage.*") - assert result.ret == 0 - - -@pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows") -@pytest.mark.skipif('platform.python_implementation() == "PyPy"', reason="often deadlocks on PyPy") -@pytest.mark.parametrize('method', ['fork', 'spawn']) -def test_multiprocessing_pool_terminate(pytester, testdir, method): - pytest.importorskip('multiprocessing.util') - pytester.path.joinpath(".coveragerc").write_text(MP_COVERAGERC) - script = testdir.makepyfile(''' -import multiprocessing - -def target_fn(a): - {}se: # pragma: nocover - return None - -def test_run_target(): - multiprocessing.set_start_method({!r}) - - for i in range(33): - p = multiprocessing.Pool(3) - try: - p.map(target_fn, [i * 3 + j for j in range(3)]) - finally: - p.terminate() - p.join() -'''.format(''.join('''if a == %r: - return a - el''' % i for i in range(99)), method)) - - result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), - '--cov-report=term-missing', - script) - - assert "Doesn't seem to be a coverage.py data file" not in result.stdout.str() - assert "Doesn't seem to be a coverage.py data file" not in result.stderr.str() - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'test_multiprocessing_pool* 100%*', - '*1 passed*' - ]) - assert not testdir.tmpdir.listdir(".coverage.*") - assert result.ret == 0 - - -@pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows") -@pytest.mark.skipif('sys.version_info[0] > 2 and platform.python_implementation() == "PyPy"', reason="broken on PyPy3") -@pytest.mark.parametrize('method', ['fork', 'spawn']) -def test_multiprocessing_pool_close(pytester, testdir, method): - pytest.importorskip('multiprocessing.util') - pytester.path.joinpath(".coveragerc").write_text(MP_COVERAGERC) - script = testdir.makepyfile(''' -import multiprocessing - -def target_fn(a): - {}se: # pragma: nocover - return None - -def test_run_target(): - multiprocessing.set_start_method({!r}) - for i in range(33): - p = multiprocessing.Pool(3) - try: - p.map(target_fn, [i * 3 + j for j in range(3)]) - finally: - p.close() - p.join() -'''.format(''.join('''if a == %r: - return a - el''' % i for i in range(99)), method)) - - result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), - '--cov-report=term-missing', - script) - assert "Doesn't seem to be a coverage.py data file" not in result.stdout.str() - assert "Doesn't seem to be a coverage.py data file" not in result.stderr.str() - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'test_multiprocessing_pool* 100%*', - '*1 passed*' - ]) - # assert not testdir.tmpdir.listdir(".coverage.*") - assert result.ret == 0 - - -@pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows") -@pytest.mark.parametrize('method', ['fork', 'spawn']) -def test_multiprocessing_process(pytester, testdir, method): - pytest.importorskip('multiprocessing.util') - pytester.path.joinpath(".coveragerc").write_text(MP_COVERAGERC) - script = testdir.makepyfile(''' -import multiprocessing - -def target_fn(): - a = True - return a - -def test_run_target(): - multiprocessing.set_start_method({!r}) - p = multiprocessing.Process(target=target_fn) - p.start() - p.join() -'''.format(method)) - - result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), - '--cov-report=term-missing', - script) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'test_multiprocessing_process* 9 * 100%*', - '*1 passed*' - ]) - assert result.ret == 0 - - -@pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows") -def test_multiprocessing_process_no_source(pytester, testdir): - pytest.importorskip('multiprocessing.util') - pytester.path.joinpath(".coveragerc").write_text(MP_COVERAGERC) - script = testdir.makepyfile(''' -import multiprocessing - -def target_fn(): - a = True - return a - -def test_run_target(): - p = multiprocessing.Process(target=target_fn) - p.start() - p.join() -''') - - result = testdir.runpytest('-v', - '--cov', - '--cov-report=term-missing', - script) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'test_multiprocessing_process* 8 * 100%*', - '*1 passed*' - ]) - assert result.ret == 0 - - -@pytest.mark.skipif('sys.platform == "win32"', reason="multiprocessing support is broken on Windows") -def test_multiprocessing_process_with_terminate(pytester, testdir): - pytest.importorskip('multiprocessing.util') - pytester.path.joinpath(".coveragerc").write_text(MP_COVERAGERC) - script = testdir.makepyfile(''' -import multiprocessing -import time -from pytest_cov.embed import cleanup_on_sigterm -cleanup_on_sigterm() - -event = multiprocessing.Event() - -def target_fn(): - a = True - event.set() - time.sleep(5) - -def test_run_target(): - p = multiprocessing.Process(target=target_fn) - p.start() - time.sleep(0.5) - event.wait(1) - p.terminate() - p.join() -''') - - result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), - '--cov-report=term-missing', - script) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'test_multiprocessing_process* 16 * 100%*', - '*1 passed*' - ]) - assert result.ret == 0 - - @pytest.mark.skipif('sys.platform == "win32"', reason="SIGTERM isn't really supported on Windows") def test_cleanup_on_sigterm(testdir): script = testdir.makepyfile(''' From a966694e88fdb76626cddfc8648da19d5a081874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 21 Jul 2022 23:51:29 +0300 Subject: [PATCH 062/117] Fix bad refactor. --- tests/test_pytest_cov.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 1cb68577..5bf7987c 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -1312,7 +1312,7 @@ def test_cover_conftest(testdir): @pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"') -def test_cover_looponfail(pytester, testdir, monkeypatch): +def test_cover_looponfail(testdir, monkeypatch): testdir.makepyfile(mod=MODULE) testdir.makeconftest(CONFTEST) script = testdir.makepyfile(BASIC_TEST) @@ -1320,9 +1320,9 @@ def test_cover_looponfail(pytester, testdir, monkeypatch): def mock_run(*args, **kwargs): return _TestProcess(*map(str, args)) - monkeypatch.setattr(pytester, testdir, 'run', mock_run) + monkeypatch.setattr(testdir, 'run', mock_run) assert testdir.run is mock_run - if hasattr(pytester, testdir, '_pytester'): + if hasattr(testdir, '_pytester'): monkeypatch.setattr(testdir._pytester, 'run', mock_run) assert testdir._pytester.run is mock_run with testdir.runpytest('-v', From 404bc78ba06337735aab20bee4b63773414bf9fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 21 Jul 2022 23:57:44 +0300 Subject: [PATCH 063/117] Update changelog and remove last cruft. --- CHANGELOG.rst | 15 ++++++++++++++- tests/test_pytest_cov.py | 7 ------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e559ebb4..6dbf514b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,12 +2,25 @@ Changelog ========= -3.1.0 (future) +4.0.0 (future) ------------------- +**Note that this release drops support for multiprocessing.** + + * `--cov-fail-under` no longer causes `pytest --collect-only` to fail Contributed by Zac Hatfield-Dodds in `#511 `_. +* Dropped support for multiprocessing (mostly because `issue 82408 `_). This feature was + mostly working but made out test suite very flaky and slow. + + There is builtin multiprocessing support in coverage and you can switch to that if you feel lucky. All you need is this in your + ``.coveragerc``:: + + [run] + concurrency = multiprocessing + parallel = true + sigterm = true 3.0.0 (2021-10-04) diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 5bf7987c..219dd868 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -1400,13 +1400,6 @@ def test_basic(no_cover): raise NotImplementedError ''' -MP_COVERAGERC = ''' -[run] -concurrency = multiprocessing -parallel = true -sigterm = True -''' - EXCLUDED_TEST = ''' def func(): From 94e3ffabcbbca402d3a77bb5083a031a44c37b1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 22 Jul 2022 00:08:47 +0300 Subject: [PATCH 064/117] Fix pypy envs. --- .github/workflows/test.yml | 42 +++++++++++++++++++++++++++----------- tox.ini | 2 +- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0c182389..42ef1b2b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -109,23 +109,41 @@ jobs: python_arch: 'x64' tox_env: 'py310-pytest71-xdist250-coverage64' os: 'macos-latest' - - name: 'pypy3-pytest71-xdist250-coverage64 (ubuntu)' - python: 'pypy-3.' - toxpython: 'pypy3.' + - name: 'pypy37-pytest71-xdist250-coverage64 (ubuntu)' + python: 'pypy-3.7' + toxpython: 'pypy3.7' python_arch: 'x64' - tox_env: 'pypy3-pytest71-xdist250-coverage64' + tox_env: 'pypy37-pytest71-xdist250-coverage64' os: 'ubuntu-latest' - - name: 'pypy3-pytest71-xdist250-coverage64 (windows)' - python: 'pypy-3.' - toxpython: 'pypy3.' + - name: 'pypy37-pytest71-xdist250-coverage64 (windows)' + python: 'pypy-3.7' + toxpython: 'pypy3.7' python_arch: 'x64' - tox_env: 'pypy3-pytest71-xdist250-coverage64' + tox_env: 'pypy37-pytest71-xdist250-coverage64' os: 'windows-latest' - - name: 'pypy3-pytest71-xdist250-coverage64 (macos)' - python: 'pypy-3.' - toxpython: 'pypy3.' + - name: 'pypy37-pytest71-xdist250-coverage64 (macos)' + python: 'pypy-3.7' + toxpython: 'pypy3.7' python_arch: 'x64' - tox_env: 'pypy3-pytest71-xdist250-coverage64' + tox_env: 'pypy37-pytest71-xdist250-coverage64' + os: 'macos-latest' + - name: 'pypy38-pytest71-xdist250-coverage64 (ubuntu)' + python: 'pypy-3.8' + toxpython: 'pypy3.8' + python_arch: 'x64' + tox_env: 'pypy38-pytest71-xdist250-coverage64' + os: 'ubuntu-latest' + - name: 'pypy38-pytest71-xdist250-coverage64 (windows)' + python: 'pypy-3.8' + toxpython: 'pypy3.8' + python_arch: 'x64' + tox_env: 'pypy38-pytest71-xdist250-coverage64' + os: 'windows-latest' + - name: 'pypy38-pytest71-xdist250-coverage64 (macos)' + python: 'pypy-3.8' + toxpython: 'pypy3.8' + python_arch: 'x64' + tox_env: 'pypy38-pytest71-xdist250-coverage64' os: 'macos-latest' steps: - uses: actions/checkout@v2 diff --git a/tox.ini b/tox.ini index e283165c..282cd244 100644 --- a/tox.ini +++ b/tox.ini @@ -13,7 +13,7 @@ passenv = envlist = check py{36}-pytest{70}-xdist250-coverage{62} - py{37,38,39,310,py3}-pytest{71}-xdist250-coverage{64} + py{37,38,39,310,py37,py38}-pytest{71}-xdist250-coverage{64} docs [testenv] From 11a369809d69ee8a21d02369ad15293dff94c82f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 22 Jul 2022 00:57:59 +0300 Subject: [PATCH 065/117] Add an xfail. --- tests/test_pytest_cov.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 219dd868..22991c9a 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -1060,6 +1060,7 @@ def test_funcarg_not_active(testdir): @pytest.mark.skipif('sys.platform == "win32"', reason="SIGTERM isn't really supported on Windows") +@pytest.mark.xfail('platform.python_implementation() == "PyPy"', reason="Interpreter seems buggy") def test_cleanup_on_sigterm(testdir): script = testdir.makepyfile(''' import os, signal, subprocess, sys, time From 93554cba8662c1076d8490a1b26738e3edf668e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 22 Jul 2022 00:58:05 +0300 Subject: [PATCH 066/117] Give proper name to workflow. --- .github/workflows/test.yml | 2 +- ci/templates/.github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 42ef1b2b..3ad0bb49 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: build +name: Tests on: [push, pull_request] jobs: test: diff --git a/ci/templates/.github/workflows/test.yml b/ci/templates/.github/workflows/test.yml index 96d24801..fda6886a 100644 --- a/ci/templates/.github/workflows/test.yml +++ b/ci/templates/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: build +name: Tests on: [push, pull_request] jobs: test: From 9ef92ffeb4fcb78254c4a42fe7b6dc5ca77522b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 22 Jul 2022 01:22:34 +0300 Subject: [PATCH 067/117] More xfails. --- tests/test_pytest_cov.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 22991c9a..6500fdec 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -1152,6 +1152,7 @@ def test_run(): @pytest.mark.skipif('sys.platform == "win32"', reason="SIGTERM isn't really supported on Windows") +@pytest.mark.xfail('sys.platform == "darwin"', reason="Something weird going on Macs...") @pytest.mark.parametrize('setup', [ ('signal.signal(signal.SIGTERM, signal.SIG_DFL); cleanup_on_sigterm()', '88% 18-19'), ('cleanup_on_sigterm()', '88% 18-19'), @@ -1195,6 +1196,7 @@ def test_run(): @pytest.mark.skipif('sys.platform == "win32"', reason="SIGINT is subtly broken on Windows") +@pytest.mark.xfail('sys.platform == "darwin"', reason="Something weird going on Macs...") def test_cleanup_on_sigterm_sig_dfl_sigint(testdir): script = testdir.makepyfile(''' import os, signal, subprocess, sys, time From 5899be3ff6cdab5d3e3af9e2b8180f05ed632b9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 22 Jul 2022 02:00:54 +0300 Subject: [PATCH 068/117] More xfails. --- tests/test_pytest_cov.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 6500fdec..fd544345 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -1153,6 +1153,7 @@ def test_run(): @pytest.mark.skipif('sys.platform == "win32"', reason="SIGTERM isn't really supported on Windows") @pytest.mark.xfail('sys.platform == "darwin"', reason="Something weird going on Macs...") +@pytest.mark.xfail('platform.python_implementation() == "PyPy"', reason="Interpreter seems buggy") @pytest.mark.parametrize('setup', [ ('signal.signal(signal.SIGTERM, signal.SIG_DFL); cleanup_on_sigterm()', '88% 18-19'), ('cleanup_on_sigterm()', '88% 18-19'), @@ -1236,6 +1237,7 @@ def test_run(): @pytest.mark.skipif('sys.platform == "win32"', reason="fork not available on Windows") +@pytest.mark.xfail('platform.python_implementation() == "PyPy"', reason="Interpreter seems buggy") def test_cleanup_on_sigterm_sig_ign(testdir): script = testdir.makepyfile(''' import os, signal, subprocess, sys, time From dbb20f6aafba4452c29fb77d88a78875d62502ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 22 Jul 2022 08:44:18 +0300 Subject: [PATCH 069/117] Even more xfails. --- tests/test_pytest_cov.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index fd544345..4402fc7b 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -1198,6 +1198,7 @@ def test_run(): @pytest.mark.skipif('sys.platform == "win32"', reason="SIGINT is subtly broken on Windows") @pytest.mark.xfail('sys.platform == "darwin"', reason="Something weird going on Macs...") +@pytest.mark.xfail('platform.python_implementation() == "PyPy"', reason="Interpreter seems buggy") def test_cleanup_on_sigterm_sig_dfl_sigint(testdir): script = testdir.makepyfile(''' import os, signal, subprocess, sys, time From 5503d74ac54ab4806cf4b3d9efe4c70922a75162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 22 Jul 2022 12:48:27 +0300 Subject: [PATCH 070/117] Remove dupe workflow. --- .github/workflows/lint.yml | 41 -------------------------------------- 1 file changed, 41 deletions(-) delete mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml deleted file mode 100644 index 001ccfee..00000000 --- a/.github/workflows/lint.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Lint - -on: [push, pull_request, workflow_dispatch] - -env: - FORCE_COLOR: 1 - -jobs: - lint: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - toxenv: ["check", "docs"] - - steps: - - uses: actions/checkout@v2 - - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: 3.9 - - - name: Cache - uses: actions/cache@v2 - with: - path: ~/.cache/pip - key: - lint-v1-${{ hashFiles('**/tox.ini') }} - restore-keys: | - lint-v1- - - - name: Install dependencies - run: | - python -m pip install -U pip - python -m pip install -U wheel - python -m pip install --progress-bar=off tox -rci/requirements.txt - - - name: Lint ${{ matrix.toxenv }} - run: | - tox -v -e ${{ matrix.toxenv }} From 60b73ec673c60942a3cf052ee8a1fdc442840558 Mon Sep 17 00:00:00 2001 From: Ben Greiner Date: Sun, 17 Jul 2022 22:09:03 +0200 Subject: [PATCH 071/117] migrate build command from distutils to setuptools --- setup.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 21681887..421ec6b7 100755 --- a/setup.py +++ b/setup.py @@ -1,7 +1,6 @@ #!/usr/bin/env python import re -from distutils.command.build import build from glob import glob from itertools import chain from os.path import basename @@ -12,6 +11,11 @@ from setuptools import Command from setuptools import find_packages from setuptools import setup +try: + # https://setuptools.pypa.io/en/latest/deprecated/distutils-legacy.html + from setuptools.command.build import build +except ImportError: + from distutils.command.build import build from setuptools.command.develop import develop from setuptools.command.easy_install import easy_install from setuptools.command.install_lib import install_lib From 218419f665229d61356f1eea3ddc8e18aa21f87c Mon Sep 17 00:00:00 2001 From: Delgan <4193924+Delgan@users.noreply.github.com> Date: Sat, 18 Jun 2022 00:04:26 +0200 Subject: [PATCH 072/117] Prevent undesirable new lines to be displayed when report is disabled --- src/pytest_cov/plugin.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pytest_cov/plugin.py b/src/pytest_cov/plugin.py index e915246d..252439ed 100644 --- a/src/pytest_cov/plugin.py +++ b/src/pytest_cov/plugin.py @@ -326,7 +326,11 @@ def pytest_terminal_summary(self, terminalreporter): # we shouldn't report, or report generation failed (error raised above) return - terminalreporter.write('\n' + self.cov_report.getvalue() + '\n') + report = self.cov_report.getvalue() + + # Avoid undesirable new lines when output is disabled with "--cov-report=". + if report: + terminalreporter.write('\n' + report + '\n') if self.options.cov_fail_under is not None and self.options.cov_fail_under > 0: failed = self.cov_total < self.options.cov_fail_under From b3dda36fddd3ca75689bb3645cd320aa8392aaf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Mon, 25 Jul 2022 11:28:10 +0300 Subject: [PATCH 073/117] Improve workflow with a collecting status check. (#548) * Improve workflow with a collecting status check. * Style fix. --- .github/workflows/examples.yml | 49 -------------------- .github/workflows/test.yml | 55 +++++++++++++++++++++- ci/templates/.github/workflows/test.yml | 61 ++++++++++++++++++++++++- setup.py | 2 + 4 files changed, 116 insertions(+), 51 deletions(-) delete mode 100644 .github/workflows/examples.yml diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml deleted file mode 100644 index dbd62a6c..00000000 --- a/.github/workflows/examples.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Examples - -on: [push, pull_request, workflow_dispatch] - -env: - FORCE_COLOR: 1 - -jobs: - examples: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python-version: ["pypy-3.7", "3.9"] - target: [ - "src-layout", - "adhoc-layout", - ] - include: - # Add new helper variables to existing jobs - - {python-version: "pypy-3.7", tox-python-version: "pypy3"} - - {python-version: "3.9", tox-python-version: "py39"} - steps: - - uses: actions/checkout@v2 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - - name: Cache - uses: actions/cache@v2 - with: - path: ~/.cache/pip - key: - examples-v1-${{ hashFiles('**/tox.ini') }} - restore-keys: | - examples-v1- - - - name: Install dependencies - run: | - python -m pip install -U pip - python -m pip install -U wheel - python -m pip install --progress-bar=off tox -rci/requirements.txt - - - name: Examples - run: | - cd examples/${{ matrix.target }} - tox -v -e ${{ matrix.tox-python-version }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3ad0bb49..a85a1e03 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,47 @@ name: Tests -on: [push, pull_request] +on: [push, pull_request, workflow_dispatch] jobs: + examples: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["pypy-3.7", "3.9"] + target: [ + "src-layout", + "adhoc-layout", + ] + include: + # Add new helper variables to existing jobs + - {python-version: "pypy-3.7", tox-python-version: "pypy3"} + - {python-version: "3.9", tox-python-version: "py39"} + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Cache + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: + examples-v1-${{ hashFiles('**/tox.ini') }} + restore-keys: | + examples-v1- + + - name: Install dependencies + run: | + python -m pip install -U pip + python -m pip install -U wheel + python -m pip install --progress-bar=off tox -rci/requirements.txt + + - name: Examples + run: | + cd examples/${{ matrix.target }} + tox -v -e ${{ matrix.tox-python-version }} test: name: ${{ matrix.name }} runs-on: ${{ matrix.os }} @@ -165,3 +206,15 @@ jobs: TOXPYTHON: '${{ matrix.toxpython }}' run: > tox -e ${{ matrix.tox_env }} -v + + check: + if: always() + needs: + - test + - examples + runs-on: ubuntu-latest + steps: + - name: Decide whether the needed jobs succeeded or failed + uses: re-actors/alls-green@release/v1 + with: + jobs: ${{ toJSON(needs) }} diff --git a/ci/templates/.github/workflows/test.yml b/ci/templates/.github/workflows/test.yml index fda6886a..3e99ef08 100644 --- a/ci/templates/.github/workflows/test.yml +++ b/ci/templates/.github/workflows/test.yml @@ -1,6 +1,50 @@ name: Tests -on: [push, pull_request] +on: [push, pull_request, workflow_dispatch] jobs: +{%- raw %} + examples: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["pypy-3.7", "3.9"] + target: [ + "src-layout", + "adhoc-layout", + ] + include: + # Add new helper variables to existing jobs + - {python-version: "pypy-3.7", tox-python-version: "pypy3"} + - {python-version: "3.9", tox-python-version: "py39"} + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Cache + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: + examples-v1-${{ hashFiles('**/tox.ini') }} + restore-keys: | + examples-v1- + + - name: Install dependencies + run: | + python -m pip install -U pip + python -m pip install -U wheel + python -m pip install --progress-bar=off tox -rci/requirements.txt + + - name: Examples + run: | + cd examples/${{ matrix.target }} + tox -v -e ${{ matrix.tox-python-version }} +{%- endraw %} + test: name: {{ '${{ matrix.name }}' }} runs-on: {{ '${{ matrix.os }}' }} @@ -63,3 +107,18 @@ jobs: TOXPYTHON: '{{ '${{ matrix.toxpython }}' }}' run: > tox -e {{ '${{ matrix.tox_env }}' }} -v +{% raw %} + successful: + # this provides a single status check for branch merge rules + # (use this in `Require status checks to pass before merging` in branch settings) + if: always() + needs: + - test + - examples + runs-on: ubuntu-latest + steps: + - name: Decide whether the needed jobs succeeded or failed + uses: re-actors/alls-green@release/v1 + with: + jobs: ${{ toJSON(needs) }} +{% endraw %} diff --git a/setup.py b/setup.py index 421ec6b7..8d20c134 100755 --- a/setup.py +++ b/setup.py @@ -11,11 +11,13 @@ from setuptools import Command from setuptools import find_packages from setuptools import setup + try: # https://setuptools.pypa.io/en/latest/deprecated/distutils-legacy.html from setuptools.command.build import build except ImportError: from distutils.command.build import build + from setuptools.command.develop import develop from setuptools.command.easy_install import easy_install from setuptools.command.install_lib import install_lib From 00713b3fec90cb8c98a9e4bfb3212e574c08e67b Mon Sep 17 00:00:00 2001 From: Andre Brisco Date: Sun, 26 Jun 2022 13:36:58 -0700 Subject: [PATCH 074/117] removed incorrect docs on `data_file`. --- docs/config.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index e1e4c3d3..6712292b 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -23,8 +23,8 @@ For full details refer to the `coverage config file`_ documentation. .. note:: Important Note - This plugin overrides the ``data_file`` and ``parallel`` options of coverage. Unless you also run coverage without - pytest-cov it's pointless to set those options in your ``.coveragerc``. + This plugin overrides the ``parallel`` option of coverage. Unless you also run coverage without pytest-cov it's + pointless to set those options in your ``.coveragerc``. If you use the ``--cov=something`` option (with a value) then coverage's ``source`` option will also get overridden. If you have multiple sources it might be easier to set those in ``.coveragerc`` and always use ``--cov`` (without a value) From b077753f5d9d200815fe500d0ef23e306784e65b Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 31 Jul 2022 12:52:32 -0300 Subject: [PATCH 075/117] Use modern approach to specify hook options The old way using marks is being deprecated in pytest 7.2: https://github.com/pytest-dev/pytest/pull/9118 --- CHANGELOG.rst | 2 ++ src/pytest_cov/compat.py | 7 ------- src/pytest_cov/plugin.py | 10 +++++----- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6dbf514b..95c9eb9e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -22,6 +22,8 @@ Changelog parallel = true sigterm = true +* Use modern way to specify hook options to avoid deprecation warnings with pytest >=7.2. + 3.0.0 (2021-10-04) ------------------- diff --git a/src/pytest_cov/compat.py b/src/pytest_cov/compat.py index f422f25c..614419cb 100644 --- a/src/pytest_cov/compat.py +++ b/src/pytest_cov/compat.py @@ -3,17 +3,10 @@ except ImportError: from io import StringIO -import pytest StringIO # pyflakes, this is for re-export -if hasattr(pytest, 'hookimpl'): - hookwrapper = pytest.hookimpl(hookwrapper=True) -else: - hookwrapper = pytest.mark.hookwrapper - - class SessionWrapper: def __init__(self, session): self._session = session diff --git a/src/pytest_cov/plugin.py b/src/pytest_cov/plugin.py index 252439ed..bf42971e 100644 --- a/src/pytest_cov/plugin.py +++ b/src/pytest_cov/plugin.py @@ -133,7 +133,7 @@ def _prepare_cov_source(cov_source): return None if True in cov_source else [path for path in cov_source if path is not True] -@pytest.mark.tryfirst +@pytest.hookimpl(tryfirst=True) def pytest_load_initial_conftests(early_config, parser, args): options = early_config.known_args_namespace no_cov = options.no_cov_should_warn = False @@ -253,6 +253,7 @@ def pytest_sessionstart(self, session): if self.options.cov_context == 'test': session.config.pluginmanager.register(TestContextPlugin(self.cov_controller.cov), '_cov_contexts') + @pytest.hookimpl(optionalhook=True) def pytest_configure_node(self, node): """Delegate to our implementation. @@ -260,8 +261,8 @@ def pytest_configure_node(self, node): """ if not self._disabled: self.cov_controller.configure_node(node) - pytest_configure_node.optionalhook = True + @pytest.hookimpl(optionalhook=True) def pytest_testnodedown(self, node, error): """Delegate to our implementation. @@ -269,7 +270,6 @@ def pytest_testnodedown(self, node, error): """ if not self._disabled: self.cov_controller.testnodedown(node, error) - pytest_testnodedown.optionalhook = True def _should_report(self): return not (self.failed and self.options.no_cov_on_fail) @@ -280,7 +280,7 @@ def _failed_cov_total(self): # we need to wrap pytest_runtestloop. by the time pytest_sessionfinish # runs, it's too late to set testsfailed - @compat.hookwrapper + @pytest.hookimpl(hookwrapper=True) def pytest_runtestloop(self, session): yield @@ -356,7 +356,7 @@ def pytest_runtest_setup(self, item): def pytest_runtest_teardown(self, item): embed.cleanup() - @compat.hookwrapper + @pytest.hookimpl(hookwrapper=True) def pytest_runtest_call(self, item): if (item.get_closest_marker('no_cover') or 'no_cover' in getattr(item, 'fixturenames', ())): From 1211d3134bb74abb7b00c3c2209091aaab440417 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 31 Jul 2022 12:57:39 -0300 Subject: [PATCH 076/117] Fix flake8 error --- tests/test_pytest_cov.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 4402fc7b..10d2bd58 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -1761,8 +1761,11 @@ def bad_init(): monkeypatch.setattr(sys, 'stderr', buff) monkeypatch.setitem(os.environ, 'COV_CORE_SOURCE', 'foobar') exec(payload) - assert buff.getvalue() == '''pytest-cov: Failed to setup subprocess coverage. Environ: {'COV_CORE_SOURCE': 'foobar'} Exception: SpecificError() -''' + expected = ( + "pytest-cov: Failed to setup subprocess coverage. " + "Environ: {'COV_CORE_SOURCE': 'foobar'} Exception: SpecificError()\n" + ) + assert buff.getvalue() == expected def test_double_cov(testdir): From f7fced579e36b72b57e14768026467e4c4511a40 Mon Sep 17 00:00:00 2001 From: Christian Fetzer Date: Fri, 27 May 2022 11:41:46 +0200 Subject: [PATCH 077/117] Add support for LCOV output Coverage.py 6.3 gained support for the LCOV output format. Add support for this to pytest-cov via '--cov-report=lcov[:dest]'. Fix: #535 --- AUTHORS.rst | 1 + CHANGELOG.rst | 3 +++ docs/config.rst | 4 ++-- docs/reporting.rst | 8 +++++--- src/pytest_cov/engine.py | 12 ++++++++++++ src/pytest_cov/plugin.py | 9 ++++++--- tests/test_pytest_cov.py | 41 ++++++++++++++++++++++++++++++++++++---- 7 files changed, 66 insertions(+), 12 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 489903c2..d8271272 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -51,3 +51,4 @@ Authors * Danilo Šegan - https://github.com/dsegan * Michał Bielawski - https://github.com/D3X * Zac Hatfield-Dodds - https://github.com/Zac-HD +* Christian Fetzer - https://github.com/fetzerch diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 95c9eb9e..1c1b524e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -21,6 +21,9 @@ Changelog concurrency = multiprocessing parallel = true sigterm = true +* Added support for LCOV output format via `--cov-report=lcov`. Only works with coverage 6.3+. + Contributed by Christian Fetzer in + `#536 `_. * Use modern way to specify hook options to avoid deprecation warnings with pytest >=7.2. diff --git a/docs/config.rst b/docs/config.rst index 6712292b..fa257037 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -56,9 +56,9 @@ The complete list of command line options is: --cov=PATH Measure coverage for filesystem path. (multi-allowed) --cov-report=type Type of report to generate: term, term-missing, - annotate, html, xml (multi-allowed). term, term- + annotate, html, xml, lcov (multi-allowed). term, term- missing may be followed by ":skip-covered". annotate, - html and xml may be followed by ":DEST" where DEST + html, xml and lcov may be followed by ":DEST" where DEST specifies the output location. Use --cov-report= to not generate any output. --cov-config=path Config file for coverage. Default: .coveragerc diff --git a/docs/reporting.rst b/docs/reporting.rst index eaa99ad3..69191d48 100644 --- a/docs/reporting.rst +++ b/docs/reporting.rst @@ -3,7 +3,7 @@ Reporting It is possible to generate any combination of the reports for a single test run. -The available reports are terminal (with or without missing line numbers shown), HTML, XML and +The available reports are terminal (with or without missing line numbers shown), HTML, XML, LCOV and annotated source code. The terminal report without line numbers (default):: @@ -49,19 +49,21 @@ The terminal report with skip covered:: You can use ``skip-covered`` with ``term-missing`` as well. e.g. ``--cov-report term-missing:skip-covered`` -These three report options output to files without showing anything on the terminal:: +These four report options output to files without showing anything on the terminal:: pytest --cov-report html --cov-report xml + --cov-report lcov --cov-report annotate --cov=myproj tests/ -The output location for each of these reports can be specified. The output location for the XML +The output location for each of these reports can be specified. The output location for the XML and LCOV report is a file. Where as the output location for the HTML and annotated source code reports are directories:: pytest --cov-report html:cov_html --cov-report xml:cov.xml + --cov-report lcov:cov.info --cov-report annotate:cov_annotate --cov=myproj tests/ diff --git a/src/pytest_cov/engine.py b/src/pytest_cov/engine.py index 0303c2f1..bfede8c7 100644 --- a/src/pytest_cov/engine.py +++ b/src/pytest_cov/engine.py @@ -196,6 +196,18 @@ def summary(self, stream): total = self.cov.xml_report(ignore_errors=True, outfile=output) stream.write('Coverage XML written to file %s\n' % (self.cov.config.xml_output if output is None else output)) + # Produce lcov report if wanted. + if 'lcov' in self.cov_report: + output = self.cov_report['lcov'] + with _backup(self.cov, "config"): + self.cov.lcov_report(ignore_errors=True, outfile=output) + + # We need to call Coverage.report here, just to get the total + # Coverage.lcov_report doesn't return any total and we need it for --cov-fail-under. + total = self.cov.report(ignore_errors=True, file=_NullFile) + + stream.write('Coverage LCOV written to file %s\n' % (self.cov.config.lcov_output if output is None else output)) + return total diff --git a/src/pytest_cov/plugin.py b/src/pytest_cov/plugin.py index bf42971e..dd7b8c4e 100644 --- a/src/pytest_cov/plugin.py +++ b/src/pytest_cov/plugin.py @@ -29,7 +29,7 @@ class CovReportWarning(PytestCovWarning): def validate_report(arg): - file_choices = ['annotate', 'html', 'xml'] + file_choices = ['annotate', 'html', 'xml', 'lcov'] term_choices = ['term', 'term-missing'] term_modifier_choices = ['skip-covered'] all_choices = term_choices + file_choices @@ -39,6 +39,9 @@ def validate_report(arg): msg = f'invalid choice: "{arg}" (choose from "{all_choices}")' raise argparse.ArgumentTypeError(msg) + if report_type == 'lcov' and coverage.version_info <= (6, 3): + raise argparse.ArgumentTypeError('LCOV output is only supported with coverage.py >= 6.3') + if len(values) == 1: return report_type, None @@ -96,9 +99,9 @@ def pytest_addoption(parser): group.addoption('--cov-report', action=StoreReport, default={}, metavar='TYPE', type=validate_report, help='Type of report to generate: term, term-missing, ' - 'annotate, html, xml (multi-allowed). ' + 'annotate, html, xml, lcov (multi-allowed). ' 'term, term-missing may be followed by ":skip-covered". ' - 'annotate, html and xml may be followed by ":DEST" ' + 'annotate, html, xml and lcov may be followed by ":DEST" ' 'where DEST specifies the output location. ' 'Use --cov-report= to not generate any output.') group.addoption('--cov-config', action='store', default='.coveragerc', diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 10d2bd58..84fe42ba 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -150,7 +150,8 @@ def test_foo(cov): CHILD_SCRIPT_RESULT = '[56] * 100%' PARENT_SCRIPT_RESULT = '9 * 100%' DEST_DIR = 'cov_dest' -REPORT_NAME = 'cov.xml' +XML_REPORT_NAME = 'cov.xml' +LCOV_REPORT_NAME = 'cov.info' xdist_params = pytest.mark.parametrize('opts', [ '', @@ -333,18 +334,50 @@ def test_xml_output_dir(testdir): result = testdir.runpytest('-v', '--cov=%s' % script.dirpath(), - '--cov-report=xml:' + REPORT_NAME, + '--cov-report=xml:' + XML_REPORT_NAME, script) result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'Coverage XML written to file ' + REPORT_NAME, + 'Coverage XML written to file ' + XML_REPORT_NAME, '*10 passed*', ]) - assert testdir.tmpdir.join(REPORT_NAME).check() + assert testdir.tmpdir.join(XML_REPORT_NAME).check() assert result.ret == 0 +@pytest.mark.skipif("coverage.version_info < (6, 3)") +def test_lcov_output_dir(testdir): + script = testdir.makepyfile(SCRIPT) + + result = testdir.runpytest('-v', + '--cov=%s' % script.dirpath(), + '--cov-report=lcov:' + LCOV_REPORT_NAME, + script) + + result.stdout.fnmatch_lines([ + '*- coverage: platform *, python * -*', + 'Coverage LCOV written to file ' + LCOV_REPORT_NAME, + '*10 passed*', + ]) + assert testdir.tmpdir.join(LCOV_REPORT_NAME).check() + assert result.ret == 0 + + +@pytest.mark.skipif("coverage.version_info >= (6, 3)") +def test_lcov_not_supported(testdir): + script = testdir.makepyfile("a = 1") + result = testdir.runpytest('-v', + '--cov=%s' % script.dirpath(), + '--cov-report=lcov', + script, + ) + result.stderr.fnmatch_lines([ + '*argument --cov-report: LCOV output is only supported with coverage.py >= 6.3', + ]) + assert result.ret != 0 + + def test_term_output_dir(testdir): script = testdir.makepyfile(SCRIPT) From 56b810b91c9ae15d1462633c6a8a1b522ebf8e65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 28 Sep 2022 18:04:11 +0300 Subject: [PATCH 078/117] Update chagelog. --- AUTHORS.rst | 1 + CHANGELOG.rst | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index d8271272..ae14600e 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -51,4 +51,5 @@ Authors * Danilo Šegan - https://github.com/dsegan * Michał Bielawski - https://github.com/D3X * Zac Hatfield-Dodds - https://github.com/Zac-HD +* Ronny Pfannschmidt - https://github.com/RonnyPfannschmidt * Christian Fetzer - https://github.com/fetzerch diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1c1b524e..479112ba 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,8 +2,8 @@ Changelog ========= -4.0.0 (future) -------------------- +4.0.0 (2022-09-28) +------------------ **Note that this release drops support for multiprocessing.** @@ -24,8 +24,8 @@ Changelog * Added support for LCOV output format via `--cov-report=lcov`. Only works with coverage 6.3+. Contributed by Christian Fetzer in `#536 `_. - * Use modern way to specify hook options to avoid deprecation warnings with pytest >=7.2. + Contributed by Ronny Pfannschmidt in `#550 `_. 3.0.0 (2021-10-04) From 57e9354a86f658556fe6f15f07625c4b9a9ddf53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 28 Sep 2022 18:19:04 +0300 Subject: [PATCH 079/117] Really update the changelog. --- AUTHORS.rst | 4 ++++ CHANGELOG.rst | 22 ++++++++++++++-------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index ae14600e..2ad80a00 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -51,5 +51,9 @@ Authors * Danilo Šegan - https://github.com/dsegan * Michał Bielawski - https://github.com/D3X * Zac Hatfield-Dodds - https://github.com/Zac-HD +* Ben Greiner - https://github.com/bnavigator +* Delgan - https://github.com/Delgan +* Andre Brisco - https://github.com/abrisco +* Colin O'Dell - https://github.com/colinodell * Ronny Pfannschmidt - https://github.com/RonnyPfannschmidt * Christian Fetzer - https://github.com/fetzerch diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 479112ba..c49683f5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,23 +9,29 @@ Changelog * `--cov-fail-under` no longer causes `pytest --collect-only` to fail - Contributed by Zac Hatfield-Dodds in - `#511 `_. + Contributed by Zac Hatfield-Dodds in `#511 `_. * Dropped support for multiprocessing (mostly because `issue 82408 `_). This feature was - mostly working but made out test suite very flaky and slow. + mostly working but very broken in certain scenarios and made the test suite very flaky and slow. - There is builtin multiprocessing support in coverage and you can switch to that if you feel lucky. All you need is this in your + There is builtin multiprocessing support in coverage and you can migrate to that. All you need is this in your ``.coveragerc``:: [run] concurrency = multiprocessing parallel = true sigterm = true +* Fixed deprecation in ``setup.py`` by trying to import setuptools before distutils. + Contributed by Ben Greiner in `#545 `_. +* Removed undesirable new lines that were displayed while reporting was disabled. + Contributed by Delgan in `#540 `_. +* Documentation fixes. + Contributed by Andre Brisco in `#543 `_ + and Colin O'Dell in `#525 `_. * Added support for LCOV output format via `--cov-report=lcov`. Only works with coverage 6.3+. - Contributed by Christian Fetzer in - `#536 `_. -* Use modern way to specify hook options to avoid deprecation warnings with pytest >=7.2. - Contributed by Ronny Pfannschmidt in `#550 `_. + Contributed by Christian Fetzer in `#536 `_. +* Modernized pytest hook implementation. + Contributed by Bruno Oliveira in `#549 `_ + and Ronny Pfannschmidt in `#550 `_. 3.0.0 (2021-10-04) From 28db055bebbf3ee016a2144c8b69dd7b80b48cc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 28 Sep 2022 18:20:20 +0300 Subject: [PATCH 080/117] =?UTF-8?q?Bump=20version:=203.0.0=20=E2=86=92=204?= =?UTF-8?q?.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- README.rst | 4 ++-- docs/conf.py | 2 +- setup.py | 2 +- src/pytest_cov/__init__.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 27a843fb..09bfcde0 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.0.0 +current_version = 4.0.0 commit = True tag = True diff --git a/README.rst b/README.rst index 2137c118..84a85786 100644 --- a/README.rst +++ b/README.rst @@ -39,9 +39,9 @@ Overview .. |conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pytest-cov.svg :target: https://anaconda.org/conda-forge/pytest-cov -.. |commits-since| image:: https://img.shields.io/github/commits-since/pytest-dev/pytest-cov/v3.0.0.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/pytest-dev/pytest-cov/v4.0.0.svg :alt: Commits since latest release - :target: https://github.com/pytest-dev/pytest-cov/compare/v3.0.0...master + :target: https://github.com/pytest-dev/pytest-cov/compare/v4.0.0...master .. |wheel| image:: https://img.shields.io/pypi/wheel/pytest-cov.svg :alt: PyPI Wheel diff --git a/docs/conf.py b/docs/conf.py index 5dd4e74b..d417ed00 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,7 +23,7 @@ year = '2016' author = 'pytest-cov contributors' copyright = f'{year}, {author}' -version = release = '3.0.0' +version = release = '4.0.0' pygments_style = 'trac' templates_path = ['.'] diff --git a/setup.py b/setup.py index 8d20c134..799d4699 100755 --- a/setup.py +++ b/setup.py @@ -87,7 +87,7 @@ def run(self): setup( name='pytest-cov', - version='3.0.0', + version='4.0.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'))), diff --git a/src/pytest_cov/__init__.py b/src/pytest_cov/__init__.py index fba1d219..9dfd9823 100644 --- a/src/pytest_cov/__init__.py +++ b/src/pytest_cov/__init__.py @@ -1,2 +1,2 @@ """pytest-cov: avoid already-imported warning: PYTEST_DONT_REWRITE.""" -__version__ = '3.0.0' +__version__ = '4.0.0' From f7bdfcfe3aae34982a955b8609cd345820f2526e Mon Sep 17 00:00:00 2001 From: Stavros Ntentos <133706+stdedos@users.noreply.github.com> Date: Fri, 16 Sep 2022 12:45:14 +0300 Subject: [PATCH 081/117] Enable `pytest-cov[toml]` akin to upstream `coverage[toml]` https://github.com/nedbat/coveragepy/issues/664 (+ later fixes) Signed-off-by: Stavros Ntentos <133706+stdedos@users.noreply.github.com> --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 799d4699..bd0f8e50 100755 --- a/setup.py +++ b/setup.py @@ -142,7 +142,9 @@ def run(self): 'six', 'pytest-xdist', 'virtualenv', - ] + ], + # Enable pyproject.toml support. + 'toml': ['coverage[toml]'], }, entry_points={ 'pytest11': [ From ad9b2ada2273629796117349322be4ffcff9f0b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 20 Oct 2022 19:47:47 +0300 Subject: [PATCH 082/117] Revert "Enable `pytest-cov[toml]` akin to upstream `coverage[toml]`" This reverts commit f7bdfcfe3aae34982a955b8609cd345820f2526e. --- setup.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/setup.py b/setup.py index bd0f8e50..799d4699 100755 --- a/setup.py +++ b/setup.py @@ -142,9 +142,7 @@ def run(self): 'six', 'pytest-xdist', 'virtualenv', - ], - # Enable pyproject.toml support. - 'toml': ['coverage[toml]'], + ] }, entry_points={ 'pytest11': [ From 591f03d604cce7fb98c39b16f77eb772ea12e4c6 Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Wed, 26 Oct 2022 19:32:42 +0100 Subject: [PATCH 083/117] Remove travis integration (#559) From d08653ac3d683eb01d33c787a4f414ee576334f5 Mon Sep 17 00:00:00 2001 From: SADIK KUZU Date: Sun, 30 Oct 2022 17:35:53 +0300 Subject: [PATCH 084/117] Update and reorder pre-commit config file --- .pre-commit-config.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9bc7894f..8dbbb8c0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,10 +1,10 @@ # To install the git pre-commit hook run: # pre-commit install # To update the pre-commit hooks run: -# pre-commit install-hooks +# pre-commit autoupdate repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.2.0 + rev: v4.3.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -14,12 +14,12 @@ repos: rev: 5.10.1 hooks: - id: isort - - repo: https://github.com/PyCQA/flake8 - rev: 4.0.1 - hooks: - - id: flake8 - repo: https://github.com/asottile/pyupgrade - rev: v2.32.1 + rev: v3.2.0 hooks: - id: pyupgrade args: [--py36-plus] + - repo: https://github.com/PyCQA/flake8 + rev: 5.0.4 + hooks: + - id: flake8 From ae9d6265f80fff2abe2a042834d870aa6fc4d751 Mon Sep 17 00:00:00 2001 From: SADIK KUZU Date: Wed, 16 Nov 2022 14:25:31 +0300 Subject: [PATCH 085/117] Update .pre-commit-config.yaml Co-authored-by: Christian Clauss --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8dbbb8c0..ec537150 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: hooks: - id: isort - repo: https://github.com/asottile/pyupgrade - rev: v3.2.0 + rev: v3.2.2 hooks: - id: pyupgrade args: [--py36-plus] From d9789afd5916176a38cc2acebbaadee805064e22 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 28 Feb 2023 13:02:52 +0100 Subject: [PATCH 086/117] Add Python 3.11 and PyPy 3.9 to the testing and drop 3.6 (#567) colorama will need to be removed when isort > v5.12.0 is released. Co-authored-by: Hugo van Kemenade --- .github/workflows/test.yml | 90 +++++++++++++++++++------------------- .pre-commit-config.yaml | 10 ++--- ci/bootstrap.py | 2 +- setup.py | 4 +- tests/test_pytest_cov.py | 3 ++ tox.ini | 12 +++-- 6 files changed, 64 insertions(+), 57 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a85a1e03..2dbeb51a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,25 +6,25 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["pypy-3.7", "3.9"] + python-version: ["pypy-3.9", "3.11"] target: [ "src-layout", "adhoc-layout", ] include: # Add new helper variables to existing jobs - - {python-version: "pypy-3.7", tox-python-version: "pypy3"} - - {python-version: "3.9", tox-python-version: "py39"} + - {python-version: "pypy-3.9", tox-python-version: "pypy3"} + - {python-version: "3.11", tox-python-version: "py311"} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Cache - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.cache/pip key: @@ -34,8 +34,8 @@ jobs: - name: Install dependencies run: | - python -m pip install -U pip - python -m pip install -U wheel + python -m pip install --upgrade pip + python -m pip install --upgrade wheel python -m pip install --progress-bar=off tox -rci/requirements.txt - name: Examples @@ -51,33 +51,15 @@ jobs: matrix: include: - name: 'check' - python: '3.9' - toxpython: 'python3.9' + python: '3.11' + toxpython: 'python3.11' tox_env: 'check' os: 'ubuntu-latest' - name: 'docs' - python: '3.9' - toxpython: 'python3.9' + python: '3.11' + toxpython: 'python3.11' tox_env: 'docs' os: 'ubuntu-latest' - - name: 'py36-pytest70-xdist250-coverage62 (ubuntu)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest70-xdist250-coverage62' - os: 'ubuntu-latest' - - name: 'py36-pytest70-xdist250-coverage62 (windows)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest70-xdist250-coverage62' - os: 'windows-latest' - - name: 'py36-pytest70-xdist250-coverage62 (macos)' - python: '3.6' - toxpython: 'python3.6' - python_arch: 'x64' - tox_env: 'py36-pytest70-xdist250-coverage62' - os: 'macos-latest' - name: 'py37-pytest71-xdist250-coverage64 (ubuntu)' python: '3.7' toxpython: 'python3.7' @@ -150,23 +132,23 @@ jobs: python_arch: 'x64' tox_env: 'py310-pytest71-xdist250-coverage64' os: 'macos-latest' - - name: 'pypy37-pytest71-xdist250-coverage64 (ubuntu)' - python: 'pypy-3.7' - toxpython: 'pypy3.7' + - name: 'py311-pytest72-xdist320-coverage65 (ubuntu)' + python: '3.11' + toxpython: 'python3.11' python_arch: 'x64' - tox_env: 'pypy37-pytest71-xdist250-coverage64' + tox_env: 'py311-pytest72-xdist320-coverage65' os: 'ubuntu-latest' - - name: 'pypy37-pytest71-xdist250-coverage64 (windows)' - python: 'pypy-3.7' - toxpython: 'pypy3.7' + - name: 'py311-pytest72-xdist320-coverage65 (windows)' + python: '3.11' + toxpython: 'python3.11' python_arch: 'x64' - tox_env: 'pypy37-pytest71-xdist250-coverage64' + tox_env: 'py311-pytest72-xdist320-coverage65' os: 'windows-latest' - - name: 'pypy37-pytest71-xdist250-coverage64 (macos)' - python: 'pypy-3.7' - toxpython: 'pypy3.7' + - name: 'py311-pytest72-xdist320-coverage65 (macos)' + python: '3.11' + toxpython: 'python3.11' python_arch: 'x64' - tox_env: 'pypy37-pytest71-xdist250-coverage64' + tox_env: 'py311-pytest72-xdist320-coverage65' os: 'macos-latest' - name: 'pypy38-pytest71-xdist250-coverage64 (ubuntu)' python: 'pypy-3.8' @@ -186,17 +168,35 @@ jobs: python_arch: 'x64' tox_env: 'pypy38-pytest71-xdist250-coverage64' os: 'macos-latest' + - name: 'pypy39--pytest72-xdist320-coverage65 (ubuntu)' + python: 'pypy-3.9' + toxpython: 'pypy3.9' + python_arch: 'x64' + tox_env: 'pypy39-pytest72-xdist320-coverage65' + os: 'ubuntu-latest' + - name: 'pypy39-pytest72-xdist320-coverage65 (windows)' + python: 'pypy-3.9' + toxpython: 'pypy3.9' + python_arch: 'x64' + tox_env: 'pypy39-pytest72-xdist320-coverage65' + os: 'windows-latest' + - name: 'pypy39-pytest72-xdist320-coverage65 (macos)' + python: 'pypy-3.9' + toxpython: 'pypy3.9' + python_arch: 'x64' + tox_env: 'pypy39-pytest72-xdist320-coverage65' + os: 'macos-latest' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} architecture: ${{ matrix.python_arch }} - name: install dependencies run: | - python -mpip install --progress-bar=off -r ci/requirements.txt + python -m pip install --progress-bar=off -r ci/requirements.txt virtualenv --version pip --version tox --version diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ec537150..1b105884 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,22 +4,22 @@ # pre-commit autoupdate repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.4.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer exclude: '.*\.pth$' - id: debug-statements - repo: https://github.com/PyCQA/isort - rev: 5.10.1 + rev: 5.12.0 hooks: - id: isort - repo: https://github.com/asottile/pyupgrade - rev: v3.2.2 + rev: v3.3.1 hooks: - id: pyupgrade - args: [--py36-plus] + args: [--py37-plus] - repo: https://github.com/PyCQA/flake8 - rev: 5.0.4 + rev: 6.0.0 hooks: - id: flake8 diff --git a/ci/bootstrap.py b/ci/bootstrap.py index b0977495..48c46640 100755 --- a/ci/bootstrap.py +++ b/ci/bootstrap.py @@ -65,7 +65,7 @@ def main(): # This uses sys.executable the same way that the call in # cookiecutter-pylibrary/hooks/post_gen_project.py # invokes this bootstrap.py itself. - for line in subprocess.check_output([sys.executable, '-m', 'tox', '--listenvs'], universal_newlines=True).splitlines() + for line in subprocess.check_output([sys.executable, '-m', 'tox', '--listenvs'], text=True).splitlines() ] tox_environments = [line for line in tox_environments if line.startswith('py')] diff --git a/setup.py b/setup.py index 799d4699..7c3eaad1 100755 --- a/setup.py +++ b/setup.py @@ -111,11 +111,11 @@ def run(self): 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Software Development :: Testing', @@ -133,7 +133,7 @@ def run(self): 'pytest>=4.6', 'coverage[toml]>=5.2.1' ], - python_requires='>=3.6', + python_requires='>=3.7', extras_require={ 'testing': [ 'fields', diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 84fe42ba..77859bf4 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -1556,6 +1556,8 @@ def test_foo(): SCRIPT_SIMPLE_RESULT = '4 * 100%' +@pytest.mark.skipif('tuple(int(x) for x in xdist.__version__.split(".")) >= (2, 5, 0)', + reason="--boxed option was removed in version 2.5.0") @pytest.mark.skipif('sys.platform == "win32"') def test_dist_boxed(testdir): script = testdir.makepyfile(SCRIPT_SIMPLE) @@ -1916,6 +1918,7 @@ def find_labels(text, pattern): @pytest.mark.skipif("coverage.version_info < (5, 0)") +@pytest.mark.skipif("coverage.version_info > (6, 4)") @xdist_params def test_contexts(pytester, testdir, opts): with open(os.path.join(os.path.dirname(__file__), "contextful.py")) as f: diff --git a/tox.ini b/tox.ini index 282cd244..aeccd9de 100644 --- a/tox.ini +++ b/tox.ini @@ -12,8 +12,8 @@ passenv = [tox] envlist = check - py{36}-pytest{70}-xdist250-coverage{62} py{37,38,39,310,py37,py38}-pytest{71}-xdist250-coverage{64} + py{311,py39}-pytest{72}-xdist320-coverage{65} docs [testenv] @@ -30,6 +30,7 @@ setenv = pytest62: _DEP_PYTEST=pytest==6.2.5 pytest70: _DEP_PYTEST=pytest==7.0.1 pytest71: _DEP_PYTEST=pytest==7.1.2 + pytest72: _DEP_PYTEST=pytest==7.2.0 xdist127: _DEP_PYTESTXDIST=pytest-xdist==1.27.0 xdist129: _DEP_PYTESTXDIST=pytest-xdist==1.29.0 @@ -41,6 +42,7 @@ setenv = xdist201: _DEP_PYTESTXDIST=pytest-xdist==2.1.0 xdist202: _DEP_PYTESTXDIST=pytest-xdist==2.2.0 xdist250: _DEP_PYTESTXDIST=pytest-xdist==2.5.0 + xdist320: _DEP_PYTESTXDIST=pytest-xdist==3.2.0 xdistdev: _DEP_PYTESTXDIST=git+https://github.com/pytest-dev/pytest-xdist.git#egg=pytest-xdist coverage45: _DEP_COVERAGE=coverage==4.5.4 @@ -55,6 +57,7 @@ setenv = coverage62: _DEP_COVERAGE=coverage==6.2 coverage63: _DEP_COVERAGE=coverage==6.3.3 coverage64: _DEP_COVERAGE=coverage==6.4.2 + coverage65: _DEP_COVERAGE=coverage==6.5.0 # For testing against a coverage.py working tree. coveragedev: _DEP_COVERAGE=-e{env:COVERAGE_HOME} passenv = @@ -87,12 +90,13 @@ commands = [testenv:check] deps = - docutils check-manifest + colorama # TODO Remove when isort > v6.0.0b2 is released. + docutils flake8 - readme-renderer - pygments isort + pygments + readme-renderer skip_install = true usedevelop = false commands = From 2ea944212d62841cbd95ce49ef4586c8450435fd Mon Sep 17 00:00:00 2001 From: Sorin Sbarnea Date: Wed, 26 Oct 2022 18:46:42 +0100 Subject: [PATCH 087/117] Remove use of rsyncdir Fixes: #557 --- src/pytest_cov/engine.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pytest_cov/engine.py b/src/pytest_cov/engine.py index bfede8c7..27e1a090 100644 --- a/src/pytest_cov/engine.py +++ b/src/pytest_cov/engine.py @@ -260,7 +260,9 @@ def start(self): # Ensure coverage rc file rsynced if appropriate. if self.cov_config and os.path.exists(self.cov_config): - self.config.option.rsyncdir.append(self.cov_config) + # rsyncdir is going away in pytest-xdist 4.0, already deprecated + if hasattr(self.config.option, 'rsyncdir'): + self.config.option.rsyncdir.append(self.cov_config) self.cov = coverage.Coverage(source=self.cov_source, branch=self.cov_branch, From 74eb4cc8b684269b89735e31b623f0f9795c5d5c Mon Sep 17 00:00:00 2001 From: Jonathan Stewmon Date: Mon, 27 Mar 2023 11:26:00 -0500 Subject: [PATCH 088/117] perf: only call summary when the report will be used --- src/pytest_cov/plugin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pytest_cov/plugin.py b/src/pytest_cov/plugin.py index dd7b8c4e..59af4b17 100644 --- a/src/pytest_cov/plugin.py +++ b/src/pytest_cov/plugin.py @@ -275,7 +275,8 @@ def pytest_testnodedown(self, node, error): self.cov_controller.testnodedown(node, error) def _should_report(self): - return not (self.failed and self.options.no_cov_on_fail) + needed = self.options.cov_report or self.options.cov_fail_under + return needed and not (self.failed and self.options.no_cov_on_fail) def _failed_cov_total(self): cov_fail_under = self.options.cov_fail_under From 88a7d348986bace58e26c88a713ef35f900ce2ef Mon Sep 17 00:00:00 2001 From: Jonathan Stewmon Date: Mon, 27 Mar 2023 13:03:12 -0500 Subject: [PATCH 089/117] chore: update AUTHORS and CHANGELOG --- AUTHORS.rst | 1 + CHANGELOG.rst | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/AUTHORS.rst b/AUTHORS.rst index 2ad80a00..22ae71f5 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -57,3 +57,4 @@ Authors * Colin O'Dell - https://github.com/colinodell * Ronny Pfannschmidt - https://github.com/RonnyPfannschmidt * Christian Fetzer - https://github.com/fetzerch +* Jonathan Stewmon = https://github.com/jstewmon diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c49683f5..efeaf34c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,13 @@ Changelog ========= +4.0.1 (2023-03-27) +------------------ + +* Skip generating the in-memory coverage report when it will not be used. For example, + when ``--cov-report=''`` is used without ``--cov-fail-under``. + + 4.0.0 (2022-09-28) ------------------ From dec02abeb9fa8ee3547baa054bde6006bea530ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 21 May 2023 15:58:34 +0300 Subject: [PATCH 090/117] Update test deps. --- setup.cfg | 2 +- tox.ini | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/setup.cfg b/setup.cfg index 360a416d..c33a9042 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,7 +7,7 @@ testpaths = tests python_files = test_*.py addopts = -ra - --strict + --strict-markers -p pytester [tool:isort] diff --git a/tox.ini b/tox.ini index aeccd9de..7d49a3be 100644 --- a/tox.ini +++ b/tox.ini @@ -12,8 +12,7 @@ passenv = [tox] envlist = check - py{37,38,39,310,py37,py38}-pytest{71}-xdist250-coverage{64} - py{311,py39}-pytest{72}-xdist320-coverage{65} + py{37,38,39,310,311,py37,py38,py39}-pytest{73}-xdist330-coverage{72} docs [testenv] @@ -31,6 +30,7 @@ setenv = pytest70: _DEP_PYTEST=pytest==7.0.1 pytest71: _DEP_PYTEST=pytest==7.1.2 pytest72: _DEP_PYTEST=pytest==7.2.0 + pytest73: _DEP_PYTEST=pytest==7.3.1 xdist127: _DEP_PYTESTXDIST=pytest-xdist==1.27.0 xdist129: _DEP_PYTESTXDIST=pytest-xdist==1.29.0 @@ -43,6 +43,7 @@ setenv = xdist202: _DEP_PYTESTXDIST=pytest-xdist==2.2.0 xdist250: _DEP_PYTESTXDIST=pytest-xdist==2.5.0 xdist320: _DEP_PYTESTXDIST=pytest-xdist==3.2.0 + xdist330: _DEP_PYTESTXDIST=pytest-xdist==3.3.1 xdistdev: _DEP_PYTESTXDIST=git+https://github.com/pytest-dev/pytest-xdist.git#egg=pytest-xdist coverage45: _DEP_COVERAGE=coverage==4.5.4 @@ -58,6 +59,7 @@ setenv = coverage63: _DEP_COVERAGE=coverage==6.3.3 coverage64: _DEP_COVERAGE=coverage==6.4.2 coverage65: _DEP_COVERAGE=coverage==6.5.0 + coverage72: _DEP_COVERAGE=coverage==7.2.5 # For testing against a coverage.py working tree. coveragedev: _DEP_COVERAGE=-e{env:COVERAGE_HOME} passenv = From f3d8d8380f6a4b265353fe7cd509b040702f1e64 Mon Sep 17 00:00:00 2001 From: Matthew Gamble Date: Mon, 27 Feb 2023 20:48:52 +1100 Subject: [PATCH 091/117] Add support for JSON reporter This exposes an additional reporter that's provided by coverage.py. THe implementation is basically a carbon copy of the code that powers the XML implementation. --- docs/config.rst | 4 ++-- docs/reporting.rst | 6 ++++-- src/pytest_cov/engine.py | 7 +++++++ src/pytest_cov/plugin.py | 6 +++--- tests/test_pytest_cov.py | 18 ++++++++++++++++++ 5 files changed, 34 insertions(+), 7 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index fa257037..c7bef037 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -56,9 +56,9 @@ The complete list of command line options is: --cov=PATH Measure coverage for filesystem path. (multi-allowed) --cov-report=type Type of report to generate: term, term-missing, - annotate, html, xml, lcov (multi-allowed). term, term- + annotate, html, xml, json, lcov (multi-allowed). term, term- missing may be followed by ":skip-covered". annotate, - html, xml and lcov may be followed by ":DEST" where DEST + html, xml, json and lcov may be followed by ":DEST" where DEST specifies the output location. Use --cov-report= to not generate any output. --cov-config=path Config file for coverage. Default: .coveragerc diff --git a/docs/reporting.rst b/docs/reporting.rst index 69191d48..d3ae06b2 100644 --- a/docs/reporting.rst +++ b/docs/reporting.rst @@ -3,7 +3,7 @@ Reporting It is possible to generate any combination of the reports for a single test run. -The available reports are terminal (with or without missing line numbers shown), HTML, XML, LCOV and +The available reports are terminal (with or without missing line numbers shown), HTML, XML, JSON, LCOV and annotated source code. The terminal report without line numbers (default):: @@ -53,16 +53,18 @@ These four report options output to files without showing anything on the termin pytest --cov-report html --cov-report xml + --cov-report json --cov-report lcov --cov-report annotate --cov=myproj tests/ -The output location for each of these reports can be specified. The output location for the XML and LCOV +The output location for each of these reports can be specified. The output location for the XML, JSON and LCOV report is a file. Where as the output location for the HTML and annotated source code reports are directories:: pytest --cov-report html:cov_html --cov-report xml:cov.xml + --cov-report json:cov.json --cov-report lcov:cov.info --cov-report annotate:cov_annotate --cov=myproj tests/ diff --git a/src/pytest_cov/engine.py b/src/pytest_cov/engine.py index 27e1a090..f045aed5 100644 --- a/src/pytest_cov/engine.py +++ b/src/pytest_cov/engine.py @@ -196,6 +196,13 @@ def summary(self, stream): total = self.cov.xml_report(ignore_errors=True, outfile=output) stream.write('Coverage XML written to file %s\n' % (self.cov.config.xml_output if output is None else output)) + # Produce json report if wanted + if 'json' in self.cov_report: + output = self.cov_report['json'] + with _backup(self.cov, "config"): + total = self.cov.json_report(ignore_errors=True, outfile=output) + stream.write('Coverage JSON written to file %s\n' % (self.cov.config.json_output if output is None else output)) + # Produce lcov report if wanted. if 'lcov' in self.cov_report: output = self.cov_report['lcov'] diff --git a/src/pytest_cov/plugin.py b/src/pytest_cov/plugin.py index 59af4b17..036c6375 100644 --- a/src/pytest_cov/plugin.py +++ b/src/pytest_cov/plugin.py @@ -29,7 +29,7 @@ class CovReportWarning(PytestCovWarning): def validate_report(arg): - file_choices = ['annotate', 'html', 'xml', 'lcov'] + file_choices = ['annotate', 'html', 'xml', 'json', 'lcov'] term_choices = ['term', 'term-missing'] term_modifier_choices = ['skip-covered'] all_choices = term_choices + file_choices @@ -99,9 +99,9 @@ def pytest_addoption(parser): group.addoption('--cov-report', action=StoreReport, default={}, metavar='TYPE', type=validate_report, help='Type of report to generate: term, term-missing, ' - 'annotate, html, xml, lcov (multi-allowed). ' + 'annotate, html, xml, json, lcov (multi-allowed). ' 'term, term-missing may be followed by ":skip-covered". ' - 'annotate, html, xml and lcov may be followed by ":DEST" ' + 'annotate, html, xml, json and lcov may be followed by ":DEST" ' 'where DEST specifies the output location. ' 'Use --cov-report= to not generate any output.') group.addoption('--cov-config', action='store', default='.coveragerc', diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 77859bf4..5e3d0a3f 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -151,6 +151,7 @@ def test_foo(cov): PARENT_SCRIPT_RESULT = '9 * 100%' DEST_DIR = 'cov_dest' XML_REPORT_NAME = 'cov.xml' +JSON_REPORT_NAME = 'cov.json' LCOV_REPORT_NAME = 'cov.info' xdist_params = pytest.mark.parametrize('opts', [ @@ -346,6 +347,23 @@ def test_xml_output_dir(testdir): assert result.ret == 0 +def test_json_output_dir(testdir): + script = testdir.makepyfile(SCRIPT) + + result = testdir.runpytest('-v', + '--cov=%s' % script.dirpath(), + '--cov-report=json:' + JSON_REPORT_NAME, + script) + + result.stdout.fnmatch_lines([ + '*- coverage: platform *, python * -*', + 'Coverage JSON written to file ' + JSON_REPORT_NAME, + '*10 passed*', + ]) + assert testdir.tmpdir.join(JSON_REPORT_NAME).check() + assert result.ret == 0 + + @pytest.mark.skipif("coverage.version_info < (6, 3)") def test_lcov_output_dir(testdir): script = testdir.makepyfile(SCRIPT) From 0d63ede0d2ca9f4acc8329aa4261a7cec489ffdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 21 May 2023 16:27:29 +0300 Subject: [PATCH 092/117] Update test config. Reapply some of the changes from PR567 to the right file (the template). --- .github/workflows/test.yml | 108 ++++++++++++++---------- ci/templates/.github/workflows/test.yml | 28 +++--- 2 files changed, 78 insertions(+), 58 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2dbeb51a..7fade785 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -60,131 +60,149 @@ jobs: toxpython: 'python3.11' tox_env: 'docs' os: 'ubuntu-latest' - - name: 'py37-pytest71-xdist250-coverage64 (ubuntu)' + - name: 'py37-pytest73-xdist330-coverage72 (ubuntu)' python: '3.7' toxpython: 'python3.7' python_arch: 'x64' - tox_env: 'py37-pytest71-xdist250-coverage64' + tox_env: 'py37-pytest73-xdist330-coverage72' os: 'ubuntu-latest' - - name: 'py37-pytest71-xdist250-coverage64 (windows)' + - name: 'py37-pytest73-xdist330-coverage72 (windows)' python: '3.7' toxpython: 'python3.7' python_arch: 'x64' - tox_env: 'py37-pytest71-xdist250-coverage64' + tox_env: 'py37-pytest73-xdist330-coverage72' os: 'windows-latest' - - name: 'py37-pytest71-xdist250-coverage64 (macos)' + - name: 'py37-pytest73-xdist330-coverage72 (macos)' python: '3.7' toxpython: 'python3.7' python_arch: 'x64' - tox_env: 'py37-pytest71-xdist250-coverage64' + tox_env: 'py37-pytest73-xdist330-coverage72' os: 'macos-latest' - - name: 'py38-pytest71-xdist250-coverage64 (ubuntu)' + - name: 'py38-pytest73-xdist330-coverage72 (ubuntu)' python: '3.8' toxpython: 'python3.8' python_arch: 'x64' - tox_env: 'py38-pytest71-xdist250-coverage64' + tox_env: 'py38-pytest73-xdist330-coverage72' os: 'ubuntu-latest' - - name: 'py38-pytest71-xdist250-coverage64 (windows)' + - name: 'py38-pytest73-xdist330-coverage72 (windows)' python: '3.8' toxpython: 'python3.8' python_arch: 'x64' - tox_env: 'py38-pytest71-xdist250-coverage64' + tox_env: 'py38-pytest73-xdist330-coverage72' os: 'windows-latest' - - name: 'py38-pytest71-xdist250-coverage64 (macos)' + - name: 'py38-pytest73-xdist330-coverage72 (macos)' python: '3.8' toxpython: 'python3.8' python_arch: 'x64' - tox_env: 'py38-pytest71-xdist250-coverage64' + tox_env: 'py38-pytest73-xdist330-coverage72' os: 'macos-latest' - - name: 'py39-pytest71-xdist250-coverage64 (ubuntu)' + - name: 'py39-pytest73-xdist330-coverage72 (ubuntu)' python: '3.9' toxpython: 'python3.9' python_arch: 'x64' - tox_env: 'py39-pytest71-xdist250-coverage64' + tox_env: 'py39-pytest73-xdist330-coverage72' os: 'ubuntu-latest' - - name: 'py39-pytest71-xdist250-coverage64 (windows)' + - name: 'py39-pytest73-xdist330-coverage72 (windows)' python: '3.9' toxpython: 'python3.9' python_arch: 'x64' - tox_env: 'py39-pytest71-xdist250-coverage64' + tox_env: 'py39-pytest73-xdist330-coverage72' os: 'windows-latest' - - name: 'py39-pytest71-xdist250-coverage64 (macos)' + - name: 'py39-pytest73-xdist330-coverage72 (macos)' python: '3.9' toxpython: 'python3.9' python_arch: 'x64' - tox_env: 'py39-pytest71-xdist250-coverage64' + tox_env: 'py39-pytest73-xdist330-coverage72' os: 'macos-latest' - - name: 'py310-pytest71-xdist250-coverage64 (ubuntu)' + - name: 'py310-pytest73-xdist330-coverage72 (ubuntu)' python: '3.10' toxpython: 'python3.10' python_arch: 'x64' - tox_env: 'py310-pytest71-xdist250-coverage64' + tox_env: 'py310-pytest73-xdist330-coverage72' os: 'ubuntu-latest' - - name: 'py310-pytest71-xdist250-coverage64 (windows)' + - name: 'py310-pytest73-xdist330-coverage72 (windows)' python: '3.10' toxpython: 'python3.10' python_arch: 'x64' - tox_env: 'py310-pytest71-xdist250-coverage64' + tox_env: 'py310-pytest73-xdist330-coverage72' os: 'windows-latest' - - name: 'py310-pytest71-xdist250-coverage64 (macos)' + - name: 'py310-pytest73-xdist330-coverage72 (macos)' python: '3.10' toxpython: 'python3.10' python_arch: 'x64' - tox_env: 'py310-pytest71-xdist250-coverage64' + tox_env: 'py310-pytest73-xdist330-coverage72' os: 'macos-latest' - - name: 'py311-pytest72-xdist320-coverage65 (ubuntu)' + - name: 'py311-pytest73-xdist330-coverage72 (ubuntu)' python: '3.11' toxpython: 'python3.11' python_arch: 'x64' - tox_env: 'py311-pytest72-xdist320-coverage65' + tox_env: 'py311-pytest73-xdist330-coverage72' os: 'ubuntu-latest' - - name: 'py311-pytest72-xdist320-coverage65 (windows)' + - name: 'py311-pytest73-xdist330-coverage72 (windows)' python: '3.11' toxpython: 'python3.11' python_arch: 'x64' - tox_env: 'py311-pytest72-xdist320-coverage65' + tox_env: 'py311-pytest73-xdist330-coverage72' os: 'windows-latest' - - name: 'py311-pytest72-xdist320-coverage65 (macos)' + - name: 'py311-pytest73-xdist330-coverage72 (macos)' python: '3.11' toxpython: 'python3.11' python_arch: 'x64' - tox_env: 'py311-pytest72-xdist320-coverage65' + tox_env: 'py311-pytest73-xdist330-coverage72' os: 'macos-latest' - - name: 'pypy38-pytest71-xdist250-coverage64 (ubuntu)' + - name: 'pypy37-pytest73-xdist330-coverage72 (ubuntu)' + python: 'pypy-3.7' + toxpython: 'pypy3.7' + python_arch: 'x64' + tox_env: 'pypy37-pytest73-xdist330-coverage72' + os: 'ubuntu-latest' + - name: 'pypy37-pytest73-xdist330-coverage72 (windows)' + python: 'pypy-3.7' + toxpython: 'pypy3.7' + python_arch: 'x64' + tox_env: 'pypy37-pytest73-xdist330-coverage72' + os: 'windows-latest' + - name: 'pypy37-pytest73-xdist330-coverage72 (macos)' + python: 'pypy-3.7' + toxpython: 'pypy3.7' + python_arch: 'x64' + tox_env: 'pypy37-pytest73-xdist330-coverage72' + os: 'macos-latest' + - name: 'pypy38-pytest73-xdist330-coverage72 (ubuntu)' python: 'pypy-3.8' toxpython: 'pypy3.8' python_arch: 'x64' - tox_env: 'pypy38-pytest71-xdist250-coverage64' + tox_env: 'pypy38-pytest73-xdist330-coverage72' os: 'ubuntu-latest' - - name: 'pypy38-pytest71-xdist250-coverage64 (windows)' + - name: 'pypy38-pytest73-xdist330-coverage72 (windows)' python: 'pypy-3.8' toxpython: 'pypy3.8' python_arch: 'x64' - tox_env: 'pypy38-pytest71-xdist250-coverage64' + tox_env: 'pypy38-pytest73-xdist330-coverage72' os: 'windows-latest' - - name: 'pypy38-pytest71-xdist250-coverage64 (macos)' + - name: 'pypy38-pytest73-xdist330-coverage72 (macos)' python: 'pypy-3.8' toxpython: 'pypy3.8' python_arch: 'x64' - tox_env: 'pypy38-pytest71-xdist250-coverage64' + tox_env: 'pypy38-pytest73-xdist330-coverage72' os: 'macos-latest' - - name: 'pypy39--pytest72-xdist320-coverage65 (ubuntu)' + - name: 'pypy39-pytest73-xdist330-coverage72 (ubuntu)' python: 'pypy-3.9' toxpython: 'pypy3.9' python_arch: 'x64' - tox_env: 'pypy39-pytest72-xdist320-coverage65' + tox_env: 'pypy39-pytest73-xdist330-coverage72' os: 'ubuntu-latest' - - name: 'pypy39-pytest72-xdist320-coverage65 (windows)' + - name: 'pypy39-pytest73-xdist330-coverage72 (windows)' python: 'pypy-3.9' toxpython: 'pypy3.9' python_arch: 'x64' - tox_env: 'pypy39-pytest72-xdist320-coverage65' + tox_env: 'pypy39-pytest73-xdist330-coverage72' os: 'windows-latest' - - name: 'pypy39-pytest72-xdist320-coverage65 (macos)' + - name: 'pypy39-pytest73-xdist330-coverage72 (macos)' python: 'pypy-3.9' toxpython: 'pypy3.9' python_arch: 'x64' - tox_env: 'pypy39-pytest72-xdist320-coverage65' + tox_env: 'pypy39-pytest73-xdist330-coverage72' os: 'macos-latest' steps: - uses: actions/checkout@v3 @@ -196,7 +214,7 @@ jobs: architecture: ${{ matrix.python_arch }} - name: install dependencies run: | - python -m pip install --progress-bar=off -r ci/requirements.txt + python -mpip install --progress-bar=off -r ci/requirements.txt virtualenv --version pip --version tox --version @@ -207,7 +225,9 @@ jobs: run: > tox -e ${{ matrix.tox_env }} -v - check: + successful: + # this provides a single status check for branch merge rules + # (use this in `Require status checks to pass before merging` in branch settings) if: always() needs: - test diff --git a/ci/templates/.github/workflows/test.yml b/ci/templates/.github/workflows/test.yml index 3e99ef08..6aaf1dbc 100644 --- a/ci/templates/.github/workflows/test.yml +++ b/ci/templates/.github/workflows/test.yml @@ -7,25 +7,25 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["pypy-3.7", "3.9"] + python-version: ["pypy-3.9", "3.11"] target: [ "src-layout", "adhoc-layout", ] include: # Add new helper variables to existing jobs - - {python-version: "pypy-3.7", tox-python-version: "pypy3"} - - {python-version: "3.9", tox-python-version: "py39"} + - {python-version: "pypy-3.9", tox-python-version: "pypy3"} + - {python-version: "3.11", tox-python-version: "py311"} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Cache - uses: actions/cache@v2 + uses: actions/cache@v3 with: path: ~/.cache/pip key: @@ -35,8 +35,8 @@ jobs: - name: Install dependencies run: | - python -m pip install -U pip - python -m pip install -U wheel + python -m pip install --upgrade pip + python -m pip install --upgrade wheel python -m pip install --progress-bar=off tox -rci/requirements.txt - name: Examples @@ -54,13 +54,13 @@ jobs: matrix: include: - name: 'check' - python: '3.9' - toxpython: 'python3.9' + python: '3.11' + toxpython: 'python3.11' tox_env: 'check' os: 'ubuntu-latest' - name: 'docs' - python: '3.9' - toxpython: 'python3.9' + python: '3.11' + toxpython: 'python3.11' tox_env: 'docs' os: 'ubuntu-latest' {% for env in tox_environments %} @@ -88,10 +88,10 @@ jobs: {% endfor %} {% endfor %} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 with: python-version: {{ '${{ matrix.python }}' }} architecture: {{ '${{ matrix.python_arch }}' }} From 76fb2a6cb2c4a4a788a5b62710848daf9c8fb7ce Mon Sep 17 00:00:00 2001 From: Mark Mayo Date: Sun, 11 Dec 2022 23:51:58 +1300 Subject: [PATCH 093/117] introduced f-strings converted to more modern f-strings --- setup.py | 3 +- src/pytest_cov/engine.py | 14 +- src/pytest_cov/plugin.py | 6 +- tests/test_pytest_cov.py | 272 +++++++++++++++++++-------------------- 4 files changed, 147 insertions(+), 148 deletions(-) diff --git a/setup.py b/setup.py index 7c3eaad1..e2cafde7 100755 --- a/setup.py +++ b/setup.py @@ -80,8 +80,7 @@ def run(self): with open(join(dirname(__file__), 'src', 'pytest-cov.pth'), 'w') as fh: with open(join(dirname(__file__), 'src', 'pytest-cov.embed')) as sh: fh.write( - 'import os, sys;' - 'exec(%r)' % sh.read().replace(' ', ' ') + f"import os, sys;exec({sh.read().replace(' ', ' ')!r})" ) diff --git a/src/pytest_cov/engine.py b/src/pytest_cov/engine.py index f045aed5..97d4d017 100644 --- a/src/pytest_cov/engine.py +++ b/src/pytest_cov/engine.py @@ -140,11 +140,11 @@ def summary(self, stream): # Output coverage section header. if len(self.node_descs) == 1: - self.sep(stream, '-', 'coverage: %s' % ''.join(self.node_descs)) + self.sep(stream, '-', f"coverage: {''.join(self.node_descs)}") else: self.sep(stream, '-', 'coverage') for node_desc in sorted(self.node_descs): - self.sep(stream, ' ', '%s' % node_desc) + self.sep(stream, ' ', f'{node_desc}') # Report on any failed workers. if self.failed_workers: @@ -152,7 +152,7 @@ def summary(self, stream): 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('%s\n' % node.gateway.id) + stream.write(f'{node.gateway.id}\n') # Produce terminal report if wanted. if any(x in self.cov_report for x in ['term', 'term-missing']): @@ -178,7 +178,7 @@ def summary(self, stream): with _backup(self.cov, "config"): total = self.cov.report(ignore_errors=True, file=_NullFile) if annotate_dir: - stream.write('Coverage annotated source written to dir %s\n' % annotate_dir) + stream.write(f'Coverage annotated source written to dir {annotate_dir}\n') else: stream.write('Coverage annotated source written next to source\n') @@ -187,14 +187,14 @@ def summary(self, stream): output = self.cov_report['html'] with _backup(self.cov, "config"): total = self.cov.html_report(ignore_errors=True, directory=output) - stream.write('Coverage HTML written to dir %s\n' % (self.cov.config.html_dir if output is None else output)) + stream.write(f'Coverage HTML written to dir {self.cov.config.html_dir if output is None else output}\n') # Produce xml report if wanted. if 'xml' in self.cov_report: output = self.cov_report['xml'] with _backup(self.cov, "config"): total = self.cov.xml_report(ignore_errors=True, outfile=output) - stream.write('Coverage XML written to file %s\n' % (self.cov.config.xml_output if output is None else output)) + stream.write(f'Coverage XML written to file {self.cov.config.xml_output if output is None else output}\n') # Produce json report if wanted if 'json' in self.cov_report: @@ -213,7 +213,7 @@ def summary(self, stream): # Coverage.lcov_report doesn't return any total and we need it for --cov-fail-under. total = self.cov.report(ignore_errors=True, file=_NullFile) - stream.write('Coverage LCOV written to file %s\n' % (self.cov.config.lcov_output if output is None else output)) + stream.write(f'Coverage LCOV written to file {self.cov.config.lcov_output if output is None else output}\n') return total diff --git a/src/pytest_cov/plugin.py b/src/pytest_cov/plugin.py index 036c6375..2a1544a6 100644 --- a/src/pytest_cov/plugin.py +++ b/src/pytest_cov/plugin.py @@ -306,9 +306,9 @@ def pytest_runtestloop(self, session): try: self.cov_total = self.cov_controller.summary(self.cov_report) except CoverageException as exc: - message = 'Failed to generate report: %s\n' % exc + message = f'Failed to generate report: {exc}\n' session.config.pluginmanager.getplugin("terminalreporter").write( - 'WARNING: %s\n' % message, red=True, bold=True) + f'WARNING: {message}\n', red=True, bold=True) warnings.warn(CovReportWarning(message)) self.cov_total = 0 assert self.cov_total is not None, 'Test coverage should never be `None`' @@ -320,7 +320,7 @@ def pytest_terminal_summary(self, terminalreporter): if self._disabled: if self.options.no_cov_should_warn: message = 'Coverage disabled via --no-cov switch!' - terminalreporter.write('WARNING: %s\n' % message, red=True, bold=True) + terminalreporter.write(f'WARNING: {message}\n', red=True, bold=True) warnings.warn(CovDisabledWarning(message)) return if self.cov_controller is None: diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 5e3d0a3f..dde64f8f 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -188,8 +188,8 @@ def prop(request): code=SCRIPT, code2=SCRIPT2, conf=request.param[0], - fullconf='[run]\n%s\n' % request.param[0], - prefixedfullconf='[coverage:run]\n%s\n' % request.param[0], + fullconf=f'[run]\n{request.param[0]}\n', + prefixedfullconf=f'[coverage:run]\n{request.param[0]}\n', args=request.param[1].split(), result=request.param[2], result2=request.param[3], @@ -201,14 +201,14 @@ def test_central(pytester, testdir, prop): testdir.tmpdir.join('.coveragerc').write(prop.fullconf) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', script, *prop.args) result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'test_central* %s *' % prop.result, + f'test_central* {prop.result} *', '*10 passed*' ]) assert result.ret == 0 @@ -218,7 +218,7 @@ def test_annotate(testdir): script = testdir.makepyfile(SCRIPT) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=annotate', script) @@ -234,7 +234,7 @@ def test_annotate_output_dir(testdir): script = testdir.makepyfile(SCRIPT) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=annotate:' + DEST_DIR, script) @@ -253,7 +253,7 @@ def test_html(testdir): script = testdir.makepyfile(SCRIPT) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=html', script) @@ -272,7 +272,7 @@ def test_html_output_dir(testdir): script = testdir.makepyfile(SCRIPT) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=html:' + DEST_DIR, script) @@ -291,7 +291,7 @@ def test_term_report_does_not_interact_with_html_output(testdir): script = testdir.makepyfile(test_funcarg=SCRIPT_FUNCARG) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing:skip-covered', '--cov-report=html:' + DEST_DIR, script) @@ -315,7 +315,7 @@ def test_html_configured_output_dir(testdir): directory = somewhere """) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=html', script) @@ -334,7 +334,7 @@ def test_xml_output_dir(testdir): script = testdir.makepyfile(SCRIPT) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=xml:' + XML_REPORT_NAME, script) @@ -369,7 +369,7 @@ def test_lcov_output_dir(testdir): script = testdir.makepyfile(SCRIPT) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=lcov:' + LCOV_REPORT_NAME, script) @@ -386,7 +386,7 @@ def test_lcov_output_dir(testdir): def test_lcov_not_supported(testdir): script = testdir.makepyfile("a = 1") result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=lcov', script, ) @@ -400,12 +400,12 @@ def test_term_output_dir(testdir): script = testdir.makepyfile(SCRIPT) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term:' + DEST_DIR, script) result.stderr.fnmatch_lines([ - '*argument --cov-report: output specifier not supported for: "term:%s"*' % DEST_DIR, + f'*argument --cov-report: output specifier not supported for: "term:{DEST_DIR}"*', ]) assert result.ret != 0 @@ -414,7 +414,7 @@ def test_term_missing_output_dir(testdir): script = testdir.makepyfile(SCRIPT) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing:' + DEST_DIR, script) @@ -429,7 +429,7 @@ def test_cov_min_100(testdir): script = testdir.makepyfile(SCRIPT) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', '--cov-fail-under=100', script) @@ -444,7 +444,7 @@ def test_cov_min_100_passes_if_collectonly(testdir): script = testdir.makepyfile(SCRIPT) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', '--cov-fail-under=100', '--collect-only', @@ -457,7 +457,7 @@ def test_cov_min_50(testdir): script = testdir.makepyfile(SCRIPT) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=html', '--cov-report=xml', '--cov-fail-under=50', @@ -473,7 +473,7 @@ def test_cov_min_float_value(testdir): script = testdir.makepyfile(SCRIPT) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', '--cov-fail-under=88.88', script) @@ -487,7 +487,7 @@ def test_cov_min_float_value_not_reached(testdir): script = testdir.makepyfile(SCRIPT) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', '--cov-fail-under=88.89', script) @@ -501,7 +501,7 @@ def test_cov_min_no_report(testdir): script = testdir.makepyfile(SCRIPT) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=', '--cov-fail-under=50', script) @@ -522,7 +522,7 @@ def test_central_nonspecific(pytester, testdir, prop): result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'test_central_nonspecific* %s *' % prop.result, + f'test_central_nonspecific* {prop.result} *', '*10 passed*' ]) @@ -540,7 +540,7 @@ def test_cov_min_from_coveragerc(testdir): """) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', script) @@ -558,7 +558,7 @@ def test_central_coveragerc(pytester, testdir, prop): result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'test_central_coveragerc* %s *' % prop.result, + f'test_central_coveragerc* {prop.result} *', '*10 passed*', ]) assert result.ret == 0 @@ -573,7 +573,7 @@ def test_central_with_path_aliasing(pytester, testdir, monkeypatch, opts, prop): script = testdir.makepyfile(''' from mod import * ''') - testdir.tmpdir.join('setup.cfg').write(""" + testdir.tmpdir.join('setup.cfg').write(f""" [coverage:paths] source = src @@ -581,8 +581,8 @@ def test_central_with_path_aliasing(pytester, testdir, monkeypatch, opts, prop): [coverage:run] source = mod parallel = true -%s -""" % prop.conf) +{prop.conf} +""") monkeypatch.setitem(os.environ, 'PYTHONPATH', os.pathsep.join([os.environ.get('PYTHONPATH', ''), 'aliased'])) result = testdir.runpytest('-v', '-s', @@ -592,7 +592,7 @@ def test_central_with_path_aliasing(pytester, testdir, monkeypatch, opts, prop): result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'src[\\/]mod* %s *' % prop.result, + f'src[\\/]mod* {prop.result} *', '*10 passed*', ]) assert result.ret == 0 @@ -668,22 +668,22 @@ def test_subprocess_with_path_aliasing(pytester, testdir, monkeypatch): result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'src[\\/]child_script* %s*' % CHILD_SCRIPT_RESULT, - 'src[\\/]parent_script* %s*' % PARENT_SCRIPT_RESULT, + f'src[\\/]child_script* {CHILD_SCRIPT_RESULT}*', + f'src[\\/]parent_script* {PARENT_SCRIPT_RESULT}*', ]) assert result.ret == 0 def test_show_missing_coveragerc(pytester, testdir, prop): script = testdir.makepyfile(prop.code) - testdir.tmpdir.join('.coveragerc').write(""" + testdir.tmpdir.join('.coveragerc').write(f""" [run] source = . -%s +{prop.conf} [report] show_missing = true -""" % prop.conf) +""") result = testdir.runpytest('-v', '--cov', @@ -693,7 +693,7 @@ def test_show_missing_coveragerc(pytester, testdir, prop): result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', 'Name * Stmts * Miss * Cover * Missing', - 'test_show_missing_coveragerc* %s * 11*' % prop.result, + f'test_show_missing_coveragerc* {prop.result} * 11*', '*10 passed*', ]) @@ -708,7 +708,7 @@ def test_fail(): ''') result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', '--no-cov-on-fail', script) @@ -724,7 +724,7 @@ def test_no_cov(pytester, testdir, monkeypatch): addopts=--no-cov """) result = testdir.runpytest('-vvv', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', '-rw', script) @@ -742,7 +742,7 @@ def test_fail(p): ''') result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-fail-under=100', '--cov-report=html', script) @@ -762,13 +762,13 @@ def test_dist_combine_racecondition(testdir): @pytest.mark.parametrize("foo", range(1000)) def test_foo(foo): -""" + "\n".join(""" - if foo == %s: +""" + "\n".join(f""" + if foo == {i}: assert True -""" % i for i in range(1000))) +""" for i in range(1000))) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', '-n', '5', '-s', script) @@ -788,7 +788,7 @@ def test_dist_collocated(pytester, testdir, prop): script = testdir.makepyfile(prop.code) testdir.tmpdir.join('.coveragerc').write(prop.fullconf) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', '--dist=load', '--tx=2*popen', @@ -797,7 +797,7 @@ def test_dist_collocated(pytester, testdir, prop): result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'test_dist_collocated* %s *' % prop.result, + f'test_dist_collocated* {prop.result} *', '*10 passed*' ]) assert result.ret == 0 @@ -808,29 +808,29 @@ def test_dist_not_collocated(pytester, testdir, prop): script = testdir.makepyfile(prop.code) dir1 = testdir.mkdir('dir1') dir2 = testdir.mkdir('dir2') - testdir.tmpdir.join('.coveragerc').write(''' + testdir.tmpdir.join('.coveragerc').write(f''' [run] -%s +{prop.conf} [paths] source = . dir1 - dir2''' % prop.conf) + dir2''') result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', '--dist=load', - '--tx=popen//chdir=%s' % dir1, - '--tx=popen//chdir=%s' % dir2, - '--rsyncdir=%s' % script.basename, + f'--tx=popen//chdir={dir1}', + f'--tx=popen//chdir={dir2}', + f'--rsyncdir={script.basename}', '--rsyncdir=.coveragerc', max_worker_restart_0, '-s', script, *prop.args) result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'test_dist_not_collocated* %s *' % prop.result, + f'test_dist_not_collocated* {prop.result} *', '*10 passed*' ]) assert result.ret == 0 @@ -841,30 +841,30 @@ def test_dist_not_collocated_coveragerc_source(pytester, testdir, prop): script = testdir.makepyfile(prop.code) dir1 = testdir.mkdir('dir1') dir2 = testdir.mkdir('dir2') - testdir.tmpdir.join('.coveragerc').write(''' + testdir.tmpdir.join('.coveragerc').write(f''' [run] -{} -source = {} +{prop.conf} +source = {script.dirpath()} [paths] source = . dir1 - dir2'''.format(prop.conf, script.dirpath())) + dir2''') result = testdir.runpytest('-v', '--cov', '--cov-report=term-missing', '--dist=load', - '--tx=popen//chdir=%s' % dir1, - '--tx=popen//chdir=%s' % dir2, - '--rsyncdir=%s' % script.basename, + f'--tx=popen//chdir={dir1}', + f'--tx=popen//chdir={dir2}', + f'--rsyncdir={script.basename}', '--rsyncdir=.coveragerc', max_worker_restart_0, '-s', script, *prop.args) result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'test_dist_not_collocated* %s *' % prop.result, + f'test_dist_not_collocated* {prop.result} *', '*10 passed*' ]) assert result.ret == 0 @@ -876,14 +876,14 @@ def test_central_subprocess(testdir): parent_script = scripts.dirpath().join('parent_script.py') result = testdir.runpytest('-v', - '--cov=%s' % scripts.dirpath(), + f'--cov={scripts.dirpath()}', '--cov-report=term-missing', parent_script) result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'child_script* %s*' % CHILD_SCRIPT_RESULT, - 'parent_script* %s*' % PARENT_SCRIPT_RESULT, + f'child_script* {CHILD_SCRIPT_RESULT}*', + f'parent_script* {PARENT_SCRIPT_RESULT}*', ]) assert result.ret == 0 @@ -899,14 +899,14 @@ def test_central_subprocess_change_cwd(testdir): """) result = testdir.runpytest('-v', '-s', - '--cov=%s' % scripts.dirpath(), + f'--cov={scripts.dirpath()}', '--cov-config=coveragerc', '--cov-report=term-missing', parent_script) result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - '*child_script* %s*' % CHILD_SCRIPT_RESULT, + f'*child_script* {CHILD_SCRIPT_RESULT}*', '*parent_script* 100%*', ]) assert result.ret == 0 @@ -932,7 +932,7 @@ def test_central_subprocess_change_cwd_with_pythonpath(pytester, testdir, monkey result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - '*child_script* %s*' % CHILD_SCRIPT_RESULT, + f'*child_script* {CHILD_SCRIPT_RESULT}*', ]) assert result.ret == 0 @@ -950,7 +950,7 @@ def test_foo(): """) result = testdir.runpytest('-v', '--cov-config=coveragerc', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-branch', script) result.stdout.fnmatch_lines([ @@ -967,7 +967,7 @@ def test_dist_subprocess_collocated(testdir): parent_script = scripts.dirpath().join('parent_script.py') result = testdir.runpytest('-v', - '--cov=%s' % scripts.dirpath(), + f'--cov={scripts.dirpath()}', '--cov-report=term-missing', '--dist=load', '--tx=2*popen', @@ -976,8 +976,8 @@ def test_dist_subprocess_collocated(testdir): result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'child_script* %s*' % CHILD_SCRIPT_RESULT, - 'parent_script* %s*' % PARENT_SCRIPT_RESULT, + f'child_script* {CHILD_SCRIPT_RESULT}*', + f'parent_script* {PARENT_SCRIPT_RESULT}*', ]) assert result.ret == 0 @@ -991,28 +991,28 @@ def test_dist_subprocess_not_collocated(pytester, testdir, tmpdir): dir1 = tmpdir.mkdir('dir1') dir2 = tmpdir.mkdir('dir2') - testdir.tmpdir.join('.coveragerc').write(''' + testdir.tmpdir.join('.coveragerc').write(f''' [paths] source = - %s + {scripts.dirpath()} */dir1 */dir2 -''' % scripts.dirpath()) +''') result = testdir.runpytest('-v', - '--cov=%s' % scripts.dirpath(), + f'--cov={scripts.dirpath()}', '--dist=load', - '--tx=popen//chdir=%s' % dir1, - '--tx=popen//chdir=%s' % dir2, - '--rsyncdir=%s' % child_script, - '--rsyncdir=%s' % parent_script, + f'--tx=popen//chdir={dir1}', + f'--tx=popen//chdir={dir2}', + f'--rsyncdir={child_script}', + f'--rsyncdir={parent_script}', '--rsyncdir=.coveragerc', max_worker_restart_0, parent_script) result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'child_script* %s*' % CHILD_SCRIPT_RESULT, - 'parent_script* %s*' % PARENT_SCRIPT_RESULT, + f'child_script* {CHILD_SCRIPT_RESULT}*', + f'parent_script* {PARENT_SCRIPT_RESULT}*', ]) assert result.ret == 0 @@ -1062,19 +1062,19 @@ def test_dist_missing_data(testdir): exe, '-mpip', 'install', - 'py==%s' % py.__version__, - 'pytest==%s' % pytest.__version__, - 'pytest_xdist==%s' % xdist.__version__ + f'py=={py.__version__}', + f'pytest=={pytest.__version__}', + f'pytest_xdist=={xdist.__version__}' ]) script = testdir.makepyfile(SCRIPT) result = testdir.runpytest('-v', '--assert=plain', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', '--dist=load', - '--tx=popen//python=%s' % exe, + f'--tx=popen//python={exe}', max_worker_restart_0, str(script)) result.stdout.fnmatch_lines([ @@ -1086,7 +1086,7 @@ def test_funcarg(testdir): script = testdir.makepyfile(SCRIPT_FUNCARG) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', script) @@ -1144,7 +1144,7 @@ def test_run(): ''') result = testdir.runpytest('-vv', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', script) @@ -1190,13 +1190,13 @@ def test_run(): ''') result = testdir.runpytest('-vv', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', script) result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'test_cleanup_on_sigterm* %s' % setup[1], + f'test_cleanup_on_sigterm* {setup[1]}', '*1 passed*' ]) assert result.ret == 0 @@ -1235,13 +1235,13 @@ def test_run(): result = testdir.runpytest('-vv', '--assert=plain', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', script) result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'test_cleanup_on_sigterm* %s' % setup[1], + f'test_cleanup_on_sigterm* {setup[1]}', '*1 passed*' ]) assert result.ret == 0 @@ -1276,7 +1276,7 @@ def test_run(): result = testdir.runpytest('-vv', '--assert=plain', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', script) @@ -1320,7 +1320,7 @@ def test_run(): result = testdir.runpytest('-vv', '--assert=plain', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', script) @@ -1361,7 +1361,7 @@ def test_cover_conftest(testdir): testdir.makeconftest(CONFTEST) script = testdir.makepyfile(BASIC_TEST) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', script) assert result.ret == 0 @@ -1383,7 +1383,7 @@ def mock_run(*args, **kwargs): monkeypatch.setattr(testdir._pytester, 'run', mock_run) assert testdir._pytester.run is mock_run with testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--looponfail', script) as process: with dump_on_error(process.read): @@ -1400,7 +1400,7 @@ def test_cover_conftest_dist(testdir): testdir.makeconftest(CONFTEST) script = testdir.makepyfile(BASIC_TEST) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', '--dist=load', '--tx=2*popen', @@ -1424,7 +1424,7 @@ def test_basic(): subprocess.check_call([sys.executable, '-c', 'from mod import func; func()']) ''') result = testdir.runpytest('-v', '-ra', '--strict', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', script) assert result.ret == 0 @@ -1443,7 +1443,7 @@ def test_basic(no_cover): subprocess.check_call([sys.executable, '-c', 'from mod import func; func()']) ''') result = testdir.runpytest('-v', '-ra', '--strict', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', script) assert result.ret == 0 @@ -1476,11 +1476,11 @@ def test_coveragerc(testdir): script = testdir.makepyfile(EXCLUDED_TEST) result = testdir.runpytest('-v', '--cov-config=coveragerc', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', script) assert result.ret == 0 - result.stdout.fnmatch_lines(['test_coveragerc* %s' % EXCLUDED_RESULT]) + result.stdout.fnmatch_lines([f'test_coveragerc* {EXCLUDED_RESULT}']) @pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"') @@ -1489,14 +1489,14 @@ def test_coveragerc_dist(testdir): script = testdir.makepyfile(EXCLUDED_TEST) result = testdir.runpytest('-v', '--cov-config=coveragerc', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', '-n', '2', max_worker_restart_0, script) assert result.ret == 0 result.stdout.fnmatch_lines( - ['test_coveragerc_dist* %s' % EXCLUDED_RESULT]) + [f'test_coveragerc_dist* {EXCLUDED_RESULT}']) SKIP_COVERED_COVERAGERC = ''' @@ -1525,8 +1525,8 @@ def test_skip_covered_cli(pytester, testdir, report_option): testdir.makefile('', coveragerc=SKIP_COVERED_COVERAGERC) script = testdir.makepyfile(SKIP_COVERED_TEST) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), - '--cov-report=%s' % report_option, + f'--cov={script.dirpath()}', + f'--cov-report={report_option}', script) assert result.ret == 0 result.stdout.fnmatch_lines([SKIP_COVERED_RESULT]) @@ -1537,7 +1537,7 @@ def test_skip_covered_coveragerc_config(testdir): script = testdir.makepyfile(SKIP_COVERED_TEST) result = testdir.runpytest('-v', '--cov-config=coveragerc', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', script) assert result.ret == 0 result.stdout.fnmatch_lines([SKIP_COVERED_RESULT]) @@ -1556,7 +1556,7 @@ def test_basic(): def test_clear_environ(testdir): script = testdir.makepyfile(CLEAR_ENVIRON_TEST) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=term-missing', script) assert result.ret == 0 @@ -1582,13 +1582,13 @@ def test_dist_boxed(testdir): result = testdir.runpytest('-v', '--assert=plain', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--boxed', script) result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'test_dist_boxed* %s*' % SCRIPT_SIMPLE_RESULT, + f'test_dist_boxed* {SCRIPT_SIMPLE_RESULT}*', '*1 passed*' ]) assert result.ret == 0 @@ -1607,7 +1607,7 @@ def test_dist_bare_cov(testdir): result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'test_dist_bare_cov* %s*' % SCRIPT_SIMPLE_RESULT, + f'test_dist_bare_cov* {SCRIPT_SIMPLE_RESULT}*', '*1 passed*' ]) assert result.ret == 0 @@ -1627,7 +1627,7 @@ def test_default_output_setting(testdir): script = testdir.makepyfile(SCRIPT) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', script) result.stdout.fnmatch_lines([ @@ -1640,7 +1640,7 @@ def test_disabled_output(testdir): script = testdir.makepyfile(SCRIPT) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-report=', script) @@ -1657,7 +1657,7 @@ def test_coverage_file(testdir): data_file_name = 'covdata' os.environ['COVERAGE_FILE'] = data_file_name try: - result = testdir.runpytest('-v', '--cov=%s' % script.dirpath(), + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', script) assert result.ret == 0 data_file = testdir.tmpdir.join(data_file_name) @@ -1674,7 +1674,7 @@ def test_external_data_file(testdir): """ % testdir.tmpdir.join('some/special/place/coverage-data').ensure()) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', script) assert result.ret == 0 assert glob.glob(str(testdir.tmpdir.join('some/special/place/coverage-data*'))) @@ -1690,7 +1690,7 @@ def test_external_data_file_xdist(testdir): """ % testdir.tmpdir.join('some/special/place/coverage-data').ensure()) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '-n', '1', max_worker_restart_0, script) @@ -1722,7 +1722,7 @@ def test_external_data_file_negative(testdir): testdir.tmpdir.join('.coveragerc').write("") result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', script) assert result.ret == 0 assert glob.glob(str(testdir.tmpdir.join('.coverage*'))) @@ -1733,21 +1733,21 @@ def test_append_coverage(pytester, testdir, opts, prop): script = testdir.makepyfile(test_1=prop.code) testdir.tmpdir.join('.coveragerc').write(prop.fullconf) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', script, *opts.split() + prop.args) result.stdout.fnmatch_lines([ - 'test_1* %s*' % prop.result, + f'test_1* {prop.result}*', ]) script2 = testdir.makepyfile(test_2=prop.code2) result = testdir.runpytest('-v', '--cov-append', - '--cov=%s' % script2.dirpath(), + f'--cov={script2.dirpath()}', script2, *opts.split() + prop.args) result.stdout.fnmatch_lines([ - 'test_1* %s*' % prop.result, - 'test_2* %s*' % prop.result2, + f'test_1* {prop.result}*', + f'test_2* {prop.result2}*', ]) @@ -1756,20 +1756,20 @@ def test_do_not_append_coverage(pytester, testdir, opts, prop): script = testdir.makepyfile(test_1=prop.code) testdir.tmpdir.join('.coveragerc').write(prop.fullconf) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', script, *opts.split()+prop.args) result.stdout.fnmatch_lines([ - 'test_1* %s*' % prop.result, + f'test_1* {prop.result}*', ]) script2 = testdir.makepyfile(test_2=prop.code2) result = testdir.runpytest('-v', - '--cov=%s' % script2.dirpath(), + f'--cov={script2.dirpath()}', script2, *opts.split()+prop.args) result.stdout.fnmatch_lines([ 'test_1* 0%', - 'test_2* %s*' % prop.result2, + f'test_2* {prop.result2}*', ]) @@ -1780,7 +1780,7 @@ def test_append_coverage_subprocess(testdir): parent_script = scripts.dirpath().join('parent_script.py') result = testdir.runpytest('-v', - '--cov=%s' % scripts.dirpath(), + f'--cov={scripts.dirpath()}', '--cov-append', '--cov-report=term-missing', '--dist=load', @@ -1790,8 +1790,8 @@ def test_append_coverage_subprocess(testdir): result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'child_script* %s*' % CHILD_SCRIPT_RESULT, - 'parent_script* %s*' % PARENT_SCRIPT_RESULT, + f'child_script* {CHILD_SCRIPT_RESULT}*', + f'parent_script* {PARENT_SCRIPT_RESULT}*', ]) assert result.ret == 0 @@ -1825,12 +1825,12 @@ def test_double_cov(testdir): script = testdir.makepyfile(SCRIPT_SIMPLE) result = testdir.runpytest('-v', '--assert=plain', - '--cov', '--cov=%s' % script.dirpath(), + '--cov', f'--cov={script.dirpath()}', script) result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'test_double_cov* %s*' % SCRIPT_SIMPLE_RESULT, + f'test_double_cov* {SCRIPT_SIMPLE_RESULT}*', '*1 passed*' ]) assert result.ret == 0 @@ -1845,7 +1845,7 @@ def test_double_cov2(testdir): result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'test_double_cov2* %s*' % SCRIPT_SIMPLE_RESULT, + f'test_double_cov2* {SCRIPT_SIMPLE_RESULT}*', '*1 passed*' ]) assert result.ret == 0 @@ -1855,7 +1855,7 @@ def test_cov_reset(testdir): script = testdir.makepyfile(SCRIPT_SIMPLE) result = testdir.runpytest('-v', '--assert=plain', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-reset', script) @@ -1866,14 +1866,14 @@ def test_cov_reset_then_set(testdir): script = testdir.makepyfile(SCRIPT_SIMPLE) result = testdir.runpytest('-v', '--assert=plain', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-reset', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', script) result.stdout.fnmatch_lines([ '*- coverage: platform *, python * -*', - 'test_cov_reset_then_set* %s*' % SCRIPT_SIMPLE_RESULT, + f'test_cov_reset_then_set* {SCRIPT_SIMPLE_RESULT}*', '*1 passed*' ]) @@ -1943,7 +1943,7 @@ def test_contexts(pytester, testdir, opts): contextful_tests = f.read() script = testdir.makepyfile(contextful_tests) result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-context=test', script, *opts.split() @@ -1974,7 +1974,7 @@ def test_contexts(pytester, testdir, opts): def test_contexts_not_supported(testdir): script = testdir.makepyfile("a = 1") result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), + f'--cov={script.dirpath()}', '--cov-context=test', script, ) From 605d6902b3b3d17aad0bf811b8c580fc895724ca Mon Sep 17 00:00:00 2001 From: Chedi Date: Tue, 15 Nov 2022 11:18:31 +0100 Subject: [PATCH 094/117] disabling boxed test if version xdist newer than 2.5.0 --- tests/test_pytest_cov.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index dde64f8f..f23adbdc 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -1574,7 +1574,7 @@ def test_foo(): SCRIPT_SIMPLE_RESULT = '4 * 100%' -@pytest.mark.skipif('tuple(int(x) for x in xdist.__version__.split(".")) >= (2, 5, 0)', +@pytest.mark.skipif('tuple(map(int, xdist.__version__.split("."))) >= (2, 5, 0)', reason="--boxed option was removed in version 2.5.0") @pytest.mark.skipif('sys.platform == "win32"') def test_dist_boxed(testdir): From 7b095c84ae521b14058d7d520ef36372849063a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 21 May 2023 16:03:34 +0300 Subject: [PATCH 095/117] Skip starting from xdist 3.0.2 (where boxed was removed). Co-authored-by: Hugo van Kemenade --- tests/test_pytest_cov.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index f23adbdc..84f959fb 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -1574,8 +1574,8 @@ def test_foo(): SCRIPT_SIMPLE_RESULT = '4 * 100%' -@pytest.mark.skipif('tuple(map(int, xdist.__version__.split("."))) >= (2, 5, 0)', - reason="--boxed option was removed in version 2.5.0") +@pytest.mark.skipif('tuple(map(int, xdist.__version__.split("."))) >= (3, 0, 2)', + reason="--boxed option was removed in version 3.0.2") @pytest.mark.skipif('sys.platform == "win32"') def test_dist_boxed(testdir): script = testdir.makepyfile(SCRIPT_SIMPLE) From 4d245df8f75e434a9e1b162b51265d6a45017465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 24 May 2023 03:18:07 +0300 Subject: [PATCH 096/117] Update changelog and authors. --- AUTHORS.rst | 3 ++- CHANGELOG.rst | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 22ae71f5..61e7915c 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -57,4 +57,5 @@ Authors * Colin O'Dell - https://github.com/colinodell * Ronny Pfannschmidt - https://github.com/RonnyPfannschmidt * Christian Fetzer - https://github.com/fetzerch -* Jonathan Stewmon = https://github.com/jstewmon +* Jonathan Stewmon - https://github.com/jstewmon +* Matthew Gamble - https://github.com/mwgamble diff --git a/CHANGELOG.rst b/CHANGELOG.rst index efeaf34c..4d00d95e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,12 +1,21 @@ Changelog ========= - -4.0.1 (2023-03-27) +4.1.0 (2023-05-24) ------------------ -* Skip generating the in-memory coverage report when it will not be used. For example, - when ``--cov-report=''`` is used without ``--cov-fail-under``. +* Updated CI with new Pythons and dependencies. +* Removed rsyncdir support. This makes pytest-cov compatible with xdist 3.0. + Contributed by Sorin Sbarnea in `#558 `_. +* Optimized summary generation to not be performed if no reporting is active (for example, + when ``--cov-report=''`` is used without ``--cov-fail-under``). + Contributed by Jonathan Stewmon in `#589 `_. +* Added support for JSON reporting. + Contributed by Matthew Gamble in `#582 `_. +* Refactored code to use f-strings. + Contributed by Mark Mayo in `#572 `_. +* Fixed a skip in the test suite for some old xdist. + Contributed by a bunch of people in `#565 `_. 4.0.0 (2022-09-28) From 2c9f2170d8575b21bafb6402eb30ca7de31e20b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 24 May 2023 21:28:10 +0300 Subject: [PATCH 097/117] =?UTF-8?q?Bump=20version:=204.0.0=20=E2=86=92=204?= =?UTF-8?q?.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- README.rst | 4 ++-- docs/conf.py | 2 +- setup.py | 2 +- src/pytest_cov/__init__.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 09bfcde0..38f20c66 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 4.0.0 +current_version = 4.1.0 commit = True tag = True diff --git a/README.rst b/README.rst index 84a85786..9a78ffd6 100644 --- a/README.rst +++ b/README.rst @@ -39,9 +39,9 @@ Overview .. |conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pytest-cov.svg :target: https://anaconda.org/conda-forge/pytest-cov -.. |commits-since| image:: https://img.shields.io/github/commits-since/pytest-dev/pytest-cov/v4.0.0.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/pytest-dev/pytest-cov/v4.1.0.svg :alt: Commits since latest release - :target: https://github.com/pytest-dev/pytest-cov/compare/v4.0.0...master + :target: https://github.com/pytest-dev/pytest-cov/compare/v4.1.0...master .. |wheel| image:: https://img.shields.io/pypi/wheel/pytest-cov.svg :alt: PyPI Wheel diff --git a/docs/conf.py b/docs/conf.py index d417ed00..e897c5e9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,7 +23,7 @@ year = '2016' author = 'pytest-cov contributors' copyright = f'{year}, {author}' -version = release = '4.0.0' +version = release = '4.1.0' pygments_style = 'trac' templates_path = ['.'] diff --git a/setup.py b/setup.py index e2cafde7..70224965 100755 --- a/setup.py +++ b/setup.py @@ -86,7 +86,7 @@ def run(self): setup( name='pytest-cov', - version='4.0.0', + version='4.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'))), diff --git a/src/pytest_cov/__init__.py b/src/pytest_cov/__init__.py index 9dfd9823..8839990b 100644 --- a/src/pytest_cov/__init__.py +++ b/src/pytest_cov/__init__.py @@ -1,2 +1,2 @@ """pytest-cov: avoid already-imported warning: PYTEST_DONT_REWRITE.""" -__version__ = '4.0.0' +__version__ = '4.1.0' From a7c5c1a57566858a607048dc0026b0a3bd544a49 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Thu, 7 Mar 2024 20:43:17 +0100 Subject: [PATCH 098/117] Upgrade docs dependency Sphinx because tests fail https://github.com/pytest-dev/pytest-cov/actions/runs/8193686290/job/22407988436 demonstrates the current test failures. --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 6fdf26f9..c944ad62 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,4 @@ -sphinx==3.0.3 +sphinx==7.2.6 sphinx-py3doc-enhanced-theme==2.4.0 docutils==0.16 jinja2<3.1 From eb830859e40226d59c43a17d0d6ca03dac977842 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Thu, 7 Mar 2024 20:46:38 +0100 Subject: [PATCH 099/117] Update requirements.txt --- docs/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index c944ad62..22428fbb 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,5 @@ sphinx==7.2.6 -sphinx-py3doc-enhanced-theme==2.4.0 -docutils==0.16 +sphinx-py3doc-enhanced-theme +docutils jinja2<3.1 -e . From c98ada9dce0488d68e5113f33766a7165c6918a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 17 Mar 2024 16:30:10 +0200 Subject: [PATCH 100/117] Update skel, and bump min python to 3.8, add 3.12 in the test grid. Closes #610. --- .bumpversion.cfg | 4 + .cookiecutterrc | 54 ++++------ .github/workflows/test.yml | 136 ++++++++++++------------ .pre-commit-config.yaml | 32 +++--- .readthedocs.yml | 5 +- AUTHORS.rst | 1 + CHANGELOG.rst | 1 + CONTRIBUTING.rst | 10 +- MANIFEST.in | 7 +- README.rst | 26 ++--- ci/bootstrap.py | 78 +++++++------- ci/requirements.txt | 1 + ci/templates/.github/workflows/test.yml | 8 +- docs/conf.py | 11 +- pyproject.toml | 58 ++++++++++ pytest.ini | 29 +++++ setup.cfg | 19 ---- setup.py | 12 +-- tox.ini | 80 +++++++++----- 19 files changed, 318 insertions(+), 254 deletions(-) create mode 100644 pyproject.toml create mode 100644 pytest.ini delete mode 100644 setup.cfg diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 38f20c66..3260d906 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -22,3 +22,7 @@ replace = version = release = '{new_version}' [bumpversion:file:src/pytest_cov/__init__.py] search = __version__ = '{current_version}' replace = __version__ = '{new_version}' + +[bumpversion:file:.cookiecutterrc] +search = version: {current_version} +replace = version: {new_version} diff --git a/.cookiecutterrc b/.cookiecutterrc index 49e9880e..3c998545 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -1,55 +1,47 @@ # Generated by cookiepatcher, a small shim around cookiecutter (pip install cookiepatcher) default_context: - allow_tests_inside_package: no - appveyor: no + allow_tests_inside_package: 'no' c_extension_function: '-' c_extension_module: '-' - c_extension_optional: no - c_extension_support: no - c_extension_test_pypi: no - c_extension_test_pypi_username: '-' - codacy: no + c_extension_optional: 'no' + c_extension_support: 'no' + codacy: 'no' codacy_projectid: '[Get ID from https://app.codacy.com/app/ionelmc/pytest-cov/settings]' - codeclimate: no - codecov: no - command_line_interface: no + codeclimate: 'no' + codecov: 'no' + command_line_interface: 'no' command_line_interface_bin_name: '-' - coveralls: no + coveralls: 'no' distribution_name: pytest-cov email: contact@ionelmc.ro + formatter_quote_style: single full_name: Ionel Cristian Mărieș - github_actions: yes - legacy_python: yes + github_actions: 'yes' + github_actions_osx: 'yes' + github_actions_windows: 'yes' license: MIT license - linter: flake8 package_name: pytest_cov - pre_commit: yes + pre_commit: 'yes' project_name: pytest-cov project_short_description: This plugin produces coverage reports. It supports centralised testing and distributed testing in both load and each modes. It also supports coverage of subprocesses. - pypi_badge: yes - pypi_disable_upload: no - release_date: '2021-10-04' + pypi_badge: 'yes' + pypi_disable_upload: 'no' + release_date: '2023-05-24' repo_hosting: github.com repo_hosting_domain: github.com repo_main_branch: master repo_name: pytest-cov repo_username: pytest-dev - requiresio: yes - scrutinizer: no - setup_py_uses_setuptools_scm: no - setup_py_uses_test_runner: no - sphinx_docs: yes + scrutinizer: 'no' + setup_py_uses_setuptools_scm: 'no' + sphinx_docs: 'yes' sphinx_docs_hosting: https://pytest-cov.readthedocs.io/ - sphinx_doctest: no + sphinx_doctest: 'no' sphinx_theme: sphinx-py3doc-enhanced-theme - test_matrix_configurator: no - test_matrix_separate_coverage: no - test_runner: pytest - travis: no - travis_osx: no - version: 3.0.0 + test_matrix_separate_coverage: 'no' + version: 4.1.0 version_manager: bump2version website: http://blog.ionelmc.ro year_from: '2010' - year_to: '2022' + year_to: '2024' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7fade785..4b537c22 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -60,155 +60,155 @@ jobs: toxpython: 'python3.11' tox_env: 'docs' os: 'ubuntu-latest' - - name: 'py37-pytest73-xdist330-coverage72 (ubuntu)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest73-xdist330-coverage72' - os: 'ubuntu-latest' - - name: 'py37-pytest73-xdist330-coverage72 (windows)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest73-xdist330-coverage72' - os: 'windows-latest' - - name: 'py37-pytest73-xdist330-coverage72 (macos)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-pytest73-xdist330-coverage72' - os: 'macos-latest' - - name: 'py38-pytest73-xdist330-coverage72 (ubuntu)' + - name: 'py38-pytest81-xdist350-coverage74 (ubuntu)' python: '3.8' toxpython: 'python3.8' python_arch: 'x64' - tox_env: 'py38-pytest73-xdist330-coverage72' + tox_env: 'py38-pytest81-xdist350-coverage74' os: 'ubuntu-latest' - - name: 'py38-pytest73-xdist330-coverage72 (windows)' + - name: 'py38-pytest81-xdist350-coverage74 (windows)' python: '3.8' toxpython: 'python3.8' python_arch: 'x64' - tox_env: 'py38-pytest73-xdist330-coverage72' + tox_env: 'py38-pytest81-xdist350-coverage74' os: 'windows-latest' - - name: 'py38-pytest73-xdist330-coverage72 (macos)' + - name: 'py38-pytest81-xdist350-coverage74 (macos)' python: '3.8' toxpython: 'python3.8' python_arch: 'x64' - tox_env: 'py38-pytest73-xdist330-coverage72' + tox_env: 'py38-pytest81-xdist350-coverage74' os: 'macos-latest' - - name: 'py39-pytest73-xdist330-coverage72 (ubuntu)' + - name: 'py39-pytest81-xdist350-coverage74 (ubuntu)' python: '3.9' toxpython: 'python3.9' python_arch: 'x64' - tox_env: 'py39-pytest73-xdist330-coverage72' + tox_env: 'py39-pytest81-xdist350-coverage74' os: 'ubuntu-latest' - - name: 'py39-pytest73-xdist330-coverage72 (windows)' + - name: 'py39-pytest81-xdist350-coverage74 (windows)' python: '3.9' toxpython: 'python3.9' python_arch: 'x64' - tox_env: 'py39-pytest73-xdist330-coverage72' + tox_env: 'py39-pytest81-xdist350-coverage74' os: 'windows-latest' - - name: 'py39-pytest73-xdist330-coverage72 (macos)' + - name: 'py39-pytest81-xdist350-coverage74 (macos)' python: '3.9' toxpython: 'python3.9' python_arch: 'x64' - tox_env: 'py39-pytest73-xdist330-coverage72' + tox_env: 'py39-pytest81-xdist350-coverage74' os: 'macos-latest' - - name: 'py310-pytest73-xdist330-coverage72 (ubuntu)' + - name: 'py310-pytest81-xdist350-coverage74 (ubuntu)' python: '3.10' toxpython: 'python3.10' python_arch: 'x64' - tox_env: 'py310-pytest73-xdist330-coverage72' + tox_env: 'py310-pytest81-xdist350-coverage74' os: 'ubuntu-latest' - - name: 'py310-pytest73-xdist330-coverage72 (windows)' + - name: 'py310-pytest81-xdist350-coverage74 (windows)' python: '3.10' toxpython: 'python3.10' python_arch: 'x64' - tox_env: 'py310-pytest73-xdist330-coverage72' + tox_env: 'py310-pytest81-xdist350-coverage74' os: 'windows-latest' - - name: 'py310-pytest73-xdist330-coverage72 (macos)' + - name: 'py310-pytest81-xdist350-coverage74 (macos)' python: '3.10' toxpython: 'python3.10' python_arch: 'x64' - tox_env: 'py310-pytest73-xdist330-coverage72' + tox_env: 'py310-pytest81-xdist350-coverage74' os: 'macos-latest' - - name: 'py311-pytest73-xdist330-coverage72 (ubuntu)' + - name: 'py311-pytest81-xdist350-coverage74 (ubuntu)' python: '3.11' toxpython: 'python3.11' python_arch: 'x64' - tox_env: 'py311-pytest73-xdist330-coverage72' + tox_env: 'py311-pytest81-xdist350-coverage74' os: 'ubuntu-latest' - - name: 'py311-pytest73-xdist330-coverage72 (windows)' + - name: 'py311-pytest81-xdist350-coverage74 (windows)' python: '3.11' toxpython: 'python3.11' python_arch: 'x64' - tox_env: 'py311-pytest73-xdist330-coverage72' + tox_env: 'py311-pytest81-xdist350-coverage74' os: 'windows-latest' - - name: 'py311-pytest73-xdist330-coverage72 (macos)' + - name: 'py311-pytest81-xdist350-coverage74 (macos)' python: '3.11' toxpython: 'python3.11' python_arch: 'x64' - tox_env: 'py311-pytest73-xdist330-coverage72' + tox_env: 'py311-pytest81-xdist350-coverage74' os: 'macos-latest' - - name: 'pypy37-pytest73-xdist330-coverage72 (ubuntu)' - python: 'pypy-3.7' - toxpython: 'pypy3.7' + - name: 'py312-pytest81-xdist350-coverage74 (ubuntu)' + python: '3.12' + toxpython: 'python3.12' python_arch: 'x64' - tox_env: 'pypy37-pytest73-xdist330-coverage72' + tox_env: 'py312-pytest81-xdist350-coverage74' os: 'ubuntu-latest' - - name: 'pypy37-pytest73-xdist330-coverage72 (windows)' - python: 'pypy-3.7' - toxpython: 'pypy3.7' + - name: 'py312-pytest81-xdist350-coverage74 (windows)' + python: '3.12' + toxpython: 'python3.12' python_arch: 'x64' - tox_env: 'pypy37-pytest73-xdist330-coverage72' + tox_env: 'py312-pytest81-xdist350-coverage74' os: 'windows-latest' - - name: 'pypy37-pytest73-xdist330-coverage72 (macos)' - python: 'pypy-3.7' - toxpython: 'pypy3.7' + - name: 'py312-pytest81-xdist350-coverage74 (macos)' + python: '3.12' + toxpython: 'python3.12' python_arch: 'x64' - tox_env: 'pypy37-pytest73-xdist330-coverage72' + tox_env: 'py312-pytest81-xdist350-coverage74' os: 'macos-latest' - - name: 'pypy38-pytest73-xdist330-coverage72 (ubuntu)' + - name: 'pypy38-pytest81-xdist350-coverage74 (ubuntu)' python: 'pypy-3.8' toxpython: 'pypy3.8' python_arch: 'x64' - tox_env: 'pypy38-pytest73-xdist330-coverage72' + tox_env: 'pypy38-pytest81-xdist350-coverage74' os: 'ubuntu-latest' - - name: 'pypy38-pytest73-xdist330-coverage72 (windows)' + - name: 'pypy38-pytest81-xdist350-coverage74 (windows)' python: 'pypy-3.8' toxpython: 'pypy3.8' python_arch: 'x64' - tox_env: 'pypy38-pytest73-xdist330-coverage72' + tox_env: 'pypy38-pytest81-xdist350-coverage74' os: 'windows-latest' - - name: 'pypy38-pytest73-xdist330-coverage72 (macos)' + - name: 'pypy38-pytest81-xdist350-coverage74 (macos)' python: 'pypy-3.8' toxpython: 'pypy3.8' python_arch: 'x64' - tox_env: 'pypy38-pytest73-xdist330-coverage72' + tox_env: 'pypy38-pytest81-xdist350-coverage74' os: 'macos-latest' - - name: 'pypy39-pytest73-xdist330-coverage72 (ubuntu)' + - name: 'pypy39-pytest81-xdist350-coverage74 (ubuntu)' python: 'pypy-3.9' toxpython: 'pypy3.9' python_arch: 'x64' - tox_env: 'pypy39-pytest73-xdist330-coverage72' + tox_env: 'pypy39-pytest81-xdist350-coverage74' os: 'ubuntu-latest' - - name: 'pypy39-pytest73-xdist330-coverage72 (windows)' + - name: 'pypy39-pytest81-xdist350-coverage74 (windows)' python: 'pypy-3.9' toxpython: 'pypy3.9' python_arch: 'x64' - tox_env: 'pypy39-pytest73-xdist330-coverage72' + tox_env: 'pypy39-pytest81-xdist350-coverage74' os: 'windows-latest' - - name: 'pypy39-pytest73-xdist330-coverage72 (macos)' + - name: 'pypy39-pytest81-xdist350-coverage74 (macos)' python: 'pypy-3.9' toxpython: 'pypy3.9' python_arch: 'x64' - tox_env: 'pypy39-pytest73-xdist330-coverage72' + tox_env: 'pypy39-pytest81-xdist350-coverage74' + os: 'macos-latest' + - name: 'pypy310-pytest81-xdist350-coverage74 (ubuntu)' + python: 'pypy-3.10' + toxpython: 'pypy3.10' + python_arch: 'x64' + tox_env: 'pypy310-pytest81-xdist350-coverage74' + os: 'ubuntu-latest' + - name: 'pypy310-pytest81-xdist350-coverage74 (windows)' + python: 'pypy-3.10' + toxpython: 'pypy3.10' + python_arch: 'x64' + tox_env: 'pypy310-pytest81-xdist350-coverage74' + os: 'windows-latest' + - name: 'pypy310-pytest81-xdist350-coverage74 (macos)' + python: 'pypy-3.10' + toxpython: 'pypy3.10' + python_arch: 'x64' + tox_env: 'pypy310-pytest81-xdist350-coverage74' os: 'macos-latest' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} architecture: ${{ matrix.python_arch }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1b105884..c45f47f9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,25 +1,23 @@ -# To install the git pre-commit hook run: -# pre-commit install -# To update the pre-commit hooks run: +# To install the git pre-commit hooks run: +# pre-commit install --install-hooks +# To update the versions: # pre-commit autoupdate +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.3.3 + hooks: + - id: ruff + args: [--fix, --exit-non-zero-on-fix, --show-fixes] + - repo: https://github.com/psf/black + rev: 24.3.0 + hooks: + - id: black - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer exclude: '.*\.pth$' - id: debug-statements - - repo: https://github.com/PyCQA/isort - rev: 5.12.0 - hooks: - - id: isort - - repo: https://github.com/asottile/pyupgrade - rev: v3.3.1 - hooks: - - id: pyupgrade - args: [--py37-plus] - - repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 - hooks: - - id: flake8 diff --git a/.readthedocs.yml b/.readthedocs.yml index ac76971c..009a913c 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -3,8 +3,11 @@ version: 2 sphinx: configuration: docs/conf.py formats: all +build: + os: ubuntu-22.04 + tools: + python: "3" python: - version: 3 install: - requirements: docs/requirements.txt - method: pip diff --git a/AUTHORS.rst b/AUTHORS.rst index 61e7915c..fc608064 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -1,3 +1,4 @@ + Authors ======= diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4d00d95e..21ba765a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,4 @@ + Changelog ========= diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index e742be47..8e19ab39 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -49,7 +49,7 @@ To set up `pytest-cov` for local development: Now you can make your changes locally. -4. When you're done making changes run all the checks and docs builder with `tox `_ one command:: +4. When you're done making changes run all the checks and docs builder with one command:: tox @@ -68,17 +68,11 @@ If you need some code review or feedback while you're developing the code just m For merging, you should: -1. Include passing tests (run ``tox``) [1]_. +1. Include passing tests (run ``tox``). 2. Update documentation when there's new API, functionality etc. 3. Add a note to ``CHANGELOG.rst`` about the changes. 4. Add yourself to ``AUTHORS.rst``. -.. [1] If you don't have all the necessary Python versions available locally you can rely on GitHub Actions - it will - `run the tests `_ - for each change you add in the pull request. - - It will be slower though ... - Tips ---- diff --git a/MANIFEST.in b/MANIFEST.in index cbb88f74..22042a59 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -15,14 +15,15 @@ include .bumpversion.cfg include .cookiecutterrc include .coveragerc include .editorconfig -include tox.ini -include .readthedocs.yml include .pre-commit-config.yaml +include .readthedocs.yml +include pytest.ini +include tox.ini + include AUTHORS.rst include CHANGELOG.rst include CONTRIBUTING.rst include LICENSE include README.rst - global-exclude *.py[cod] __pycache__/* *.so *.dylib diff --git a/README.rst b/README.rst index 9a78ffd6..0718510b 100644 --- a/README.rst +++ b/README.rst @@ -10,39 +10,23 @@ Overview * - docs - |docs| * - tests - - | |github-actions| |requires| - | + - |github-actions| * - package - - | |version| |conda-forge| |wheel| |supported-versions| |supported-implementations| - | |commits-since| - + - |version| |conda-forge| |wheel| |supported-versions| |supported-implementations| |commits-since| .. |docs| image:: https://readthedocs.org/projects/pytest-cov/badge/?style=flat - :target: https://readthedocs.org/projects/pytest-cov + :target: https://readthedocs.org/projects/pytest-cov/ :alt: Documentation Status .. |github-actions| image:: https://github.com/pytest-dev/pytest-cov/actions/workflows/test.yml/badge.svg :alt: GitHub Actions Status :target: https://github.com/pytest-dev/pytest-cov/actions -.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/github/pytest-dev/pytest-cov?branch=master&svg=true - :alt: AppVeyor Build Status - :target: https://ci.appveyor.com/project/pytestbot/pytest-cov - -.. |requires| image:: https://requires.io/github/pytest-dev/pytest-cov/requirements.svg?branch=master - :alt: Requirements Status - :target: https://requires.io/github/pytest-dev/pytest-cov/requirements/?branch=master - .. |version| image:: https://img.shields.io/pypi/v/pytest-cov.svg :alt: PyPI Package latest release :target: https://pypi.org/project/pytest-cov .. |conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pytest-cov.svg :target: https://anaconda.org/conda-forge/pytest-cov - -.. |commits-since| image:: https://img.shields.io/github/commits-since/pytest-dev/pytest-cov/v4.1.0.svg - :alt: Commits since latest release - :target: https://github.com/pytest-dev/pytest-cov/compare/v4.1.0...master - .. |wheel| image:: https://img.shields.io/pypi/wheel/pytest-cov.svg :alt: PyPI Wheel :target: https://pypi.org/project/pytest-cov @@ -55,6 +39,10 @@ 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/v4.1.0.svg + :alt: Commits since latest release + :target: https://github.com/pytest-dev/pytest-cov/compare/v4.1.0...master + .. end-badges This plugin produces coverage reports. Compared to just using ``coverage run`` this plugin does some extras: diff --git a/ci/bootstrap.py b/ci/bootstrap.py index 48c46640..f3c9a7ea 100755 --- a/ci/bootstrap.py +++ b/ci/bootstrap.py @@ -1,63 +1,57 @@ #!/usr/bin/env python - import os +import pathlib import subprocess import sys -from os.path import abspath -from os.path import dirname -from os.path import exists -from os.path import join -from os.path import relpath -base_path = dirname(dirname(abspath(__file__))) -templates_path = join(base_path, "ci", "templates") +base_path: pathlib.Path = pathlib.Path(__file__).resolve().parent.parent +templates_path = base_path / 'ci' / 'templates' def check_call(args): - print("+", *args) + print('+', *args) subprocess.check_call(args) def exec_in_env(): - env_path = join(base_path, ".tox", "bootstrap") - if sys.platform == "win32": - bin_path = join(env_path, "Scripts") + env_path = base_path / '.tox' / 'bootstrap' + if sys.platform == 'win32': + bin_path = env_path / 'Scripts' else: - bin_path = join(env_path, "bin") - if not exists(env_path): + bin_path = env_path / 'bin' + if not env_path.exists(): import subprocess - print(f"Making bootstrap env in: {env_path} ...") + print(f'Making bootstrap env in: {env_path} ...') try: - check_call([sys.executable, "-m", "venv", env_path]) + check_call([sys.executable, '-m', 'venv', env_path]) except subprocess.CalledProcessError: try: - check_call([sys.executable, "-m", "virtualenv", env_path]) + check_call([sys.executable, '-m', 'virtualenv', env_path]) except subprocess.CalledProcessError: - check_call(["virtualenv", env_path]) - print("Installing `jinja2` into bootstrap environment...") - check_call([join(bin_path, "pip"), "install", "jinja2", "tox"]) - python_executable = join(bin_path, "python") - if not os.path.exists(python_executable): - python_executable += '.exe' + check_call(['virtualenv', env_path]) + print('Installing `jinja2` into bootstrap environment...') + check_call([bin_path / 'pip', 'install', 'jinja2', 'tox']) + python_executable = bin_path / 'python' + if not python_executable.exists(): + python_executable = python_executable.with_suffix('.exe') - print(f"Re-executing with: {python_executable}") - print("+ exec", python_executable, __file__, "--no-env") - os.execv(python_executable, [python_executable, __file__, "--no-env"]) + print(f'Re-executing with: {python_executable}') + print('+ exec', python_executable, __file__, '--no-env') + os.execv(python_executable, [python_executable, __file__, '--no-env']) def main(): import jinja2 - print(f"Project path: {base_path}") + print(f'Project path: {base_path}') jinja = jinja2.Environment( - loader=jinja2.FileSystemLoader(templates_path), + loader=jinja2.FileSystemLoader(str(templates_path)), trim_blocks=True, lstrip_blocks=True, - keep_trailing_newline=True + keep_trailing_newline=True, ) - tox_environments = [ line.strip() # 'tox' need not be installed globally, but must be importable @@ -65,25 +59,25 @@ def main(): # This uses sys.executable the same way that the call in # cookiecutter-pylibrary/hooks/post_gen_project.py # invokes this bootstrap.py itself. - for line in subprocess.check_output([sys.executable, '-m', 'tox', '--listenvs'], text=True).splitlines() + for line in subprocess.check_output([sys.executable, '-m', 'tox', '--listenvs'], universal_newlines=True).splitlines() ] tox_environments = [line for line in tox_environments if line.startswith('py')] - - for root, _, files in os.walk(templates_path): - for name in files: - relative = relpath(root, templates_path) - with open(join(base_path, relative, name), "w") as fh: - fh.write(jinja.get_template(join(relative, name)).render(tox_environments=tox_environments)) - print(f"Wrote {name}") - print("DONE.") + for template in templates_path.rglob('*'): + if template.is_file(): + template_path = template.relative_to(templates_path).as_posix() + destination = base_path / template_path + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_text(jinja.get_template(template_path).render(tox_environments=tox_environments)) + print(f'Wrote {template_path}') + print('DONE.') -if __name__ == "__main__": +if __name__ == '__main__': args = sys.argv[1:] - if args == ["--no-env"]: + if args == ['--no-env']: main() elif not args: exec_in_env() else: - print(f"Unexpected arguments {args}", file=sys.stderr) + print(f'Unexpected arguments: {args}', file=sys.stderr) sys.exit(1) diff --git a/ci/requirements.txt b/ci/requirements.txt index a0ef106f..a1708f40 100644 --- a/ci/requirements.txt +++ b/ci/requirements.txt @@ -3,3 +3,4 @@ pip>=19.1.1 setuptools>=18.0.1 six>=1.14.0 tox +twine diff --git a/ci/templates/.github/workflows/test.yml b/ci/templates/.github/workflows/test.yml index 6aaf1dbc..39e64078 100644 --- a/ci/templates/.github/workflows/test.yml +++ b/ci/templates/.github/workflows/test.yml @@ -66,9 +66,9 @@ jobs: {% for env in tox_environments %} {% set prefix = env.split('-')[0] -%} {% if prefix.startswith('pypy') %} -{% set python %}pypy-{{ prefix[4] }}.{{ prefix[5] }}{% endset %} +{% set python %}pypy-{{ prefix[4] }}.{{ prefix[5:] }}{% endset %} {% set cpython %}pp{{ prefix[4:5] }}{% endset %} -{% set toxpython %}pypy{{ prefix[4] }}.{{ prefix[5] }}{% endset %} +{% set toxpython %}pypy{{ prefix[4] }}.{{ prefix[5:] }}{% endset %} {% else %} {% set python %}{{ prefix[2] }}.{{ prefix[3:] }}{% endset %} {% set cpython %}cp{{ prefix[2:] }}{% endset %} @@ -88,10 +88,10 @@ jobs: {% endfor %} {% endfor %} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: {{ '${{ matrix.python }}' }} architecture: {{ '${{ matrix.python_arch }}' }} diff --git a/docs/conf.py b/docs/conf.py index e897c5e9..20757bb2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,7 +20,7 @@ source_suffix = '.rst' master_doc = 'index' project = 'pytest-cov' -year = '2016' +year = '2010-2024' author = 'pytest-cov contributors' copyright = f'{year}, {author}' version = release = '4.1.0' @@ -31,18 +31,17 @@ 'issue': ('https://github.com/pytest-dev/pytest-cov/issues/%s', '#'), 'pr': ('https://github.com/pytest-dev/pytest-cov/pull/%s', 'PR #'), } - -html_theme = "sphinx_py3doc_enhanced_theme" +html_theme = 'sphinx_py3doc_enhanced_theme' html_theme_path = [sphinx_py3doc_enhanced_theme.get_html_theme_path()] html_theme_options = { - 'githuburl': 'https://github.com/pytest-dev/pytest-cov/' + 'githuburl': 'https://github.com/pytest-dev/pytest-cov/', } html_use_smartypants = True html_last_updated_fmt = '%b %d, %Y' -html_split_index = True +html_split_index = False html_sidebars = { - '**': ['searchbox.html', 'globaltoc.html', 'sourcelink.html'], + '**': ['searchbox.html', 'globaltoc.html', 'sourcelink.html'], } html_short_title = f'{project}-{version}' diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..61be7748 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,58 @@ +[build-system] +requires = [ + "setuptools>=30.3.0", +] + +[tool.ruff] +extend-exclude = ["static", "ci/templates"] +line-length = 140 +src = ["src", "tests"] +target-version = "py38" + +[tool.ruff.lint.per-file-ignores] +"ci/*" = ["S"] + +[tool.ruff.lint] +ignore = [ + "RUF001", # ruff-specific rules ambiguous-unicode-character-string + "S101", # flake8-bandit assert + "S308", # flake8-bandit suspicious-mark-safe-usage + "E501", # pycodestyle line-too-long +] +select = [ + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "DTZ", # flake8-datetimez + "E", # pycodestyle errors + "EXE", # flake8-executable + "F", # pyflakes + "I", # isort + "INT", # flake8-gettext + "PIE", # flake8-pie + "PLC", # pylint convention + "PLE", # pylint errors + "PT", # flake8-pytest-style + "PTH", # flake8-use-pathlib + "Q", # flake8-quotes + "RSE", # flake8-raise + "RUF", # ruff-specific rules + "S", # flake8-bandit + "UP", # pyupgrade + "W", # pycodestyle warnings +] + +[tool.ruff.lint.flake8-pytest-style] +fixture-parentheses = false +mark-parentheses = false + +[tool.ruff.lint.flake8-quotes] +inline-quotes = "single" + +[tool.ruff.lint.isort] +forced-separate = ["conftest"] +force-single-line = true + +[tool.black] +line-length = 140 +target-version = ["py38"] +skip-string-normalization = true diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..0f32c842 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,29 @@ +[pytest] +# If a pytest section is found in one of the possible config files +# (pytest.ini, tox.ini or setup.cfg), then pytest will not look for any others, +# so if you add a pytest config section elsewhere, +# you will need to delete this section from setup.cfg. +norecursedirs = + migrations + +python_files = + test_*.py + *_test.py + tests.py +addopts = + -ra + --strict-markers + --doctest-modules + --doctest-glob=\*.rst + --tb=short + -p pytester +testpaths = + tests + +# Idea from: https://til.simonwillison.net/pytest/treat-warnings-as-errors +filterwarnings = + error +# You can add exclusions, some examples: +# ignore:'pytest_cov' defines default_app_config:PendingDeprecationWarning:: +# ignore:The {{% if::: +# ignore:Coverage disabled via --no-cov switch! diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index c33a9042..00000000 --- a/setup.cfg +++ /dev/null @@ -1,19 +0,0 @@ -[flake8] -max-line-length = 140 -exclude = .tox,.eggs,ci/templates,build,dist - -[tool:pytest] -testpaths = tests -python_files = test_*.py -addopts = - -ra - --strict-markers - -p pytester - -[tool:isort] -force_single_line = True -line_length = 120 -known_first_party = pytest_cov -default_section = THIRDPARTY -forced_separate = test_pytest_cov -skip = .tox,.eggs,ci/templates,build,dist diff --git a/setup.py b/setup.py index 70224965..d6dd2d31 100755 --- a/setup.py +++ b/setup.py @@ -1,6 +1,7 @@ #!/usr/bin/env python import re +from pathlib import Path from glob import glob from itertools import chain from os.path import basename @@ -24,10 +25,7 @@ def read(*names, **kwargs): - with open( - join(dirname(__file__), *names), - encoding=kwargs.get('encoding', 'utf8') - ) as fh: + with Path(__file__).parent.joinpath(*names).open(encoding=kwargs.get('encoding', 'utf8')) as fh: return fh.read() @@ -95,7 +93,7 @@ def run(self): url='https://github.com/pytest-dev/pytest-cov', packages=find_packages('src'), package_dir={'': 'src'}, - py_modules=[splitext(basename(path))[0] for path in glob('src/*.py')], + py_modules=[path.stem for path in Path('src').glob('*.py')], include_package_data=True, zip_safe=False, classifiers=[ @@ -110,11 +108,11 @@ def run(self): 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Software Development :: Testing', @@ -128,11 +126,11 @@ def run(self): keywords=[ 'cover', 'coverage', 'pytest', 'py.test', 'distributed', 'parallel', ], + python_requires='>=3.8', install_requires=[ 'pytest>=4.6', 'coverage[toml]>=5.2.1' ], - python_requires='>=3.7', extras_require={ 'testing': [ 'fields', diff --git a/tox.ini b/tox.ini index 7d49a3be..4cbe6c8a 100644 --- a/tox.ini +++ b/tox.ini @@ -7,17 +7,31 @@ commands = python ci/bootstrap.py --no-env passenv = * -; a generative tox configuration, see: https://tox.readthedocs.io/en/latest/config.html#generative-envlist +; a generative tox configuration, see: https://tox.wiki/en/latest/user_guide.html#generative-environments [tox] envlist = - check - py{37,38,39,310,311,py37,py38,py39}-pytest{73}-xdist330-coverage{72} - docs + clean, + check, + docs, + {py38,py39,py310,py311,py312,pypy38,pypy39,pypy310}-pytest{81}-xdist{350}-coverage{74}, + report +ignore_basepython_conflict = true [testenv] +basepython = + pypy38: {env:TOXPYTHON:pypy3.8} + pypy39: {env:TOXPYTHON:pypy3.9} + pypy310: {env:TOXPYTHON:pypy3.10} + py38: {env:TOXPYTHON:python3.8} + py39: {env:TOXPYTHON:python3.9} + py310: {env:TOXPYTHON:python3.10} + py311: {env:TOXPYTHON:python3.11} + py312: {env:TOXPYTHON:python3.12} + {bootstrap,clean,check,report,docs}: {env:TOXPYTHON:python3} extras = testing setenv = + PYTHONPATH={toxinidir}/tests PYTHONUNBUFFERED=yes # Use env vars for (optional) pinning of deps. @@ -30,7 +44,10 @@ setenv = pytest70: _DEP_PYTEST=pytest==7.0.1 pytest71: _DEP_PYTEST=pytest==7.1.2 pytest72: _DEP_PYTEST=pytest==7.2.0 - pytest73: _DEP_PYTEST=pytest==7.3.1 + pytest73: _DEP_PYTEST=pytest==7.3.3 + pytest74: _DEP_PYTEST=pytest==7.4.4 + pytest80: _DEP_PYTEST=pytest==8.0.2 + pytest81: _DEP_PYTEST=pytest==8.1.1 xdist127: _DEP_PYTESTXDIST=pytest-xdist==1.27.0 xdist129: _DEP_PYTESTXDIST=pytest-xdist==1.29.0 @@ -44,6 +61,8 @@ setenv = xdist250: _DEP_PYTESTXDIST=pytest-xdist==2.5.0 xdist320: _DEP_PYTESTXDIST=pytest-xdist==3.2.0 xdist330: _DEP_PYTESTXDIST=pytest-xdist==3.3.1 + xdist340: _DEP_PYTESTXDIST=pytest-xdist==3.4.0 + xdist350: _DEP_PYTESTXDIST=pytest-xdist==3.5.0 xdistdev: _DEP_PYTESTXDIST=git+https://github.com/pytest-dev/pytest-xdist.git#egg=pytest-xdist coverage45: _DEP_COVERAGE=coverage==4.5.4 @@ -59,7 +78,9 @@ setenv = coverage63: _DEP_COVERAGE=coverage==6.3.3 coverage64: _DEP_COVERAGE=coverage==6.4.2 coverage65: _DEP_COVERAGE=coverage==6.5.0 - coverage72: _DEP_COVERAGE=coverage==7.2.5 + coverage72: _DEP_COVERAGE=coverage==7.2.7 + coverage73: _DEP_COVERAGE=coverage==7.3.4 + coverage74: _DEP_COVERAGE=coverage==7.4.4 # For testing against a coverage.py working tree. coveragedev: _DEP_COVERAGE=-e{env:COVERAGE_HOME} passenv = @@ -72,37 +93,38 @@ pip_pre = true commands = {posargs:pytest -vv} -[testenv:spell] -setenv = - SPELLCHECK=1 -commands = - sphinx-build -b spelling docs dist/docs -skip_install = true -usedevelop = false +[testenv:check] deps = - -r{toxinidir}/docs/requirements.txt - sphinxcontrib-spelling - pyenchant + docutils + check-manifest + pre-commit + readme-renderer + pygments + isort +skip_install = true +commands = + python setup.py check --strict --metadata --restructuredtext + check-manifest . + pre-commit run --all-files --show-diff-on-failure [testenv:docs] +usedevelop = true deps = -r{toxinidir}/docs/requirements.txt commands = sphinx-build {posargs:-E} -b html docs dist/docs + sphinx-build -b linkcheck docs dist/docs -[testenv:check] +[testenv:report] deps = - check-manifest - colorama # TODO Remove when isort > v6.0.0b2 is released. - docutils - flake8 - isort - pygments - readme-renderer + coverage skip_install = true -usedevelop = false commands = - python setup.py check --strict --metadata --restructuredtext - check-manifest {toxinidir} - flake8 src tests setup.py - isort --check-only --diff src tests setup.py + coverage report + coverage html + +[testenv:clean] +commands = coverage erase +skip_install = true +deps = + coverage From ff3fdd8039bb11e93d7a5b06a32091d58e97bfbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 17 Mar 2024 17:07:40 +0200 Subject: [PATCH 101/117] Apply black and ruff to code, and fix some ruff lint issues. Also close #613. --- .pre-commit-config.yaml | 2 +- ci/requirements.txt | 1 - docs/conf.py | 2 +- examples/adhoc-layout/example/__init__.py | 4 +- examples/adhoc-layout/setup.py | 2 +- examples/src-layout/src/example/__init__.py | 4 +- setup.py | 43 +- src/pytest_cov/__init__.py | 1 + src/pytest_cov/compat.py | 9 - src/pytest_cov/embed.py | 7 +- src/pytest_cov/engine.py | 142 +- src/pytest_cov/plugin.py | 178 ++- tests/contextful.py | 76 +- tests/test_pytest_cov.py | 1562 +++++++++---------- 14 files changed, 955 insertions(+), 1078 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c45f47f9..6a9d1217 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: rev: v0.3.3 hooks: - id: ruff - args: [--fix, --exit-non-zero-on-fix, --show-fixes] + args: [--fix, --exit-non-zero-on-fix, --show-fixes, --unsafe-fixes] - repo: https://github.com/psf/black rev: 24.3.0 hooks: diff --git a/ci/requirements.txt b/ci/requirements.txt index a1708f40..b4f18520 100644 --- a/ci/requirements.txt +++ b/ci/requirements.txt @@ -1,6 +1,5 @@ virtualenv>=16.6.0 pip>=19.1.1 setuptools>=18.0.1 -six>=1.14.0 tox twine diff --git a/docs/conf.py b/docs/conf.py index 20757bb2..5151b09d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -13,7 +13,7 @@ 'sphinx.ext.extlinks', ] if os.getenv('SPELLCHECK'): - extensions += 'sphinxcontrib.spelling', + extensions += ('sphinxcontrib.spelling',) spelling_show_suggestions = True spelling_lang = 'en_US' diff --git a/examples/adhoc-layout/example/__init__.py b/examples/adhoc-layout/example/__init__.py index 684905a1..36b78a3d 100644 --- a/examples/adhoc-layout/example/__init__.py +++ b/examples/adhoc-layout/example/__init__.py @@ -2,10 +2,12 @@ # test merging multiple tox runs with a platform # based branch -if platform.python_implementation() == "PyPy": +if platform.python_implementation() == 'PyPy': + def add(a, b): return a + b else: + def add(a, b): return a + b diff --git a/examples/adhoc-layout/setup.py b/examples/adhoc-layout/setup.py index e52b68d1..86a4bf68 100644 --- a/examples/adhoc-layout/setup.py +++ b/examples/adhoc-layout/setup.py @@ -3,5 +3,5 @@ setup( name='example', - packages=find_packages(include=['example']) + packages=find_packages(include=['example']), ) diff --git a/examples/src-layout/src/example/__init__.py b/examples/src-layout/src/example/__init__.py index 684905a1..36b78a3d 100644 --- a/examples/src-layout/src/example/__init__.py +++ b/examples/src-layout/src/example/__init__.py @@ -2,10 +2,12 @@ # test merging multiple tox runs with a platform # based branch -if platform.python_implementation() == "PyPy": +if platform.python_implementation() == 'PyPy': + def add(a, b): return a + b else: + def add(a, b): return a + b diff --git a/setup.py b/setup.py index d6dd2d31..94c24201 100755 --- a/setup.py +++ b/setup.py @@ -1,13 +1,8 @@ #!/usr/bin/env python import re -from pathlib import Path -from glob import glob from itertools import chain -from os.path import basename -from os.path import dirname -from os.path import join -from os.path import splitext +from pathlib import Path from setuptools import Command from setuptools import find_packages @@ -32,24 +27,24 @@ def read(*names, **kwargs): class BuildWithPTH(build): def run(self, *args, **kwargs): super().run(*args, **kwargs) - path = join(dirname(__file__), 'src', 'pytest-cov.pth') - dest = join(self.build_lib, basename(path)) + path = str(Path(__file__).parent / 'src' / 'pytest-cov.pth') + dest = str(Path(self.build_lib) / Path(path).name) self.copy_file(path, dest) class EasyInstallWithPTH(easy_install): def run(self, *args, **kwargs): super().run(*args, **kwargs) - path = join(dirname(__file__), 'src', 'pytest-cov.pth') - dest = join(self.install_dir, basename(path)) + path = str(Path(__file__).parent / 'src' / 'pytest-cov.pth') + dest = str(Path(self.install_dir) / Path(path).name) self.copy_file(path, dest) class InstallLibWithPTH(install_lib): def run(self, *args, **kwargs): super().run(*args, **kwargs) - path = join(dirname(__file__), 'src', 'pytest-cov.pth') - dest = join(self.install_dir, basename(path)) + path = str(Path(__file__).parent / 'src' / 'pytest-cov.pth') + dest = str(Path(self.install_dir) / Path(path).name) self.copy_file(path, dest) self.outputs = [dest] @@ -60,13 +55,13 @@ def get_outputs(self): class DevelopWithPTH(develop): def run(self, *args, **kwargs): super().run(*args, **kwargs) - path = join(dirname(__file__), 'src', 'pytest-cov.pth') - dest = join(self.install_dir, basename(path)) + path = str(Path(__file__).parent / 'src' / 'pytest-cov.pth') + dest = str(Path(self.install_dir) / Path(path).name) self.copy_file(path, dest) class GeneratePTH(Command): - user_options = [] + user_options = () def initialize_options(self): pass @@ -75,11 +70,9 @@ def finalize_options(self): pass def run(self): - with open(join(dirname(__file__), 'src', 'pytest-cov.pth'), 'w') as fh: - with open(join(dirname(__file__), 'src', 'pytest-cov.embed')) as sh: - fh.write( - f"import os, sys;exec({sh.read().replace(' ', ' ')!r})" - ) + 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})") setup( @@ -124,19 +117,23 @@ def run(self): 'Issue Tracker': 'https://github.com/pytest-dev/pytest-cov/issues', }, keywords=[ - 'cover', 'coverage', 'pytest', 'py.test', 'distributed', 'parallel', + 'cover', + 'coverage', + 'pytest', + 'py.test', + 'distributed', + 'parallel', ], python_requires='>=3.8', install_requires=[ 'pytest>=4.6', - 'coverage[toml]>=5.2.1' + 'coverage[toml]>=5.2.1', ], extras_require={ 'testing': [ 'fields', 'hunter', 'process-tests', - 'six', 'pytest-xdist', 'virtualenv', ] diff --git a/src/pytest_cov/__init__.py b/src/pytest_cov/__init__.py index 8839990b..93390efa 100644 --- a/src/pytest_cov/__init__.py +++ b/src/pytest_cov/__init__.py @@ -1,2 +1,3 @@ """pytest-cov: avoid already-imported warning: PYTEST_DONT_REWRITE.""" + __version__ = '4.1.0' diff --git a/src/pytest_cov/compat.py b/src/pytest_cov/compat.py index 614419cb..453709d7 100644 --- a/src/pytest_cov/compat.py +++ b/src/pytest_cov/compat.py @@ -1,12 +1,3 @@ -try: - from StringIO import StringIO -except ImportError: - from io import StringIO - - -StringIO # pyflakes, this is for re-export - - class SessionWrapper: def __init__(self, session): self._session = session diff --git a/src/pytest_cov/embed.py b/src/pytest_cov/embed.py index f8a2749f..57c2f423 100644 --- a/src/pytest_cov/embed.py +++ b/src/pytest_cov/embed.py @@ -13,6 +13,7 @@ that code coverage is being collected we activate coverage based on info passed via env vars. """ + import atexit import os import signal @@ -52,7 +53,7 @@ def init(): data_suffix=True, config_file=cov_config, auto_data=True, - data_file=cov_datafile + data_file=cov_datafile, ) cov.load() cov.start() @@ -70,7 +71,7 @@ def _cleanup(cov): cov._auto_save = False # prevent autosaving from cov._atexit in case the interpreter lacks atexit.unregister try: atexit.unregister(cov._atexit) - except Exception: + except Exception: # noqa: S110 pass @@ -108,7 +109,7 @@ def _signal_cleanup_handler(signum, frame): elif signum == signal.SIGTERM: os._exit(128 + signum) elif signum == signal.SIGINT: - raise KeyboardInterrupt() + raise KeyboardInterrupt def cleanup_on_signal(signum): diff --git a/src/pytest_cov/engine.py b/src/pytest_cov/engine.py index 97d4d017..8ba4704d 100644 --- a/src/pytest_cov/engine.py +++ b/src/pytest_cov/engine.py @@ -1,4 +1,5 @@ """Coverage controllers for use by pytest-cov and nose-cov.""" + import contextlib import copy import functools @@ -6,11 +7,12 @@ import random import socket import sys +from io import StringIO +from pathlib import Path import coverage from coverage.data import CoverageData -from .compat import StringIO from .embed import cleanup @@ -34,7 +36,7 @@ def _ensure_topdir(meth): @functools.wraps(meth) def ensure_topdir_wrapper(self, *args, **kwargs): try: - original_cwd = os.getcwd() + original_cwd = Path.cwd() except OSError: # Looks like it's gone, this is non-ideal because a side-effect will # be introduced in the tests here but we can't do anything about it. @@ -67,12 +69,12 @@ def __init__(self, cov_source, cov_report, cov_config, cov_append, cov_branch, c self.data_file = None self.node_descs = set() self.failed_workers = [] - self.topdir = os.getcwd() + self.topdir = str(Path.cwd()) self.is_collocated = None @contextlib.contextmanager def ensure_topdir(self): - original_cwd = os.getcwd() + original_cwd = Path.cwd() os.chdir(self.topdir) yield os.chdir(original_cwd) @@ -94,12 +96,12 @@ def set_env(self): os.environ['COV_CORE_SOURCE'] = os.pathsep else: os.environ['COV_CORE_SOURCE'] = os.pathsep.join(self.cov_source) - config_file = os.path.abspath(self.cov_config) - if os.path.exists(config_file): - os.environ['COV_CORE_CONFIG'] = config_file + config_file = Path(self.cov_config).resolve() + if config_file.exists(): + os.environ['COV_CORE_CONFIG'] = str(config_file) else: os.environ['COV_CORE_CONFIG'] = os.pathsep - os.environ['COV_CORE_DATAFILE'] = os.path.abspath(self.cov.config.data_file) + os.environ['COV_CORE_DATAFILE'] = str(Path(self.cov.config.data_file).resolve()) if self.cov_branch: os.environ['COV_CORE_BRANCH'] = 'enabled' @@ -116,7 +118,7 @@ def unset_env(): def get_node_desc(platform, version_info): """Return a description of this node.""" - return 'platform {}, python {}'.format(platform, '%s.%s.%s-%s-%s' % version_info[:5]) + return 'platform {}, python {}'.format(platform, '{}.{}.{}-{}-{}'.format(*version_info[:5])) @staticmethod def sep(stream, s, txt): @@ -135,7 +137,7 @@ def summary(self, stream): total = None if not self.cov_report: - with _backup(self.cov, "config"): + with _backup(self.cov, 'config'): return self.cov.report(show_missing=True, ignore_errors=True, file=_NullFile) # Output coverage section header. @@ -149,8 +151,7 @@ def summary(self, stream): # Report on any failed workers. if self.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') + 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') @@ -163,19 +164,19 @@ def summary(self, stream): } skip_covered = isinstance(self.cov_report, dict) and 'skip-covered' in self.cov_report.values() options.update({'skip_covered': skip_covered or None}) - with _backup(self.cov, "config"): + with _backup(self.cov, 'config'): total = self.cov.report(**options) # Produce annotated source code report if wanted. if 'annotate' in self.cov_report: annotate_dir = self.cov_report['annotate'] - with _backup(self.cov, "config"): + with _backup(self.cov, 'config'): self.cov.annotate(ignore_errors=True, directory=annotate_dir) # We need to call Coverage.report here, just to get the total # Coverage.annotate don't return any total and we need it for --cov-fail-under. - with _backup(self.cov, "config"): + with _backup(self.cov, 'config'): total = self.cov.report(ignore_errors=True, file=_NullFile) if annotate_dir: stream.write(f'Coverage annotated source written to dir {annotate_dir}\n') @@ -185,28 +186,28 @@ def summary(self, stream): # Produce html report if wanted. if 'html' in self.cov_report: output = self.cov_report['html'] - with _backup(self.cov, "config"): + with _backup(self.cov, 'config'): total = self.cov.html_report(ignore_errors=True, directory=output) stream.write(f'Coverage HTML written to dir {self.cov.config.html_dir if output is None else output}\n') # Produce xml report if wanted. if 'xml' in self.cov_report: output = self.cov_report['xml'] - with _backup(self.cov, "config"): + with _backup(self.cov, 'config'): total = self.cov.xml_report(ignore_errors=True, outfile=output) stream.write(f'Coverage XML written to file {self.cov.config.xml_output if output is None else output}\n') # Produce json report if wanted if 'json' in self.cov_report: output = self.cov_report['json'] - with _backup(self.cov, "config"): + with _backup(self.cov, 'config'): total = self.cov.json_report(ignore_errors=True, outfile=output) stream.write('Coverage JSON written to file %s\n' % (self.cov.config.json_output if output is None else output)) # Produce lcov report if wanted. if 'lcov' in self.cov_report: output = self.cov_report['lcov'] - with _backup(self.cov, "config"): + with _backup(self.cov, 'config'): self.cov.lcov_report(ignore_errors=True, outfile=output) # We need to call Coverage.report here, just to get the total @@ -225,15 +226,19 @@ class Central(CovController): def start(self): cleanup() - self.cov = coverage.Coverage(source=self.cov_source, - branch=self.cov_branch, - data_suffix=True, - config_file=self.cov_config) - self.combining_cov = coverage.Coverage(source=self.cov_source, - branch=self.cov_branch, - data_suffix=True, - data_file=os.path.abspath(self.cov.config.data_file), - config_file=self.cov_config) + self.cov = coverage.Coverage( + source=self.cov_source, + branch=self.cov_branch, + data_suffix=True, + config_file=self.cov_config, + ) + self.combining_cov = coverage.Coverage( + source=self.cov_source, + branch=self.cov_branch, + data_suffix=True, + data_file=str(Path(self.cov.config.data_file).resolve()), + config_file=self.cov_config, + ) # Erase or load any previous coverage data and start coverage. if not self.cov_append: @@ -266,23 +271,27 @@ def start(self): cleanup() # Ensure coverage rc file rsynced if appropriate. - if self.cov_config and os.path.exists(self.cov_config): + if self.cov_config and Path(self.cov_config).exists(): # rsyncdir is going away in pytest-xdist 4.0, already deprecated if hasattr(self.config.option, 'rsyncdir'): self.config.option.rsyncdir.append(self.cov_config) - self.cov = coverage.Coverage(source=self.cov_source, - branch=self.cov_branch, - data_suffix=True, - config_file=self.cov_config) + self.cov = coverage.Coverage( + source=self.cov_source, + branch=self.cov_branch, + data_suffix=True, + config_file=self.cov_config, + ) self.cov._warn_no_data = False self.cov._warn_unimported_source = False self.cov._warn_preimported_source = False - self.combining_cov = coverage.Coverage(source=self.cov_source, - branch=self.cov_branch, - data_suffix=True, - data_file=os.path.abspath(self.cov.config.data_file), - config_file=self.cov_config) + self.combining_cov = coverage.Coverage( + source=self.cov_source, + branch=self.cov_branch, + data_suffix=True, + data_file=str(Path(self.cov.config.data_file).resolve()), + config_file=self.cov_config, + ) if not self.cov_append: self.cov.erase() self.cov.start() @@ -291,11 +300,13 @@ def start(self): def configure_node(self, node): """Workers need to know if they are collocated and what files have moved.""" - node.workerinput.update({ - 'cov_master_host': socket.gethostname(), - 'cov_master_topdir': self.topdir, - 'cov_master_rsync_roots': [str(root) for root in node.nodemanager.roots], - }) + node.workerinput.update( + { + 'cov_master_host': socket.gethostname(), + 'cov_master_topdir': self.topdir, + 'cov_master_rsync_roots': [str(root) for root in node.nodemanager.roots], + } + ) def testnodedown(self, node, error): """Collect data file name from worker.""" @@ -311,15 +322,13 @@ def testnodedown(self, node, error): # that it returns to us. if 'cov_worker_data' in output: data_suffix = '%s.%s.%06d.%s' % ( - socket.gethostname(), os.getpid(), - random.randint(0, 999999), - output['cov_worker_node_id'] + socket.gethostname(), + os.getpid(), + random.randint(0, 999999), # noqa: S311 + output['cov_worker_node_id'], ) - cov = coverage.Coverage(source=self.cov_source, - branch=self.cov_branch, - data_suffix=data_suffix, - config_file=self.cov_config) + cov = coverage.Coverage(source=self.cov_source, branch=self.cov_branch, data_suffix=data_suffix, config_file=self.cov_config) cov.start() if coverage.version_info < (5, 0): data = CoverageData() @@ -361,23 +370,26 @@ def start(self): cleanup() # Determine whether we are collocated with master. - self.is_collocated = (socket.gethostname() == self.config.workerinput['cov_master_host'] and - self.topdir == self.config.workerinput['cov_master_topdir']) + self.is_collocated = ( + socket.gethostname() == self.config.workerinput['cov_master_host'] + and self.topdir == self.config.workerinput['cov_master_topdir'] + ) # If we are not collocated then rewrite master paths to worker paths. if not self.is_collocated: master_topdir = self.config.workerinput['cov_master_topdir'] worker_topdir = self.topdir if self.cov_source is not None: - self.cov_source = [source.replace(master_topdir, worker_topdir) - for source in self.cov_source] + self.cov_source = [source.replace(master_topdir, worker_topdir) for source in self.cov_source] self.cov_config = self.cov_config.replace(master_topdir, worker_topdir) # Erase any previous data and start coverage. - self.cov = coverage.Coverage(source=self.cov_source, - branch=self.cov_branch, - data_suffix=True, - config_file=self.cov_config) + self.cov = coverage.Coverage( + source=self.cov_source, + branch=self.cov_branch, + data_suffix=True, + config_file=self.cov_config, + ) self.cov.start() self.set_env() @@ -411,13 +423,13 @@ def finish(self): else: data = self.cov.get_data().dumps() - self.config.workeroutput.update({ - 'cov_worker_path': self.topdir, - 'cov_worker_node_id': self.nodeid, - 'cov_worker_data': data, - }) + self.config.workeroutput.update( + { + 'cov_worker_path': self.topdir, + 'cov_worker_node_id': self.nodeid, + 'cov_worker_data': data, + } + ) def summary(self, stream): """Only the master reports so do nothing.""" - - pass diff --git a/src/pytest_cov/plugin.py b/src/pytest_cov/plugin.py index 2a1544a6..49413d46 100644 --- a/src/pytest_cov/plugin.py +++ b/src/pytest_cov/plugin.py @@ -1,7 +1,10 @@ """Coverage plugin for pytest.""" + import argparse import os import warnings +from io import StringIO +from pathlib import Path import coverage import pytest @@ -33,9 +36,9 @@ def validate_report(arg): term_choices = ['term', 'term-missing'] term_modifier_choices = ['skip-covered'] all_choices = term_choices + file_choices - values = arg.split(":", 1) + values = arg.split(':', 1) report_type = values[0] - if report_type not in all_choices + ['']: + if report_type not in [*all_choices, '']: msg = f'invalid choice: "{arg}" (choose from "{all_choices}")' raise argparse.ArgumentTypeError(msg) @@ -50,8 +53,7 @@ def validate_report(arg): return report_type, report_modifier if report_type not in file_choices: - msg = 'output specifier not supported for: "{}" (choose from "{}")'.format(arg, - file_choices) + msg = f'output specifier not supported for: "{arg}" (choose from "{file_choices}")' raise argparse.ArgumentTypeError(msg) return values @@ -64,17 +66,19 @@ def validate_fail_under(num_str): try: value = float(num_str) except ValueError: - raise argparse.ArgumentTypeError('An integer or float value is required.') + 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?') + raise argparse.ArgumentTypeError( + 'Your desire for over-achievement is admirable but misplaced. ' + 'The maximum value is 100. Perhaps write more integration tests?' + ) return value def validate_context(arg): if coverage.version_info <= (5, 0): raise argparse.ArgumentTypeError('Contexts are only supported with coverage.py >= 5.x') - if arg != "test": + if arg != 'test': raise argparse.ArgumentTypeError('The only supported value is "test".') return arg @@ -88,42 +92,83 @@ def __call__(self, parser, namespace, values, option_string=None): def pytest_addoption(parser): """Add options to control coverage.""" - group = parser.getgroup( - 'cov', 'coverage reporting with distributed testing support') - group.addoption('--cov', action='append', default=[], metavar='SOURCE', - nargs='?', const=True, dest='cov_source', - help='Path or package name to measure during execution (multi-allowed). ' - 'Use --cov= to not do any source filtering and record everything.') - group.addoption('--cov-reset', action='store_const', const=[], dest='cov_source', - help='Reset cov sources accumulated in options so far. ') - group.addoption('--cov-report', action=StoreReport, default={}, - metavar='TYPE', type=validate_report, - help='Type of report to generate: term, term-missing, ' - 'annotate, html, xml, json, lcov (multi-allowed). ' - 'term, term-missing may be followed by ":skip-covered". ' - 'annotate, html, xml, json and lcov may be followed by ":DEST" ' - 'where DEST specifies the output location. ' - 'Use --cov-report= to not generate any output.') - group.addoption('--cov-config', action='store', default='.coveragerc', - metavar='PATH', - help='Config file for coverage. Default: .coveragerc') - group.addoption('--no-cov-on-fail', action='store_true', default=False, - help='Do not report coverage if test run fails. ' - 'Default: False') - group.addoption('--no-cov', action='store_true', default=False, - help='Disable coverage report completely (useful for debuggers). ' - 'Default: False') - group.addoption('--cov-fail-under', action='store', metavar='MIN', - type=validate_fail_under, - help='Fail if the total coverage is less than MIN.') - group.addoption('--cov-append', action='store_true', default=False, - help='Do not delete coverage but append to current. ' - 'Default: False') - group.addoption('--cov-branch', action='store_true', default=None, - help='Enable branch coverage.') - group.addoption('--cov-context', action='store', metavar='CONTEXT', - type=validate_context, - help='Dynamic contexts to use. "test" for now.') + group = parser.getgroup('cov', 'coverage reporting with distributed testing support') + group.addoption( + '--cov', + action='append', + default=[], + metavar='SOURCE', + nargs='?', + const=True, + dest='cov_source', + help='Path or package name to measure during execution (multi-allowed). ' + 'Use --cov= to not do any source filtering and record everything.', + ) + group.addoption( + '--cov-reset', + action='store_const', + const=[], + dest='cov_source', + help='Reset cov sources accumulated in options so far. ', + ) + group.addoption( + '--cov-report', + action=StoreReport, + default={}, + metavar='TYPE', + type=validate_report, + help='Type of report to generate: term, term-missing, ' + 'annotate, html, xml, json, lcov (multi-allowed). ' + 'term, term-missing may be followed by ":skip-covered". ' + 'annotate, html, xml, json and lcov may be followed by ":DEST" ' + 'where DEST specifies the output location. ' + 'Use --cov-report= to not generate any output.', + ) + group.addoption( + '--cov-config', + action='store', + default='.coveragerc', + metavar='PATH', + help='Config file for coverage. Default: .coveragerc', + ) + group.addoption( + '--no-cov-on-fail', + action='store_true', + default=False, + help='Do not report coverage if test run fails. Default: False', + ) + group.addoption( + '--no-cov', + action='store_true', + default=False, + help='Disable coverage report completely (useful for debuggers). Default: False', + ) + group.addoption( + '--cov-fail-under', + action='store', + metavar='MIN', + type=validate_fail_under, + help='Fail if the total coverage is less than MIN.', + ) + group.addoption( + '--cov-append', + action='store_true', + default=False, + help='Do not delete coverage but append to current. Default: False', + ) + group.addoption( + '--cov-branch', + action='store_true', + default=None, + help='Enable branch coverage.', + ) + group.addoption( + '--cov-context', + action='store', + metavar='CONTEXT', + type=validate_context, + help='Dynamic contexts to use. "test" for now.', + ) def _prepare_cov_source(cov_source): @@ -172,7 +217,7 @@ def __init__(self, options, pluginmanager, start=True, no_cov_should_warn=False) # Our implementation is unknown at this time. self.pid = None self.cov_controller = None - self.cov_report = compat.StringIO() + self.cov_report = StringIO() self.cov_total = None self.failed = False self._started = False @@ -180,9 +225,7 @@ def __init__(self, options, pluginmanager, start=True, no_cov_should_warn=False) self._disabled = False self.options = options - is_dist = (getattr(options, 'numprocesses', False) or - getattr(options, 'distload', False) or - getattr(options, 'dist', 'no') != 'no') + is_dist = getattr(options, 'numprocesses', False) or getattr(options, 'distload', False) or getattr(options, 'dist', 'no') != 'no' if getattr(options, 'no_cov', False): self._disabled = True return @@ -220,11 +263,11 @@ class Config: self.options.cov_append, self.options.cov_branch, config, - nodeid + nodeid, ) self.cov_controller.start() self._started = True - self._start_path = os.getcwd() + self._start_path = Path.cwd() cov_config = self.cov_controller.cov.config if self.options.cov_fail_under is None and hasattr(cov_config, 'fail_under'): self.options.cov_fail_under = cov_config.fail_under @@ -246,9 +289,7 @@ def pytest_sessionstart(self, session): self.pid = os.getpid() if self._is_worker(session): - nodeid = ( - session.config.workerinput.get('workerid', getattr(session, 'nodeid')) - ) + nodeid = session.config.workerinput.get('workerid', session.nodeid) self.start(engine.DistWorker, session.config, nodeid) elif not self._started: self.start(engine.Central) @@ -307,9 +348,8 @@ def pytest_runtestloop(self, session): self.cov_total = self.cov_controller.summary(self.cov_report) except CoverageException as exc: message = f'Failed to generate report: {exc}\n' - session.config.pluginmanager.getplugin("terminalreporter").write( - f'WARNING: {message}\n', red=True, bold=True) - warnings.warn(CovReportWarning(message)) + session.config.pluginmanager.getplugin('terminalreporter').write(f'WARNING: {message}\n', red=True, bold=True) + warnings.warn(CovReportWarning(message), stacklevel=1) self.cov_total = 0 assert self.cov_total is not None, 'Test coverage should never be `None`' if self._failed_cov_total() and not self.options.collectonly: @@ -321,7 +361,7 @@ def pytest_terminal_summary(self, terminalreporter): if self.options.no_cov_should_warn: message = 'Coverage disabled via --no-cov switch!' terminalreporter.write(f'WARNING: {message}\n', red=True, bold=True) - warnings.warn(CovDisabledWarning(message)) + warnings.warn(CovDisabledWarning(message), stacklevel=1) return if self.cov_controller is None: return @@ -339,15 +379,11 @@ def pytest_terminal_summary(self, terminalreporter): if self.options.cov_fail_under is not None and self.options.cov_fail_under > 0: 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( - required=self.options.cov_fail_under, - actual=self.cov_total, - fail="FAIL " if failed else "", - reached="not reached" if failed else "reached" - ) + 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 '', + reached='not reached' if failed else 'reached', ) terminalreporter.write(message, **markup) @@ -362,8 +398,7 @@ def pytest_runtest_teardown(self, item): @pytest.hookimpl(hookwrapper=True) def pytest_runtest_call(self, item): - if (item.get_closest_marker('no_cover') - or 'no_cover' in getattr(item, 'fixturenames', ())): + if item.get_closest_marker('no_cover') or 'no_cover' in getattr(item, 'fixturenames', ()): self.cov_controller.pause() yield self.cov_controller.resume() @@ -385,15 +420,14 @@ def pytest_runtest_call(self, item): self.switch_context(item, 'run') def switch_context(self, item, when): - context = f"{item.nodeid}|{when}" + context = f'{item.nodeid}|{when}' self.cov.switch_context(context) os.environ['COV_CORE_CONTEXT'] = context @pytest.fixture -def no_cover(): +def no_cover(): # noqa: PT004 """A pytest fixture to disable coverage.""" - pass @pytest.fixture @@ -409,4 +443,4 @@ def cov(request): def pytest_configure(config): - config.addinivalue_line("markers", "no_cover: disable coverage for this test.") + config.addinivalue_line('markers', 'no_cover: disable coverage for this test.') diff --git a/tests/contextful.py b/tests/contextful.py index 3527e499..b1d0804b 100644 --- a/tests/contextful.py +++ b/tests/contextful.py @@ -1,105 +1,113 @@ # A test file for test_pytest_cov.py:test_contexts import unittest +from typing import ClassVar import pytest def test_01(): - assert 1 == 1 # r1 + assert 1 == 1 # r1 def test_02(): - assert 2 == 2 # r2 + assert 2 == 2 # r2 class OldStyleTests(unittest.TestCase): - items = [] + items: ClassVar = [] @classmethod def setUpClass(cls): - cls.items.append("hello") # s3 + cls.items.append('hello') # s3 @classmethod def tearDownClass(cls): - cls.items.pop() # t4 + cls.items.pop() # t4 def setUp(self): - self.number = 1 # r3 r4 + self.number = 1 # r3 r4 def tearDown(self): - self.number = None # r3 r4 + self.number = None # r3 r4 def test_03(self): - assert self.number == 1 # r3 - assert self.items[0] == "hello" # r3 + assert self.number == 1 # r3 + assert self.items[0] == 'hello' # r3 def test_04(self): - assert self.number == 1 # r4 - assert self.items[0] == "hello" # r4 + assert self.number == 1 # r4 + assert self.items[0] == 'hello' # r4 @pytest.fixture def some_data(): - return [1, 2, 3] # s5 s6 + return [1, 2, 3] # s5 s6 def test_05(some_data): - assert len(some_data) == 3 # r5 + assert len(some_data) == 3 # r5 @pytest.fixture def more_data(some_data): - return [2*x for x in some_data] # s6 + return [2 * x for x in some_data] # s6 def test_06(some_data, more_data): - assert len(some_data) == len(more_data) # r6 + assert len(some_data) == len(more_data) # r6 @pytest.fixture(scope='session') def expensive_data(): - return list(range(10)) # s7 + return list(range(10)) # s7 def test_07(expensive_data): - assert len(expensive_data) == 10 # r7 + assert len(expensive_data) == 10 # r7 def test_08(expensive_data): - assert len(expensive_data) == 10 # r8 + assert len(expensive_data) == 10 # r8 @pytest.fixture(params=[1, 2, 3]) def parametrized_number(request): - return request.param # s9-1 s9-2 s9-3 + return request.param # s9-1 s9-2 s9-3 def test_09(parametrized_number): - assert parametrized_number > 0 # r9-1 r9-2 r9-3 + assert parametrized_number > 0 # r9-1 r9-2 r9-3 def test_10(): - assert 1 == 1 # r10 + assert 1 == 1 # r10 -@pytest.mark.parametrize("x, ans", [ - (1, 101), - (2, 202), -]) +@pytest.mark.parametrize( + ('x', 'ans'), + [ + (1, 101), + (2, 202), + ], +) def test_11(x, ans): - assert 100 * x + x == ans # r11-1 r11-2 + assert 100 * x + x == ans # r11-1 r11-2 -@pytest.mark.parametrize("x, ans", [ - (1, 101), - (2, 202), -], ids=['one', 'two']) +@pytest.mark.parametrize( + ('x', 'ans'), + [ + (1, 101), + (2, 202), + ], + ids=['one', 'two'], +) def test_12(x, ans): - assert 100 * x + x == ans # r12-1 r12-2 + assert 100 * x + x == ans # r12-1 r12-2 -@pytest.mark.parametrize("x", [1, 2]) -@pytest.mark.parametrize("y", [3, 4]) +@pytest.mark.parametrize('x', [1, 2]) +@pytest.mark.parametrize('y', [3, 4]) def test_13(x, y): - assert x + y > 0 # r13-1 r13-2 r13-3 r13-4 + assert x + y > 0 # r13-1 r13-2 r13-3 r13-4 diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 84f959fb..1c9bca86 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -1,3 +1,4 @@ +# ruff: noqa import collections import glob import os @@ -24,11 +25,11 @@ except ImportError: from io import StringIO -coverage, platform # required for skipif mark on test_cov_min_from_coveragerc +coverage, platform # required for skipif mark on test_cov_min_from_coveragerc -max_worker_restart_0 = "--max-worker-restart=0" +max_worker_restart_0 = '--max-worker-restart=0' -SCRIPT = ''' +SCRIPT = """ import sys, helper def pytest_generate_tests(metafunc): @@ -40,24 +41,24 @@ def test_foo(p): helper.do_stuff() # get some coverage in some other completely different location if sys.version_info[0] > 5: assert False -''' +""" -SCRIPT2 = ''' +SCRIPT2 = """ # def test_bar(): x = True assert x -''' +""" -COVERAGERC_SOURCE = '''\ +COVERAGERC_SOURCE = """\ [run] source = . -''' +""" -SCRIPT_CHILD = ''' +SCRIPT_CHILD = """ import sys idx = int(sys.argv[1]) @@ -66,9 +67,9 @@ def test_bar(): foo = "a" # previously there was a "pass" here but Python 3.5 optimizes it away. if idx == 1: foo = "b" # previously there was a "pass" here but Python 3.5 optimizes it away. -''' +""" -SCRIPT_PARENT = ''' +SCRIPT_PARENT = """ import os import subprocess import sys @@ -86,9 +87,9 @@ def test_foo(idx): # there is a issue in coverage.py with multiline statements at # end of file: https://bitbucket.org/ned/coveragepy/issue/293 pass -''' +""" -SCRIPT_PARENT_CHANGE_CWD = ''' +SCRIPT_PARENT_CHANGE_CWD = """ import subprocess import sys import os @@ -110,9 +111,9 @@ def test_foo(idx): # there is a issue in coverage.py with multiline statements at # end of file: https://bitbucket.org/ned/coveragepy/issue/293 pass -''' +""" -SCRIPT_PARENT_CHANGE_CWD_IMPORT_CHILD = ''' +SCRIPT_PARENT_CHANGE_CWD_IMPORT_CHILD = """ import subprocess import sys import os @@ -133,19 +134,19 @@ def test_foo(idx): # there is a issue in coverage.py with multiline statements at # end of file: https://bitbucket.org/ned/coveragepy/issue/293 pass -''' +""" -SCRIPT_FUNCARG = ''' +SCRIPT_FUNCARG = """ import coverage def test_foo(cov): assert isinstance(cov, coverage.Coverage) -''' +""" -SCRIPT_FUNCARG_NOT_ACTIVE = ''' +SCRIPT_FUNCARG_NOT_ACTIVE = """ def test_foo(cov): assert cov is None -''' +""" CHILD_SCRIPT_RESULT = '[56] * 100%' PARENT_SCRIPT_RESULT = '9 * 100%' @@ -154,10 +155,11 @@ def test_foo(cov): JSON_REPORT_NAME = 'cov.json' LCOV_REPORT_NAME = 'cov.info' -xdist_params = pytest.mark.parametrize('opts', [ - '', - pytest.param('-n 1', marks=pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"')) -], ids=['nodist', 'xdist']) +xdist_params = pytest.mark.parametrize( + 'opts', + ['', pytest.param('-n 1', marks=pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"'))], + ids=['nodist', 'xdist'], +) @pytest.fixture(scope='session', autouse=True) @@ -177,12 +179,15 @@ def adjust_sys_path(): os.environ['PYTHONPATH'] = orig_path -@pytest.fixture(params=[ - ('branch=true', '--cov-branch', '9 * 85%', '3 * 100%'), - ('branch=true', '', '9 * 85%', '3 * 100%'), - ('', '--cov-branch', '9 * 85%', '3 * 100%'), - ('', '', '9 * 89%', '3 * 100%'), -], ids=['branch2x', 'branch1c', 'branch1a', 'nobranch']) +@pytest.fixture( + params=[ + ('branch=true', '--cov-branch', '9 * 85%', '3 * 100%'), + ('branch=true', '', '9 * 85%', '3 * 100%'), + ('', '--cov-branch', '9 * 85%', '3 * 100%'), + ('', '', '9 * 89%', '3 * 100%'), + ], + ids=['branch2x', 'branch1c', 'branch1a', 'nobranch'], +) def prop(request): return Namespace( code=SCRIPT, @@ -200,149 +205,137 @@ def test_central(pytester, testdir, prop): script = testdir.makepyfile(prop.code) testdir.tmpdir.join('.coveragerc').write(prop.fullconf) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - script, - *prop.args) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=term-missing', script, *prop.args) - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'test_central* {prop.result} *', - '*10 passed*' - ]) + result.stdout.fnmatch_lines(['*- coverage: platform *, python * -*', f'test_central* {prop.result} *', '*10 passed*']) assert result.ret == 0 def test_annotate(testdir): script = testdir.makepyfile(SCRIPT) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=annotate', - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=annotate', script) - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'Coverage annotated source written next to source', - '*10 passed*', - ]) + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + 'Coverage annotated source written next to source', + '*10 passed*', + ] + ) assert result.ret == 0 def test_annotate_output_dir(testdir): script = testdir.makepyfile(SCRIPT) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=annotate:' + DEST_DIR, - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=annotate:' + DEST_DIR, script) - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'Coverage annotated source written to dir ' + DEST_DIR, - '*10 passed*', - ]) + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + 'Coverage annotated source written to dir ' + DEST_DIR, + '*10 passed*', + ] + ) dest_dir = testdir.tmpdir.join(DEST_DIR) assert dest_dir.check(dir=True) - assert dest_dir.join(script.basename + ",cover").check() + assert dest_dir.join(script.basename + ',cover').check() assert result.ret == 0 def test_html(testdir): script = testdir.makepyfile(SCRIPT) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=html', - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=html', script) - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'Coverage HTML written to dir htmlcov', - '*10 passed*', - ]) + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + 'Coverage HTML written to dir htmlcov', + '*10 passed*', + ] + ) dest_dir = testdir.tmpdir.join('htmlcov') assert dest_dir.check(dir=True) - assert dest_dir.join("index.html").check() + assert dest_dir.join('index.html').check() assert result.ret == 0 def test_html_output_dir(testdir): script = testdir.makepyfile(SCRIPT) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=html:' + DEST_DIR, - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=html:' + DEST_DIR, script) - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'Coverage HTML written to dir ' + DEST_DIR, - '*10 passed*', - ]) + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + 'Coverage HTML written to dir ' + DEST_DIR, + '*10 passed*', + ] + ) dest_dir = testdir.tmpdir.join(DEST_DIR) assert dest_dir.check(dir=True) - assert dest_dir.join("index.html").check() + assert dest_dir.join('index.html').check() assert result.ret == 0 def test_term_report_does_not_interact_with_html_output(testdir): script = testdir.makepyfile(test_funcarg=SCRIPT_FUNCARG) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=term-missing:skip-covered', - '--cov-report=html:' + DEST_DIR, - script) + result = testdir.runpytest( + '-v', f'--cov={script.dirpath()}', '--cov-report=term-missing:skip-covered', '--cov-report=html:' + DEST_DIR, script + ) - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'Coverage HTML written to dir ' + DEST_DIR, - '*1 passed*', - ]) + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + 'Coverage HTML written to dir ' + DEST_DIR, + '*1 passed*', + ] + ) dest_dir = testdir.tmpdir.join(DEST_DIR) assert dest_dir.check(dir=True) - assert sorted(dest_dir.visit("**/*.html")) == [dest_dir.join("index.html"), dest_dir.join("test_funcarg_py.html")] - assert dest_dir.join("index.html").check() + assert sorted(dest_dir.visit('**/*.html')) == [dest_dir.join('index.html'), dest_dir.join('test_funcarg_py.html')] + assert dest_dir.join('index.html').check() assert result.ret == 0 def test_html_configured_output_dir(testdir): script = testdir.makepyfile(SCRIPT) - testdir.tmpdir.join('.coveragerc').write(""" + testdir.tmpdir.join('.coveragerc').write( + """ [html] directory = somewhere -""") - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=html', - script) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'Coverage HTML written to dir somewhere', - '*10 passed*', - ]) +""" + ) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=html', script) + + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + 'Coverage HTML written to dir somewhere', + '*10 passed*', + ] + ) dest_dir = testdir.tmpdir.join('somewhere') assert dest_dir.check(dir=True) - assert dest_dir.join("index.html").check() + assert dest_dir.join('index.html').check() assert result.ret == 0 def test_xml_output_dir(testdir): script = testdir.makepyfile(SCRIPT) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=xml:' + XML_REPORT_NAME, - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=xml:' + XML_REPORT_NAME, script) - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'Coverage XML written to file ' + XML_REPORT_NAME, - '*10 passed*', - ]) + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + 'Coverage XML written to file ' + XML_REPORT_NAME, + '*10 passed*', + ] + ) assert testdir.tmpdir.join(XML_REPORT_NAME).check() assert result.ret == 0 @@ -350,105 +343,94 @@ def test_xml_output_dir(testdir): def test_json_output_dir(testdir): script = testdir.makepyfile(SCRIPT) - result = testdir.runpytest('-v', - '--cov=%s' % script.dirpath(), - '--cov-report=json:' + JSON_REPORT_NAME, - script) + result = testdir.runpytest('-v', '--cov=%s' % script.dirpath(), '--cov-report=json:' + JSON_REPORT_NAME, script) - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'Coverage JSON written to file ' + JSON_REPORT_NAME, - '*10 passed*', - ]) + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + 'Coverage JSON written to file ' + JSON_REPORT_NAME, + '*10 passed*', + ] + ) assert testdir.tmpdir.join(JSON_REPORT_NAME).check() assert result.ret == 0 -@pytest.mark.skipif("coverage.version_info < (6, 3)") +@pytest.mark.skipif('coverage.version_info < (6, 3)') def test_lcov_output_dir(testdir): script = testdir.makepyfile(SCRIPT) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=lcov:' + LCOV_REPORT_NAME, - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=lcov:' + LCOV_REPORT_NAME, script) - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'Coverage LCOV written to file ' + LCOV_REPORT_NAME, - '*10 passed*', - ]) + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + 'Coverage LCOV written to file ' + LCOV_REPORT_NAME, + '*10 passed*', + ] + ) assert testdir.tmpdir.join(LCOV_REPORT_NAME).check() assert result.ret == 0 -@pytest.mark.skipif("coverage.version_info >= (6, 3)") +@pytest.mark.skipif('coverage.version_info >= (6, 3)') def test_lcov_not_supported(testdir): - script = testdir.makepyfile("a = 1") - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=lcov', - script, - ) - result.stderr.fnmatch_lines([ - '*argument --cov-report: LCOV output is only supported with coverage.py >= 6.3', - ]) + script = testdir.makepyfile('a = 1') + result = testdir.runpytest( + '-v', + f'--cov={script.dirpath()}', + '--cov-report=lcov', + script, + ) + result.stderr.fnmatch_lines( + [ + '*argument --cov-report: LCOV output is only supported with coverage.py >= 6.3', + ] + ) assert result.ret != 0 def test_term_output_dir(testdir): script = testdir.makepyfile(SCRIPT) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=term:' + DEST_DIR, - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=term:' + DEST_DIR, script) - result.stderr.fnmatch_lines([ - f'*argument --cov-report: output specifier not supported for: "term:{DEST_DIR}"*', - ]) + result.stderr.fnmatch_lines( + [ + f'*argument --cov-report: output specifier not supported for: "term:{DEST_DIR}"*', + ] + ) assert result.ret != 0 def test_term_missing_output_dir(testdir): script = testdir.makepyfile(SCRIPT) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=term-missing:' + DEST_DIR, - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=term-missing:' + DEST_DIR, script) - result.stderr.fnmatch_lines([ - '*argument --cov-report: output specifier not supported for: ' - '"term-missing:%s"*' % DEST_DIR, - ]) + result.stderr.fnmatch_lines( + [ + '*argument --cov-report: output specifier not supported for: ' '"term-missing:%s"*' % DEST_DIR, + ] + ) assert result.ret != 0 def test_cov_min_100(testdir): script = testdir.makepyfile(SCRIPT) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - '--cov-fail-under=100', - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=term-missing', '--cov-fail-under=100', script) assert result.ret != 0 - result.stdout.fnmatch_lines([ - 'FAIL Required test coverage of 100% not reached. Total coverage: *%' - ]) + result.stdout.fnmatch_lines(['FAIL Required test coverage of 100% not reached. Total coverage: *%']) def test_cov_min_100_passes_if_collectonly(testdir): script = testdir.makepyfile(SCRIPT) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - '--cov-fail-under=100', - '--collect-only', - script) + result = testdir.runpytest( + '-v', f'--cov={script.dirpath()}', '--cov-report=term-missing', '--cov-fail-under=100', '--collect-only', script + ) assert result.ret == 0 @@ -456,75 +438,43 @@ def test_cov_min_100_passes_if_collectonly(testdir): def test_cov_min_50(testdir): script = testdir.makepyfile(SCRIPT) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=html', - '--cov-report=xml', - '--cov-fail-under=50', - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=html', '--cov-report=xml', '--cov-fail-under=50', script) assert result.ret == 0 - result.stdout.fnmatch_lines([ - 'Required test coverage of 50% reached. Total coverage: *%' - ]) + result.stdout.fnmatch_lines(['Required test coverage of 50% reached. Total coverage: *%']) def test_cov_min_float_value(testdir): script = testdir.makepyfile(SCRIPT) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - '--cov-fail-under=88.88', - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=term-missing', '--cov-fail-under=88.88', script) assert result.ret == 0 - result.stdout.fnmatch_lines([ - 'Required test coverage of 88.88% reached. Total coverage: 88.89%' - ]) + result.stdout.fnmatch_lines(['Required test coverage of 88.88% reached. Total coverage: 88.89%']) def test_cov_min_float_value_not_reached(testdir): script = testdir.makepyfile(SCRIPT) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - '--cov-fail-under=88.89', - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=term-missing', '--cov-fail-under=88.89', script) assert result.ret == 1 - result.stdout.fnmatch_lines([ - 'FAIL Required test coverage of 88.89% not reached. Total coverage: 88.89%' - ]) + result.stdout.fnmatch_lines(['FAIL Required test coverage of 88.89% not reached. Total coverage: 88.89%']) def test_cov_min_no_report(testdir): script = testdir.makepyfile(SCRIPT) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=', - '--cov-fail-under=50', - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=', '--cov-fail-under=50', script) assert result.ret == 0 - result.stdout.fnmatch_lines([ - 'Required test coverage of 50% reached. Total coverage: *%' - ]) + result.stdout.fnmatch_lines(['Required test coverage of 50% reached. Total coverage: *%']) def test_central_nonspecific(pytester, testdir, prop): script = testdir.makepyfile(prop.code) testdir.tmpdir.join('.coveragerc').write(prop.fullconf) - result = testdir.runpytest('-v', - '--cov', - '--cov-report=term-missing', - script, *prop.args) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'test_central_nonspecific* {prop.result} *', - '*10 passed*' - ]) + result = testdir.runpytest('-v', '--cov', '--cov-report=term-missing', script, *prop.args) + + result.stdout.fnmatch_lines(['*- coverage: platform *, python * -*', f'test_central_nonspecific* {prop.result} *', '*10 passed*']) # multi-module coverage report assert any(line.startswith('TOTAL ') for line in result.stdout.lines) @@ -534,15 +484,14 @@ def test_central_nonspecific(pytester, testdir, prop): def test_cov_min_from_coveragerc(testdir): script = testdir.makepyfile(SCRIPT) - testdir.tmpdir.join('.coveragerc').write(""" + testdir.tmpdir.join('.coveragerc').write( + """ [report] fail_under = 100 -""") +""" + ) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=term-missing', script) assert result.ret != 0 @@ -551,16 +500,15 @@ def test_central_coveragerc(pytester, testdir, prop): script = testdir.makepyfile(prop.code) testdir.tmpdir.join('.coveragerc').write(COVERAGERC_SOURCE + prop.conf) - result = testdir.runpytest('-v', - '--cov', - '--cov-report=term-missing', - script, *prop.args) + result = testdir.runpytest('-v', '--cov', '--cov-report=term-missing', script, *prop.args) - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'test_central_coveragerc* {prop.result} *', - '*10 passed*', - ]) + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + f'test_central_coveragerc* {prop.result} *', + '*10 passed*', + ] + ) assert result.ret == 0 @@ -570,10 +518,13 @@ def test_central_with_path_aliasing(pytester, testdir, monkeypatch, opts, prop): mod1.write(SCRIPT) mod2 = testdir.mkdir('aliased').join('mod.py') mod2.write(SCRIPT) - script = testdir.makepyfile(''' + script = testdir.makepyfile( + """ from mod import * -''') - testdir.tmpdir.join('setup.cfg').write(f""" +""" + ) + testdir.tmpdir.join('setup.cfg').write( + f""" [coverage:paths] source = src @@ -582,30 +533,33 @@ def test_central_with_path_aliasing(pytester, testdir, monkeypatch, opts, prop): source = mod parallel = true {prop.conf} -""") +""" + ) monkeypatch.setitem(os.environ, 'PYTHONPATH', os.pathsep.join([os.environ.get('PYTHONPATH', ''), 'aliased'])) - result = testdir.runpytest('-v', '-s', - '--cov', - '--cov-report=term-missing', - script, *opts.split()+prop.args) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'src[\\/]mod* {prop.result} *', - '*10 passed*', - ]) + result = testdir.runpytest('-v', '-s', '--cov', '--cov-report=term-missing', script, *opts.split() + prop.args) + + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + f'src[\\/]mod* {prop.result} *', + '*10 passed*', + ] + ) assert result.ret == 0 @xdist_params def test_borken_cwd(pytester, testdir, monkeypatch, opts): - testdir.makepyfile(mod=''' + testdir.makepyfile( + mod=""" def foobar(a, b): return a + b -''') +""" + ) - script = testdir.makepyfile(''' + script = testdir.makepyfile( + """ import os import tempfile import pytest @@ -623,17 +577,17 @@ def bad(): def test_foobar(bad): assert mod.foobar(1, 2) == 3 -''') - result = testdir.runpytest('-v', '-s', - '--cov=mod', - '--cov-branch', - script, *opts.split()) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - '*mod* 100%', - '*1 passed*', - ]) +""" + ) + result = testdir.runpytest('-v', '-s', '--cov=mod', '--cov-branch', script, *opts.split()) + + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + '*mod* 100%', + '*1 passed*', + ] + ) assert result.ret == 0 @@ -647,7 +601,8 @@ def test_subprocess_with_path_aliasing(pytester, testdir, monkeypatch): parent_script.write(SCRIPT_PARENT) aliased.join('child_script.py').write(SCRIPT_CHILD) - testdir.tmpdir.join('.coveragerc').write(""" + testdir.tmpdir.join('.coveragerc').write( + """ [paths] source = src @@ -657,61 +612,59 @@ def test_subprocess_with_path_aliasing(pytester, testdir, monkeypatch): parent_script child_script parallel = true -""") - - monkeypatch.setitem(os.environ, 'PYTHONPATH', os.pathsep.join([ - os.environ.get('PYTHONPATH', ''), 'aliased'])) - result = testdir.runpytest('-v', - '--cov', - '--cov-report=term-missing', - parent_script) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'src[\\/]child_script* {CHILD_SCRIPT_RESULT}*', - f'src[\\/]parent_script* {PARENT_SCRIPT_RESULT}*', - ]) +""" + ) + + monkeypatch.setitem(os.environ, 'PYTHONPATH', os.pathsep.join([os.environ.get('PYTHONPATH', ''), 'aliased'])) + result = testdir.runpytest('-v', '--cov', '--cov-report=term-missing', parent_script) + + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + f'src[\\/]child_script* {CHILD_SCRIPT_RESULT}*', + f'src[\\/]parent_script* {PARENT_SCRIPT_RESULT}*', + ] + ) assert result.ret == 0 def test_show_missing_coveragerc(pytester, testdir, prop): script = testdir.makepyfile(prop.code) - testdir.tmpdir.join('.coveragerc').write(f""" + testdir.tmpdir.join('.coveragerc').write( + f""" [run] source = . {prop.conf} [report] show_missing = true -""") - - result = testdir.runpytest('-v', - '--cov', - '--cov-report=term', - script, *prop.args) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'Name * Stmts * Miss * Cover * Missing', - f'test_show_missing_coveragerc* {prop.result} * 11*', - '*10 passed*', - ]) +""" + ) + + result = testdir.runpytest('-v', '--cov', '--cov-report=term', script, *prop.args) + + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + 'Name * Stmts * Miss * Cover * Missing', + f'test_show_missing_coveragerc* {prop.result} * 11*', + '*10 passed*', + ] + ) assert result.ret == 0 def test_no_cov_on_fail(testdir): - script = testdir.makepyfile(''' + script = testdir.makepyfile( + """ def test_fail(): assert False -''') +""" + ) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - '--no-cov-on-fail', - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=term-missing', '--no-cov-on-fail', script) assert 'coverage: platform' not in result.stdout.str() result.stdout.fnmatch_lines(['*1 failed*']) @@ -719,63 +672,63 @@ def test_fail(): def test_no_cov(pytester, testdir, monkeypatch): script = testdir.makepyfile(SCRIPT) - testdir.makeini(""" + testdir.makeini( + """ [pytest] addopts=--no-cov - """) - result = testdir.runpytest('-vvv', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - '-rw', - script) - result.stdout.fnmatch_lines_random([ - 'WARNING: Coverage disabled via --no-cov switch!', - '*Coverage disabled via --no-cov switch!', - ]) + """ + ) + result = testdir.runpytest('-vvv', f'--cov={script.dirpath()}', '--cov-report=term-missing', '-rw', script) + result.stdout.fnmatch_lines_random( + [ + 'WARNING: Coverage disabled via --no-cov switch!', + '*Coverage disabled via --no-cov switch!', + ] + ) def test_cov_and_failure_report_on_fail(testdir): - script = testdir.makepyfile(SCRIPT + ''' + script = testdir.makepyfile( + SCRIPT + + """ def test_fail(p): assert False -''') +""" + ) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-fail-under=100', - '--cov-report=html', - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-fail-under=100', '--cov-report=html', script) - result.stdout.fnmatch_lines_random([ - '*10 failed*', - '*coverage: platform*', - '*FAIL Required test coverage of 100% not reached*', - '*assert False*', - ]) + result.stdout.fnmatch_lines_random( + [ + '*10 failed*', + '*coverage: platform*', + '*FAIL Required test coverage of 100% not reached*', + '*assert False*', + ] + ) @pytest.mark.skipif('sys.platform == "win32" or platform.python_implementation() == "PyPy"') def test_dist_combine_racecondition(testdir): - script = testdir.makepyfile(""" + script = testdir.makepyfile( + """ import pytest @pytest.mark.parametrize("foo", range(1000)) def test_foo(foo): -""" + "\n".join(f""" +""" + + '\n'.join( + f""" if foo == {i}: assert True -""" for i in range(1000))) - - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - '-n', '5', '-s', - script) - result.stdout.fnmatch_lines([ - 'test_dist_combine_racecondition* 0 * 100%*', - '*1000 passed*' - ]) +""" + for i in range(1000) + ) + ) + + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=term-missing', '-n', '5', '-s', script) + result.stdout.fnmatch_lines(['test_dist_combine_racecondition* 0 * 100%*', '*1000 passed*']) for line in chain(result.stdout.lines, result.stderr.lines): assert 'The following workers failed to return coverage data' not in line @@ -787,19 +740,18 @@ def test_foo(foo): def test_dist_collocated(pytester, testdir, prop): script = testdir.makepyfile(prop.code) testdir.tmpdir.join('.coveragerc').write(prop.fullconf) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - '--dist=load', - '--tx=2*popen', - max_worker_restart_0, - script, *prop.args) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'test_dist_collocated* {prop.result} *', - '*10 passed*' - ]) + result = testdir.runpytest( + '-v', + f'--cov={script.dirpath()}', + '--cov-report=term-missing', + '--dist=load', + '--tx=2*popen', + max_worker_restart_0, + script, + *prop.args, + ) + + result.stdout.fnmatch_lines(['*- coverage: platform *, python * -*', f'test_dist_collocated* {prop.result} *', '*10 passed*']) assert result.ret == 0 @@ -808,31 +760,33 @@ def test_dist_not_collocated(pytester, testdir, prop): script = testdir.makepyfile(prop.code) dir1 = testdir.mkdir('dir1') dir2 = testdir.mkdir('dir2') - testdir.tmpdir.join('.coveragerc').write(f''' + testdir.tmpdir.join('.coveragerc').write( + f""" [run] {prop.conf} [paths] source = . dir1 - dir2''') - - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - '--dist=load', - f'--tx=popen//chdir={dir1}', - f'--tx=popen//chdir={dir2}', - f'--rsyncdir={script.basename}', - '--rsyncdir=.coveragerc', - max_worker_restart_0, '-s', - script, *prop.args) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'test_dist_not_collocated* {prop.result} *', - '*10 passed*' - ]) + dir2""" + ) + + result = testdir.runpytest( + '-v', + f'--cov={script.dirpath()}', + '--cov-report=term-missing', + '--dist=load', + f'--tx=popen//chdir={dir1}', + f'--tx=popen//chdir={dir2}', + f'--rsyncdir={script.basename}', + '--rsyncdir=.coveragerc', + max_worker_restart_0, + '-s', + script, + *prop.args, + ) + + result.stdout.fnmatch_lines(['*- coverage: platform *, python * -*', f'test_dist_not_collocated* {prop.result} *', '*10 passed*']) assert result.ret == 0 @@ -841,7 +795,8 @@ def test_dist_not_collocated_coveragerc_source(pytester, testdir, prop): script = testdir.makepyfile(prop.code) dir1 = testdir.mkdir('dir1') dir2 = testdir.mkdir('dir2') - testdir.tmpdir.join('.coveragerc').write(f''' + testdir.tmpdir.join('.coveragerc').write( + f""" [run] {prop.conf} source = {script.dirpath()} @@ -849,66 +804,67 @@ def test_dist_not_collocated_coveragerc_source(pytester, testdir, prop): source = . dir1 - dir2''') - - result = testdir.runpytest('-v', - '--cov', - '--cov-report=term-missing', - '--dist=load', - f'--tx=popen//chdir={dir1}', - f'--tx=popen//chdir={dir2}', - f'--rsyncdir={script.basename}', - '--rsyncdir=.coveragerc', - max_worker_restart_0, '-s', - script, *prop.args) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'test_dist_not_collocated* {prop.result} *', - '*10 passed*' - ]) + dir2""" + ) + + result = testdir.runpytest( + '-v', + '--cov', + '--cov-report=term-missing', + '--dist=load', + f'--tx=popen//chdir={dir1}', + f'--tx=popen//chdir={dir2}', + f'--rsyncdir={script.basename}', + '--rsyncdir=.coveragerc', + max_worker_restart_0, + '-s', + script, + *prop.args, + ) + + result.stdout.fnmatch_lines(['*- coverage: platform *, python * -*', f'test_dist_not_collocated* {prop.result} *', '*10 passed*']) assert result.ret == 0 def test_central_subprocess(testdir): - scripts = testdir.makepyfile(parent_script=SCRIPT_PARENT, - child_script=SCRIPT_CHILD) + scripts = testdir.makepyfile(parent_script=SCRIPT_PARENT, child_script=SCRIPT_CHILD) parent_script = scripts.dirpath().join('parent_script.py') - result = testdir.runpytest('-v', - f'--cov={scripts.dirpath()}', - '--cov-report=term-missing', - parent_script) + result = testdir.runpytest('-v', f'--cov={scripts.dirpath()}', '--cov-report=term-missing', parent_script) - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'child_script* {CHILD_SCRIPT_RESULT}*', - f'parent_script* {PARENT_SCRIPT_RESULT}*', - ]) + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + f'child_script* {CHILD_SCRIPT_RESULT}*', + f'parent_script* {PARENT_SCRIPT_RESULT}*', + ] + ) assert result.ret == 0 def test_central_subprocess_change_cwd(testdir): - scripts = testdir.makepyfile(parent_script=SCRIPT_PARENT_CHANGE_CWD, - child_script=SCRIPT_CHILD) + scripts = testdir.makepyfile(parent_script=SCRIPT_PARENT_CHANGE_CWD, child_script=SCRIPT_CHILD) parent_script = scripts.dirpath().join('parent_script.py') - testdir.makefile('', coveragerc=""" + testdir.makefile( + '', + coveragerc=""" [run] branch = true parallel = true -""") - - result = testdir.runpytest('-v', '-s', - f'--cov={scripts.dirpath()}', - '--cov-config=coveragerc', - '--cov-report=term-missing', - parent_script) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'*child_script* {CHILD_SCRIPT_RESULT}*', - '*parent_script* 100%*', - ]) +""", + ) + + result = testdir.runpytest( + '-v', '-s', f'--cov={scripts.dirpath()}', '--cov-config=coveragerc', '--cov-report=term-missing', parent_script + ) + + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + f'*child_script* {CHILD_SCRIPT_RESULT}*', + '*parent_script* 100%*', + ] + ) assert result.ret == 0 @@ -917,126 +873,130 @@ def test_central_subprocess_change_cwd_with_pythonpath(pytester, testdir, monkey parent_script = stuff.join('parent_script.py') parent_script.write(SCRIPT_PARENT_CHANGE_CWD_IMPORT_CHILD) stuff.join('child_script.py').write(SCRIPT_CHILD) - testdir.makefile('', coveragerc=""" + testdir.makefile( + '', + coveragerc=""" [run] parallel = true -""") +""", + ) monkeypatch.setitem(os.environ, 'PYTHONPATH', str(stuff)) - result = testdir.runpytest('-vv', '-s', - '--cov=child_script', - '--cov-config=coveragerc', - '--cov-report=term-missing', - '--cov-branch', - parent_script) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'*child_script* {CHILD_SCRIPT_RESULT}*', - ]) + result = testdir.runpytest( + '-vv', '-s', '--cov=child_script', '--cov-config=coveragerc', '--cov-report=term-missing', '--cov-branch', parent_script + ) + + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + f'*child_script* {CHILD_SCRIPT_RESULT}*', + ] + ) assert result.ret == 0 def test_central_subprocess_no_subscript(testdir): - script = testdir.makepyfile(""" + script = testdir.makepyfile( + """ import subprocess, sys def test_foo(): subprocess.check_call([sys.executable, '-c', 'print("Hello World")']) -""") - testdir.makefile('', coveragerc=""" +""" + ) + testdir.makefile( + '', + coveragerc=""" [run] parallel = true -""") - result = testdir.runpytest('-v', - '--cov-config=coveragerc', - f'--cov={script.dirpath()}', - '--cov-branch', - script) - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'test_central_subprocess_no_subscript* * 3 * 0 * 100%*', - ]) +""", + ) + result = testdir.runpytest('-v', '--cov-config=coveragerc', f'--cov={script.dirpath()}', '--cov-branch', script) + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + 'test_central_subprocess_no_subscript* * 3 * 0 * 100%*', + ] + ) assert result.ret == 0 @pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"') def test_dist_subprocess_collocated(testdir): - scripts = testdir.makepyfile(parent_script=SCRIPT_PARENT, - child_script=SCRIPT_CHILD) + scripts = testdir.makepyfile(parent_script=SCRIPT_PARENT, child_script=SCRIPT_CHILD) parent_script = scripts.dirpath().join('parent_script.py') - result = testdir.runpytest('-v', - f'--cov={scripts.dirpath()}', - '--cov-report=term-missing', - '--dist=load', - '--tx=2*popen', - max_worker_restart_0, - parent_script) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'child_script* {CHILD_SCRIPT_RESULT}*', - f'parent_script* {PARENT_SCRIPT_RESULT}*', - ]) + result = testdir.runpytest( + '-v', f'--cov={scripts.dirpath()}', '--cov-report=term-missing', '--dist=load', '--tx=2*popen', max_worker_restart_0, parent_script + ) + + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + f'child_script* {CHILD_SCRIPT_RESULT}*', + f'parent_script* {PARENT_SCRIPT_RESULT}*', + ] + ) assert result.ret == 0 @pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"') def test_dist_subprocess_not_collocated(pytester, testdir, tmpdir): - scripts = testdir.makepyfile(parent_script=SCRIPT_PARENT, - child_script=SCRIPT_CHILD) + scripts = testdir.makepyfile(parent_script=SCRIPT_PARENT, child_script=SCRIPT_CHILD) parent_script = scripts.dirpath().join('parent_script.py') child_script = scripts.dirpath().join('child_script.py') dir1 = tmpdir.mkdir('dir1') dir2 = tmpdir.mkdir('dir2') - testdir.tmpdir.join('.coveragerc').write(f''' + testdir.tmpdir.join('.coveragerc').write( + f""" [paths] source = {scripts.dirpath()} */dir1 */dir2 -''') - result = testdir.runpytest('-v', - f'--cov={scripts.dirpath()}', - '--dist=load', - f'--tx=popen//chdir={dir1}', - f'--tx=popen//chdir={dir2}', - f'--rsyncdir={child_script}', - f'--rsyncdir={parent_script}', - '--rsyncdir=.coveragerc', - max_worker_restart_0, - parent_script) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'child_script* {CHILD_SCRIPT_RESULT}*', - f'parent_script* {PARENT_SCRIPT_RESULT}*', - ]) +""" + ) + result = testdir.runpytest( + '-v', + f'--cov={scripts.dirpath()}', + '--dist=load', + f'--tx=popen//chdir={dir1}', + f'--tx=popen//chdir={dir2}', + f'--rsyncdir={child_script}', + f'--rsyncdir={parent_script}', + '--rsyncdir=.coveragerc', + max_worker_restart_0, + parent_script, + ) + + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + f'child_script* {CHILD_SCRIPT_RESULT}*', + f'parent_script* {PARENT_SCRIPT_RESULT}*', + ] + ) assert result.ret == 0 def test_invalid_coverage_source(testdir): script = testdir.makepyfile(SCRIPT) - testdir.makeini(""" + testdir.makeini( + """ [pytest] console_output_style=classic - """) - result = testdir.runpytest('-v', - '--cov=non_existent_module', - '--cov-report=term-missing', - script) - - result.stdout.fnmatch_lines([ - '*10 passed*' - ]) - result.stderr.fnmatch_lines([ - '*No data was collected.*' - ]) - result.stdout.fnmatch_lines([ - '*Failed to generate report: No data to report.', - ]) + """ + ) + result = testdir.runpytest('-v', '--cov=non_existent_module', '--cov-report=term-missing', script) + + result.stdout.fnmatch_lines(['*10 passed*']) + result.stderr.fnmatch_lines(['*No data was collected.*']) + result.stdout.fnmatch_lines( + [ + '*Failed to generate report: No data to report.', + ] + ) assert result.ret == 0 matching_lines = [line for line in result.outlines if '%' in line] @@ -1045,75 +1005,64 @@ def test_invalid_coverage_source(testdir): @pytest.mark.skipif("'dev' in pytest.__version__") @pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"') -@pytest.mark.skipif('tuple(map(int, xdist.__version__.split("."))) >= (2, 3, 0)', - reason="Since pytest-xdist 2.3.0 the parent sys.path is copied in the child process") +@pytest.mark.skipif( + 'tuple(map(int, xdist.__version__.split("."))) >= (2, 3, 0)', + reason='Since pytest-xdist 2.3.0 the parent sys.path is copied in the child process', +) def test_dist_missing_data(testdir): """Test failure when using a worker without pytest-cov installed.""" venv_path = os.path.join(str(testdir.tmpdir), 'venv') virtualenv.cli_run([venv_path]) if sys.platform == 'win32': - if platform.python_implementation() == "PyPy": + if platform.python_implementation() == 'PyPy': exe = os.path.join(venv_path, 'bin', 'python.exe') else: exe = os.path.join(venv_path, 'Scripts', 'python.exe') else: exe = os.path.join(venv_path, 'bin', 'python') - subprocess.check_call([ - exe, - '-mpip', - 'install', - f'py=={py.__version__}', - f'pytest=={pytest.__version__}', - f'pytest_xdist=={xdist.__version__}' - - ]) + subprocess.check_call( + [exe, '-mpip', 'install', f'py=={py.__version__}', f'pytest=={pytest.__version__}', f'pytest_xdist=={xdist.__version__}'] + ) script = testdir.makepyfile(SCRIPT) - result = testdir.runpytest('-v', - '--assert=plain', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - '--dist=load', - f'--tx=popen//python={exe}', - max_worker_restart_0, - str(script)) - result.stdout.fnmatch_lines([ - 'The following workers failed to return coverage data, ensure that pytest-cov is installed on these workers.' - ]) + result = testdir.runpytest( + '-v', + '--assert=plain', + f'--cov={script.dirpath()}', + '--cov-report=term-missing', + '--dist=load', + f'--tx=popen//python={exe}', + max_worker_restart_0, + str(script), + ) + result.stdout.fnmatch_lines( + ['The following workers failed to return coverage data, ensure that pytest-cov is installed on these workers.'] + ) def test_funcarg(testdir): script = testdir.makepyfile(SCRIPT_FUNCARG) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=term-missing', script) - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'test_funcarg* 3 * 100%*', - '*1 passed*' - ]) + result.stdout.fnmatch_lines(['*- coverage: platform *, python * -*', 'test_funcarg* 3 * 100%*', '*1 passed*']) assert result.ret == 0 def test_funcarg_not_active(testdir): script = testdir.makepyfile(SCRIPT_FUNCARG_NOT_ACTIVE) - result = testdir.runpytest('-v', - script) + result = testdir.runpytest('-v', script) - result.stdout.fnmatch_lines([ - '*1 passed*' - ]) + result.stdout.fnmatch_lines(['*1 passed*']) assert result.ret == 0 @pytest.mark.skipif('sys.platform == "win32"', reason="SIGTERM isn't really supported on Windows") -@pytest.mark.xfail('platform.python_implementation() == "PyPy"', reason="Interpreter seems buggy") +@pytest.mark.xfail('platform.python_implementation() == "PyPy"', reason='Interpreter seems buggy') def test_cleanup_on_sigterm(testdir): - script = testdir.makepyfile(''' + script = testdir.makepyfile( + ''' import os, signal, subprocess, sys, time def cleanup(num, frame): @@ -1141,30 +1090,28 @@ def test_run(): time.sleep(10) except BaseException as exc: print("captured %r" % exc) -''') +''' + ) - result = testdir.runpytest('-vv', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - script) + result = testdir.runpytest('-vv', f'--cov={script.dirpath()}', '--cov-report=term-missing', script) - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'test_cleanup_on_sigterm* 26-27', - '*1 passed*' - ]) + result.stdout.fnmatch_lines(['*- coverage: platform *, python * -*', 'test_cleanup_on_sigterm* 26-27', '*1 passed*']) assert result.ret == 0 @pytest.mark.skipif('sys.platform != "win32"') -@pytest.mark.parametrize('setup', [ - ('signal.signal(signal.SIGBREAK, signal.SIG_DFL); cleanup_on_signal(signal.SIGBREAK)', '87% 21-22'), - ('cleanup_on_signal(signal.SIGBREAK)', '87% 21-22'), - ('cleanup()', '73% 19-22'), -]) +@pytest.mark.parametrize( + 'setup', + [ + ('signal.signal(signal.SIGBREAK, signal.SIG_DFL); cleanup_on_signal(signal.SIGBREAK)', '87% 21-22'), + ('cleanup_on_signal(signal.SIGBREAK)', '87% 21-22'), + ('cleanup()', '73% 19-22'), + ], +) def test_cleanup_on_sigterm_sig_break(pytester, testdir, setup): # worth a read: https://stefan.sofa-rockers.org/2013/08/15/handling-sub-process-hierarchies-python-linux-os-x/ - script = testdir.makepyfile(''' + script = testdir.makepyfile( + """ import os, signal, subprocess, sys, time def test_run(): @@ -1181,37 +1128,37 @@ def test_run(): if __name__ == "__main__": from pytest_cov.embed import cleanup_on_signal, cleanup - ''' + setup[0] + ''' + """ + + setup[0] + + """ try: time.sleep(10) except BaseException as exc: print("captured %r" % exc) -''') +""" + ) - result = testdir.runpytest('-vv', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - script) + result = testdir.runpytest('-vv', f'--cov={script.dirpath()}', '--cov-report=term-missing', script) - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'test_cleanup_on_sigterm* {setup[1]}', - '*1 passed*' - ]) + result.stdout.fnmatch_lines(['*- coverage: platform *, python * -*', f'test_cleanup_on_sigterm* {setup[1]}', '*1 passed*']) assert result.ret == 0 @pytest.mark.skipif('sys.platform == "win32"', reason="SIGTERM isn't really supported on Windows") -@pytest.mark.xfail('sys.platform == "darwin"', reason="Something weird going on Macs...") -@pytest.mark.xfail('platform.python_implementation() == "PyPy"', reason="Interpreter seems buggy") -@pytest.mark.parametrize('setup', [ - ('signal.signal(signal.SIGTERM, signal.SIG_DFL); cleanup_on_sigterm()', '88% 18-19'), - ('cleanup_on_sigterm()', '88% 18-19'), - ('cleanup()', '75% 16-19'), -]) +@pytest.mark.xfail('sys.platform == "darwin"', reason='Something weird going on Macs...') +@pytest.mark.xfail('platform.python_implementation() == "PyPy"', reason='Interpreter seems buggy') +@pytest.mark.parametrize( + 'setup', + [ + ('signal.signal(signal.SIGTERM, signal.SIG_DFL); cleanup_on_sigterm()', '88% 18-19'), + ('cleanup_on_sigterm()', '88% 18-19'), + ('cleanup()', '75% 16-19'), + ], +) def test_cleanup_on_sigterm_sig_dfl(pytester, testdir, setup): - script = testdir.makepyfile(''' + script = testdir.makepyfile( + """ import os, signal, subprocess, sys, time def test_run(): @@ -1225,33 +1172,29 @@ def test_run(): if __name__ == "__main__": from pytest_cov.embed import cleanup_on_sigterm, cleanup - ''' + setup[0] + ''' + """ + + setup[0] + + """ try: time.sleep(10) except BaseException as exc: print("captured %r" % exc) -''') - - result = testdir.runpytest('-vv', - '--assert=plain', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - script) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'test_cleanup_on_sigterm* {setup[1]}', - '*1 passed*' - ]) +""" + ) + + result = testdir.runpytest('-vv', '--assert=plain', f'--cov={script.dirpath()}', '--cov-report=term-missing', script) + + result.stdout.fnmatch_lines(['*- coverage: platform *, python * -*', f'test_cleanup_on_sigterm* {setup[1]}', '*1 passed*']) assert result.ret == 0 -@pytest.mark.skipif('sys.platform == "win32"', reason="SIGINT is subtly broken on Windows") -@pytest.mark.xfail('sys.platform == "darwin"', reason="Something weird going on Macs...") -@pytest.mark.xfail('platform.python_implementation() == "PyPy"', reason="Interpreter seems buggy") +@pytest.mark.skipif('sys.platform == "win32"', reason='SIGINT is subtly broken on Windows') +@pytest.mark.xfail('sys.platform == "darwin"', reason='Something weird going on Macs...') +@pytest.mark.xfail('platform.python_implementation() == "PyPy"', reason='Interpreter seems buggy') def test_cleanup_on_sigterm_sig_dfl_sigint(testdir): - script = testdir.makepyfile(''' + script = testdir.makepyfile( + ''' import os, signal, subprocess, sys, time def test_run(): @@ -1272,26 +1215,20 @@ def test_run(): time.sleep(10) except BaseException as exc: print("captured %r" % exc) -''') - - result = testdir.runpytest('-vv', - '--assert=plain', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - script) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'test_cleanup_on_sigterm* 88% 19-20', - '*1 passed*' - ]) +''' + ) + + result = testdir.runpytest('-vv', '--assert=plain', f'--cov={script.dirpath()}', '--cov-report=term-missing', script) + + result.stdout.fnmatch_lines(['*- coverage: platform *, python * -*', 'test_cleanup_on_sigterm* 88% 19-20', '*1 passed*']) assert result.ret == 0 -@pytest.mark.skipif('sys.platform == "win32"', reason="fork not available on Windows") -@pytest.mark.xfail('platform.python_implementation() == "PyPy"', reason="Interpreter seems buggy") +@pytest.mark.skipif('sys.platform == "win32"', reason='fork not available on Windows') +@pytest.mark.xfail('platform.python_implementation() == "PyPy"', reason='Interpreter seems buggy') def test_cleanup_on_sigterm_sig_ign(testdir): - script = testdir.makepyfile(''' + script = testdir.makepyfile( + """ import os, signal, subprocess, sys, time def test_run(): @@ -1316,42 +1253,35 @@ def test_run(): time.sleep(10) except BaseException as exc: print("captured %r" % exc) - ''') - - result = testdir.runpytest('-vv', - '--assert=plain', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - script) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - 'test_cleanup_on_sigterm* 89% 23-24', - '*1 passed*' - ]) + """ + ) + + result = testdir.runpytest('-vv', '--assert=plain', f'--cov={script.dirpath()}', '--cov-report=term-missing', script) + + result.stdout.fnmatch_lines(['*- coverage: platform *, python * -*', 'test_cleanup_on_sigterm* 89% 23-24', '*1 passed*']) assert result.ret == 0 -MODULE = ''' +MODULE = """ def func(): return 1 -''' +""" -CONFTEST = ''' +CONFTEST = """ import mod mod.func() -''' +""" -BASIC_TEST = ''' +BASIC_TEST = """ def test_basic(): x = True assert x -''' +""" CONF_RESULT = 'mod* 2 * 100%*' @@ -1360,10 +1290,7 @@ def test_cover_conftest(testdir): testdir.makepyfile(mod=MODULE) testdir.makeconftest(CONFTEST) script = testdir.makepyfile(BASIC_TEST) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=term-missing', script) assert result.ret == 0 result.stdout.fnmatch_lines([CONF_RESULT]) @@ -1382,16 +1309,9 @@ def mock_run(*args, **kwargs): if hasattr(testdir, '_pytester'): monkeypatch.setattr(testdir._pytester, 'run', mock_run) assert testdir._pytester.run is mock_run - with testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--looponfail', - script) as process: + with testdir.runpytest('-v', f'--cov={script.dirpath()}', '--looponfail', script) as process: with dump_on_error(process.read): - wait_for_strings( - process.read, - 30, # 30 seconds - 'Stmts Miss Cover' - ) + wait_for_strings(process.read, 30, 'Stmts Miss Cover') # 30 seconds @pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"') @@ -1399,20 +1319,17 @@ def test_cover_conftest_dist(testdir): testdir.makepyfile(mod=MODULE) testdir.makeconftest(CONFTEST) script = testdir.makepyfile(BASIC_TEST) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - '--dist=load', - '--tx=2*popen', - max_worker_restart_0, - script) + result = testdir.runpytest( + '-v', f'--cov={script.dirpath()}', '--cov-report=term-missing', '--dist=load', '--tx=2*popen', max_worker_restart_0, script + ) assert result.ret == 0 result.stdout.fnmatch_lines([CONF_RESULT]) def test_no_cover_marker(testdir): testdir.makepyfile(mod=MODULE) - script = testdir.makepyfile(''' + script = testdir.makepyfile( + """ import pytest import mod import subprocess @@ -1422,18 +1339,17 @@ def test_no_cover_marker(testdir): def test_basic(): mod.func() subprocess.check_call([sys.executable, '-c', 'from mod import func; func()']) -''') - result = testdir.runpytest('-v', '-ra', '--strict', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - script) +""" + ) + result = testdir.runpytest('-v', '-ra', '--strict', f'--cov={script.dirpath()}', '--cov-report=term-missing', script) assert result.ret == 0 result.stdout.fnmatch_lines(['mod* 2 * 1 * 50% * 2']) def test_no_cover_fixture(testdir): testdir.makepyfile(mod=MODULE) - script = testdir.makepyfile(''' + script = testdir.makepyfile( + """ import mod import subprocess import sys @@ -1441,23 +1357,21 @@ def test_no_cover_fixture(testdir): def test_basic(no_cover): mod.func() subprocess.check_call([sys.executable, '-c', 'from mod import func; func()']) -''') - result = testdir.runpytest('-v', '-ra', '--strict', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - script) +""" + ) + result = testdir.runpytest('-v', '-ra', '--strict', f'--cov={script.dirpath()}', '--cov-report=term-missing', script) assert result.ret == 0 result.stdout.fnmatch_lines(['mod* 2 * 1 * 50% * 2']) -COVERAGERC = ''' +COVERAGERC = """ [report] # Regexes for lines to exclude from consideration exclude_lines = raise NotImplementedError -''' +""" -EXCLUDED_TEST = ''' +EXCLUDED_TEST = """ def func(): raise NotImplementedError @@ -1466,7 +1380,7 @@ def test_basic(): x = True assert x -''' +""" EXCLUDED_RESULT = '4 * 100%*' @@ -1474,11 +1388,7 @@ def test_basic(): def test_coveragerc(testdir): testdir.makefile('', coveragerc=COVERAGERC) script = testdir.makepyfile(EXCLUDED_TEST) - result = testdir.runpytest('-v', - '--cov-config=coveragerc', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - script) + result = testdir.runpytest('-v', '--cov-config=coveragerc', f'--cov={script.dirpath()}', '--cov-report=term-missing', script) assert result.ret == 0 result.stdout.fnmatch_lines([f'test_coveragerc* {EXCLUDED_RESULT}']) @@ -1487,25 +1397,20 @@ def test_coveragerc(testdir): def test_coveragerc_dist(testdir): testdir.makefile('', coveragerc=COVERAGERC) script = testdir.makepyfile(EXCLUDED_TEST) - result = testdir.runpytest('-v', - '--cov-config=coveragerc', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - '-n', '2', - max_worker_restart_0, - script) + result = testdir.runpytest( + '-v', '--cov-config=coveragerc', f'--cov={script.dirpath()}', '--cov-report=term-missing', '-n', '2', max_worker_restart_0, script + ) assert result.ret == 0 - result.stdout.fnmatch_lines( - [f'test_coveragerc_dist* {EXCLUDED_RESULT}']) + result.stdout.fnmatch_lines([f'test_coveragerc_dist* {EXCLUDED_RESULT}']) -SKIP_COVERED_COVERAGERC = ''' +SKIP_COVERED_COVERAGERC = """ [report] skip_covered = True -''' +""" -SKIP_COVERED_TEST = ''' +SKIP_COVERED_TEST = """ def func(): return "full coverage" @@ -1513,21 +1418,16 @@ def func(): def test_basic(): assert func() == "full coverage" -''' +""" SKIP_COVERED_RESULT = '1 file skipped due to complete coverage.' -@pytest.mark.parametrize('report_option', [ - 'term-missing:skip-covered', - 'term:skip-covered']) +@pytest.mark.parametrize('report_option', ['term-missing:skip-covered', 'term:skip-covered']) def test_skip_covered_cli(pytester, testdir, report_option): testdir.makefile('', coveragerc=SKIP_COVERED_COVERAGERC) script = testdir.makepyfile(SKIP_COVERED_TEST) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - f'--cov-report={report_option}', - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', f'--cov-report={report_option}', script) assert result.ret == 0 result.stdout.fnmatch_lines([SKIP_COVERED_RESULT]) @@ -1535,81 +1435,58 @@ def test_skip_covered_cli(pytester, testdir, report_option): def test_skip_covered_coveragerc_config(testdir): testdir.makefile('', coveragerc=SKIP_COVERED_COVERAGERC) script = testdir.makepyfile(SKIP_COVERED_TEST) - result = testdir.runpytest('-v', - '--cov-config=coveragerc', - f'--cov={script.dirpath()}', - script) + result = testdir.runpytest('-v', '--cov-config=coveragerc', f'--cov={script.dirpath()}', script) assert result.ret == 0 result.stdout.fnmatch_lines([SKIP_COVERED_RESULT]) -CLEAR_ENVIRON_TEST = ''' +CLEAR_ENVIRON_TEST = """ import os def test_basic(): os.environ.clear() -''' +""" def test_clear_environ(testdir): script = testdir.makepyfile(CLEAR_ENVIRON_TEST) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=term-missing', - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=term-missing', script) assert result.ret == 0 -SCRIPT_SIMPLE = ''' +SCRIPT_SIMPLE = """ def test_foo(): assert 1 == 1 x = True assert x -''' +""" SCRIPT_SIMPLE_RESULT = '4 * 100%' -@pytest.mark.skipif('tuple(map(int, xdist.__version__.split("."))) >= (3, 0, 2)', - reason="--boxed option was removed in version 3.0.2") +@pytest.mark.skipif('tuple(map(int, xdist.__version__.split("."))) >= (3, 0, 2)', reason='--boxed option was removed in version 3.0.2') @pytest.mark.skipif('sys.platform == "win32"') def test_dist_boxed(testdir): script = testdir.makepyfile(SCRIPT_SIMPLE) - result = testdir.runpytest('-v', - '--assert=plain', - f'--cov={script.dirpath()}', - '--boxed', - script) + result = testdir.runpytest('-v', '--assert=plain', f'--cov={script.dirpath()}', '--boxed', script) - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'test_dist_boxed* {SCRIPT_SIMPLE_RESULT}*', - '*1 passed*' - ]) + result.stdout.fnmatch_lines(['*- coverage: platform *, python * -*', f'test_dist_boxed* {SCRIPT_SIMPLE_RESULT}*', '*1 passed*']) assert result.ret == 0 @pytest.mark.skipif('sys.platform == "win32"') -@pytest.mark.skipif('sys.version_info[0] > 2 and platform.python_implementation() == "PyPy"', - reason="strange optimization on PyPy3") +@pytest.mark.skipif('sys.version_info[0] > 2 and platform.python_implementation() == "PyPy"', reason='strange optimization on PyPy3') def test_dist_bare_cov(testdir): script = testdir.makepyfile(SCRIPT_SIMPLE) - result = testdir.runpytest('-v', - '--cov', - '-n', '1', - script) + result = testdir.runpytest('-v', '--cov', '-n', '1', script) - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'test_dist_bare_cov* {SCRIPT_SIMPLE_RESULT}*', - '*1 passed*' - ]) + result.stdout.fnmatch_lines(['*- coverage: platform *, python * -*', f'test_dist_bare_cov* {SCRIPT_SIMPLE_RESULT}*', '*1 passed*']) assert result.ret == 0 @@ -1626,28 +1503,21 @@ class ns: def test_default_output_setting(testdir): script = testdir.makepyfile(SCRIPT) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', script) - result.stdout.fnmatch_lines([ - '*coverage*' - ]) + result.stdout.fnmatch_lines(['*coverage*']) assert result.ret == 0 def test_disabled_output(testdir): script = testdir.makepyfile(SCRIPT) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-report=', - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=', script) stdout = result.stdout.str() # We don't want the path to the executable to fail the test if we happen # to put the project in a directory with "coverage" in it. - stdout = stdout.replace(sys.executable, "") + stdout = stdout.replace(sys.executable, '') assert 'coverage' not in stdout assert result.ret == 0 @@ -1657,8 +1527,7 @@ def test_coverage_file(testdir): data_file_name = 'covdata' os.environ['COVERAGE_FILE'] = data_file_name try: - result = testdir.runpytest('-v', f'--cov={script.dirpath()}', - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', script) assert result.ret == 0 data_file = testdir.tmpdir.join(data_file_name) assert data_file.check() @@ -1668,14 +1537,15 @@ def test_coverage_file(testdir): def test_external_data_file(testdir): script = testdir.makepyfile(SCRIPT) - testdir.tmpdir.join('.coveragerc').write(""" + testdir.tmpdir.join('.coveragerc').write( + """ [run] data_file = %s -""" % testdir.tmpdir.join('some/special/place/coverage-data').ensure()) +""" + % testdir.tmpdir.join('some/special/place/coverage-data').ensure() + ) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', script) assert result.ret == 0 assert glob.glob(str(testdir.tmpdir.join('some/special/place/coverage-data*'))) @@ -1683,33 +1553,31 @@ def test_external_data_file(testdir): @pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"') def test_external_data_file_xdist(testdir): script = testdir.makepyfile(SCRIPT) - testdir.tmpdir.join('.coveragerc').write(""" + testdir.tmpdir.join('.coveragerc').write( + """ [run] parallel = true data_file = %s -""" % testdir.tmpdir.join('some/special/place/coverage-data').ensure()) +""" + % testdir.tmpdir.join('some/special/place/coverage-data').ensure() + ) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '-n', '1', - max_worker_restart_0, - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '-n', '1', max_worker_restart_0, script) assert result.ret == 0 assert glob.glob(str(testdir.tmpdir.join('some/special/place/coverage-data*'))) @pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"') def test_xdist_no_data_collected(testdir): - testdir.makepyfile(target="x = 123") - script = testdir.makepyfile(""" + testdir.makepyfile(target='x = 123') + script = testdir.makepyfile( + """ import target def test_foobar(): assert target.x == 123 -""") - result = testdir.runpytest('-v', - '--cov=target', - '-n', '1', - script) +""" + ) + result = testdir.runpytest('-v', '--cov=target', '-n', '1', script) assert 'no-data-collected' not in result.stderr.str() assert 'no-data-collected' not in result.stdout.str() assert 'module-not-imported' not in result.stderr.str() @@ -1719,11 +1587,9 @@ def test_foobar(): def test_external_data_file_negative(testdir): script = testdir.makepyfile(SCRIPT) - testdir.tmpdir.join('.coveragerc').write("") + testdir.tmpdir.join('.coveragerc').write('') - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - script) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', script) assert result.ret == 0 assert glob.glob(str(testdir.tmpdir.join('.coverage*'))) @@ -1732,67 +1598,65 @@ def test_external_data_file_negative(testdir): def test_append_coverage(pytester, testdir, opts, prop): script = testdir.makepyfile(test_1=prop.code) testdir.tmpdir.join('.coveragerc').write(prop.fullconf) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - script, - *opts.split() + prop.args) - result.stdout.fnmatch_lines([ - f'test_1* {prop.result}*', - ]) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', script, *opts.split() + prop.args) + result.stdout.fnmatch_lines( + [ + f'test_1* {prop.result}*', + ] + ) script2 = testdir.makepyfile(test_2=prop.code2) - result = testdir.runpytest('-v', - '--cov-append', - f'--cov={script2.dirpath()}', - script2, - *opts.split() + prop.args) - result.stdout.fnmatch_lines([ - f'test_1* {prop.result}*', - f'test_2* {prop.result2}*', - ]) + result = testdir.runpytest('-v', '--cov-append', f'--cov={script2.dirpath()}', script2, *opts.split() + prop.args) + result.stdout.fnmatch_lines( + [ + f'test_1* {prop.result}*', + f'test_2* {prop.result2}*', + ] + ) @xdist_params def test_do_not_append_coverage(pytester, testdir, opts, prop): script = testdir.makepyfile(test_1=prop.code) testdir.tmpdir.join('.coveragerc').write(prop.fullconf) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - script, - *opts.split()+prop.args) - result.stdout.fnmatch_lines([ - f'test_1* {prop.result}*', - ]) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', script, *opts.split() + prop.args) + result.stdout.fnmatch_lines( + [ + f'test_1* {prop.result}*', + ] + ) script2 = testdir.makepyfile(test_2=prop.code2) - result = testdir.runpytest('-v', - f'--cov={script2.dirpath()}', - script2, - *opts.split()+prop.args) - result.stdout.fnmatch_lines([ - 'test_1* 0%', - f'test_2* {prop.result2}*', - ]) + result = testdir.runpytest('-v', f'--cov={script2.dirpath()}', script2, *opts.split() + prop.args) + result.stdout.fnmatch_lines( + [ + 'test_1* 0%', + f'test_2* {prop.result2}*', + ] + ) @pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"') def test_append_coverage_subprocess(testdir): - scripts = testdir.makepyfile(parent_script=SCRIPT_PARENT, - child_script=SCRIPT_CHILD) + scripts = testdir.makepyfile(parent_script=SCRIPT_PARENT, child_script=SCRIPT_CHILD) parent_script = scripts.dirpath().join('parent_script.py') - result = testdir.runpytest('-v', - f'--cov={scripts.dirpath()}', - '--cov-append', - '--cov-report=term-missing', - '--dist=load', - '--tx=2*popen', - max_worker_restart_0, - parent_script) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'child_script* {CHILD_SCRIPT_RESULT}*', - f'parent_script* {PARENT_SCRIPT_RESULT}*', - ]) + result = testdir.runpytest( + '-v', + f'--cov={scripts.dirpath()}', + '--cov-append', + '--cov-report=term-missing', + '--dist=load', + '--tx=2*popen', + max_worker_restart_0, + parent_script, + ) + + result.stdout.fnmatch_lines( + [ + '*- coverage: platform *, python * -*', + f'child_script* {CHILD_SCRIPT_RESULT}*', + f'parent_script* {PARENT_SCRIPT_RESULT}*', + ] + ) assert result.ret == 0 @@ -1804,7 +1668,7 @@ class SpecificError(Exception): pass def bad_init(): - raise SpecificError() + raise SpecificError buff = StringIO() @@ -1814,78 +1678,44 @@ def bad_init(): monkeypatch.setattr(sys, 'stderr', buff) monkeypatch.setitem(os.environ, 'COV_CORE_SOURCE', 'foobar') exec(payload) - expected = ( - "pytest-cov: Failed to setup subprocess coverage. " - "Environ: {'COV_CORE_SOURCE': 'foobar'} Exception: SpecificError()\n" - ) + expected = "pytest-cov: Failed to setup subprocess coverage. " "Environ: {'COV_CORE_SOURCE': 'foobar'} Exception: SpecificError()\n" assert buff.getvalue() == expected def test_double_cov(testdir): script = testdir.makepyfile(SCRIPT_SIMPLE) - result = testdir.runpytest('-v', - '--assert=plain', - '--cov', f'--cov={script.dirpath()}', - script) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'test_double_cov* {SCRIPT_SIMPLE_RESULT}*', - '*1 passed*' - ]) + result = testdir.runpytest('-v', '--assert=plain', '--cov', f'--cov={script.dirpath()}', script) + + result.stdout.fnmatch_lines(['*- coverage: platform *, python * -*', f'test_double_cov* {SCRIPT_SIMPLE_RESULT}*', '*1 passed*']) assert result.ret == 0 def test_double_cov2(testdir): script = testdir.makepyfile(SCRIPT_SIMPLE) - result = testdir.runpytest('-v', - '--assert=plain', - '--cov', '--cov', - script) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'test_double_cov2* {SCRIPT_SIMPLE_RESULT}*', - '*1 passed*' - ]) + result = testdir.runpytest('-v', '--assert=plain', '--cov', '--cov', script) + + result.stdout.fnmatch_lines(['*- coverage: platform *, python * -*', f'test_double_cov2* {SCRIPT_SIMPLE_RESULT}*', '*1 passed*']) assert result.ret == 0 def test_cov_reset(testdir): script = testdir.makepyfile(SCRIPT_SIMPLE) - result = testdir.runpytest('-v', - '--assert=plain', - f'--cov={script.dirpath()}', - '--cov-reset', - script) + result = testdir.runpytest('-v', '--assert=plain', f'--cov={script.dirpath()}', '--cov-reset', script) assert 'coverage: platform' not in result.stdout.str() def test_cov_reset_then_set(testdir): script = testdir.makepyfile(SCRIPT_SIMPLE) - result = testdir.runpytest('-v', - '--assert=plain', - f'--cov={script.dirpath()}', - '--cov-reset', - f'--cov={script.dirpath()}', - script) - - result.stdout.fnmatch_lines([ - '*- coverage: platform *, python * -*', - f'test_cov_reset_then_set* {SCRIPT_SIMPLE_RESULT}*', - '*1 passed*' - ]) + result = testdir.runpytest('-v', '--assert=plain', f'--cov={script.dirpath()}', '--cov-reset', f'--cov={script.dirpath()}', script) + + result.stdout.fnmatch_lines(['*- coverage: platform *, python * -*', f'test_cov_reset_then_set* {SCRIPT_SIMPLE_RESULT}*', '*1 passed*']) @pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"') def test_cov_and_no_cov(testdir): script = testdir.makepyfile(SCRIPT_SIMPLE) - result = testdir.runpytest('-v', - '--cov', '--no-cov', - '-n', '1', - '-s', - script) + result = testdir.runpytest('-v', '--cov', '--no-cov', '-n', '1', '-s', script) assert 'Coverage disabled via --no-cov switch!' not in result.stdout.str() assert 'Coverage disabled via --no-cov switch!' not in result.stderr.str() assert result.ret == 0 @@ -1935,56 +1765,56 @@ def find_labels(text, pattern): } -@pytest.mark.skipif("coverage.version_info < (5, 0)") -@pytest.mark.skipif("coverage.version_info > (6, 4)") +@pytest.mark.skipif('coverage.version_info < (5, 0)') +@pytest.mark.skipif('coverage.version_info > (6, 4)') @xdist_params def test_contexts(pytester, testdir, opts): - with open(os.path.join(os.path.dirname(__file__), "contextful.py")) as f: + with open(os.path.join(os.path.dirname(__file__), 'contextful.py')) as f: contextful_tests = f.read() script = testdir.makepyfile(contextful_tests) - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-context=test', - script, - *opts.split() - ) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-context=test', script, *opts.split()) assert result.ret == 0 - result.stdout.fnmatch_lines([ - 'test_contexts* 100%*', - ]) + result.stdout.fnmatch_lines( + [ + 'test_contexts* 100%*', + ] + ) - data = coverage.CoverageData(".coverage") + data = coverage.CoverageData('.coverage') data.read() assert data.measured_contexts() == set(EXPECTED_CONTEXTS) measured = data.measured_files() assert len(measured) == 1 - test_context_path = list(measured)[0] - assert test_context_path.lower() == os.path.abspath("test_contexts.py").lower() + test_context_path = next(iter(measured)) + assert test_context_path.lower() == os.path.abspath('test_contexts.py').lower() - line_data = find_labels(contextful_tests, r"[crst]\d+(?:-\d+)?") + line_data = find_labels(contextful_tests, r'[crst]\d+(?:-\d+)?') for context, label in EXPECTED_CONTEXTS.items(): if context == '': continue data.set_query_context(context) actual = set(data.lines(test_context_path)) - assert line_data[label] == actual, f"Wrong lines for context {context!r}" + assert line_data[label] == actual, f'Wrong lines for context {context!r}' -@pytest.mark.skipif("coverage.version_info >= (5, 0)") +@pytest.mark.skipif('coverage.version_info >= (5, 0)') def test_contexts_not_supported(testdir): - script = testdir.makepyfile("a = 1") - result = testdir.runpytest('-v', - f'--cov={script.dirpath()}', - '--cov-context=test', - script, - ) - result.stderr.fnmatch_lines([ - '*argument --cov-context: Contexts are only supported with coverage.py >= 5.x', - ]) + script = testdir.makepyfile('a = 1') + result = testdir.runpytest( + '-v', + f'--cov={script.dirpath()}', + '--cov-context=test', + script, + ) + result.stderr.fnmatch_lines( + [ + '*argument --cov-context: Contexts are only supported with coverage.py >= 5.x', + ] + ) assert result.ret != 0 def test_issue_417(testdir): # https://github.com/pytest-dev/pytest-cov/issues/417 - whatever = testdir.maketxtfile(whatever="") + whatever = testdir.maketxtfile(whatever='') testdir.inline_genitems(whatever) From 61ce622f49f46f0e0dd91449955204ff1f2de483 Mon Sep 17 00:00:00 2001 From: Matthias Reichenbach Date: Tue, 28 Nov 2023 15:46:13 -0800 Subject: [PATCH 102/117] remove rsyncdir usage since pytest-xdist has deprecated it --- src/pytest_cov/engine.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/pytest_cov/engine.py b/src/pytest_cov/engine.py index 8ba4704d..3999a1b9 100644 --- a/src/pytest_cov/engine.py +++ b/src/pytest_cov/engine.py @@ -270,12 +270,6 @@ class DistMaster(CovController): def start(self): cleanup() - # Ensure coverage rc file rsynced if appropriate. - if self.cov_config and Path(self.cov_config).exists(): - # rsyncdir is going away in pytest-xdist 4.0, already deprecated - if hasattr(self.config.option, 'rsyncdir'): - self.config.option.rsyncdir.append(self.cov_config) - self.cov = coverage.Coverage( source=self.cov_source, branch=self.cov_branch, From 6525fddd1442323cad8898438b9c0ad6ea0e4728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Mon, 18 Mar 2024 05:34:12 +0200 Subject: [PATCH 103/117] Refactor some more and revert some of the abspath changes (turns out Path.resolve is no bueno on Python3.9&Windows). --- src/pytest_cov/engine.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/pytest_cov/engine.py b/src/pytest_cov/engine.py index 3999a1b9..0dab6c03 100644 --- a/src/pytest_cov/engine.py +++ b/src/pytest_cov/engine.py @@ -69,7 +69,7 @@ def __init__(self, cov_source, cov_report, cov_config, cov_append, cov_branch, c self.data_file = None self.node_descs = set() self.failed_workers = [] - self.topdir = str(Path.cwd()) + self.topdir = os.fspath(Path.cwd()) self.is_collocated = None @contextlib.contextmanager @@ -96,12 +96,13 @@ def set_env(self): os.environ['COV_CORE_SOURCE'] = os.pathsep else: os.environ['COV_CORE_SOURCE'] = os.pathsep.join(self.cov_source) - config_file = Path(self.cov_config).resolve() + config_file = Path(self.cov_config) if config_file.exists(): - os.environ['COV_CORE_CONFIG'] = str(config_file) + os.environ['COV_CORE_CONFIG'] = os.fspath(config_file.resolve()) else: os.environ['COV_CORE_CONFIG'] = os.pathsep - os.environ['COV_CORE_DATAFILE'] = str(Path(self.cov.config.data_file).resolve()) + # this still uses the old abspath cause apparently Python 3.9 on Windows has a buggy Path.resolve() + os.environ['COV_CORE_DATAFILE'] = os.path.abspath(self.cov.config.data_file) # noqa: PTH100 if self.cov_branch: os.environ['COV_CORE_BRANCH'] = 'enabled' @@ -236,7 +237,7 @@ def start(self): source=self.cov_source, branch=self.cov_branch, data_suffix=True, - data_file=str(Path(self.cov.config.data_file).resolve()), + data_file=os.path.abspath(self.cov.config.data_file), # noqa: PTH100 config_file=self.cov_config, ) @@ -283,7 +284,7 @@ def start(self): source=self.cov_source, branch=self.cov_branch, data_suffix=True, - data_file=str(Path(self.cov.config.data_file).resolve()), + data_file=os.path.abspath(self.cov.config.data_file), # noqa: PTH100 config_file=self.cov_config, ) if not self.cov_append: From 1a4b2c51a6e2a03995bdbc80242437a60373a971 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sun, 17 Mar 2024 17:47:04 +0200 Subject: [PATCH 104/117] Bump GitHub Actions --- .github/workflows/test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4b537c22..84753edb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,15 +16,15 @@ jobs: - {python-version: "pypy-3.9", tox-python-version: "pypy3"} - {python-version: "3.11", tox-python-version: "py311"} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.cache/pip key: From 6aefc1d508045510183f6285645c95f73fe24e7f Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sun, 17 Mar 2024 18:51:32 +0200 Subject: [PATCH 105/117] Remove redundant code for Python 2 --- ci/bootstrap.py | 2 +- tests/test_pytest_cov.py | 12 ++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/ci/bootstrap.py b/ci/bootstrap.py index f3c9a7ea..08d6c90b 100755 --- a/ci/bootstrap.py +++ b/ci/bootstrap.py @@ -59,7 +59,7 @@ def main(): # This uses sys.executable the same way that the call in # cookiecutter-pylibrary/hooks/post_gen_project.py # invokes this bootstrap.py itself. - for line in subprocess.check_output([sys.executable, '-m', 'tox', '--listenvs'], universal_newlines=True).splitlines() + for line in subprocess.check_output([sys.executable, '-m', 'tox', '--listenvs'], text=True).splitlines() ] tox_environments = [line for line in tox_environments if line.startswith('py')] for template in templates_path.rglob('*'): diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 1c9bca86..6778d065 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -20,10 +20,7 @@ import pytest_cov.plugin -try: - from StringIO import StringIO -except ImportError: - from io import StringIO +from io import StringIO coverage, platform # required for skipif mark on test_cov_min_from_coveragerc @@ -1240,8 +1237,7 @@ def test_run(): stdout, stderr = proc.communicate() assert not stderr assert stdout == b"" - # it appears signal handling is buggy on python 2? - if sys.version_info == 3: assert proc.returncode in [128 + signal.SIGTERM, -signal.SIGTERM] + assert proc.returncode in [128 + signal.SIGTERM, -signal.SIGTERM] if __name__ == "__main__": signal.signal(signal.SIGINT, signal.SIG_IGN) @@ -1258,7 +1254,7 @@ def test_run(): result = testdir.runpytest('-vv', '--assert=plain', f'--cov={script.dirpath()}', '--cov-report=term-missing', script) - result.stdout.fnmatch_lines(['*- coverage: platform *, python * -*', 'test_cleanup_on_sigterm* 89% 23-24', '*1 passed*']) + result.stdout.fnmatch_lines(['*- coverage: platform *, python * -*', 'test_cleanup_on_sigterm* 89% 22-23', '*1 passed*']) assert result.ret == 0 @@ -1480,7 +1476,7 @@ def test_dist_boxed(testdir): @pytest.mark.skipif('sys.platform == "win32"') -@pytest.mark.skipif('sys.version_info[0] > 2 and platform.python_implementation() == "PyPy"', reason='strange optimization on PyPy3') +@pytest.mark.skipif('platform.python_implementation() == "PyPy"', reason='strange optimization on PyPy3') def test_dist_bare_cov(testdir): script = testdir.makepyfile(SCRIPT_SIMPLE) From ead3f1a72c6ffdab12f3153c01518efaf9d0feb3 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sun, 17 Mar 2024 19:21:08 +0200 Subject: [PATCH 106/117] Sort imports --- tests/test_pytest_cov.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 6778d065..2049ef7b 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -6,6 +6,7 @@ import re import subprocess import sys +from io import StringIO from itertools import chain import coverage @@ -20,8 +21,6 @@ import pytest_cov.plugin -from io import StringIO - coverage, platform # required for skipif mark on test_cov_min_from_coveragerc max_worker_restart_0 = '--max-worker-restart=0' From 9619cf7607fa51851f411f3443ed475f3232cf32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Mon, 18 Mar 2024 14:30:16 +0200 Subject: [PATCH 107/117] Fix or disable some broken links. --- AUTHORS.rst | 6 +++--- CHANGELOG.rst | 2 +- docs/contexts.rst | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index fc608064..00a833fa 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -2,15 +2,15 @@ Authors ======= -* Marc Schlaich - http://www.schlamar.org +* Marc Schlaich - \http://www.schlamar.org * Rick van Hattem - http://wol.ph * Buck Evan - https://github.com/bukzor * Eric Larson - http://larsoner.com -* Marc Abramowitz - http://marc-abramowitz.com +* Marc Abramowitz - \http://marc-abramowitz.com * Thomas Kluyver - https://github.com/takluyver * Guillaume Ayoub - http://www.yabz.fr * Federico Ceratto - http://firelet.net -* Josh Kalderimis - http://blog.cookiestack.com +* Josh Kalderimis - \http://blog.cookiestack.com * Ionel Cristian Mărieș - https://blog.ionelmc.ro * Christian Ledermann - https://github.com/cleder * Alec Nikolas Reiter - https://github.com/justanr diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 21ba765a..777f1787 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -116,7 +116,7 @@ Changelog * Removed the empty `console_scripts` entrypoint that confused some Gentoo build script. I didn't ask why it was so broken cause I didn't want to ruin my day. Contributed by Michał Górny in `#434 `_. -* Fixed the missing `coverage context `_ +* Fixed the missing `coverage context `_ when using subprocesses. Contributed by Bernát Gábor in `#443 `_. * Updated the config section in the docs. diff --git a/docs/contexts.rst b/docs/contexts.rst index cde920d5..2b447463 100644 --- a/docs/contexts.rst +++ b/docs/contexts.rst @@ -6,7 +6,7 @@ Coverage.py 5.0 can record separate coverage data for `different contexts`_ duri one run of a test suite. Pytest-cov can use this feature to record coverage data for each test individually, with the ``--cov-context=test`` option. -.. _different contexts: https://coverage.readthedocs.io/en/stable/contexts.html +.. _different contexts: https://coverage.readthedocs.io/en/latest/contexts.html The context name recorded in the coverage.py database is the pytest test id, and the phase of execution, one of "setup", "run", or "teardown". These two From d9fe8dfed15023d3410dd299c5092e755b8981c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Mon, 18 Mar 2024 14:32:48 +0200 Subject: [PATCH 108/117] Switch to furo. Closes #618. --- docs/conf.py | 8 +------- docs/requirements.txt | 6 ++---- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 5151b09d..95250654 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,7 +1,5 @@ import os -import sphinx_py3doc_enhanced_theme - extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', @@ -31,8 +29,7 @@ 'issue': ('https://github.com/pytest-dev/pytest-cov/issues/%s', '#'), 'pr': ('https://github.com/pytest-dev/pytest-cov/pull/%s', 'PR #'), } -html_theme = 'sphinx_py3doc_enhanced_theme' -html_theme_path = [sphinx_py3doc_enhanced_theme.get_html_theme_path()] +html_theme = 'furo' html_theme_options = { 'githuburl': 'https://github.com/pytest-dev/pytest-cov/', } @@ -40,9 +37,6 @@ html_use_smartypants = True html_last_updated_fmt = '%b %d, %Y' html_split_index = False -html_sidebars = { - '**': ['searchbox.html', 'globaltoc.html', 'sourcelink.html'], -} html_short_title = f'{project}-{version}' napoleon_use_ivar = True diff --git a/docs/requirements.txt b/docs/requirements.txt index 22428fbb..4c1e3b7d 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,3 @@ -sphinx==7.2.6 -sphinx-py3doc-enhanced-theme -docutils -jinja2<3.1 +sphinx +furo -e . From 6a5af8e85b8242ac815f33e26adf9068f5f0ebc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Mon, 18 Mar 2024 14:38:30 +0200 Subject: [PATCH 109/117] Update changelog. --- CHANGELOG.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 777f1787..82dca4c2 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,13 @@ Changelog ========= +5.0.0 (2024-03-18) +------------------ + +* Removed support for xdist rsync (now deprecated). + Contributed by Matthias Reichenbach in `#623 `_. +* Switched docs theme to Furo. + 4.1.0 (2023-05-24) ------------------ From 1d7f55963d5138f41c452a946f7cca7e0b6ee8b2 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Sun, 17 Mar 2024 23:42:39 +0100 Subject: [PATCH 110/117] Fix or remove URLs that are causing docs tests to fail --- CHANGELOG.rst | 4 ++-- README.rst | 2 +- docs/plugins.rst | 2 +- docs/tox.rst | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 82dca4c2..a74497fd 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -52,7 +52,7 @@ Changelog Contributed by Andre Brisco in `#543 `_ and Colin O'Dell in `#525 `_. * Added support for LCOV output format via `--cov-report=lcov`. Only works with coverage 6.3+. - Contributed by Christian Fetzer in `#536 `_. + Contributed by Christian Fetzer in `#536 `_. * Modernized pytest hook implementation. Contributed by Bruno Oliveira in `#549 `_ and Ronny Pfannschmidt in `#550 `_. @@ -152,7 +152,7 @@ Changelog * Made pytest startup faster when plugin not active by lazy-importing. Contributed by Anders Hovmöller in `#339 `_. * Various CI improvements. - Contributed by Daniel Hahler in `#363 `_ and + Contributed by Daniel Hahler in `#363 `_ and `#364 `_. * Various Python support updates (drop EOL 3.4, test against 3.8 final). Contributed by Hugo van Kemenade in diff --git a/README.rst b/README.rst index 0718510b..7b7e3ce8 100644 --- a/README.rst +++ b/README.rst @@ -108,7 +108,7 @@ Would produce a report like:: Documentation ============= - http://pytest-cov.rtfd.org/ + https://pytest-cov.readthedocs.io/en/latest/ diff --git a/docs/plugins.rst b/docs/plugins.rst index d06c4ffe..ab5897d0 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -10,7 +10,7 @@ The current way of dealing with this problem is using the append feature and man COV_CORE_SOURCE=src COV_CORE_CONFIG=.coveragerc COV_CORE_DATAFILE=.coverage.eager pytest --cov=src --cov-append -Alternatively you can have this in ``tox.ini`` (if you're using `Tox `_ of course):: +Alternatively you can have this in ``tox.ini`` (if you're using `Tox `_ of course):: [testenv] setenv = diff --git a/docs/tox.rst b/docs/tox.rst index 18f9137e..e44de028 100644 --- a/docs/tox.rst +++ b/docs/tox.rst @@ -2,7 +2,7 @@ Tox === -When using `tox `_ you can have ultra-compact configuration - you can have all of it in +When using `tox `_ you can have ultra-compact configuration - you can have all of it in ``tox.ini``:: [tox] From 4a5a4b5fa4b1c63ddcab5cbc1813798c9b6f1d36 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Thu, 7 Mar 2024 20:23:30 +0100 Subject: [PATCH 111/117] Keep GitHub Actions up to date with GitHub's Dependabot Dependabot will generate a pull request like rapidfuzz/RapidFuzz#362 to keep the six GitHub Actions used in `.github/workflows/test.yml` up-to-date. * https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot * https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem --- .github/dependabot.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..be006de9 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +# Keep GitHub Actions up to date with GitHub's Dependabot... +# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot +# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + groups: + github-actions: + patterns: + - "*" # Group all Actions updates into a single larger pull request + schedule: + interval: weekly From ff50860d7c67b920503745d92a3f0944cf41f982 Mon Sep 17 00:00:00 2001 From: Dawn James Date: Mon, 25 Dec 2023 07:45:31 +0000 Subject: [PATCH 112/117] docs: add config instructions for pyproject.toml. --- docs/config.rst | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index c7bef037..852c014b 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -32,12 +32,19 @@ For full details refer to the `coverage config file`_ documentation. If you use the ``--cov-branch`` option then coverage's ``branch`` option will also get overridden. -If you wish to always add pytest-cov with pytest, you can use ``addopts`` under ``pytest`` or ``tool:pytest`` section. -For example: :: +If you wish to always add pytest-cov with pytest, you can use ``addopts`` under the ``pytest`` or ``tool:pytest`` section of +your ``setup.cfg``, or the ``tool.pytest.ini_options`` section of your ``pyproject.toml`` file. + +For example, in ``setup.cfg``: :: [tool:pytest] addopts = --cov= --cov-report html +Or for ``pyproject.toml``: :: + + [tool.pytest.ini_options] + addopts = "--cov= --cov-report html" + Caveats ======= From 93b5047ec5050d63c10a6fe16a09b671a7a03df8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 19 Mar 2024 03:07:39 +0200 Subject: [PATCH 113/117] Add test for pyproject.toml loading without explicit --cov-config. Ref #508. --- tests/test_pytest_cov.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/test_pytest_cov.py b/tests/test_pytest_cov.py index 2049ef7b..bd9df38e 100644 --- a/tests/test_pytest_cov.py +++ b/tests/test_pytest_cov.py @@ -1365,6 +1365,13 @@ def test_basic(no_cover): exclude_lines = raise NotImplementedError """ +PYPROJECTTOML = """ +[tool.coverage.report] +# Regexes for lines to exclude from consideration +exclude_lines = [ + 'raise NotImplementedError', +] +""" EXCLUDED_TEST = """ @@ -1388,6 +1395,14 @@ def test_coveragerc(testdir): result.stdout.fnmatch_lines([f'test_coveragerc* {EXCLUDED_RESULT}']) +def test_pyproject_toml(testdir): + testdir.makefile('.toml', pyproject=PYPROJECTTOML) + script = testdir.makepyfile(EXCLUDED_TEST) + result = testdir.runpytest('-v', f'--cov={script.dirpath()}', '--cov-report=term-missing', script) + assert result.ret == 0 + result.stdout.fnmatch_lines([f'test_pyproject_toml* {EXCLUDED_RESULT}']) + + @pytest.mark.skipif('sys.platform == "win32" and platform.python_implementation() == "PyPy"') def test_coveragerc_dist(testdir): testdir.makefile('', coveragerc=COVERAGERC) From 9f5cd81a0dbe3fe41681efdbef516c08988fe8ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 19 Mar 2024 03:10:48 +0200 Subject: [PATCH 114/117] Cleanup releasing instructions. Closes #616. --- docs/releasing.rst | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/releasing.rst b/docs/releasing.rst index ae78228d..9afe600d 100644 --- a/docs/releasing.rst +++ b/docs/releasing.rst @@ -4,19 +4,16 @@ Releasing The process for releasing should follow these steps: -#. Test that docs build and render properly by running ``tox -e docs,spell``. +#. Test that docs build and render properly by running ``tox -e docs``. If there are bogus spelling issues add the words in ``spelling_wordlist.txt``. #. Update ``CHANGELOG.rst`` and ``AUTHORS.rst`` to be up to date. #. Bump the version by running ``bumpversion [ major | minor | patch ]``. This will automatically add a tag. - - Alternatively, you can manually edit the files and run ``git tag v1.2.3`` yourself. #. Push changes and tags with:: git push git push --tags -#. Wait for `AppVeyor `_ - and `GitHub Actions `_ to give the green builds. +#. Wait `GitHub Actions `_ to give the green builds. #. Check that the docs on `ReadTheDocs `_ are built. #. Make sure you have a clean checkout, run ``git status`` to verify. #. Manually clean temporary files (that are ignored and won't show up in ``git status``):: From 9757222e2e044361e70125ebdd96e5eb87395983 Mon Sep 17 00:00:00 2001 From: Dave T <17680170+davet2001@users.noreply.github.com> Date: Fri, 22 Mar 2024 09:02:42 +0000 Subject: [PATCH 115/117] Fix a minor grammar error (#636) --- docs/plugins.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugins.rst b/docs/plugins.rst index ab5897d0..577870de 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -2,7 +2,7 @@ Plugin coverage =============== -Getting coverage on pytest plugins is a very particular situation. Because how pytest implements plugins (using setuptools +Getting coverage on pytest plugins is a very particular situation. Because of how pytest implements plugins (using setuptools entrypoints) it doesn't allow controlling the order in which the plugins load. See `pytest/issues/935 `_ for technical details. From 1181b067972bf94569f8011f3b18f271690f9ab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 24 Mar 2024 22:06:14 +0200 Subject: [PATCH 116/117] Update changelog. --- AUTHORS.rst | 2 ++ CHANGELOG.rst | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 00a833fa..44e92874 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -60,3 +60,5 @@ Authors * Christian Fetzer - https://github.com/fetzerch * Jonathan Stewmon - https://github.com/jstewmon * Matthew Gamble - https://github.com/mwgamble +* Christian Clauss - https://github.com/cclauss +* Dawn James - https://github.com/dawngerpony diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a74497fd..5dd46aa3 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,12 +2,22 @@ Changelog ========= -5.0.0 (2024-03-18) +5.0.0 (2024-03-24) ------------------ * Removed support for xdist rsync (now deprecated). Contributed by Matthias Reichenbach in `#623 `_. * Switched docs theme to Furo. +* Various legacy Python cleanup and CI improvements. + Contributed by Christian Clauss and Hugo van Kemenade in + `#630 `_, + `#631 `_, + `#632 `_ and + `#633 `_. +* Added a ``pyproject.toml`` example in the docs. + Contributed by Dawn James in `#626 `_. +* Modernized project's pre-commit hooks to use ruff. Initial POC contributed by + Christian Clauss in `#584 `_. 4.1.0 (2023-05-24) ------------------ From 5295ce01c84262cec88f31255e9ac538718f3047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 24 Mar 2024 22:18:03 +0200 Subject: [PATCH 117/117] =?UTF-8?q?Bump=20version:=204.1.0=20=E2=86=92=205?= =?UTF-8?q?.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- .cookiecutterrc | 2 +- README.rst | 4 ++-- docs/conf.py | 2 +- setup.py | 2 +- src/pytest_cov/__init__.py | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 3260d906..cca1163f 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 4.1.0 +current_version = 5.0.0 commit = True tag = True diff --git a/.cookiecutterrc b/.cookiecutterrc index 3c998545..18487fef 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -40,7 +40,7 @@ default_context: sphinx_doctest: 'no' sphinx_theme: sphinx-py3doc-enhanced-theme test_matrix_separate_coverage: 'no' - version: 4.1.0 + version: 5.0.0 version_manager: bump2version website: http://blog.ionelmc.ro year_from: '2010' diff --git a/README.rst b/README.rst index 7b7e3ce8..19b24e9f 100644 --- a/README.rst +++ b/README.rst @@ -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/v4.1.0.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/pytest-dev/pytest-cov/v5.0.0.svg :alt: Commits since latest release - :target: https://github.com/pytest-dev/pytest-cov/compare/v4.1.0...master + :target: https://github.com/pytest-dev/pytest-cov/compare/v5.0.0...master .. end-badges diff --git a/docs/conf.py b/docs/conf.py index 95250654..f15a262c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,7 +21,7 @@ year = '2010-2024' author = 'pytest-cov contributors' copyright = f'{year}, {author}' -version = release = '4.1.0' +version = release = '5.0.0' pygments_style = 'trac' templates_path = ['.'] diff --git a/setup.py b/setup.py index 94c24201..221111e8 100755 --- a/setup.py +++ b/setup.py @@ -77,7 +77,7 @@ def run(self): setup( name='pytest-cov', - version='4.1.0', + version='5.0.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'))), diff --git a/src/pytest_cov/__init__.py b/src/pytest_cov/__init__.py index 93390efa..82ce08bb 100644 --- a/src/pytest_cov/__init__.py +++ b/src/pytest_cov/__init__.py @@ -1,3 +1,3 @@ """pytest-cov: avoid already-imported warning: PYTEST_DONT_REWRITE.""" -__version__ = '4.1.0' +__version__ = '5.0.0'