Skip to content

Commit

Permalink
feat: unrecognized options are now a warning rather than error. #1035
Browse files Browse the repository at this point in the history
Because they are warnings issued while parsing the configuration file, it's not
possible to suppress them with the coverage configuration.
  • Loading branch information
nedbat committed Aug 5, 2021
1 parent 57a691f commit 3c0e762
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 24 deletions.
6 changes: 6 additions & 0 deletions CHANGES.rst
Expand Up @@ -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:

Expand Down
9 changes: 5 additions & 4 deletions coverage/config.py
Expand Up @@ -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.
Expand Down Expand Up @@ -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
)
Expand Down Expand Up @@ -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.
Expand All @@ -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:
Expand Down
32 changes: 18 additions & 14 deletions coverage/control.py
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down
12 changes: 6 additions & 6 deletions tests/test_config.py
Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand All @@ -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):
Expand All @@ -410,16 +410,16 @@ 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):
self.make_file("setup.cfg", """\
[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):
Expand Down

0 comments on commit 3c0e762

Please sign in to comment.