diff --git a/RELEASE.md b/RELEASE.md index 182aa16a02..e4b293342f 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,6 +1,7 @@ # Upcoming Release 0.19.4 ## Major features and improvements +* Kedro CLI now provides a better error message when project commands are run outside of a project i.e. `kedro run` ## Bug fixes and other changes * Updated `kedro pipeline create` and `kedro pipeline delete` to read the base environment from the project settings. diff --git a/kedro/framework/cli/__init__.py b/kedro/framework/cli/__init__.py index fcbb427ef7..8816ace3ea 100644 --- a/kedro/framework/cli/__init__.py +++ b/kedro/framework/cli/__init__.py @@ -1,7 +1,11 @@ """``kedro.framework.cli`` implements commands available from Kedro's CLI. """ -from .cli import main -from .utils import command_with_verbosity, load_entry_points +# The constant need to be defined first otherwise it causes circular depdencies +ORANGE = (255, 175, 0) +BRIGHT_BLACK = (128, 128, 128) + +from .cli import main # noqa: E402 +from .utils import command_with_verbosity, load_entry_points # noqa: E402 __all__ = ["main", "command_with_verbosity", "load_entry_points"] diff --git a/kedro/framework/cli/cli.py b/kedro/framework/cli/cli.py index 5ecd5b9f9c..8ee3567b7a 100644 --- a/kedro/framework/cli/cli.py +++ b/kedro/framework/cli/cli.py @@ -6,6 +6,7 @@ import importlib import sys +import traceback from collections import defaultdict from pathlib import Path from typing import Any, Sequence @@ -13,6 +14,7 @@ import click from kedro import __version__ as version +from kedro.framework.cli import BRIGHT_BLACK, ORANGE from kedro.framework.cli.catalog import catalog_cli from kedro.framework.cli.hooks import get_cli_hook_manager from kedro.framework.cli.jupyter import jupyter_cli @@ -133,10 +135,40 @@ def main( ) # click.core.main() method exits by default, we capture this and then # exit as originally intended + except SystemExit as exc: self._cli_hook_manager.hook.after_command_run( project_metadata=self._metadata, command_args=args, exit_code=exc.code ) + # When CLI is run outside of a project, project_groups are not registered + catch_exception = "click.exceptions.UsageError: No such command" + # click convert exception handles to error message + if catch_exception in traceback.format_exc() and not self.project_groups: + warn = click.style( + "\nKedro project not found in this directory. ", + fg=ORANGE, + bold=True, + ) + result = ( + click.style("Project specific commands such as ") + + click.style("'run' ", fg="cyan") + + "or " + + click.style("'jupyter' ", fg="cyan") + + "are only available within a project directory." + ) + message = warn + result + hint = ( + click.style( + "\nHint: Kedro is looking for a file called ", fg=BRIGHT_BLACK + ) + + click.style("'pyproject.toml", fg="magenta") + + click.style( + ", is one present in your current working directory?", + fg=BRIGHT_BLACK, + ) + ) + click.echo(message) + click.echo(hint) sys.exit(exc.code) @property diff --git a/tests/framework/cli/test_cli.py b/tests/framework/cli/test_cli.py index c1d348e8b2..c1e5ce51ec 100644 --- a/tests/framework/cli/test_cli.py +++ b/tests/framework/cli/test_cli.py @@ -386,6 +386,21 @@ def test_kedro_cli_no_project(self, mocker, tmp_path): assert "Global commands from Kedro" in result.output assert "Project specific commands from Kedro" not in result.output + def test_kedro_run_no_project(self, mocker, tmp_path): + mocker.patch("kedro.framework.cli.cli._is_project", return_value=False) + kedro_cli = KedroCLI(tmp_path) + + result = CliRunner().invoke(kedro_cli, ["run"]) + assert ( + "Kedro project not found in this directory. Project specific commands such as 'run' or " + "'jupyter' are only available within a project directory." in result.output + ) + + assert ( + "Hint: Kedro is looking for a file called 'pyproject.toml, is one present in" + " your current working directory?" in result.output + ) + def test_kedro_cli_with_project(self, mocker, fake_metadata): Module = namedtuple("Module", ["cli"]) mocker.patch("kedro.framework.cli.cli._is_project", return_value=True)