diff --git a/coverage/config.py b/coverage/config.py index e15d2affc..9518e5356 100644 --- a/coverage/config.py +++ b/coverage/config.py @@ -215,6 +215,7 @@ def __init__(self) -> None: # Defaults for [report] self.exclude_list = DEFAULT_EXCLUDE[:] + self.exclude_also: List[str] = [] self.fail_under = 0.0 self.format: Optional[str] = None self.ignore_errors = False @@ -392,6 +393,7 @@ def copy(self) -> CoverageConfig: # [report] ('exclude_list', 'report:exclude_lines', 'regexlist'), + ('exclude_also', 'report:exclude_also', 'regexlist'), ('fail_under', 'report:fail_under', 'float'), ('format', 'report:format', 'boolean'), ('ignore_errors', 'report:ignore_errors', 'boolean'), @@ -523,6 +525,7 @@ def post_process(self) -> None: (k, [self.post_process_file(f) for f in v]) for k, v in self.paths.items() ) + self.exclude_list += self.exclude_also def debug_info(self) -> List[Tuple[str, Any]]: """Make a list of (name, value) pairs for writing debug info.""" diff --git a/doc/config.rst b/doc/config.rst index 8e3d885be..5b159d900 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -381,7 +381,7 @@ Settings common to many kinds of reporting. ...................... (multi-string) A list of regular expressions. Any line of your source code -containing a match for one of these regexes is excluded from being reported as +containing a match for one of these regexes is excluded from being reported as missing. More details are in :ref:`excluding`. If you use this option, you are replacing all the exclude regexes, so you'll need to also supply the "pragma: no cover" regex if you still want to use it. @@ -395,12 +395,22 @@ you'll exclude any line with three or more of any character. If you write ``pass``, you'll also exclude the line ``my_pass="foo"``, and so on. +.. _config_report_exclude_also: + +[report] exclude_also +..................... + +(multi-string) A list of regular expressions. This setting will preserve the +default exclude pattern instead of overwriting it. See +:ref:`config_report_exclude_lines` for details on exclusion regexes. + + .. _config_report_fail_under: [report] fail_under ................... -(float) A target coverage percentage. If the total coverage measurement is +(float) A target coverage percentage. If the total coverage measurement is under this value, then exit with a status code of 2. If you specify a non-integral value, you must also set ``[report] precision`` properly to make use of the decimal places. A setting of 100 will fail any value under 100, diff --git a/doc/excluding.rst b/doc/excluding.rst index 315d4e290..4651e6bba 100644 --- a/doc/excluding.rst +++ b/doc/excluding.rst @@ -101,7 +101,9 @@ For example, here's a list of exclusions I've used:: Note that when using the ``exclude_lines`` option in a configuration file, you are taking control of the entire list of regexes, so you need to re-specify the -default "pragma: no cover" match if you still want it to apply. +default "pragma: no cover" match if you still want it to apply. The +``exclude_also`` option can be used instead to preserve the default +exclusions while adding new ones. The regexes only have to match part of a line. Be careful not to over-match. A value of ``...`` will match any line with more than three characters in it. diff --git a/tests/test_config.py b/tests/test_config.py index 2ee5eae08..2befa2e3b 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -449,6 +449,15 @@ 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", """\ + [tool.coverage.report] + exclude_also = ["foobar"] + """) + cov = coverage.Coverage() + + assert cov.config.exclude_list == coverage.config.DEFAULT_EXCLUDE + ["foobar"] + class ConfigFileTest(UsingModulesMixin, CoverageTest): """Tests of the config file settings in particular."""