Skip to content

Commit 4f10d5f

Browse files
feat(dataset): default dataset add action in configuration (#3398)
1 parent aafb6ae commit 4f10d5f

File tree

4 files changed

+102
-5
lines changed

4 files changed

+102
-5
lines changed

renku/core/dataset/dataset_add.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ def copy_file(file: DatasetAddMetadata, dataset: Dataset, storage: Optional[ISto
371371
if not file.has_action:
372372
return []
373373

374-
# NOTE: If file is in a sub-directory of a dataset's remote storage URI, only update the metadata
374+
# NOTE: If file is in a subdirectory of a dataset's remote storage URI, only update the metadata
375375
if file.from_cloud_storage:
376376
if dataset.storage and is_uri_subfolder(resolve_uri(dataset.storage), file.url):
377377
file.action = DatasetAddAction.METADATA_ONLY

renku/core/dataset/providers/local.py

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from typing import TYPE_CHECKING, List, Optional
2323

2424
from renku.core import errors
25+
from renku.core.config import get_value
2526
from renku.core.dataset.providers.api import (
2627
AddProviderInterface,
2728
ExporterApi,
@@ -114,14 +115,32 @@ def get_metadata(
114115
if flags > 1:
115116
raise errors.ParameterError("--move, --copy and --link are mutually exclusive.")
116117

117-
prompt_action = True if flags == 0 else False
118+
prompt_action = False
118119

119120
if move:
120121
default_action = DatasetAddAction.MOVE
121122
elif link:
122123
default_action = DatasetAddAction.SYMLINK
123-
else:
124+
elif copy:
124125
default_action = DatasetAddAction.COPY
126+
else:
127+
prompt_action = True
128+
action = get_value("renku", "default_dataset_add_action")
129+
if action:
130+
prompt_action = False
131+
if action.lower() == "copy":
132+
default_action = DatasetAddAction.COPY
133+
elif action.lower() == "move":
134+
default_action = DatasetAddAction.MOVE
135+
elif action.lower() == "link":
136+
default_action = DatasetAddAction.SYMLINK
137+
else:
138+
raise errors.ParameterError(
139+
f"Invalid default action for adding to datasets in Renku config: '{action}'. "
140+
"Valid values are 'copy', 'link', and 'move'."
141+
)
142+
else:
143+
default_action = DatasetAddAction.COPY
125144

126145
ends_with_slash = False
127146
u = urllib.parse.urlparse(uri)
@@ -180,6 +199,14 @@ def get_file_metadata(src: Path) -> DatasetAddMetadata:
180199

181200
destination_root = get_destination_root()
182201

202+
if not is_subpath(source_root, project_context.path):
203+
if link:
204+
raise errors.ParameterError(f"Cannot use '--link' for files outside of project: '{uri}'")
205+
if default_action == DatasetAddAction.SYMLINK:
206+
# NOTE: A default action of 'link' cannot be used for external files
207+
action = DatasetAddAction.COPY
208+
prompt_action = True
209+
183210
results = []
184211
if source_root.is_dir():
185212
for file in source_root.rglob("*"):
@@ -195,8 +222,9 @@ def get_file_metadata(src: Path) -> DatasetAddMetadata:
195222

196223
if not force and prompt_action:
197224
communication.confirm(
198-
f"The following files will be copied to {destination.relative_to(project_context.path)} "
199-
"(use '--move' or '--link' to move or symlink them instead, '--copy' to not show this warning):\n\t"
225+
f"The following files will be copied to {destination.relative_to(project_context.path)}:\n\t"
226+
"(use '--move' or '--link' to move or symlink them instead, '--copy' to not show this warning).\n\t"
227+
"(run 'renku config set renku.default_dataset_add_action copy' to make copy the default action).\n\t"
200228
+ "\n\t".join(str(e.source) for e in results)
201229
+ "\nProceed?",
202230
abort=True,

renku/ui/cli/config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@
9393
| ``dataverse.server_url`` | URL for the Dataverse API server | ``None`` |
9494
| | to use | |
9595
+--------------------------------+-------------------------------------+-----------+
96+
| ``default_dataset_add_action`` | Default action when adding files to | ``None`` |
97+
| | datasets. Can be either ``copy`` or | |
98+
| | ``move``. | |
99+
+--------------------------------+-------------------------------------+-----------+
96100
| ``lfs_threshold`` | Threshold file size below which | ``100kb`` |
97101
| | files are not added to git LFS | |
98102
+--------------------------------+-------------------------------------+-----------+

tests/cli/test_datasets.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,71 @@ def test_add_local_actions(runner, project, action, existing_paths, missing_path
669669
assert path.is_symlink()
670670

671671

672+
@pytest.mark.parametrize("action, source_exists_after", [("--copy", True), ("--move", False)])
673+
def test_add_non_local_actions(runner, project, directory_tree, action, source_exists_after):
674+
"""Test adding data outside the project with different actions."""
675+
path = directory_tree / "file1"
676+
677+
result = runner.invoke(cli, ["dataset", "add", action, "--create", "local", path])
678+
679+
assert 0 == result.exit_code, format_result_exception(result)
680+
assert source_exists_after == path.exists()
681+
assert (project.path / "data" / "local" / "file1").exists()
682+
683+
684+
def test_add_non_local_link_action(runner, project, directory_tree):
685+
"""Test cannot add and link data outside the project."""
686+
path = directory_tree / "file1"
687+
688+
result = runner.invoke(cli, ["dataset", "add", "--link", "--create", "local", path])
689+
690+
assert 2 == result.exit_code, format_result_exception(result)
691+
assert "Cannot use '--link' for files outside of project:" in result.output
692+
693+
694+
@pytest.mark.parametrize("action, source_exists_after", [("copy", True), ("move", False)])
695+
@pytest.mark.serial
696+
def test_add_default_configured_actions(runner, project, directory_tree, action, source_exists_after):
697+
"""Test adding data with different actions set in Renku configuration file."""
698+
path = directory_tree / "file1"
699+
set_value("renku", "default_dataset_add_action", action, global_only=True)
700+
701+
result = runner.invoke(cli, ["dataset", "add", "--create", "local", path])
702+
703+
assert 0 == result.exit_code, format_result_exception(result)
704+
assert "The following files will be copied to" not in result.output
705+
assert path.exists() is source_exists_after
706+
assert (project.path / "data" / "local" / "file1").exists()
707+
708+
709+
@pytest.mark.serial
710+
def test_add_default_configured_link(runner, project, directory_tree):
711+
"""Test adding data with default ``link`` action should prompt the user."""
712+
path = directory_tree / "file1"
713+
set_value("renku", "default_dataset_add_action", "link", global_only=True)
714+
715+
result = runner.invoke(cli, ["dataset", "add", "--create", "local", path], input="y\n")
716+
717+
assert 0 == result.exit_code, format_result_exception(result)
718+
assert "The following files will be copied to" in result.output
719+
assert path.exists()
720+
assert (project.path / "data" / "local" / "file1").exists()
721+
assert not (project.path / "data" / "local" / "file1").is_symlink()
722+
723+
724+
@pytest.mark.serial
725+
def test_add_default_configured_invalid_action(runner, project, directory_tree):
726+
"""Test adding data with an invalid actions set in Renku configuration file."""
727+
path = directory_tree / "file1"
728+
set_value("renku", "default_dataset_add_action", "invalid", global_only=True)
729+
730+
result = runner.invoke(cli, ["dataset", "add", "--create", "local", path])
731+
732+
assert 2 == result.exit_code, format_result_exception(result)
733+
assert "Invalid default action for adding to datasets in Renku config: 'invalid'." in result.output
734+
assert "Valid values are 'copy', 'link', and 'move'." in result.output
735+
736+
672737
def test_add_an_empty_directory(runner, project, directory_tree):
673738
"""Test adding an empty directory to a dataset."""
674739
path = directory_tree / "empty-directory"

0 commit comments

Comments
 (0)