From 89095bb1346a71f1d35f6c4ba9d2bf7a8740bfa5 Mon Sep 17 00:00:00 2001 From: Olena Date: Wed, 16 Apr 2025 02:04:00 +0300 Subject: [PATCH 1/5] Support `.coveragerc.toml` for configuration This patch provides a new configuration option for coverage.py. Considering that many projects have switched to toml configurations, this change offers a more flexible approach to manage coverage settings. --- coverage/config.py | 1 + tests/test_config.py | 107 ++++++++++++++++++++++++++----------------- 2 files changed, 67 insertions(+), 41 deletions(-) diff --git a/coverage/config.py b/coverage/config.py index 435ddd5f9..00fa17fac 100644 --- a/coverage/config.py +++ b/coverage/config.py @@ -573,6 +573,7 @@ def config_files_to_try(config_file: bool | str) -> list[tuple[str, bool, bool]] assert isinstance(config_file, str) files_to_try = [ (config_file, True, specified_file), + (".coveragerc.toml", True, False), ("setup.cfg", False, False), ("tox.ini", False, False), ("pyproject.toml", False, False), diff --git a/tests/test_config.py b/tests/test_config.py index f1631a7fe..db29a3df5 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -66,9 +66,10 @@ def test_named_config_file(self, file_class: FilePathType) -> None: assert not cov.config.branch assert cov.config.data_file == "delete.me" - def test_toml_config_file(self) -> None: - # A pyproject.toml file will be read into the configuration. - self.make_file("pyproject.toml", """\ + @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) + def test_toml_config_file(self, filename) -> None: + # A pyproject.toml and coveragerc.toml will be read into the configuration. + self.make_file(filename, """\ # This is just a bogus toml file for testing. [tool.somethingelse] authors = ["Joe D'Ávila "] @@ -96,9 +97,10 @@ def test_toml_config_file(self) -> None: assert cov.config.fail_under == 90.5 assert cov.config.get_plugin_options("plugins.a_plugin") == {"hello": "world"} - def test_toml_ints_can_be_floats(self) -> None: + @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) + def test_toml_ints_can_be_floats(self, filename) -> None: # Test that our class doesn't reject integers when loading floats - self.make_file("pyproject.toml", """\ + self.make_file(filename, """\ # This is just a bogus toml file for testing. [tool.coverage.report] fail_under = 90 @@ -207,7 +209,8 @@ def test_parse_errors(self, bad_config: str, msg: str) -> None: self.make_file(".coveragerc", bad_config) with pytest.raises(ConfigError, match=msg): coverage.Coverage() - + + @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) @pytest.mark.parametrize("bad_config, msg", [ ("[tool.coverage.run]\ntimid = \"maybe?\"\n", r"maybe[?]"), ("[tool.coverage.run\n", None), @@ -225,9 +228,9 @@ def test_parse_errors(self, bad_config: str, msg: str) -> None: ("[tool.coverage.report]\nprecision=1.23", "not an integer"), ('[tool.coverage.report]\nfail_under="s"', "couldn't convert to a float"), ]) - def test_toml_parse_errors(self, bad_config: str, msg: str) -> None: + def test_toml_parse_errors(self, filename, bad_config: str, msg: str) -> None: # Im-parsable values raise ConfigError, with details. - self.make_file("pyproject.toml", bad_config) + self.make_file(filename, bad_config) with pytest.raises(ConfigError, match=msg): coverage.Coverage() @@ -253,9 +256,10 @@ def test_environment_vars_in_config(self) -> None: assert cov.config.branch is True assert cov.config.exclude_list == ["the_$one", "anotherZZZ", "xZZZy", "xy", "huh${X}what"] - def test_environment_vars_in_toml_config(self) -> None: + @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) + def test_environment_vars_in_toml_config(self, filename) -> None: # Config files can have $envvars in them. - self.make_file("pyproject.toml", """\ + self.make_file(filename, """\ [tool.coverage.run] data_file = "$DATA_FILE.fooey" branch = "$BRANCH" @@ -327,9 +331,10 @@ def expanduser(s: str) -> str: assert cov.config.exclude_list == ["~/data.file", "~joe/html_dir"] assert cov.config.paths == {'mapping': ['/Users/me/src', '/Users/joe/source']} - def test_tilde_in_toml_config(self) -> None: + @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) + def test_tilde_in_toml_config(self, filename) -> None: # Config entries that are file paths can be tilde-expanded. - self.make_file("pyproject.toml", """\ + self.make_file(filename, """\ [tool.coverage.run] data_file = "~/data.file" @@ -443,22 +448,14 @@ def test_unknown_option(self) -> None: msg = r"Unrecognized option '\[run\] xyzzy=' in config file .coveragerc" with pytest.warns(CoverageWarning, match=msg): _ = coverage.Coverage() - - def test_unknown_option_toml(self) -> None: - self.make_file("pyproject.toml", """\ + + @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) + def test_unknown_option_toml(self, filename) -> None: + self.make_file(filename, """\ [tool.coverage.run] xyzzy = 17 """) - msg = r"Unrecognized option '\[tool.coverage.run\] xyzzy=' in config file pyproject.toml" - with pytest.warns(CoverageWarning, match=msg): - _ = coverage.Coverage() - - def test_misplaced_option(self) -> None: - self.make_file(".coveragerc", """\ - [report] - branch = True - """) - msg = r"Unrecognized option '\[report\] branch=' in config file .coveragerc" + msg = f"Unrecognized option '\\[tool.coverage.run\\] xyzzy=' in config file {filename}" with pytest.warns(CoverageWarning, match=msg): _ = coverage.Coverage() @@ -470,7 +467,7 @@ def test_unknown_option_in_other_ini_file(self) -> None: msg = r"Unrecognized option '\[coverage:run\] huh=' in config file setup.cfg" with pytest.warns(CoverageWarning, match=msg): _ = coverage.Coverage() - + def test_exceptions_from_missing_things(self) -> None: self.make_file("config.ini", """\ [run] @@ -483,8 +480,10 @@ def test_exceptions_from_missing_things(self) -> None: with pytest.raises(ConfigError, match="No option 'foo' in section: 'xyzzy'"): config.get("xyzzy", "foo") - def test_exclude_also(self) -> None: - self.make_file("pyproject.toml", """\ + + @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) + def test_exclude_also(self, filename) -> None: + self.make_file(filename, """\ [tool.coverage.report] exclude_also = ["foobar", "raise .*Error"] """) @@ -800,35 +799,39 @@ def test_no_toml_installed_explicit_toml(self) -> None: coverage.Coverage(config_file="cov.toml") @pytest.mark.skipif(env.PYVERSION >= (3, 11), reason="Python 3.11 has toml in stdlib") - def test_no_toml_installed_pyproject_toml(self) -> None: - # Can't have coverage config in pyproject.toml without toml installed. - self.make_file("pyproject.toml", """\ + @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) + def test_no_toml_installed_pyproject_toml(self, filename) -> None: + # Can't have coverage config in pyproject.toml and .coveragerc.toml without toml installed. + self.make_file(filename, """\ # A toml file! [tool.coverage.run] xyzzy = 17 """) with mock.patch.object(coverage.tomlconfig, "has_tomllib", False): - msg = "Can't read 'pyproject.toml' without TOML support" + msg = "Can't read '{filename}' without TOML support" with pytest.raises(ConfigError, match=msg): coverage.Coverage() @pytest.mark.skipif(env.PYVERSION >= (3, 11), reason="Python 3.11 has toml in stdlib") - def test_no_toml_installed_pyproject_toml_shorter_syntax(self) -> None: + @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) + def test_no_toml_installed_pyproject_toml_shorter_syntax(self, filename) -> None: # Can't have coverage config in pyproject.toml without toml installed. - self.make_file("pyproject.toml", """\ + self.make_file(filename, """\ # A toml file! [tool.coverage] run.parallel = true """) with mock.patch.object(coverage.tomlconfig, "has_tomllib", False): - msg = "Can't read 'pyproject.toml' without TOML support" + msg = "Can't read '{filename}' without TOML support" with pytest.raises(ConfigError, match=msg): coverage.Coverage() + @pytest.mark.skipif(env.PYVERSION >= (3, 11), reason="Python 3.11 has toml in stdlib") - def test_no_toml_installed_pyproject_no_coverage(self) -> None: + @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) + def test_no_toml_installed_pyproject_no_coverage(self, filename) -> None: # It's ok to have non-coverage pyproject.toml without toml installed. - self.make_file("pyproject.toml", """\ + self.make_file(filename, """\ # A toml file! [tool.something] xyzzy = 17 @@ -839,17 +842,39 @@ def test_no_toml_installed_pyproject_no_coverage(self) -> None: assert not cov.config.timid assert not cov.config.branch assert cov.config.data_file == ".coverage" - - def test_exceptions_from_missing_toml_things(self) -> None: - self.make_file("pyproject.toml", """\ + + @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) + def test_exceptions_from_missing_toml_things(self, filename) -> None: + self.make_file(filename, """\ [tool.coverage.run] branch = true """) config = TomlConfigParser(False) - config.read("pyproject.toml") + config.read(filename) with pytest.raises(ConfigError, match="No section: 'xyzzy'"): config.options("xyzzy") with pytest.raises(ConfigError, match="No section: 'xyzzy'"): config.get("xyzzy", "foo") with pytest.raises(ConfigError, match="No option 'foo' in section: 'tool.coverage.run'"): config.get("run", "foo") + + def test_coveragerc_toml_priority(self) -> None: + """Test that .coveragerc.toml has priority over pyproject.toml.""" + self.make_file(".coveragerc.toml", """\ + [tool.coverage.run] + timid = true + data_file = ".toml-data.dat" + branch = true + """) + + self.make_file("pyproject.toml", """\ + [tool.coverage.run] + timid = false + data_file = "pyproject-data.dat" + branch = false + """) + cov = coverage.Coverage() + + assert cov.config.timid is True + assert cov.config.data_file == ".toml-data.dat" + assert cov.config.branch is True From 9378e8551d760b9f120b6e917f2c7bf36de2ff13 Mon Sep 17 00:00:00 2001 From: Olena <107187316+OlenaYefymenko@users.noreply.github.com> Date: Fri, 18 Apr 2025 23:59:10 +0300 Subject: [PATCH 2/5] Add f-string for error message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 🇺🇦 Sviatoslav Sydorenko (Святослав Сидоренко) --- tests/test_config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index db29a3df5..94ed90651 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -808,7 +808,7 @@ def test_no_toml_installed_pyproject_toml(self, filename) -> None: xyzzy = 17 """) with mock.patch.object(coverage.tomlconfig, "has_tomllib", False): - msg = "Can't read '{filename}' without TOML support" + msg = f"Can't read '{filename}' without TOML support" with pytest.raises(ConfigError, match=msg): coverage.Coverage() @@ -822,7 +822,7 @@ def test_no_toml_installed_pyproject_toml_shorter_syntax(self, filename) -> None run.parallel = true """) with mock.patch.object(coverage.tomlconfig, "has_tomllib", False): - msg = "Can't read '{filename}' without TOML support" + msg = f"Can't read '{filename}' without TOML support" with pytest.raises(ConfigError, match=msg): coverage.Coverage() From ce5e02fe818c50a17055519a6b4cdfc026950a15 Mon Sep 17 00:00:00 2001 From: Olena Date: Fri, 6 Jun 2025 01:56:25 +0300 Subject: [PATCH 3/5] This patch parametrizes tests for configuration files. Adds a new test to verify behavior when TOML support is unavailable. --- tests/test_config.py | 46 +++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index 94ed90651..8e5fcf355 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -799,39 +799,35 @@ def test_no_toml_installed_explicit_toml(self) -> None: coverage.Coverage(config_file="cov.toml") @pytest.mark.skipif(env.PYVERSION >= (3, 11), reason="Python 3.11 has toml in stdlib") - @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) - def test_no_toml_installed_pyproject_toml(self, filename) -> None: - # Can't have coverage config in pyproject.toml and .coveragerc.toml without toml installed. - self.make_file(filename, """\ + def test_no_toml_installed_pyproject_toml(self) -> None: + # Can't have coverage config in pyproject.toml without toml installed. + self.make_file("pyproject.toml", """\ # A toml file! [tool.coverage.run] xyzzy = 17 """) with mock.patch.object(coverage.tomlconfig, "has_tomllib", False): - msg = f"Can't read '{filename}' without TOML support" + msg = "Can't read 'pyproject.toml' without TOML support" with pytest.raises(ConfigError, match=msg): coverage.Coverage() @pytest.mark.skipif(env.PYVERSION >= (3, 11), reason="Python 3.11 has toml in stdlib") - @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) - def test_no_toml_installed_pyproject_toml_shorter_syntax(self, filename) -> None: + def test_no_toml_installed_pyproject_toml_shorter_syntax(self) -> None: # Can't have coverage config in pyproject.toml without toml installed. - self.make_file(filename, """\ + self.make_file("pyproject.toml", """\ # A toml file! [tool.coverage] run.parallel = true """) with mock.patch.object(coverage.tomlconfig, "has_tomllib", False): - msg = f"Can't read '{filename}' without TOML support" + msg = "Can't read 'pyproject.toml' without TOML support" with pytest.raises(ConfigError, match=msg): coverage.Coverage() - @pytest.mark.skipif(env.PYVERSION >= (3, 11), reason="Python 3.11 has toml in stdlib") - @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) - def test_no_toml_installed_pyproject_no_coverage(self, filename) -> None: + def test_no_toml_installed_pyproject_no_coverage(self) -> None: # It's ok to have non-coverage pyproject.toml without toml installed. - self.make_file(filename, """\ + self.make_file("pyproject.toml", """\ # A toml file! [tool.something] xyzzy = 17 @@ -878,3 +874,27 @@ def test_coveragerc_toml_priority(self) -> None: assert cov.config.timid is True assert cov.config.data_file == ".toml-data.dat" assert cov.config.branch is True + + + @pytest.mark.skipif(env.PYVERSION >= (3, 11), reason="Python 3.11 has toml in stdlib") + def test_toml_file_exists_but_no_toml_support(self) -> None: + """Test behavior when .coveragerc.toml exists but TOML support is missing.""" + self.make_file(".coveragerc.toml", """\ + [tool.coverage.run] + timid = true + data_file = ".toml-data.dat" + """) + + with mock.patch.object(coverage.tomlconfig, "has_tomllib", False): + msg = "Can't read '.coveragerc.toml' without TOML support" + with pytest.raises(ConfigError, match=msg): + coverage.Coverage(config_file=".coveragerc.toml") + self.make_file(".coveragerc", """\ + [run] + timid = false + data_file = .ini-data.dat + """) + + cov = coverage.Coverage() + assert not cov.config.timid + assert cov.config.data_file == ".ini-data.dat" From 9839ed9b21465ace56364a53889564163ffc8d47 Mon Sep 17 00:00:00 2001 From: Olena Date: Fri, 6 Jun 2025 02:44:43 +0300 Subject: [PATCH 4/5] Fix linter issues --- tests/test_config.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index 8e5fcf355..ba05c7f42 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -67,7 +67,7 @@ def test_named_config_file(self, file_class: FilePathType) -> None: assert cov.config.data_file == "delete.me" @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) - def test_toml_config_file(self, filename) -> None: + def test_toml_config_file(self, filename: str) -> None: # A pyproject.toml and coveragerc.toml will be read into the configuration. self.make_file(filename, """\ # This is just a bogus toml file for testing. @@ -98,7 +98,7 @@ def test_toml_config_file(self, filename) -> None: assert cov.config.get_plugin_options("plugins.a_plugin") == {"hello": "world"} @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) - def test_toml_ints_can_be_floats(self, filename) -> None: + def test_toml_ints_can_be_floats(self, filename: str) -> None: # Test that our class doesn't reject integers when loading floats self.make_file(filename, """\ # This is just a bogus toml file for testing. @@ -209,7 +209,7 @@ def test_parse_errors(self, bad_config: str, msg: str) -> None: self.make_file(".coveragerc", bad_config) with pytest.raises(ConfigError, match=msg): coverage.Coverage() - + @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) @pytest.mark.parametrize("bad_config, msg", [ ("[tool.coverage.run]\ntimid = \"maybe?\"\n", r"maybe[?]"), @@ -228,7 +228,7 @@ def test_parse_errors(self, bad_config: str, msg: str) -> None: ("[tool.coverage.report]\nprecision=1.23", "not an integer"), ('[tool.coverage.report]\nfail_under="s"', "couldn't convert to a float"), ]) - def test_toml_parse_errors(self, filename, bad_config: str, msg: str) -> None: + def test_toml_parse_errors(self, filename: str, bad_config: str, msg: str) -> None: # Im-parsable values raise ConfigError, with details. self.make_file(filename, bad_config) with pytest.raises(ConfigError, match=msg): @@ -257,7 +257,7 @@ def test_environment_vars_in_config(self) -> None: assert cov.config.exclude_list == ["the_$one", "anotherZZZ", "xZZZy", "xy", "huh${X}what"] @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) - def test_environment_vars_in_toml_config(self, filename) -> None: + def test_environment_vars_in_toml_config(self, filename: str) -> None: # Config files can have $envvars in them. self.make_file(filename, """\ [tool.coverage.run] @@ -332,7 +332,7 @@ def expanduser(s: str) -> str: assert cov.config.paths == {'mapping': ['/Users/me/src', '/Users/joe/source']} @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) - def test_tilde_in_toml_config(self, filename) -> None: + def test_tilde_in_toml_config(self, filename: str) -> None: # Config entries that are file paths can be tilde-expanded. self.make_file(filename, """\ [tool.coverage.run] @@ -448,9 +448,9 @@ def test_unknown_option(self) -> None: msg = r"Unrecognized option '\[run\] xyzzy=' in config file .coveragerc" with pytest.warns(CoverageWarning, match=msg): _ = coverage.Coverage() - + @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) - def test_unknown_option_toml(self, filename) -> None: + def test_unknown_option_toml(self, filename: str) -> None: self.make_file(filename, """\ [tool.coverage.run] xyzzy = 17 @@ -467,7 +467,7 @@ def test_unknown_option_in_other_ini_file(self) -> None: msg = r"Unrecognized option '\[coverage:run\] huh=' in config file setup.cfg" with pytest.warns(CoverageWarning, match=msg): _ = coverage.Coverage() - + def test_exceptions_from_missing_things(self) -> None: self.make_file("config.ini", """\ [run] @@ -480,9 +480,9 @@ def test_exceptions_from_missing_things(self) -> None: with pytest.raises(ConfigError, match="No option 'foo' in section: 'xyzzy'"): config.get("xyzzy", "foo") - + @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) - def test_exclude_also(self, filename) -> None: + def test_exclude_also(self, filename: str) -> None: self.make_file(filename, """\ [tool.coverage.report] exclude_also = ["foobar", "raise .*Error"] @@ -838,9 +838,9 @@ def test_no_toml_installed_pyproject_no_coverage(self) -> None: assert not cov.config.timid assert not cov.config.branch assert cov.config.data_file == ".coverage" - + @pytest.mark.parametrize("filename", ["pyproject.toml", ".coveragerc.toml"]) - def test_exceptions_from_missing_toml_things(self, filename) -> None: + def test_exceptions_from_missing_toml_things(self, filename: str) -> None: self.make_file(filename, """\ [tool.coverage.run] branch = true @@ -853,7 +853,7 @@ def test_exceptions_from_missing_toml_things(self, filename) -> None: config.get("xyzzy", "foo") with pytest.raises(ConfigError, match="No option 'foo' in section: 'tool.coverage.run'"): config.get("run", "foo") - + def test_coveragerc_toml_priority(self) -> None: """Test that .coveragerc.toml has priority over pyproject.toml.""" self.make_file(".coveragerc.toml", """\ @@ -862,7 +862,7 @@ def test_coveragerc_toml_priority(self) -> None: data_file = ".toml-data.dat" branch = true """) - + self.make_file("pyproject.toml", """\ [tool.coverage.run] timid = false @@ -870,12 +870,12 @@ def test_coveragerc_toml_priority(self) -> None: branch = false """) cov = coverage.Coverage() - + assert cov.config.timid is True assert cov.config.data_file == ".toml-data.dat" assert cov.config.branch is True - - + + @pytest.mark.skipif(env.PYVERSION >= (3, 11), reason="Python 3.11 has toml in stdlib") def test_toml_file_exists_but_no_toml_support(self) -> None: """Test behavior when .coveragerc.toml exists but TOML support is missing.""" @@ -884,7 +884,7 @@ def test_toml_file_exists_but_no_toml_support(self) -> None: timid = true data_file = ".toml-data.dat" """) - + with mock.patch.object(coverage.tomlconfig, "has_tomllib", False): msg = "Can't read '.coveragerc.toml' without TOML support" with pytest.raises(ConfigError, match=msg): @@ -894,7 +894,7 @@ def test_toml_file_exists_but_no_toml_support(self) -> None: timid = false data_file = .ini-data.dat """) - + cov = coverage.Coverage() assert not cov.config.timid assert cov.config.data_file == ".ini-data.dat" From 673b0c05071fd551b04376825017d1f76608dcff Mon Sep 17 00:00:00 2001 From: Olena Date: Mon, 16 Jun 2025 02:10:54 +0300 Subject: [PATCH 5/5] Update documentation, changelog, and contributors list for new `.coveragerc.toml` configuration file support. --- CHANGES.rst | 7 ++++- CONTRIBUTORS.txt | 1 + doc/config.rst | 66 +++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 70 insertions(+), 4 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index e186939fc..e6111b3fd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -23,7 +23,12 @@ upgrading your version of coverage.py. Unreleased ---------- -Nothing yet. +- Feature: Added support for ``.coveragerc.toml`` configuration files. This + provides a more flexible approach to manage coverage settings as many + projects have switched to TOML configurations. Closes `issue 1643`_. + +.. _issue 1643: https://github.com/nedbat/coveragepy/issues/1643 +.. _pull 1952: https://github.com/nedbat/coveragepy/pull/1952 .. start-releases diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index bd83cfb3f..fcb419dc3 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -185,6 +185,7 @@ Nils Kattenbeck Noel O'Boyle Oleg Höfling Oleh Krehel +Olena Yefymenko Olivier Grisel Ori Avtalion Pablo Carballo diff --git a/doc/config.rst b/doc/config.rst index 5d2eab092..96f664fc9 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -39,12 +39,13 @@ environment variable. If ``.coveragerc`` doesn't exist and another file hasn't been specified, then coverage.py will look for settings in other common configuration files, in this -order: setup.cfg, tox.ini, or pyproject.toml. The first file found with +order: .coveragerc.toml, setup.cfg, tox.ini, or pyproject.toml. The first file found with coverage.py settings will be used and other files won't be consulted. -Coverage.py will read from "pyproject.toml" if TOML support is available, +Coverage.py will read from ".coveragerc.toml" and "pyproject.toml" if TOML support is available, either because you are running on Python 3.11 or later, or because you -installed with the ``toml`` extra (``pip install coverage[toml]``). +installed with the ``toml`` extra (``pip install coverage[toml]``). Both files +use the same ``[tool.coverage]`` section structure. Syntax @@ -136,6 +137,35 @@ Here's a sample configuration file, in each syntax: [html] directory = coverage_html_report """, + + coveragerc_toml=r""" + [tool.coverage.run] + branch = true + + [tool.coverage.report] + # Regexes for lines to exclude from consideration + exclude_also = [ + # Don't complain about missing debug-only code: + "def __repr__", + "if self\\.debug", + + # Don't complain if tests don't hit defensive assertion code: + "raise AssertionError", + "raise NotImplementedError", + + # Don't complain if non-runnable code isn't run: + "if 0:", + "if __name__ == .__main__.:", + + # Don't complain about abstract methods, they aren't run: + "@(abc\\.)?abstractmethod", + ] + + ignore_errors = true + + [tool.coverage.html] + directory = "coverage_html_report" + """, toml=r""" [tool.coverage.run] branch = true @@ -198,6 +228,36 @@ Here's a sample configuration file, in each syntax: [html] directory = coverage_html_report + .. code-tab:: toml + :caption: .coveragerc.toml + + [tool.coverage.run] + branch = true + + [tool.coverage.report] + # Regexes for lines to exclude from consideration + exclude_also = [ + # Don't complain about missing debug-only code: + "def __repr__", + "if self\\.debug", + + # Don't complain if tests don't hit defensive assertion code: + "raise AssertionError", + "raise NotImplementedError", + + # Don't complain if non-runnable code isn't run: + "if 0:", + "if __name__ == .__main__.:", + + # Don't complain about abstract methods, they aren't run: + "@(abc\\.)?abstractmethod", + ] + + ignore_errors = true + + [tool.coverage.html] + directory = "coverage_html_report" + .. code-tab:: toml :caption: pyproject.toml