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

Make kedro commands work from inside subdirectories in project #3683

Merged
merged 9 commits into from
Mar 7, 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 commands now work from any subdirectory within a Kedro project.
Copy link
Member

Choose a reason for hiding this comment

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

_is_project and _find_kedro_project are private methods, but it might be worth mentioning in the release notes section "Breaking changes to the API" that these were moved to kedro.utils, just in case people are using them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated!


## 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: 8 additions & 0 deletions features/run.feature
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,11 @@ Feature: Run Project
When I execute the kedro command "run --params extra1=1,extra2=value2"
Then I should get a successful exit code
And the logs should show that 4 nodes were run

Scenario: Run kedro run from within a sub-directory
Given I have prepared a config file
And I have run a non-interactive kedro new with starter "default"
And I have changed the current working directory to "data"
When I execute the kedro command "run"
Then I should get a successful exit code
And the logs should show that 4 nodes were run
6 changes: 6 additions & 0 deletions features/steps/cli_steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -732,3 +732,9 @@ def add_micropkg_to_pyproject_toml(context: behave.runner.Context):
)
with pyproject_toml_path.open(mode="a") as file:
file.write(project_toml_str)


@given('I have changed the current working directory to "{dir}"')
def change_dir(context, dir):
"""Execute Kedro target."""
util.chdir(dir)
7 changes: 5 additions & 2 deletions kedro/framework/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
load_entry_points,
)
from kedro.framework.project import LOGGING # noqa: F401
from kedro.framework.startup import _is_project, bootstrap_project
from kedro.framework.startup import bootstrap_project
from kedro.utils import _find_kedro_project, _is_project

LOGO = rf"""
_ _
Expand Down Expand Up @@ -194,5 +195,7 @@ def main() -> None: # pragma: no cover
commands to `kedro`'s before invoking the CLI.
"""
_init_plugins()
cli_collection = KedroCLI(project_path=Path.cwd())
cli_collection = KedroCLI(
project_path=_find_kedro_project(Path.cwd()) or Path.cwd()
)
cli_collection()
5 changes: 4 additions & 1 deletion kedro/framework/session/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from kedro.framework.session.store import BaseSessionStore
from kedro.io.core import generate_timestamp
from kedro.runner import AbstractRunner, SequentialRunner
from kedro.utils import _find_kedro_project


def _describe_git(project_path: Path) -> dict[str, dict[str, Any]]:
Expand Down Expand Up @@ -104,7 +105,9 @@ def __init__( # noqa: PLR0913
save_on_close: bool = False,
conf_source: str | None = None,
):
self._project_path = Path(project_path or Path.cwd()).resolve()
self._project_path = Path(
project_path or _find_kedro_project(Path.cwd()).resolve() or Path.cwd()
Copy link
Member

Choose a reason for hiding this comment

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

Previously we called .resolve() on all options project_path as well as Path.cwd() and now only on _find_kedro_project(Path.cwd()).resolve(), why did that change?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated!

)
self.session_id = session_id
self.save_on_close = save_on_close
self._package_name = package_name
Expand Down
11 changes: 0 additions & 11 deletions kedro/framework/startup.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,6 @@ def _version_mismatch_error(kedro_init_version: str) -> str:
)


def _is_project(project_path: Union[str, Path]) -> bool:
metadata_file = Path(project_path).expanduser().resolve() / _PYPROJECT
if not metadata_file.is_file():
return False

try:
return "[tool.kedro]" in metadata_file.read_text(encoding="utf-8")
except Exception: # noqa: broad-except
return False


def _get_project_metadata(project_path: Union[str, Path]) -> ProjectMetadata:
"""Read project metadata from `<project_root>/pyproject.toml` config file,
under the `[tool.kedro]` section.
Expand Down
13 changes: 2 additions & 11 deletions kedro/ipython/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@
pipelines,
)
from kedro.framework.session import KedroSession
from kedro.framework.startup import _is_project, bootstrap_project
from kedro.framework.startup import bootstrap_project
from kedro.pipeline.node import Node
from kedro.utils import _is_databricks
from kedro.utils import _find_kedro_project, _is_databricks

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -186,15 +186,6 @@ def _remove_cached_modules(package_name: str) -> None: # pragma: no cover
del sys.modules[module]


def _find_kedro_project(current_dir: Path) -> Any: # pragma: no cover
while current_dir != current_dir.parent:
if _is_project(current_dir):
return current_dir
current_dir = current_dir.parent

return None


def _guess_run_environment() -> str: # pragma: no cover
"""Best effort to guess the IPython/Jupyter environment"""
# https://github.com/microsoft/vscode-jupyter/issues/7380
Expand Down
24 changes: 23 additions & 1 deletion kedro/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
"""
import importlib
import os
from typing import Any
from pathlib import Path
from typing import Any, Union

_PYPROJECT = "pyproject.toml"


def load_obj(obj_path: str, default_obj_path: str = "") -> Any:
Expand All @@ -29,3 +32,22 @@ def load_obj(obj_path: str, default_obj_path: str = "") -> Any:

def _is_databricks() -> bool:
return "DATABRICKS_RUNTIME_VERSION" in os.environ


def _is_project(project_path: Union[str, Path]) -> bool:
metadata_file = Path(project_path).expanduser().resolve() / _PYPROJECT
if not metadata_file.is_file():
return False

try:
return "[tool.kedro]" in metadata_file.read_text(encoding="utf-8")
except Exception: # noqa: broad-except
return False


def _find_kedro_project(current_dir: Path) -> Any: # pragma: no cover
paths_to_check = [current_dir] + list(current_dir.parents)
for parent_dir in paths_to_check:
if _is_project(parent_dir):
return parent_dir
return None
2 changes: 1 addition & 1 deletion tests/framework/test_startup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
from kedro.framework.startup import (
ProjectMetadata,
_get_project_metadata,
_is_project,
_validate_source_path,
bootstrap_project,
)
from kedro.utils import _is_project


class TestIsProject:
Expand Down