Skip to content

Commit

Permalink
Add the verdi process call-root command (#3091)
Browse files Browse the repository at this point in the history
This command will go up the call stack for a given process node and
return the root caller, if there is one.
  • Loading branch information
sphuber authored Jun 28, 2019
1 parent f83c67b commit e232f94
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 14 deletions.
51 changes: 51 additions & 0 deletions aiida/backends/tests/cmdline/commands/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,57 @@ def test_list_worker_slot_warning(self):
self.assertTrue(any([warning_phrase in line for line in get_result_lines(result)]))


class TestVerdiProcessCallRoot(AiidaTestCase):
"""Tests for `verdi process call-root`."""

@classmethod
def setUpClass(cls, *args, **kwargs):
super(TestVerdiProcessCallRoot, cls).setUpClass(*args, **kwargs)
cls.node_root = WorkflowNode()
cls.node_middle = WorkflowNode()
cls.node_terminal = WorkflowNode()

cls.node_root.store()

cls.node_middle.add_incoming(cls.node_root, link_type=LinkType.CALL_WORK, link_label='call_middle')
cls.node_middle.store()

cls.node_terminal.add_incoming(cls.node_middle, link_type=LinkType.CALL_WORK, link_label='call_terminal')
cls.node_terminal.store()

def setUp(self):
super(TestVerdiProcessCallRoot, self).setUp()
self.cli_runner = CliRunner()

def test_no_caller(self):
"""Test `verdi process call-root` when passing single process without caller."""
options = [str(self.node_root.pk)]
result = self.cli_runner.invoke(cmd_process.process_call_root, options)
self.assertClickResultNoException(result)
self.assertTrue(len(get_result_lines(result)) == 1)
self.assertIn('No callers found', get_result_lines(result)[0])

def test_single_caller(self):
"""Test `verdi process call-root` when passing single process with call root."""
# Both the middle and terminal node should have the `root` node as call root.
for node in [self.node_middle, self.node_terminal]:
options = [str(node.pk)]
result = self.cli_runner.invoke(cmd_process.process_call_root, options)
self.assertClickResultNoException(result)
self.assertTrue(len(get_result_lines(result)) == 1)
self.assertIn(str(self.node_root.pk), get_result_lines(result)[0])

def test_multiple_processes(self):
"""Test `verdi process call-root` when passing multiple processes."""
options = [str(self.node_root.pk), str(self.node_middle.pk), str(self.node_terminal.pk)]
result = self.cli_runner.invoke(cmd_process.process_call_root, options)
self.assertClickResultNoException(result)
self.assertTrue(len(get_result_lines(result)) == 3)
self.assertIn('No callers found', get_result_lines(result)[0])
self.assertIn(str(self.node_root.pk), get_result_lines(result)[1])
self.assertIn(str(self.node_root.pk), get_result_lines(result)[2])


@gen.coroutine
def with_timeout(what, timeout=5.0):
raise gen.Return((yield gen.with_timeout(datetime.timedelta(seconds=timeout), what)))
24 changes: 24 additions & 0 deletions aiida/cmdline/commands/cmd_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,30 @@ def process_show(processes):
echo.echo(get_node_info(process))


@verdi_process.command('call-root')
@arguments.PROCESSES()
@decorators.with_dbenv()
def process_call_root(processes):
"""Show the root process of the call stack for the given processes."""
for process in processes:

caller = process.caller

if caller is None:
echo.echo('No callers found for Process<{}>'.format(process.pk))
continue

while True:
next_caller = caller.caller

if next_caller is None:
break

caller = next_caller

echo.echo('{}'.format(caller.pk))


@verdi_process.command('report')
@arguments.PROCESSES()
@click.option('-i', '--indent-size', type=int, default=2, help='Set the number of spaces to indent each level by.')
Expand Down
12 changes: 6 additions & 6 deletions aiida/orm/nodes/process/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,11 +446,12 @@ def caller(self):
:returns: process node that called this process node instance or None
"""
caller = self.get_incoming(link_type=(LinkType.CALL_CALC, LinkType.CALL_WORK))
if caller:
return caller.first().node

return None
try:
caller = self.get_incoming(link_type=(LinkType.CALL_CALC, LinkType.CALL_WORK)).one().node
except ValueError:
return None
else:
return caller

def validate_incoming(self, source, link_type, link_label):
"""Validate adding a link of the given type from a given node to ourself.
Expand All @@ -466,7 +467,6 @@ def validate_incoming(self, source, link_type, link_label):
:raise ValueError: if the proposed link is invalid
"""
super(ProcessNode, self).validate_incoming(source, link_type, link_label)
# if self.is_stored and link_type in [LinkType.INPUT_CALC, LinkType.INPUT_WORK]:
if self.is_stored:
raise ValueError('attempted to add an input link after the process node was already stored.')

Expand Down
17 changes: 9 additions & 8 deletions docs/source/verdi/verdi_user_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -583,14 +583,15 @@ Below is a list with all available subcommands.
--help Show this message and exit.

Commands:
kill Kill running processes.
list Show a list of processes that are still running.
pause Pause running processes.
play Play paused processes.
report Show the log report for one or multiple processes.
show Show a summary for one or multiple processes.
status Print the status of the process.
watch Watch the state transitions for a process.
call-root Show the root process of the call stack for the given...
kill Kill running processes.
list Show a list of processes that are still running.
pause Pause running processes.
play Play paused processes.
report Show the log report for one or multiple processes.
show Show a summary for one or multiple processes.
status Print the status of the process.
watch Watch the state transitions for a process.


.. _verdi_profile:
Expand Down

0 comments on commit e232f94

Please sign in to comment.