Skip to content

Commit 641c886

Browse files
author
Lorena Bălan
authored
[KED-2503] Simplify template cli.py (kedro-org#1041)
1 parent b299028 commit 641c886

File tree

5 files changed

+107
-182
lines changed
  • features/steps/test_starter/{{ cookiecutter.repo_name }}/src/{{ cookiecutter.python_package }}
  • kedro
    • framework/cli
    • templates/project/{{ cookiecutter.repo_name }}/src/{{ cookiecutter.python_package }}
  • tests/framework/cli

5 files changed

+107
-182
lines changed

features/steps/test_starter/{{ cookiecutter.repo_name }}/src/{{ cookiecutter.python_package }}/cli.py

+9-88
Original file line numberDiff line numberDiff line change
@@ -32,24 +32,22 @@
3232
import logging
3333
from itertools import chain
3434
from pathlib import Path
35-
from typing import Dict, Iterable, Tuple
35+
from typing import Iterable, Tuple
3636

3737
import click
38-
from kedro.framework.cli.catalog import catalog as catalog_group
39-
from kedro.framework.cli.jupyter import jupyter as jupyter_group
40-
from kedro.framework.cli.pipeline import pipeline as pipeline_group
41-
from kedro.framework.cli.project import project_group
42-
from kedro.framework.cli.utils import KedroCliError, env_option, split_string
38+
from kedro.framework.cli.utils import (
39+
KedroCliError,
40+
_config_file_callback,
41+
_reformat_load_versions,
42+
_split_params,
43+
env_option,
44+
split_string,
45+
)
4346
from kedro.framework.session import KedroSession
4447
from kedro.utils import load_obj
4548

4649
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
4750

48-
# get our package onto the python path
49-
PROJ_PATH = Path(__file__).resolve().parent
50-
51-
ENV_ARG_HELP = """Run the pipeline in a configured environment. If not specified,
52-
pipeline will run using environment `local`."""
5351
FROM_INPUTS_HELP = (
5452
"""A list of dataset names which should be used as a starting point."""
5553
)
@@ -79,79 +77,10 @@
7977
so parameter values are allowed to contain colons, parameter keys are not."""
8078

8179

82-
def _config_file_callback(ctx, param, value): # pylint: disable=unused-argument
83-
"""Config file callback, that replaces command line options with config file
84-
values. If command line options are passed, they override config file values.
85-
"""
86-
# for performance reasons
87-
import anyconfig # pylint: disable=import-outside-toplevel
88-
89-
ctx.default_map = ctx.default_map or {}
90-
section = ctx.info_name
91-
92-
if value:
93-
config = anyconfig.load(value)[section]
94-
ctx.default_map.update(config)
95-
96-
return value
97-
98-
9980
def _get_values_as_tuple(values: Iterable[str]) -> Tuple[str, ...]:
10081
return tuple(chain.from_iterable(value.split(",") for value in values))
10182

10283

103-
def _reformat_load_versions( # pylint: disable=unused-argument
104-
ctx, param, value
105-
) -> Dict[str, str]:
106-
"""Reformat data structure from tuple to dictionary for `load-version`, e.g:
107-
('dataset1:time1', 'dataset2:time2') -> {"dataset1": "time1", "dataset2": "time2"}.
108-
"""
109-
load_versions_dict = {}
110-
111-
for load_version in value:
112-
load_version_list = load_version.split(":", 1)
113-
if len(load_version_list) != 2:
114-
raise KedroCliError(
115-
f"Expected the form of `load_version` to be "
116-
f"`dataset_name:YYYY-MM-DDThh.mm.ss.sssZ`,"
117-
f"found {load_version} instead"
118-
)
119-
load_versions_dict[load_version_list[0]] = load_version_list[1]
120-
121-
return load_versions_dict
122-
123-
124-
def _split_params(ctx, param, value):
125-
if isinstance(value, dict):
126-
return value
127-
result = {}
128-
for item in split_string(ctx, param, value):
129-
item = item.split(":", 1)
130-
if len(item) != 2:
131-
ctx.fail(
132-
f"Invalid format of `{param.name}` option: "
133-
f"Item `{item[0]}` must contain "
134-
f"a key and a value separated by `:`."
135-
)
136-
key = item[0].strip()
137-
if not key:
138-
ctx.fail(
139-
f"Invalid format of `{param.name}` option: Parameter key "
140-
f"cannot be an empty string."
141-
)
142-
value = item[1].strip()
143-
result[key] = _try_convert_to_numeric(value)
144-
return result
145-
146-
147-
def _try_convert_to_numeric(value):
148-
try:
149-
value = float(value)
150-
except ValueError:
151-
return value
152-
return int(value) if value.is_integer() else value
153-
154-
15584
@click.group(context_settings=CONTEXT_SETTINGS, name=__file__)
15685
def cli():
15786
"""Command line tools for manipulating a Kedro project."""
@@ -239,11 +168,3 @@ def run(
239168
# Logging parameters for some e2e tests
240169
params_to_log = session.load_context().params
241170
logging.info("Parameters: %s", json.dumps(params_to_log, sort_keys=True))
242-
243-
244-
cli.add_command(pipeline_group)
245-
cli.add_command(catalog_group)
246-
cli.add_command(jupyter_group)
247-
248-
for command in project_group.commands.values():
249-
cli.add_command(command)

kedro/framework/cli/utils.py

+71-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
from importlib import import_module
4242
from itertools import chain
4343
from pathlib import Path
44-
from typing import Iterable, List, Mapping, Sequence, Set, Tuple, Union
44+
from typing import Dict, Iterable, List, Mapping, Sequence, Set, Tuple, Union
4545

4646
import click
4747
import pkg_resources
@@ -396,3 +396,73 @@ def _add_src_to_path(source_dir: Path, project_path: Path) -> None: # pragma: n
396396
)
397397
warnings.warn(msg, FutureWarning)
398398
real_add_src_to_path(source_dir, project_path)
399+
400+
401+
def _config_file_callback(ctx, param, value): # pylint: disable=unused-argument
402+
"""CLI callback that replaces command line options
403+
with values specified in a config file. If command line
404+
options are passed, they override config file values.
405+
"""
406+
# for performance reasons
407+
import anyconfig # pylint: disable=import-outside-toplevel
408+
409+
ctx.default_map = ctx.default_map or {}
410+
section = ctx.info_name
411+
412+
if value:
413+
config = anyconfig.load(value)[section]
414+
ctx.default_map.update(config)
415+
416+
return value
417+
418+
419+
def _reformat_load_versions( # pylint: disable=unused-argument
420+
ctx, param, value
421+
) -> Dict[str, str]:
422+
"""Reformat data structure from tuple to dictionary for `load-version`, e.g.:
423+
('dataset1:time1', 'dataset2:time2') -> {"dataset1": "time1", "dataset2": "time2"}.
424+
"""
425+
load_versions_dict = {}
426+
427+
for load_version in value:
428+
load_version_list = load_version.split(":", 1)
429+
if len(load_version_list) != 2:
430+
raise KedroCliError(
431+
f"Expected the form of `load_version` to be "
432+
f"`dataset_name:YYYY-MM-DDThh.mm.ss.sssZ`,"
433+
f"found {load_version} instead"
434+
)
435+
load_versions_dict[load_version_list[0]] = load_version_list[1]
436+
437+
return load_versions_dict
438+
439+
440+
def _try_convert_to_numeric(value):
441+
try:
442+
value = float(value)
443+
except ValueError:
444+
return value
445+
return int(value) if value.is_integer() else value
446+
447+
448+
def _split_params(ctx, param, value):
449+
if isinstance(value, dict):
450+
return value
451+
result = {}
452+
for item in split_string(ctx, param, value):
453+
item = item.split(":", 1)
454+
if len(item) != 2:
455+
ctx.fail(
456+
f"Invalid format of `{param.name}` option: "
457+
f"Item `{item[0]}` must contain "
458+
f"a key and a value separated by `:`."
459+
)
460+
key = item[0].strip()
461+
if not key:
462+
ctx.fail(
463+
f"Invalid format of `{param.name}` option: Parameter key "
464+
f"cannot be an empty string."
465+
)
466+
value = item[1].strip()
467+
result[key] = _try_convert_to_numeric(value)
468+
return result

kedro/templates/project/{{ cookiecutter.repo_name }}/src/{{ cookiecutter.python_package }}/cli.py

+9-88
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,22 @@
3030
Intended to be invoked via `kedro`."""
3131
from itertools import chain
3232
from pathlib import Path
33-
from typing import Dict, Iterable, Tuple
33+
from typing import Iterable, Tuple
3434

3535
import click
36-
from kedro.framework.cli.catalog import catalog as catalog_group
37-
from kedro.framework.cli.jupyter import jupyter as jupyter_group
38-
from kedro.framework.cli.pipeline import pipeline as pipeline_group
39-
from kedro.framework.cli.project import project_group
40-
from kedro.framework.cli.utils import KedroCliError, env_option, split_string
36+
from kedro.framework.cli.utils import (
37+
KedroCliError,
38+
_config_file_callback,
39+
_reformat_load_versions,
40+
_split_params,
41+
env_option,
42+
split_string,
43+
)
4144
from kedro.framework.session import KedroSession
4245
from kedro.utils import load_obj
4346

4447
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
4548

46-
# get our package onto the python path
47-
PROJ_PATH = Path(__file__).resolve().parent
48-
49-
ENV_ARG_HELP = """Run the pipeline in a configured environment. If not specified,
50-
pipeline will run using environment `local`."""
5149
FROM_INPUTS_HELP = (
5250
"""A list of dataset names which should be used as a starting point."""
5351
)
@@ -78,79 +76,10 @@
7876
so parameter values are allowed to contain colons, parameter keys are not."""
7977

8078

81-
def _config_file_callback(ctx, param, value): # pylint: disable=unused-argument
82-
"""Config file callback, that replaces command line options with config file
83-
values. If command line options are passed, they override config file values.
84-
"""
85-
# for performance reasons
86-
import anyconfig # pylint: disable=import-outside-toplevel
87-
88-
ctx.default_map = ctx.default_map or {}
89-
section = ctx.info_name
90-
91-
if value:
92-
config = anyconfig.load(value)[section]
93-
ctx.default_map.update(config)
94-
95-
return value
96-
97-
9879
def _get_values_as_tuple(values: Iterable[str]) -> Tuple[str, ...]:
9980
return tuple(chain.from_iterable(value.split(",") for value in values))
10081

10182

102-
def _reformat_load_versions( # pylint: disable=unused-argument
103-
ctx, param, value
104-
) -> Dict[str, str]:
105-
"""Reformat data structure from tuple to dictionary for `load-version`, e.g.:
106-
('dataset1:time1', 'dataset2:time2') -> {"dataset1": "time1", "dataset2": "time2"}.
107-
"""
108-
load_versions_dict = {}
109-
110-
for load_version in value:
111-
load_version_list = load_version.split(":", 1)
112-
if len(load_version_list) != 2:
113-
raise KedroCliError(
114-
f"Expected the form of `load_version` to be "
115-
f"`dataset_name:YYYY-MM-DDThh.mm.ss.sssZ`,"
116-
f"found {load_version} instead"
117-
)
118-
load_versions_dict[load_version_list[0]] = load_version_list[1]
119-
120-
return load_versions_dict
121-
122-
123-
def _split_params(ctx, param, value):
124-
if isinstance(value, dict):
125-
return value
126-
result = {}
127-
for item in split_string(ctx, param, value):
128-
item = item.split(":", 1)
129-
if len(item) != 2:
130-
ctx.fail(
131-
f"Invalid format of `{param.name}` option: "
132-
f"Item `{item[0]}` must contain "
133-
f"a key and a value separated by `:`."
134-
)
135-
key = item[0].strip()
136-
if not key:
137-
ctx.fail(
138-
f"Invalid format of `{param.name}` option: Parameter key "
139-
f"cannot be an empty string."
140-
)
141-
value = item[1].strip()
142-
result[key] = _try_convert_to_numeric(value)
143-
return result
144-
145-
146-
def _try_convert_to_numeric(value):
147-
try:
148-
value = float(value)
149-
except ValueError:
150-
return value
151-
return int(value) if value.is_integer() else value
152-
153-
15483
@click.group(context_settings=CONTEXT_SETTINGS, name=__file__)
15584
def cli():
15685
"""Command line tools for manipulating a Kedro project."""
@@ -239,11 +168,3 @@ def run(
239168
load_versions=load_version,
240169
pipeline_name=pipeline,
241170
)
242-
243-
244-
cli.add_command(pipeline_group)
245-
cli.add_command(catalog_group)
246-
cli.add_command(jupyter_group)
247-
248-
for command in project_group.commands.values():
249-
cli.add_command(command)

test_requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ holoviews~=1.13.0
1414
import-linter==1.0
1515
ipython==7.10 # Due to https://github.com/ipython/ipykernel/issues/540
1616
joblib>=0.14
17-
matplotlib>=3.0.3, <4.0
17+
matplotlib>=3.0.3, <3.4 # 3.4.0 breaks holoviews
1818
memory_profiler>=0.50.0, <1.0
1919
moto==1.3.7
2020
mypy<=1.0

tests/framework/cli/conftest.py

+17-4
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@
4343
from pytest import fixture
4444

4545
from kedro import __version__ as kedro_version
46+
from kedro.framework.cli.catalog import catalog_cli
4647
from kedro.framework.cli.cli import cli
48+
from kedro.framework.cli.jupyter import jupyter_cli
49+
from kedro.framework.cli.pipeline import pipeline_cli
50+
from kedro.framework.cli.project import project_group
4751
from kedro.framework.cli.starters import create_cli
4852
from kedro.framework.project import configure_project, pipelines, settings
4953
from kedro.framework.startup import ProjectMetadata
@@ -114,7 +118,17 @@ def fake_metadata(fake_root_dir):
114118
# code when invoking `kedro` but when imported, they still need to be merged
115119
@fixture(scope="module")
116120
def fake_kedro_cli():
117-
return click.CommandCollection(name="Kedro", sources=[cli, create_cli])
121+
return click.CommandCollection(
122+
name="Kedro",
123+
sources=[
124+
cli,
125+
create_cli,
126+
catalog_cli,
127+
jupyter_cli,
128+
pipeline_cli,
129+
project_group,
130+
],
131+
)
118132

119133

120134
@fixture(scope="module")
@@ -125,8 +139,7 @@ def fake_project_cli(
125139
starter_path = Path(__file__).parents[3].resolve()
126140
starter_path = starter_path / "features" / "steps" / "test_starter"
127141
CliRunner().invoke(
128-
fake_kedro_cli,
129-
["new", "-c", str(dummy_config), "--starter", str(starter_path)],
142+
fake_kedro_cli, ["new", "-c", str(dummy_config), "--starter", str(starter_path)]
130143
)
131144

132145
# NOTE: Here we load a couple of modules, as they would be imported in
@@ -138,7 +151,7 @@ def fake_project_cli(
138151

139152
import_module(PACKAGE_NAME)
140153
configure_project(PACKAGE_NAME)
141-
yield getattr(import_module(f"{PACKAGE_NAME}.cli"), "cli")
154+
yield fake_kedro_cli
142155

143156
# reset side-effects of configure_project
144157
pipelines.clear()

0 commit comments

Comments
 (0)