diff options
-rw-r--r-- | pylint/config/_pylint_config/utils.py | 35 | ||||
-rw-r--r-- | tests/config/pylint_config/test_pylint_config_generate.py | 6 | ||||
-rw-r--r-- | tests/config/pylint_config/test_pylint_config_utils.py | 35 |
3 files changed, 70 insertions, 6 deletions
diff --git a/pylint/config/_pylint_config/utils.py b/pylint/config/_pylint_config/utils.py index 4e33eec91..a905cd12f 100644 --- a/pylint/config/_pylint_config/utils.py +++ b/pylint/config/_pylint_config/utils.py @@ -4,8 +4,10 @@ """Utils for the 'pylint-config' command.""" +from __future__ import annotations import sys +from collections.abc import Callable if sys.version_info >= (3, 8): from typing import Literal @@ -16,6 +18,35 @@ else: SUPPORTED_FORMATS = {"t", "toml", "i", "ini"} +class InvalidUserInput(Exception): + """Raised whenever a user input is invalid.""" + + def __init__(self, valid_input: str, input_value: str, *args: object) -> None: + self.valid = valid_input + self.input = input_value + super().__init__(*args) + + +def should_retry_after_invalid_input(func: Callable[[], str]) -> Callable[[], str]: + """Decorator that handles InvalidUserInput exceptions and retries.""" + + def inner_function() -> str: + called_once = False + while True: + try: + return func() + except InvalidUserInput as exc: + if called_once and exc.input == "exit()": + print("Stopping 'pylint-config'.") + sys.exit() + print(f"Format should be one of {exc.valid}.") + print("Type 'exit()' if you want to exit the program.") + called_once = True + + return inner_function + + +@should_retry_after_invalid_input def get_and_validate_format() -> Literal["toml", "ini"]: """Make sure that the output format is either .toml or .ini.""" # pylint: disable-next=bad-builtin @@ -24,9 +55,7 @@ def get_and_validate_format() -> Literal["toml", "ini"]: ).lower() if format_type not in SUPPORTED_FORMATS: - raise ValueError( - f"Format should be one of {', '.join(i.capitalize() for i in sorted(SUPPORTED_FORMATS))}" - ) + raise InvalidUserInput(", ".join(sorted(SUPPORTED_FORMATS)), format_type) if format_type.startswith("t"): return "toml" diff --git a/tests/config/pylint_config/test_pylint_config_generate.py b/tests/config/pylint_config/test_pylint_config_generate.py index 60a037747..118aff0fa 100644 --- a/tests/config/pylint_config/test_pylint_config_generate.py +++ b/tests/config/pylint_config/test_pylint_config_generate.py @@ -34,7 +34,7 @@ def test_format_of_output( ) -> None: """Check that we output the correct format.""" # Set the answers needed for the input() calls - answers = iter(["T", "toml", "TOML", "I", "INI", "TOMLINI"]) + answers = iter(["T", "toml", "TOML", "I", "INI", "TOMLINI", "exit()"]) monkeypatch.setattr("builtins.input", lambda x: next(answers)) with warnings.catch_warnings(): @@ -64,6 +64,6 @@ def test_format_of_output( captured = capsys.readouterr() assert "[MAIN]" in captured.out - # Check 'TOMLINI' - with pytest.raises(ValueError, match="Format should be one.*"): + # Check 'TOMLINI' and then 'exit()' + with pytest.raises(SystemExit): Run(["generate", "--interactive"], exit=False) diff --git a/tests/config/pylint_config/test_pylint_config_utils.py b/tests/config/pylint_config/test_pylint_config_utils.py new file mode 100644 index 000000000..210bcf93b --- /dev/null +++ b/tests/config/pylint_config/test_pylint_config_utils.py @@ -0,0 +1,35 @@ +# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE +# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt + +"""Test for the 'pylint-config' utils.""" + + +import pytest +from pytest import CaptureFixture, MonkeyPatch + +from pylint.config._pylint_config.utils import get_and_validate_format + + +def test_retrying_user_input_validation( + monkeypatch: MonkeyPatch, capsys: CaptureFixture[str] +) -> None: + """Check that we retry after a wrong answer.""" + # Set the answers needed for the input() calls + answers = iter(["A", "B", "EXIT", "EXIT()"]) + monkeypatch.setattr("builtins.input", lambda x: next(answers)) + + with pytest.raises(SystemExit): + get_and_validate_format() + captured = capsys.readouterr() + assert ( + captured.out + == """Format should be one of i, ini, t, toml. +Type 'exit()' if you want to exit the program. +Format should be one of i, ini, t, toml. +Type 'exit()' if you want to exit the program. +Format should be one of i, ini, t, toml. +Type 'exit()' if you want to exit the program. +Stopping 'pylint-config'. +""" + ) |