Repository: kedro-org/kedro
File: kedro/framework/cli/starters.py (Python)

Code before:

        self.text = kwargs.get("text", "")
        self.regexp = kwargs.get("regex_validator", None)
        self.error_message = kwargs.get("error_message", "")

    def __str__(self) -> str:
        # Format the title with optional color
        title = self.title.strip().title()
        title = click.style(title, fg="cyan", bold=True)
        title_line = "=" * len(self.title)
        title_line = click.style(title_line, fg="cyan", bold=True)

        # Format the main text
        text = self.text.strip()

        # Combine everything
        prompt_lines = [title, title_line, text]
        prompt_text = "\n".join(str(line).strip() for line in prompt_lines)
        return f"\n{prompt_text}\n"

    @property
    def user_input(self) -> str:
        return self._user_input

    @user_input.setter
    def user_input(self, input: str) -> None:
        """Validate and set the user input."""
        if self.regexp and not re.match(self.regexp, input):
            message = f"'{input}' is an invalid value for {(self.title).lower()}."
            click.secho(message, fg="red", err=True)
            click.secho(self.error_message, fg="red", err=True)
            sys.exit(1)
        self._user_input = input


def _remove_readonly(
    func: Callable, path: Path, excinfo: tuple
) -> None:  # pragma: no cover
    """Remove readonly files on Windows
    See: https://docs.python.org/3/library/shutil.html?highlight=shutil#rmtree-example
    """
    os.chmod(path, stat.S_IWRITE)
    func(path)


def _starter_spec_to_dict(
    starter_specs: dict[str, KedroStarterSpec],
) -> dict[str, dict[str, str]]:
    """Convert a dictionary of starters spec to a nicely formatted dictionary"""
    format_dict: dict[str, dict[str, str]] = {}
    for alias, spec in starter_specs.items():

Review comment (suggestion):
I'm not sure it's clear enough now that this is doing validation 🤔  I have a slight preference to keeping the `validate()` method as before.

Code after:
            ) from exc

        self.text = kwargs.get("text", "")
        self.regexp = kwargs.get("regex_validator", None)
        self.error_message = kwargs.get("error_message", "")

    def __str__(self) -> str:
        # Format the title with optional color
        title = self.title.strip().title()
        title = click.style(title, fg="cyan", bold=True)
        title_line = "=" * len(self.title)
        title_line = click.style(title_line, fg="cyan", bold=True)

        # Format the main text
        text = self.text.strip()

        # Combine everything
        prompt_lines = [title, title_line, text]
        prompt_text = "\n".join(str(line).strip() for line in prompt_lines)
        return f"\n{prompt_text}\n"

    def validate(self, user_input: str) -> None:
        """Validate a given prompt value against the regex validator"""
        if self.regexp and not re.match(self.regexp, user_input):
            message = f"'{user_input}' is an invalid value for {(self.title).lower()}."
            click.secho(message, fg="red", err=True)
            click.secho(self.error_message, fg="red", err=True)
            sys.exit(1)


def _remove_readonly(
    func: Callable, path: Path, excinfo: tuple
) -> None:  # pragma: no cover
    """Remove readonly files on Windows
    See: https://docs.python.org/3/library/shutil.html?highlight=shutil#rmtree-example
    """
    os.chmod(path, stat.S_IWRITE)
    func(path)


def _starter_spec_to_dict(
    starter_specs: dict[str, KedroStarterSpec],
) -> dict[str, dict[str, str]]:
    """Convert a dictionary of starters spec to a nicely formatted dictionary"""
    format_dict: dict[str, dict[str, str]] = {}
    for alias, spec in starter_specs.items():
        format_dict[alias] = {}  # Each dictionary represent 1 starter
        format_dict[alias]["template_path"] = spec.template_path
        if spec.directory:
            format_dict[alias]["directory"] = spec.directory
    return format_dict