Skip to content

Commit dae968c

Browse files
feat(cli): verbose output for renku show (#1524)
1 parent 0f739de commit dae968c

File tree

2 files changed

+82
-19
lines changed

2 files changed

+82
-19
lines changed

renku/cli/show.py

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,31 @@
8686
$ echo $? # last command finished with an error code
8787
1
8888
89+
You can use the ``-v`` or ``--verbose`` flag to print detailed information
90+
in a tabular format.
91+
92+
.. code-block:: console
93+
94+
$ renku show inputs -v
95+
PATH COMMIT USAGE TIME WORKFLOW
96+
---------- ------- ------------------- -------------------...-----------
97+
source.txt 6d10e05 2020-09-14 23:47:17 .renku/workflow/388...d8_head.yaml
98+
99+
89100
"""
101+
from collections import namedtuple
90102

91103
import click
92104

93105
from renku.core import errors
94106
from renku.core.commands.client import pass_local_client
95107
from renku.core.commands.graph import Graph
96108
from renku.core.models.entities import Entity
109+
from renku.core.models.provenance.activities import ProcessRun
110+
from renku.core.models.tabulate import tabulate
111+
112+
Result = namedtuple("Result", ["path", "commit", "time", "workflow"])
113+
HEADERS = {"path": None, "commit": None, "time": "time", "workflow": None}
97114

98115

99116
@click.group()
@@ -121,7 +138,7 @@ def siblings(client, revision, flat, verbose, paths):
121138
for node in nodes:
122139
try:
123140
sibling_sets.add(frozenset(graph.siblings(node)))
124-
except (errors.InvalidOutputPath):
141+
except errors.InvalidOutputPath:
125142
# ignore nodes that aren't outputs if no path was supplied
126143
if paths:
127144
raise
@@ -151,29 +168,28 @@ def siblings(client, revision, flat, verbose, paths):
151168

152169
@show.command()
153170
@click.option("--revision", default="HEAD")
154-
@click.argument(
155-
"paths", type=click.Path(exists=True, dir_okay=False), nargs=-1,
156-
)
171+
@click.option("-v", "--verbose", is_flag=True)
172+
@click.argument("paths", type=click.Path(exists=True, dir_okay=False), nargs=-1)
157173
@pass_local_client(requires_migration=True)
158174
@click.pass_context
159-
def inputs(ctx, client, revision, paths):
175+
def inputs(ctx, client, revision, verbose, paths):
160176
r"""Show inputs files in the repository.
161177
162178
<PATHS> Files to show. If no files are given all input files are shown.
163179
"""
164-
from renku.core.models.provenance.activities import ProcessRun
165-
166180
graph = Graph(client)
167181
paths = set(paths)
168182
nodes = graph.build(revision=revision)
169183
commits = {node.activity.commit if hasattr(node, "activity") else node.commit for node in nodes}
170184
commits |= {node.activity.commit for node in nodes if hasattr(node, "activity")}
171185
candidates = {(node.commit, node.path) for node in nodes if not paths or node.path in paths}
172186

173-
input_paths = set()
187+
input_paths = {}
174188

175189
for commit in commits:
176-
activity = graph.activities[commit]
190+
activity = graph.activities.get(commit)
191+
if not activity:
192+
continue
177193

178194
if isinstance(activity, ProcessRun):
179195
for usage in activity.qualified_usage:
@@ -182,37 +198,59 @@ def inputs(ctx, client, revision, paths):
182198
usage_key = (entity.commit, entity.path)
183199

184200
if path not in input_paths and usage_key in candidates:
185-
input_paths.add(path)
201+
input_paths[path] = Result(
202+
path=path, commit=entity.commit, time=activity.started_at_time, workflow=activity.path
203+
)
186204

187-
click.echo("\n".join(graph._format_path(path) for path in input_paths))
205+
if not verbose:
206+
click.echo("\n".join(graph._format_path(path) for path in input_paths))
207+
else:
208+
records = list(input_paths.values())
209+
records.sort(key=lambda v: v[0])
210+
HEADERS["time"] = "usage time"
211+
click.echo(tabulate(collection=records, headers=HEADERS))
188212
ctx.exit(0 if not paths or len(input_paths) == len(paths) else 1)
189213

190214

191215
@show.command()
192216
@click.option("--revision", default="HEAD")
193-
@click.argument(
194-
"paths", type=click.Path(exists=True, dir_okay=True), nargs=-1,
195-
)
217+
@click.option("-v", "--verbose", is_flag=True)
218+
@click.argument("paths", type=click.Path(exists=True, dir_okay=True), nargs=-1)
196219
@pass_local_client(requires_migration=True)
197220
@click.pass_context
198-
def outputs(ctx, client, revision, paths):
221+
def outputs(ctx, client, revision, verbose, paths):
199222
r"""Show output files in the repository.
200223
201224
<PATHS> Files to show. If no files are given all output files are shown.
202225
"""
203226
graph = Graph(client)
204-
filter = graph.build(paths=paths, revision=revision)
205-
output_paths = graph.output_paths
227+
filter_ = graph.build(paths=paths, revision=revision)
228+
output_paths = {}
206229

207-
click.echo("\n".join(graph._format_path(path) for path in output_paths))
230+
for activity in graph.activities.values():
231+
if isinstance(activity, ProcessRun):
232+
for entity in activity.generated:
233+
if entity.path not in graph.output_paths:
234+
continue
235+
output_paths[entity.path] = Result(
236+
path=entity.path, commit=entity.commit, time=activity.ended_at_time, workflow=activity.path
237+
)
238+
239+
if not verbose:
240+
click.echo("\n".join(graph._format_path(path) for path in output_paths.keys()))
241+
else:
242+
records = list(output_paths.values())
243+
records.sort(key=lambda v: v[0])
244+
HEADERS["time"] = "generation time"
245+
click.echo(tabulate(collection=records, headers=HEADERS))
208246

209247
if paths:
210248
if not output_paths:
211249
ctx.exit(1)
212250

213251
from renku.core.models.datastructures import DirectoryTree
214252

215-
tree = DirectoryTree.from_list(item.path for item in filter)
253+
tree = DirectoryTree.from_list(item.path for item in filter_)
216254

217255
for output in output_paths:
218256
if tree.get(output) is None:

tests/cli/test_show.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,28 @@ def test_show_outputs_with_directory(runner, client, run):
4444
result = runner.invoke(cli, cmd + ["output/foo", "output/bar"])
4545
assert 0 == result.exit_code
4646
assert {"output"} == set(result.output.strip().split("\n"))
47+
48+
49+
def test_show_verbose(runner, client, run):
50+
"""Show with verbose option."""
51+
base_sh = ["bash", "-c", 'DIR="$0"; mkdir -p "$DIR"; ' 'for x in "$@"; do touch "$DIR/$x"; done']
52+
assert 0 == run(args=["run"] + base_sh + ["intermediate", "foo", "bar"])
53+
input_commit = client.repo.head.commit.parents[0].hexsha
54+
assert 0 == run(args=["run", "ls", "intermediate"], stdout="baz")
55+
output_commit = client.repo.head.commit.parents[0].hexsha
56+
57+
workflow_partial_name = "_ls.yaml"
58+
59+
result = runner.invoke(cli, ["show", "inputs", "-v"])
60+
assert 0 == result.exit_code
61+
assert input_commit in result.output
62+
assert workflow_partial_name in result.output
63+
for header in ("PATH", "COMMIT", "USAGE TIME", "WORKFLOW"):
64+
assert header in result.output.split("\n")[0]
65+
66+
result = runner.invoke(cli, ["show", "outputs", "-v", "baz"])
67+
assert 0 == result.exit_code
68+
assert output_commit in result.output
69+
assert workflow_partial_name in result.output
70+
for header in ("PATH", "COMMIT", "GENERATION TIME", "WORKFLOW"):
71+
assert header in result.output.split("\n")[0]

0 commit comments

Comments
 (0)