Skip to content

Commit

Permalink
Create a new Kedro CLI command kedro jupyter setup to setup Jupyter…
Browse files Browse the repository at this point in the history
… kernel for Kedro (#2353)

* add notes

* Customize the `kedro jupyter` commands order

Signed-off-by: Nok Chan <nok.lam.chan@quantumblack.com>

* add tests

Signed-off-by: Nok Chan <nok.lam.chan@quantumblack.com>

* Fix tests and minor linting

Signed-off-by: Nok Chan <nok.lam.chan@quantumblack.com>

* fix test and refactor the command order logic

Signed-off-by: Nok Chan <nok.lam.chan@quantumblack.com>

* Fix linting & e2etest

Signed-off-by: Nok <nok_lam_chan@mckinsey.com>

* Changes base on PR comments - rename commands and simplify list_command logic

Signed-off-by: Nok Chan <nok.lam.chan@quantumblack.com>

* Fix bug with renamed command

Signed-off-by: Nok <nok_lam_chan@mckinsey.com>

* Update kedro/framework/cli/jupyter.py

Co-authored-by: Antony Milne <49395058+AntonyMilneQB@users.noreply.github.com>

* Fix e2etst

Signed-off-by: Nok <nok_lam_chan@mckinsey.com>

---------

Signed-off-by: Nok Chan <nok.lam.chan@quantumblack.com>
Signed-off-by: Nok <nok_lam_chan@mckinsey.com>
Co-authored-by: noklam <channoklam@yahoo.com.hk>
Co-authored-by: Antony Milne <49395058+AntonyMilneQB@users.noreply.github.com>
  • Loading branch information
3 people authored Mar 21, 2023
1 parent 8e5d2da commit c144b87
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 7 deletions.
2 changes: 2 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
# Upcoming Release 0.18.7

## Major features and improvements
* Added new Kedro CLI `kedro jupyter setup` to setup Jupyter Kernel for Kedro.
* `kedro package` now includes the project configuration in a compressed `tar.gz` file.
* Added functionality to the `OmegaConfigLoader` to load configuration from compressed files of `zip` or `tar` format. This feature requires `fsspec>=2023.1.0`.


## Bug fixes and other changes
* Added a guide and tooling for developing Kedro for Databricks.
* Implement missing dict-like interface for `_ProjectPipeline`.
Expand Down
8 changes: 6 additions & 2 deletions features/jupyter.feature
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ Feature: Jupyter targets in new project
Given I have prepared a config file
And I have run a non-interactive kedro new with starter "default"

Scenario: Execute jupyter-notebook target
Scenario: Execute jupyter setup target
When I execute the kedro command "jupyter setup"
Then I should get a message including "The kernel has been created successfully at"

Scenario: Execute jupyter notebook target
When I execute the kedro jupyter command "notebook --no-browser"
Then jupyter notebook should run on port 8888

Scenario: Execute jupyter-lab target
Scenario: Execute jupyter lab target
When I execute the kedro jupyter command "lab --no-browser"
Then Jupyter Lab should run on port 8888

Expand Down
28 changes: 26 additions & 2 deletions kedro/framework/cli/jupyter.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,39 @@
overwrite its contents."""


class JupyterCommandGroup(click.Group):
"""A custom class for ordering the `kedro jupyter` command groups"""

def list_commands(self, ctx):
"""List commands according to a custom order"""
return ["setup", "notebook", "lab", "convert"]


# pylint: disable=missing-function-docstring
@click.group(name="Kedro")
def jupyter_cli(): # pragma: no cover
pass


@jupyter_cli.group()
@jupyter_cli.group(cls=JupyterCommandGroup)
def jupyter():
"""Open Jupyter Notebook / Lab with project specific variables loaded, or
convert notebooks into Kedro code.
"""


@forward_command(jupyter, "setup", forward_help=True)
@click.pass_obj # this will pass the metadata as first argument
def setup(metadata: ProjectMetadata, args, **kwargs): # pylint: disable=unused-argument
"""Initialise the Jupyter Kernel for a kedro project."""
_check_module_importable("ipykernel")
validate_settings()

kernel_name = f"kedro_{metadata.package_name}"
kernel_path = _create_kernel(kernel_name, f"Kedro ({metadata.package_name})")
click.secho(f"\nThe kernel has been created successfully at {kernel_path}")


@forward_command(jupyter, "notebook", forward_help=True)
@env_option
@click.pass_obj # this will pass the metadata as first argument
Expand Down Expand Up @@ -96,7 +116,7 @@ def jupyter_lab(
)


def _create_kernel(kernel_name: str, display_name: str) -> None:
def _create_kernel(kernel_name: str, display_name: str) -> str:
"""Creates an IPython kernel for the kedro project. If one with the same kernel_name
exists already it will be replaced.
Expand Down Expand Up @@ -130,6 +150,9 @@ def _create_kernel(kernel_name: str, display_name: str) -> None:
kernel_name: Name of the kernel to create.
display_name: Kernel name as it is displayed in the UI.
Returns:
String of the path of the created kernel.
Raises:
KedroCliError: When kernel cannot be setup.
"""
Expand Down Expand Up @@ -164,6 +187,7 @@ def _create_kernel(kernel_name: str, display_name: str) -> None:
raise KedroCliError(
f"Cannot setup kedro kernel for Jupyter.\nError: {exc}"
) from exc
return kernel_path


@command_with_verbosity(jupyter, "convert")
Expand Down
4 changes: 1 addition & 3 deletions kedro/framework/cli/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,9 +417,7 @@ def _config_file_callback(ctx, param, value): # pylint: disable=unused-argument
return value


def _reformat_load_versions( # pylint: disable=unused-argument
ctx, param, value
) -> Dict[str, str]:
def _reformat_load_versions(ctx, param, value) -> Dict[str, str]:
"""Reformat data structure from tuple to dictionary for `load-version`, e.g.:
('dataset1:time1', 'dataset2:time2') -> {"dataset1": "time1", "dataset2": "time2"}.
"""
Expand Down
27 changes: 27 additions & 0 deletions tests/framework/cli/test_jupyter.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,33 @@ def create_kernel_mock(mocker):
return mocker.patch("kedro.framework.cli.jupyter._create_kernel")


@pytest.mark.usefixtures(
"chdir_to_dummy_project", "create_kernel_mock", "python_call_mock"
)
class TestJupyterSetupCommand:
def test_happy_path(self, fake_project_cli, fake_metadata, create_kernel_mock):
result = CliRunner().invoke(
fake_project_cli,
["jupyter", "setup"],
obj=fake_metadata,
)
assert not result.exit_code, result.stdout
kernel_name = f"kedro_{fake_metadata.package_name}"
display_name = f"Kedro ({fake_metadata.package_name})"
create_kernel_mock.assert_called_once_with(kernel_name, display_name)

def test_fail_no_jupyter(self, fake_project_cli, mocker):
mocker.patch.dict("sys.modules", {"notebook": None})
result = CliRunner().invoke(fake_project_cli, ["jupyter", "notebook"])

assert result.exit_code
error = (
"Module 'notebook' not found. Make sure to install required project "
"dependencies by running the 'pip install -r src/requirements.txt' command first."
)
assert error in result.output


@pytest.mark.usefixtures(
"chdir_to_dummy_project", "create_kernel_mock", "python_call_mock"
)
Expand Down

0 comments on commit c144b87

Please sign in to comment.