summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pylint/config/_pylint_config/utils.py35
-rw-r--r--tests/config/pylint_config/test_pylint_config_generate.py6
-rw-r--r--tests/config/pylint_config/test_pylint_config_utils.py35
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'.
+"""
+ )