Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

provide better error message outside of a kedro project #3680

Merged
merged 11 commits into from
Mar 6, 2024
1 change: 1 addition & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
8 changes: 6 additions & 2 deletions kedro/framework/cli/__init__.py
Original file line number Diff line number Diff line change
@@ -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)
Comment on lines +4 to +6
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can these go in kedro.utils? So you wouldn't have to define them first.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can, but I think cli is the correct scope as this only affect CLI color. Alternative is keep it in cli/cli.py because this is the only file that get used at the moment.


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"]
32 changes: 32 additions & 0 deletions kedro/framework/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@

import importlib
import sys
import traceback
from collections import defaultdict
from pathlib import Path
from typing import Any, Sequence

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
Expand Down Expand Up @@ -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
Expand Down
15 changes: 15 additions & 0 deletions tests/framework/cli/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down