diff --git a/AUTHORS b/AUTHORS index a76c0a632..e1b195b9a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -109,6 +109,7 @@ Gabriel Reis Gene Wood George Kussumoto Georgy Dyuldin +Gleb Nikonorov Graham Horler Greg Price Gregory Lee diff --git a/changelog/6856.feature.rst b/changelog/6856.feature.rst new file mode 100644 index 000000000..36892fa21 --- /dev/null +++ b/changelog/6856.feature.rst @@ -0,0 +1,3 @@ +A warning is now shown when an unknown key is read from a config INI file. + +The `--strict-config` flag has been added to treat these warnings as errors. diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index bb5034ab1..5fc23716d 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -1030,6 +1030,7 @@ class Config: self.known_args_namespace = ns = self._parser.parse_known_args( args, namespace=copy.copy(self.option) ) + self._validatekeys() if self.known_args_namespace.confcutdir is None and self.inifile: confcutdir = py.path.local(self.inifile).dirname self.known_args_namespace.confcutdir = confcutdir @@ -1072,6 +1073,17 @@ class Config: ) ) + def _validatekeys(self): + for key in self._get_unknown_ini_keys(): + message = "Unknown config ini key: {}\n".format(key) + if self.known_args_namespace.strict_config: + fail(message, pytrace=False) + sys.stderr.write("WARNING: {}".format(message)) + + def _get_unknown_ini_keys(self) -> List[str]: + parser_inicfg = self._parser._inidict + return [name for name in self.inicfg if name not in parser_inicfg] + def parse(self, args: List[str], addopts: bool = True) -> None: # parse given cmdline arguments into this config object. assert not hasattr( diff --git a/src/_pytest/main.py b/src/_pytest/main.py index de7e16744..4eb47be2c 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -70,6 +70,11 @@ def pytest_addoption(parser): default=0, help="exit after first num failures or errors.", ) + group._addoption( + "--strict-config", + action="store_true", + help="invalid ini keys for the `pytest` section of the configuration file raise errors.", + ) group._addoption( "--strict-markers", "--strict", diff --git a/testing/test_config.py b/testing/test_config.py index 17385dc17..6a08e93f3 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -147,6 +147,70 @@ class TestParseIni: result = testdir.inline_run("--confcutdir=.") assert result.ret == 0 + @pytest.mark.parametrize( + "ini_file_text, invalid_keys, stderr_output, exception_text", + [ + ( + """ + [pytest] + unknown_ini = value1 + another_unknown_ini = value2 + """, + ["unknown_ini", "another_unknown_ini"], + [ + "WARNING: Unknown config ini key: unknown_ini", + "WARNING: Unknown config ini key: another_unknown_ini", + ], + "Unknown config ini key: unknown_ini", + ), + ( + """ + [pytest] + unknown_ini = value1 + minversion = 5.0.0 + """, + ["unknown_ini"], + ["WARNING: Unknown config ini key: unknown_ini"], + "Unknown config ini key: unknown_ini", + ), + ( + """ + [some_other_header] + unknown_ini = value1 + [pytest] + minversion = 5.0.0 + """, + [], + [], + "", + ), + ( + """ + [pytest] + minversion = 5.0.0 + """, + [], + [], + "", + ), + ], + ) + def test_invalid_ini_keys( + self, testdir, ini_file_text, invalid_keys, stderr_output, exception_text + ): + testdir.tmpdir.join("pytest.ini").write(textwrap.dedent(ini_file_text)) + config = testdir.parseconfig() + assert config._get_unknown_ini_keys() == invalid_keys, str( + config._get_unknown_ini_keys() + ) + + result = testdir.runpytest() + result.stderr.fnmatch_lines(stderr_output) + + if stderr_output: + with pytest.raises(pytest.fail.Exception, match=exception_text): + testdir.runpytest("--strict-config") + class TestConfigCmdlineParsing: def test_parsing_again_fails(self, testdir):