Skip to content

feat: unrecognized options are now a warning rather than error. #1035 #1206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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