From eef4f87e7b9958687b6032f8475535ac0beca10f Mon Sep 17 00:00:00 2001 From: Gleb Nikonorov Date: Sat, 30 May 2020 20:36:02 -0400 Subject: [PATCH 1/6] Output a warning to stderr when an invalid key is read from an INI config file --- src/_pytest/config/__init__.py | 9 +++++++ testing/test_config.py | 46 ++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index bb5034ab1..65e5271c2 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -1020,6 +1020,7 @@ class Config: ) self._checkversion() + self._validatekeys() self._consider_importhook(args) self.pluginmanager.consider_preparse(args, exclude_only=False) if not os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): @@ -1072,6 +1073,14 @@ class Config: ) ) + def _validatekeys(self): + for key in self._get_unknown_ini_keys(): + sys.stderr.write("WARNING: unknown config ini key: {}\n".format(key)) + + 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/testing/test_config.py b/testing/test_config.py index 17385dc17..9323e6716 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -147,6 +147,52 @@ class TestParseIni: result = testdir.inline_run("--confcutdir=.") assert result.ret == 0 + @pytest.mark.parametrize( + "ini_file_text, invalid_keys, stderr_output", + [ + ( + """ + [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", + ], + ), + ( + """ + [pytest] + unknown_ini = value1 + minversion = 5.0.0 + """, + ["unknown_ini"], + ["WARNING: unknown config ini key: unknown_ini"], + ), + ( + """ + [pytest] + minversion = 5.0.0 + """, + [], + [], + ), + ], + ) + def test_invalid_ini_keys_generate_warings( + self, testdir, ini_file_text, invalid_keys, stderr_output + ): + 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) + class TestConfigCmdlineParsing: def test_parsing_again_fails(self, testdir): From 8f2c2a5dd9fffc2a59d3ed868c801746ce4b51b5 Mon Sep 17 00:00:00 2001 From: Gleb Nikonorov Date: Sun, 31 May 2020 00:49:21 -0400 Subject: [PATCH 2/6] Add test case for invalid ini key in different section header --- testing/test_config.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/testing/test_config.py b/testing/test_config.py index 9323e6716..e35019337 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -173,6 +173,16 @@ class TestParseIni: ), ( """ + [some_other_header] + unknown_ini = value1 + [pytest] + minversion = 5.0.0 + """, + [], + [], + ), + ( + """ [pytest] minversion = 5.0.0 """, From db203afba32cc162ab9e8d1da079dde600bd21cc Mon Sep 17 00:00:00 2001 From: Gleb Nikonorov Date: Sun, 31 May 2020 02:45:40 -0400 Subject: [PATCH 3/6] Add in --strict-config flag to force warnings to errors --- src/_pytest/config/__init__.py | 9 ++++++--- src/_pytest/main.py | 5 +++++ testing/test_config.py | 20 ++++++++++++++------ 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 65e5271c2..63f7d4cb4 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -1020,7 +1020,7 @@ class Config: ) self._checkversion() - self._validatekeys() + self._validatekeys(args) self._consider_importhook(args) self.pluginmanager.consider_preparse(args, exclude_only=False) if not os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): @@ -1073,9 +1073,12 @@ class Config: ) ) - def _validatekeys(self): + def _validatekeys(self, args: Sequence[str]): for key in self._get_unknown_ini_keys(): - sys.stderr.write("WARNING: unknown config ini key: {}\n".format(key)) + message = "Unknown config ini key: {}\n".format(key) + if "--strict-config" in args: + fail(message, pytrace=False) + sys.stderr.write("WARNING: {}".format(message)) def _get_unknown_ini_keys(self) -> List[str]: parser_inicfg = self._parser._inidict 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 e35019337..6a08e93f3 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -148,7 +148,7 @@ class TestParseIni: assert result.ret == 0 @pytest.mark.parametrize( - "ini_file_text, invalid_keys, stderr_output", + "ini_file_text, invalid_keys, stderr_output, exception_text", [ ( """ @@ -158,9 +158,10 @@ class TestParseIni: """, ["unknown_ini", "another_unknown_ini"], [ - "WARNING: unknown config ini key: unknown_ini", - "WARNING: unknown config ini key: another_unknown_ini", + "WARNING: Unknown config ini key: unknown_ini", + "WARNING: Unknown config ini key: another_unknown_ini", ], + "Unknown config ini key: unknown_ini", ), ( """ @@ -169,7 +170,8 @@ class TestParseIni: minversion = 5.0.0 """, ["unknown_ini"], - ["WARNING: unknown config ini key: unknown_ini"], + ["WARNING: Unknown config ini key: unknown_ini"], + "Unknown config ini key: unknown_ini", ), ( """ @@ -180,6 +182,7 @@ class TestParseIni: """, [], [], + "", ), ( """ @@ -188,11 +191,12 @@ class TestParseIni: """, [], [], + "", ), ], ) - def test_invalid_ini_keys_generate_warings( - self, testdir, ini_file_text, invalid_keys, stderr_output + 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() @@ -203,6 +207,10 @@ class TestParseIni: 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): From 92d15c6af1943faeb629bf3cbd6488b56d9aef52 Mon Sep 17 00:00:00 2001 From: Gleb Nikonorov Date: Sun, 31 May 2020 11:33:31 -0400 Subject: [PATCH 4/6] review feedback --- src/_pytest/config/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 63f7d4cb4..5fc23716d 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -1020,7 +1020,6 @@ class Config: ) self._checkversion() - self._validatekeys(args) self._consider_importhook(args) self.pluginmanager.consider_preparse(args, exclude_only=False) if not os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): @@ -1031,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 @@ -1073,10 +1073,10 @@ class Config: ) ) - def _validatekeys(self, args: Sequence[str]): + def _validatekeys(self): for key in self._get_unknown_ini_keys(): message = "Unknown config ini key: {}\n".format(key) - if "--strict-config" in args: + if self.known_args_namespace.strict_config: fail(message, pytrace=False) sys.stderr.write("WARNING: {}".format(message)) From 9ae94b08e2ad55aed1abc7aa414adac626d005f9 Mon Sep 17 00:00:00 2001 From: Gleb Nikonorov Date: Sun, 31 May 2020 11:58:39 -0400 Subject: [PATCH 5/6] Add documentation --- AUTHORS | 1 + changelog/6856.feature.rst | 0 2 files changed, 1 insertion(+) create mode 100644 changelog/6856.feature.rst diff --git a/AUTHORS b/AUTHORS index 5d410da18..41b0e38b0 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..e69de29bb From a5d13d4ced6dc3f8d826233e02788b86aebb3743 Mon Sep 17 00:00:00 2001 From: Gleb Nikonorov Date: Tue, 2 Jun 2020 08:21:57 -0400 Subject: [PATCH 6/6] Add changelog entry --- changelog/6856.feature.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/changelog/6856.feature.rst b/changelog/6856.feature.rst index e69de29bb..36892fa21 100644 --- a/changelog/6856.feature.rst +++ 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.