Skip to content

Commit 5cc006c

Browse files
authored
feat(cli): improve renku run error messages (#2915)
1 parent eae254e commit 5cc006c

File tree

3 files changed

+32
-40
lines changed

3 files changed

+32
-40
lines changed

renku/command/run.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,10 @@ def parse_explicit_definition(entries, type):
178178
)
179179
except FileNotFoundError:
180180
command = " ".join(factory.base_command)
181-
raise errors.ParameterError(f"Cannot execute command '{command}'")
181+
raise errors.ParameterError(
182+
f"Cannot execute command '{command}': "
183+
"This is likely because the executable doesn't exist or has the wrong permissions set."
184+
)
182185

183186
ended_at_time = local_now()
184187

renku/core/errors.py

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
# limitations under the License.
1818
"""Renku exceptions."""
1919

20-
import os
2120
from pathlib import Path
2221
from typing import List, Union
2322

@@ -212,28 +211,16 @@ class InvalidOutputPath(RenkuException):
212211
class OutputsNotFound(RenkuException):
213212
"""Raise when there are not any detected outputs in the repository."""
214213

215-
def __init__(self, repository, inputs):
214+
def __init__(self):
216215
"""Build a custom message."""
217-
from pathlib import Path
218-
219-
msg = "There are not any detected outputs in the repository."
220-
221-
paths = [
222-
os.path.relpath(input_.default_value) # relative to cur path
223-
for input_ in inputs
224-
if Path(input_.default_value).is_dir()
225-
]
226-
227-
if paths:
228-
msg += (
229-
'\n (use "git rm <file>..." to remove them first)'
230-
"\n\n" + "\n".join("\t" + click.style(path, fg="yellow") for path in paths) + "\n\n"
231-
"Once you have removed files that should be used as outputs,\n"
232-
"you can safely rerun the previous command."
233-
"\nYou can use --output flag to specify outputs explicitly."
234-
)
235-
else:
236-
msg += "\n\nIf you want to track the command anyway use " "--no-output option."
216+
217+
msg = (
218+
"There are not any detected outputs in the repository. This can be due to your command not creating "
219+
"any new files or due to files that get created already existing before the command was run. In the "
220+
"latter case, you can remove those files prior to running your command.\nIf you want to track the command"
221+
"without outputs, use the use --no-output option.\nYou can also use the --output flag to track outputs"
222+
"manually."
223+
)
237224

238225
super(OutputsNotFound, self).__init__(msg)
239226

renku/core/workflow/plan_factory.py

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -79,19 +79,23 @@ def __init__(
7979
self.no_output_detection = no_output_detection
8080

8181
if not command_line:
82-
raise errors.UsageError("Command line can not be empty.")
82+
raise errors.UsageError("Command line can not be empty. Please specify a command to execute.")
8383

8484
if not directory:
8585
directory = os.getcwd()
8686
self.directory = Path(directory)
8787
if not self.directory.exists():
88-
raise errors.UsageError("Directory must exist.")
88+
raise errors.UsageError(
89+
f"Directory '{self.directory}' doesn't exist. Please make sure you are inside an existing directory."
90+
)
8991

9092
if not working_dir:
9193
working_dir = os.getcwd()
9294
self.working_dir = Path(working_dir)
9395
if not self.working_dir.exists():
94-
raise errors.UsageError("Working Directory must exist.")
96+
raise errors.UsageError(
97+
f"Repository path '{self.working_dir}' doesn't exist. Make sure you are inside a Renku repository."
98+
)
9599

96100
if isinstance(command_line, (list, tuple)):
97101
self.command_line = list(command_line)
@@ -298,7 +302,7 @@ def add_outputs(self, candidates: Set[Tuple[Union[Path, str], Optional[str]]]):
298302
candidate = self._resolve_existing_subpath(self.working_dir / candidate_path)
299303

300304
if candidate is None:
301-
raise errors.UsageError('Path "{0}" does not exist.'.format(candidate_path))
305+
raise errors.UsageError('Path "{0}" does not exist inside the current project.'.format(candidate_path))
302306

303307
glob = str(candidate.relative_to(self.working_dir))
304308

@@ -324,17 +328,17 @@ def _check_potential_output_directory(
324328
preexisting_paths = content - subpaths
325329
if preexisting_paths:
326330
raise errors.InvalidOutputPath(
327-
'The output directory "{0}" is not empty. \n\n'
328-
"Delete existing files before running the "
329-
"command:"
330-
'\n (use "git rm <file>..." to remove them '
331-
"first)"
332-
"\n\n".format(input_path)
331+
f"The output directory '{input_path}' is not empty. \n\n"
332+
"As renku treats whole directory outputs as generated by renku,"
333+
"those directories have to be empty before being tracked by renku.\n\n"
334+
"You can solve this by:\n"
335+
f"- Deleting the existing files in the directory (use 'git rm -r {input_path}')\n"
336+
"- Using a different output folder\n"
337+
"- Using a new empty subfolder inside the output folder\n\n"
338+
"Output directories with existing files:\n\n"
333339
+ "\n".join("\t" + click.style(path, fg="yellow") for path in preexisting_paths)
334340
+ "\n\n"
335-
"Once you have removed files that should be used "
336-
"as outputs,\n"
337-
"you can safely rerun the previous command."
341+
"Once you have resolved the issues above, you can safely rerun the previous command."
338342
)
339343

340344
# Remove files from the input directory
@@ -590,8 +594,6 @@ def watch(self, client_dispatcher: IClientDispatcher, no_output=False):
590594
# List of all output paths.
591595
output_paths = []
592596

593-
inputs = {input.id: input for input in self.inputs}
594-
595597
# Keep track of unmodified output files.
596598
unmodified = set()
597599

@@ -639,7 +641,7 @@ def watch(self, client_dispatcher: IClientDispatcher, no_output=False):
639641
raise errors.UnmodifiedOutputs(repository, unmodified)
640642

641643
if not no_output and not output_paths:
642-
raise errors.OutputsNotFound(repository, inputs.values())
644+
raise errors.OutputsNotFound()
643645

644646
if client.check_external_storage():
645647
client.track_paths_in_storage(*output_paths)
@@ -725,7 +727,7 @@ def read_files_list(files_list: Path):
725727
data = yaml.safe_load(files_list.read_text())
726728

727729
if not isinstance(data, dict):
728-
raise errors.OperationError("Inputs/outputs files list must be a YAML dictionary.")
730+
raise errors.OperationError("Explicit Inputs/Outputs/Parameters files list must be a YAML dictionary.")
729731

730732
return data
731733

0 commit comments

Comments
 (0)