diff options
author | Daniël van Noord <13665637+DanielNoord@users.noreply.github.com> | 2022-04-15 14:34:18 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-15 14:34:18 +0200 |
commit | 6efa821fb68bbc8263c3fcacb29db08a8944d60c (patch) | |
tree | d64b1f7831b8c8bff225b2a4520999425835eb63 | |
parent | 17ce0efd099c45cb2e4ac8aa728805d3401b63a8 (diff) | |
download | pylint-git-6efa821fb68bbc8263c3fcacb29db08a8944d60c.tar.gz |
Add ``unrecognized-option`` message (#6330)
Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | doc/data/messages/u/unrecognize-option/details.rst | 3 | ||||
-rw-r--r-- | doc/whatsnew/2.14.rst | 4 | ||||
-rw-r--r-- | pylint/config/arguments_manager.py | 17 | ||||
-rw-r--r-- | pylint/config/config_initialization.py | 19 | ||||
-rw-r--r-- | pylint/config/exceptions.py | 10 | ||||
-rw-r--r-- | pylint/lint/pylinter.py | 5 | ||||
-rw-r--r-- | pylintrc | 31 | ||||
-rw-r--r-- | tests/config/functional/toml/toml_with_unknown_option.2.out | 2 | ||||
-rw-r--r-- | tests/config/functional/toml/toml_with_unknown_option.result.json | 3 | ||||
-rw-r--r-- | tests/config/functional/toml/toml_with_unknown_option.toml | 6 | ||||
-rw-r--r-- | tests/config/test_config.py | 17 | ||||
-rw-r--r-- | tests/regrtest_data/.pylintrc | 3 | ||||
-rw-r--r-- | tests/test_self.py | 5 |
14 files changed, 86 insertions, 43 deletions
@@ -64,6 +64,10 @@ Release date: TBA Closes #5501 +* Added the ``unrecognized-option`` message. Raised if we encounter any unrecognized options. + + Closes #5259 + * Added new checker ``typevar-double-variance``: The "covariant" and "contravariant" keyword arguments cannot both be set to "True" in a TypeVar. diff --git a/doc/data/messages/u/unrecognize-option/details.rst b/doc/data/messages/u/unrecognize-option/details.rst new file mode 100644 index 000000000..efa9a206c --- /dev/null +++ b/doc/data/messages/u/unrecognize-option/details.rst @@ -0,0 +1,3 @@ +``Pylint`` warns about options it doesn't recognize both in configuration files +and on the command-line. For example, this message would be raised when invoking +pylint with ``pylint --unknown-option=yes test.py``. diff --git a/doc/whatsnew/2.14.rst b/doc/whatsnew/2.14.rst index 375a39321..a8eac1a4b 100644 --- a/doc/whatsnew/2.14.rst +++ b/doc/whatsnew/2.14.rst @@ -119,6 +119,10 @@ Other Changes Closes #4324 +* Added the ``unrecognized-option`` message. Raised if we encounter any unrecognized options. + + Closes #5259 + * Fix false negative for ``bad-string-format-type`` if the value to be formatted is passed in as a variable holding a constant. diff --git a/pylint/config/arguments_manager.py b/pylint/config/arguments_manager.py index 5f49cabbf..47241f14a 100644 --- a/pylint/config/arguments_manager.py +++ b/pylint/config/arguments_manager.py @@ -32,7 +32,10 @@ from pylint.config.argument import ( _StoreOldNamesArgument, _StoreTrueArgument, ) -from pylint.config.exceptions import UnrecognizedArgumentAction +from pylint.config.exceptions import ( + UnrecognizedArgumentAction, + _UnrecognizedOptionError, +) from pylint.config.help_formatter import _HelpFormatter from pylint.config.option import Option from pylint.config.option_parser import OptionParser @@ -216,9 +219,15 @@ class _ArgumentsManager: def _parse_configuration_file(self, arguments: list[str]) -> None: """Parse the arguments found in a configuration file into the namespace.""" - # pylint: disable-next=fixme - # TODO: Optparse: This should parse_args instead of parse_known_args - self.config = self._arg_parser.parse_known_args(arguments, self.config)[0] + self.config, parsed_args = self._arg_parser.parse_known_args( + arguments, self.config + ) + unrecognized_options: list[str] = [] + for opt in parsed_args: + if opt.startswith("--"): + unrecognized_options.append(opt[2:]) + if unrecognized_options: + raise _UnrecognizedOptionError(options=unrecognized_options) def _parse_command_line_configuration( self, arguments: Sequence[str] | None = None diff --git a/pylint/config/config_initialization.py b/pylint/config/config_initialization.py index 644e724ba..78e540d83 100644 --- a/pylint/config/config_initialization.py +++ b/pylint/config/config_initialization.py @@ -9,6 +9,7 @@ from pathlib import Path from typing import TYPE_CHECKING from pylint import config, reporters +from pylint.config.exceptions import _UnrecognizedOptionError from pylint.utils import utils if TYPE_CHECKING: @@ -50,7 +51,11 @@ def _config_initialization( linter.load_plugin_modules(utils._splitstrip(config_data["load-plugins"])) # First we parse any options from a configuration file - linter._parse_configuration_file(config_args) + try: + linter._parse_configuration_file(config_args) + except _UnrecognizedOptionError as exc: + msg = ", ".join(exc.options) + linter.add_message("unrecognized-option", line=0, args=msg) # Then, if a custom reporter is provided as argument, it may be overridden # by file parameters, so we re-set it here. We do this before command line @@ -66,6 +71,18 @@ def _config_initialization( # the configuration file parsed_args_list = linter._parse_command_line_configuration(args_list) + # Check if there are any options that we do not recognize + unrecognized_options: list[str] = [] + for opt in parsed_args_list: + if opt.startswith("--"): + unrecognized_options.append(opt[2:]) + elif opt.startswith("-"): + unrecognized_options.append(opt[1:]) + if unrecognized_options: + msg = ", ".join(unrecognized_options) + linter.add_message("unrecognized-option", line=0, args=msg) + raise _UnrecognizedOptionError(options=unrecognized_options) + # Set the current module to configuration as we don't know where # the --load-plugins key is coming from linter.set_current_module("Command line or configuration file") diff --git a/pylint/config/exceptions.py b/pylint/config/exceptions.py index 25de13910..886dc19dc 100644 --- a/pylint/config/exceptions.py +++ b/pylint/config/exceptions.py @@ -2,6 +2,8 @@ # For details: https://github.com/PyCQA/pylint/blob/main/LICENSE # Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt +from __future__ import annotations + class UnrecognizedArgumentAction(Exception): """Raised if an ArgumentManager instance tries to add an argument for which the action @@ -9,5 +11,13 @@ class UnrecognizedArgumentAction(Exception): """ +class _UnrecognizedOptionError(Exception): + """Raised if an ArgumentManager instance tries to parse an option that is unknown.""" + + def __init__(self, options: list[str], *args: object) -> None: + self.options = options + super().__init__(*args) + + class ArgumentPreprocessingError(Exception): """Raised if an error occurs during argument preprocessing.""" diff --git a/pylint/lint/pylinter.py b/pylint/lint/pylinter.py index 0f40edb64..4d586c635 100644 --- a/pylint/lint/pylinter.py +++ b/pylint/lint/pylinter.py @@ -182,6 +182,11 @@ MSGS = { "bad-configuration-section", "Used when we detect a setting in the top level of a toml configuration that shouldn't be there.", ), + "E0015": ( + "Unrecognized option found: %s", + "unrecognized-option", + "Used when we detect an option that we do not recognize.", + ), } @@ -217,9 +217,6 @@ single-line-if-stmt=no # contains single statement. single-line-class-stmt=no -# List of optional constructs for which whitespace checking is disabled -no-space-check=trailing-comma,dict-separator - # Maximum number of lines in a module max-module-lines=2000 @@ -263,54 +260,36 @@ function-naming-style=snake_case # Regular expression matching correct function names function-rgx=[a-z_][a-z0-9_]{2,30}$ -# Naming hint for function names -function-name-hint=[a-z_][a-z0-9_]{2,30}$ - # Naming style matching correct variable names. variable-naming-style=snake_case # Regular expression matching correct variable names variable-rgx=[a-z_][a-z0-9_]{2,30}$ -# Naming hint for variable names -variable-name-hint=[a-z_][a-z0-9_]{2,30}$ - # Naming style matching correct constant names. const-naming-style=UPPER_CASE # Regular expression matching correct constant names const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ -# Naming hint for constant names -const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - # Naming style matching correct attribute names. attr-naming-style=snake_case # Regular expression matching correct attribute names attr-rgx=[a-z_][a-z0-9_]{2,}$ -# Naming hint for attribute names -attr-name-hint=[a-z_][a-z0-9_]{2,}$ - # Naming style matching correct argument names. argument-naming-style=snake_case # Regular expression matching correct argument names argument-rgx=[a-z_][a-z0-9_]{2,30}$ -# Naming hint for argument names -argument-name-hint=[a-z_][a-z0-9_]{2,30}$ - # Naming style matching correct class attribute names. class-attribute-naming-style=any # Regular expression matching correct class attribute names class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ -# Naming hint for class attribute names -class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - # Naming style matching correct class constant names. class-const-naming-style=UPPER_CASE @@ -324,17 +303,12 @@ inlinevar-naming-style=any # Regular expression matching correct inline iteration names inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ -# Naming hint for inline iteration names -inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ - # Naming style matching correct class names. class-naming-style=PascalCase # Regular expression matching correct class names class-rgx=[A-Z_][a-zA-Z0-9]+$ -# Naming hint for class names -class-name-hint=[A-Z_][a-zA-Z0-9]+$ # Naming style matching correct module names. module-naming-style=snake_case @@ -342,8 +316,6 @@ module-naming-style=snake_case # Regular expression matching correct module names module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ -# Naming hint for module names -module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ # Naming style matching correct method names. method-naming-style=snake_case @@ -351,9 +323,6 @@ method-naming-style=snake_case # Regular expression matching correct method names method-rgx=[a-z_][a-z0-9_]{2,}$ -# Naming hint for method names -method-name-hint=[a-z_][a-z0-9_]{2,}$ - # Regular expression which can overwrite the naming style set by typevar-naming-style. #typevar-rgx= diff --git a/tests/config/functional/toml/toml_with_unknown_option.2.out b/tests/config/functional/toml/toml_with_unknown_option.2.out new file mode 100644 index 000000000..d244629c0 --- /dev/null +++ b/tests/config/functional/toml/toml_with_unknown_option.2.out @@ -0,0 +1,2 @@ +************* Module {abspath} +{relpath}:1:0: E0015: Unrecognized option found: unknown-option, another-unknown-option (unrecognized-option) diff --git a/tests/config/functional/toml/toml_with_unknown_option.result.json b/tests/config/functional/toml/toml_with_unknown_option.result.json new file mode 100644 index 000000000..86a7b52ed --- /dev/null +++ b/tests/config/functional/toml/toml_with_unknown_option.result.json @@ -0,0 +1,3 @@ +{ + "jobs": 10 +} diff --git a/tests/config/functional/toml/toml_with_unknown_option.toml b/tests/config/functional/toml/toml_with_unknown_option.toml new file mode 100644 index 000000000..f0924ebfa --- /dev/null +++ b/tests/config/functional/toml/toml_with_unknown_option.toml @@ -0,0 +1,6 @@ +# Check that we handle unknown options correctly + +[tool.pylint] +unknown-option = "yes" +jobs = "10" +another-unknown-option = "yes" diff --git a/tests/config/test_config.py b/tests/config/test_config.py index 10e3fa4c1..f91e6cbf4 100644 --- a/tests/config/test_config.py +++ b/tests/config/test_config.py @@ -10,6 +10,7 @@ from pathlib import Path import pytest from pytest import CaptureFixture +from pylint.config.exceptions import _UnrecognizedOptionError from pylint.lint import Run from pylint.testutils.configuration_test import run_using_a_configuration_file @@ -61,6 +62,22 @@ def test_unknown_message_id(capsys: CaptureFixture) -> None: assert "Command line:1:0: E0012: Bad option value for --disable." in output.out +def test_unknown_option_name(capsys: CaptureFixture) -> None: + """Check that we correctly raise a message on an unknown option.""" + with pytest.raises(_UnrecognizedOptionError): + Run([str(EMPTY_MODULE), "--unknown-option=yes"], exit=False) + output = capsys.readouterr() + assert "E0015: Unrecognized option found: unknown-option=yes" in output.out + + +def test_unknown_short_option_name(capsys: CaptureFixture) -> None: + """Check that we correctly raise a message on an unknown short option.""" + with pytest.raises(_UnrecognizedOptionError): + Run([str(EMPTY_MODULE), "-Q"], exit=False) + output = capsys.readouterr() + assert "E0015: Unrecognized option found: Q" in output.out + + def test_unknown_confidence(capsys: CaptureFixture) -> None: """Check that we correctly error an unknown confidence value.""" with pytest.raises(SystemExit): diff --git a/tests/regrtest_data/.pylintrc b/tests/regrtest_data/.pylintrc deleted file mode 100644 index f5130379c..000000000 --- a/tests/regrtest_data/.pylintrc +++ /dev/null @@ -1,3 +0,0 @@ -[MASTER] - -optimize-ast=no diff --git a/tests/test_self.py b/tests/test_self.py index e2b859a21..54bbf142a 100644 --- a/tests/test_self.py +++ b/tests/test_self.py @@ -425,11 +425,8 @@ class TestRunTC: def test_bom_marker(self) -> None: path = join(HERE, "regrtest_data", "meta.py") - config_path = join(HERE, "regrtest_data", ".pylintrc") expected = "Your code has been rated at 10.00/10" - self._test_output( - [path, f"--rcfile={config_path}", "-rn"], expected_output=expected - ) + self._test_output([path, "-rn"], expected_output=expected) def test_pylintrc_plugin_duplicate_options(self) -> None: dummy_plugin_path = join(HERE, "regrtest_data", "dummy_plugin") |