From a96055861d136e7733a1d9a3ddde67799ce6ccbd Mon Sep 17 00:00:00 2001 From: "L. R. Couto" <57910428+lrcouto@users.noreply.github.com> Date: Mon, 27 Jan 2025 02:05:48 -0300 Subject: [PATCH] Use click features for project creation prompts (#4387) * Test prompt change Signed-off-by: Laura Couto * Test adding default value Signed-off-by: Laura Couto * Change other prompts Signed-off-by: Laura Couto * Move prompting logic to the inside of new function Signed-off-by: Laura Couto * fix name default Signed-off-by: Laura Couto * Add auxiliary function for converting numbers to tools Signed-off-by: Laura Couto * Normalize whitespaces in test outputs Signed-off-by: Laura Couto * Prevent prompts from happening when a config file is used Signed-off-by: Laura Couto * move click prompting to replace cookiecutter's Signed-off-by: Laura Couto * Remove reduntant variables Signed-off-by: Laura Couto * Fix more tests Signed-off-by: Laura Couto * All unit tests passing Signed-off-by: Laura Couto * Revert unnecessary changes on tests Signed-off-by: Laura Couto * remove reduntant leftover code Signed-off-by: Laura Couto * remove reduntant leftover code Signed-off-by: Laura Couto * Set up prompt appearance Signed-off-by: Laura Couto * Revert default to unformatted Signed-off-by: Laura Couto * Switch yes/no CLI prompts to click validation Signed-off-by: Laura Couto * Lint Signed-off-by: Laura Couto * Use click.Choice for telemetry, rest as our own function Signed-off-by: Laura Couto * Change example validation back to what it was initially Signed-off-by: Laura Couto * Revert verification to _Prompt.validate Signed-off-by: Laura Couto --------- Signed-off-by: Laura Couto --- kedro/framework/cli/starters.py | 65 ++++++++++++++++++++-------- tests/framework/cli/test_starters.py | 7 +-- 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/kedro/framework/cli/starters.py b/kedro/framework/cli/starters.py index 0d3b54c8ec..924db1dae0 100644 --- a/kedro/framework/cli/starters.py +++ b/kedro/framework/cli/starters.py @@ -167,6 +167,7 @@ def _kedro_version_equal_or_lower_to_starters(version: str) -> bool: "pyspark": "6", "viz": "7", } + NUMBER_TO_TOOLS_NAME = { "1": "Linting", "2": "Testing", @@ -311,10 +312,31 @@ def starter() -> None: @click.option("--starter", "-s", "starter_alias", help=STARTER_ARG_HELP) @click.option("--checkout", help=CHECKOUT_ARG_HELP) @click.option("--directory", help=DIRECTORY_ARG_HELP) -@click.option("--tools", "-t", "selected_tools", help=TOOLS_ARG_HELP) -@click.option("--name", "-n", "project_name", help=NAME_ARG_HELP) -@click.option("--example", "-e", "example_pipeline", help=EXAMPLE_ARG_HELP) -@click.option("--telemetry", "-tc", "telemetry_consent", help=TELEMETRY_ARG_HELP) +@click.option( + "--name", + "-n", + "project_name", + help=NAME_ARG_HELP, +) +@click.option( + "--tools", + "-t", + "selected_tools", + help=TOOLS_ARG_HELP, +) +@click.option( + "--example", + "-e", + "example_pipeline", + help=EXAMPLE_ARG_HELP, +) +@click.option( + "--telemetry", + "-tc", + "telemetry_consent", + help=TELEMETRY_ARG_HELP, + type=click.Choice(["yes", "no", "y", "n"], case_sensitive=False), +) def new( # noqa: PLR0913 config_path: str, starter_alias: str, @@ -337,6 +359,7 @@ def new( # noqa: PLR0913 "example": example_pipeline, "telemetry_consent": telemetry_consent, } + _validate_flag_inputs(flag_inputs) starters_dict = _get_starters_dict() @@ -381,6 +404,7 @@ def new( # noqa: PLR0913 shutil.rmtree(tmpdir, onerror=_remove_readonly) # type: ignore[arg-type] # Obtain config, either from a file or from interactive user prompts. + extra_context = _get_extra_context( prompts_required=prompts_required, config_path=config_path, @@ -399,7 +423,6 @@ def new( # noqa: PLR0913 ) if telemetry_consent is not None: - _validate_input_with_regex_pattern("yes_no", telemetry_consent) telemetry_consent = ( "true" if _parse_yes_no_to_bool(telemetry_consent) else "false" ) @@ -727,7 +750,8 @@ def _fetch_validate_parse_config_from_file( def _fetch_validate_parse_config_from_user_prompts( - prompts: dict[str, Any], cookiecutter_context: OrderedDict | None + prompts: dict[str, Any], + cookiecutter_context: OrderedDict | None, ) -> dict[str, str]: """Interactively obtains information from user prompts. @@ -739,9 +763,6 @@ def _fetch_validate_parse_config_from_user_prompts( Configuration for starting a new project. This is passed as ``extra_context`` to cookiecutter and will overwrite the cookiecutter.json defaults. """ - from cookiecutter.environment import StrictEnvironment - from cookiecutter.prompt import read_user_variable, render_variable - if not cookiecutter_context: raise Exception("No cookiecutter context available.") @@ -751,14 +772,16 @@ def _fetch_validate_parse_config_from_user_prompts( prompt = _Prompt(**prompt_dict) # render the variable on the command line - cookiecutter_variable = render_variable( - env=StrictEnvironment(context=cookiecutter_context), - raw=cookiecutter_context.get(variable_name), - cookiecutter_dict=config, - ) + default_value = cookiecutter_context.get(variable_name) or "" # read the user's input for the variable - user_input = read_user_variable(str(prompt), cookiecutter_variable) + user_input = click.prompt( + str(prompt), + default=default_value, + show_default=True, + type=str, + ).strip() + if user_input: prompt.validate(user_input) config[variable_name] = user_input @@ -997,9 +1020,17 @@ def __init__(self, *args: Any, **kwargs: Any) -> 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 + "\n" + "=" * len(title), bold=True) - prompt_lines = [title, self.text] + 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" diff --git a/tests/framework/cli/test_starters.py b/tests/framework/cli/test_starters.py index a1852c75af..e6a8bd2f60 100644 --- a/tests/framework/cli/test_starters.py +++ b/tests/framework/cli/test_starters.py @@ -1676,12 +1676,9 @@ def test_flag_value_is_invalid(self, fake_kedro_cli): ) repo_name = "new-kedro-project" - assert result.exit_code == 1 + assert result.exit_code == 2 - assert ( - "'wrong' is an invalid value for example pipeline. It must contain only y, n, YES, or NO (case insensitive)." - in result.output - ) + assert "'wrong' is not one of 'yes', 'no', 'y', 'n'" in result.output telemetry_file_path = Path(repo_name + "/.telemetry") assert not telemetry_file_path.exists()