| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
- # For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
- # Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
- """Utils for the 'pylint-config' command."""
- from __future__ import annotations
- import sys
- from collections.abc import Callable
- from pathlib import Path
- from typing import TypeVar
- if sys.version_info >= (3, 8):
- from typing import Literal
- else:
- from typing_extensions import Literal
- if sys.version_info >= (3, 10):
- from typing import ParamSpec
- else:
- from typing_extensions import ParamSpec
- _P = ParamSpec("_P")
- _ReturnValueT = TypeVar("_ReturnValueT", bool, str)
- SUPPORTED_FORMATS = {"t", "toml", "i", "ini"}
- YES_NO_ANSWERS = {"y", "yes", "n", "no"}
- 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[_P, _ReturnValueT]
- ) -> Callable[_P, _ReturnValueT]:
- """Decorator that handles InvalidUserInput exceptions and retries."""
- def inner_function(*args: _P.args, **kwargs: _P.kwargs) -> _ReturnValueT:
- called_once = False
- while True:
- try:
- return func(*args, **kwargs)
- except InvalidUserInput as exc:
- if called_once and exc.input == "exit()":
- print("Stopping 'pylint-config'.")
- sys.exit()
- print(f"Answer 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
- format_type = input(
- "Please choose the format of configuration, (T)oml or (I)ni (.cfg): "
- ).lower()
- if format_type not in SUPPORTED_FORMATS:
- raise InvalidUserInput(", ".join(sorted(SUPPORTED_FORMATS)), format_type)
- if format_type.startswith("t"):
- return "toml"
- return "ini"
- @should_retry_after_invalid_input
- def validate_yes_no(question: str, default: Literal["yes", "no"] | None) -> bool:
- """Validate that a yes or no answer is correct."""
- question = f"{question} (y)es or (n)o "
- if default:
- question += f" (default={default}) "
- # pylint: disable-next=bad-builtin
- answer = input(question).lower()
- if not answer and default:
- answer = default
- if answer not in YES_NO_ANSWERS:
- raise InvalidUserInput(", ".join(sorted(YES_NO_ANSWERS)), answer)
- return answer.startswith("y")
- def get_minimal_setting() -> bool:
- """Ask the user if they want to use the minimal setting."""
- return validate_yes_no(
- "Do you want a minimal configuration without comments or default values?", "no"
- )
- def get_and_validate_output_file() -> tuple[bool, Path]:
- """Make sure that the output file is correct."""
- to_file = validate_yes_no("Do you want to write the output to a file?", "no")
- if not to_file:
- return False, Path()
- # pylint: disable-next=bad-builtin
- file_name = Path(input("What should the file be called: "))
- if file_name.exists():
- overwrite = validate_yes_no(
- f"{file_name} already exists. Are you sure you want to overwrite?", "no"
- )
- if not overwrite:
- return False, file_name
- return True, file_name
- return True, file_name
|