diff --git a/CHANGES.rst b/CHANGES.rst index 3566eb523..f48b8ad70 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -24,9 +24,15 @@ want to know what's different in 5.0 since 4.5.x, see :ref:`whatsnew5x`. Unreleased ---------- +- Unrecognized options in the configuration file are no longer errors. They are + now warnings, to ease the use of coverage across versions. Fixes `issue + 1035`_. + - Fix another rarer instance of "Error binding parameter 0 - probably unsupported type." (`issue 1010`_). +.. _issue 1035: https://github.com/nedbat/coveragepy/issues/1035 + .. _changes_60b1: diff --git a/coverage/config.py b/coverage/config.py index 7287e963d..3b8735799 100644 --- a/coverage/config.py +++ b/coverage/config.py @@ -248,7 +248,7 @@ def from_args(self, **kwargs): setattr(self, k, v) @contract(filename=str) - def from_file(self, filename, our_file): + def from_file(self, filename, warn, our_file): """Read configuration from a .rc file. `filename` is a file name to read. @@ -297,7 +297,7 @@ def from_file(self, filename, our_file): real_section = cp.has_section(section) if real_section: for unknown in set(cp.options(section)) - options: - raise CoverageException( + warn( "Unrecognized option '[{}] {}=' in config file {}".format( real_section, unknown, filename ) @@ -517,12 +517,13 @@ def config_files_to_try(config_file): return files_to_try -def read_coverage_config(config_file, **kwargs): +def read_coverage_config(config_file, warn, **kwargs): """Read the coverage.py configuration. Arguments: config_file: a boolean or string, see the `Coverage` class for the tricky details. + warn: a function to issue warnings. all others: keyword arguments from the `Coverage` class, used for setting values in the configuration. @@ -541,7 +542,7 @@ def read_coverage_config(config_file, **kwargs): files_to_try = config_files_to_try(config_file) for fname, our_file, specified_file in files_to_try: - config_read = config.from_file(fname, our_file=our_file) + config_read = config.from_file(fname, warn, our_file=our_file) if config_read: break if specified_file: diff --git a/coverage/control.py b/coverage/control.py index e1eb9add1..c45b12458 100644 --- a/coverage/control.py +++ b/coverage/control.py @@ -192,15 +192,7 @@ def __init__( if data_file is _DEFAULT_DATAFILE: data_file = None - # Build our configuration from a number of sources. - self.config = read_coverage_config( - config_file=config_file, - data_file=data_file, cover_pylib=cover_pylib, timid=timid, - branch=branch, parallel=bool_or_none(data_suffix), - source=source, source_pkgs=source_pkgs, run_omit=omit, run_include=include, debug=debug, - report_omit=omit, report_include=include, - concurrency=concurrency, context=context, - ) + self.config = None # This is injectable by tests. self._debug_file = None @@ -235,6 +227,16 @@ def __init__( # Should we write the debug output? self._should_write_debug = True + # Build our configuration from a number of sources. + self.config = read_coverage_config( + config_file=config_file, warn=self._warn, + data_file=data_file, cover_pylib=cover_pylib, timid=timid, + branch=branch, parallel=bool_or_none(data_suffix), + source=source, source_pkgs=source_pkgs, run_omit=omit, run_include=include, debug=debug, + report_omit=omit, report_include=include, + concurrency=concurrency, context=context, + ) + # If we have sub-process measurement happening automatically, then we # want any explicit creation of a Coverage object to mean, this process # is already coverage-aware, so don't auto-measure it. By now, the @@ -352,16 +354,18 @@ def _warn(self, msg, slug=None, once=False): """ if self._no_warn_slugs is None: - self._no_warn_slugs = list(self.config.disable_warnings) + if self.config is not None: + self._no_warn_slugs = list(self.config.disable_warnings) - if slug in self._no_warn_slugs: - # Don't issue the warning - return + if self._no_warn_slugs is not None: + if slug in self._no_warn_slugs: + # Don't issue the warning + return self._warnings.append(msg) if slug: msg = f"{msg} ({slug})" - if self._debug.should('pid'): + if self._debug is not None and self._debug.should('pid'): msg = f"[{os.getpid()}] {msg}" warnings.warn(msg, category=CoverageWarning, stacklevel=2) diff --git a/tests/test_config.py b/tests/test_config.py index bf9cb4a58..9e1268276 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -10,7 +10,7 @@ import coverage from coverage.config import HandyConfigParser -from coverage.exceptions import CoverageException +from coverage.exceptions import CoverageException, CoverageWarning from tests.coveragetest import CoverageTest, UsingModulesMixin from tests.helpers import without_module @@ -392,7 +392,7 @@ def test_unknown_option(self): xyzzy = 17 """) msg = r"Unrecognized option '\[run\] xyzzy=' in config file .coveragerc" - with pytest.raises(CoverageException, match=msg): + with pytest.warns(CoverageWarning, match=msg): _ = coverage.Coverage() def test_unknown_option_toml(self): @@ -401,7 +401,7 @@ def test_unknown_option_toml(self): xyzzy = 17 """) msg = r"Unrecognized option '\[tool.coverage.run\] xyzzy=' in config file pyproject.toml" - with pytest.raises(CoverageException, match=msg): + with pytest.warns(CoverageWarning, match=msg): _ = coverage.Coverage() def test_misplaced_option(self): @@ -410,7 +410,7 @@ def test_misplaced_option(self): branch = True """) msg = r"Unrecognized option '\[report\] branch=' in config file .coveragerc" - with pytest.raises(CoverageException, match=msg): + with pytest.warns(CoverageWarning, match=msg): _ = coverage.Coverage() def test_unknown_option_in_other_ini_file(self): @@ -418,8 +418,8 @@ def test_unknown_option_in_other_ini_file(self): [coverage:run] huh = what? """) - msg = (r"Unrecognized option '\[coverage:run\] huh=' in config file setup.cfg") - with pytest.raises(CoverageException, match=msg): + 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):