Skip to content

Commit

Permalink
CLI: Add the -M/--most-recent-node option
Browse files Browse the repository at this point in the history
The `-M/--most-recent-node` option is added to the `status`, `show` and
`report` subcommands of `verdi process`. When specified, these commands
don't need specific process node identifiers but the most recent will
automatically be selected. This is a useful feature when users are
actively running processes one by one during testing and they usually
want to look at the last created process node.

Co-Authored-By: Sebastiaan Huber <mail@sphuber.net>
  • Loading branch information
ljbeal and sphuber committed Feb 9, 2024
1 parent 4f9774a commit 5aae874
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 3 deletions.
48 changes: 45 additions & 3 deletions src/aiida/cmdline/commands/cmd_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ def default_projections():
return CalculationQueryBuilder.default_projections


def get_most_recent_node():
"""Return the most recent process node.
:returns: The ``ProcessNode`` with the latest ``ctime``.
"""
from aiida.orm import ProcessNode, QueryBuilder

process = QueryBuilder().append(ProcessNode, tag='n').order_by({'n': {'ctime': 'desc'}}).first(flat=True)
echo.echo_info(f'Most recent node matched: `{process}`.')
return process


@verdi.group('process')
def verdi_process():
"""Inspect and manage processes."""
Expand Down Expand Up @@ -156,11 +168,21 @@ def process_list(

@verdi_process.command('show')
@arguments.PROCESSES()
@options.MOST_RECENT_NODE()
@decorators.with_dbenv()
def process_show(processes):
def process_show(processes, most_recent_node):
"""Show details for one or multiple processes."""
from aiida.cmdline.utils.common import get_node_info

if processes and most_recent_node:
raise click.BadOptionUsage(
'most_recent_node',
'cannot specify individual processes and the `-M/--most-recent-node` flag at the same time.',
)

if most_recent_node:
processes = [get_most_recent_node()]

for process in processes:
echo.echo(get_node_info(process))

Expand Down Expand Up @@ -190,6 +212,7 @@ def process_call_root(processes):

@verdi_process.command('report')
@arguments.PROCESSES()
@options.MOST_RECENT_NODE()
@click.option('-i', '--indent-size', type=int, default=2, help='Set the number of spaces to indent each level by.')
@click.option(
'-l',
Expand All @@ -202,11 +225,20 @@ def process_call_root(processes):
'-m', '--max-depth', 'max_depth', type=int, default=None, help='Limit the number of levels to be printed.'
)
@decorators.with_dbenv()
def process_report(processes, levelname, indent_size, max_depth):
def process_report(processes, most_recent_node, levelname, indent_size, max_depth):
"""Show the log report for one or multiple processes."""
from aiida.cmdline.utils.common import get_calcjob_report, get_process_function_report, get_workchain_report
from aiida.orm import CalcFunctionNode, CalcJobNode, WorkChainNode, WorkFunctionNode

if processes and most_recent_node:
raise click.BadOptionUsage(
'most_recent_node',
'cannot specify individual processes and the `-M/--most-recent-node` flag at the same time.',
)

if most_recent_node:
processes = [get_most_recent_node()]

for process in processes:
if isinstance(process, CalcJobNode):
echo.echo(get_calcjob_report(process))
Expand All @@ -219,15 +251,25 @@ def process_report(processes, levelname, indent_size, max_depth):


@verdi_process.command('status')
@options.MOST_RECENT_NODE()
@click.option('-c', '--call-link-label', 'call_link_label', is_flag=True, help='Include the call link label if set.')
@click.option(
'-m', '--max-depth', 'max_depth', type=int, default=None, help='Limit the number of levels to be printed.'
)
@arguments.PROCESSES()
def process_status(call_link_label, max_depth, processes):
def process_status(call_link_label, most_recent_node, max_depth, processes):
"""Print the status of one or multiple processes."""
from aiida.cmdline.utils.ascii_vis import format_call_graph

if processes and most_recent_node:
raise click.BadOptionUsage(
'most_recent_node',
'cannot specify individual processes and the `-M/--most-recent-node` flag at the same time.',
)

if most_recent_node:
processes = [get_most_recent_node()]

for process in processes:
graph = format_call_graph(process, max_depth=max_depth, call_link_label=call_link_label)
echo.echo(graph)
Expand Down
8 changes: 8 additions & 0 deletions src/aiida/cmdline/params/options/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
'INPUT_PLUGIN',
'LABEL',
'LIMIT',
'MOST_RECENT_NODE',
'NODE',
'NODES',
'NON_INTERACTIVE',
Expand Down Expand Up @@ -592,6 +593,13 @@ def set_log_level(_ctx, _param, value):
help='Only include entries created before OLDER_THAN days ago.',
)

MOST_RECENT_NODE = OverridableOption(
'-M',
'--most-recent-node',
is_flag=True,
help='Select the most recently created node.',
)

ALL = OverridableOption(
'-a',
'--all',
Expand Down
38 changes: 38 additions & 0 deletions tests/cmdline/commands/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -557,3 +557,41 @@ def test_process_repair_verbosity(monkeypatch, run_cli_command):
result = run_cli_command(cmd_process.process_repair, ['-v', 'INFO'], use_subprocess=False)
assert 'Active processes: [1, 2, 3, 4]' in result.output
assert 'Process tasks: [1, 2]' in result.output


@pytest.fixture
def process_nodes():
"""Return a list of two stored ``CalcJobNode`` instances with a finished process state."""
nodes = [CalcJobNode(), CalcJobNode()]
for node in nodes:
node.set_process_state(ProcessState.FINISHED)
node.store()
return nodes


def test_process_report_most_recent_node(run_cli_command, process_nodes):
"""Test ``verdi process report --most-recent-node``."""
result = run_cli_command(cmd_process.process_report, ['--most-recent-node'])
assert f'*** {process_nodes[0].pk}:' not in result.output
assert f'*** {process_nodes[1].pk}:' in result.output


def test_process_show_most_recent_node(run_cli_command, process_nodes):
"""Test ``verdi process show --most-recent-node``."""
result = run_cli_command(cmd_process.process_show, ['--most-recent-node'])
assert process_nodes[0].uuid not in result.output
assert process_nodes[1].uuid in result.output


def test_process_status_most_recent_node(run_cli_command, process_nodes):
"""Test ``verdi process status --most-recent-node``."""
result = run_cli_command(cmd_process.process_status, ['--most-recent-node'])
assert f'<{process_nodes[0].pk}>' not in result.output
assert f'<{process_nodes[1].pk}>' in result.output


@pytest.mark.parametrize('command', (cmd_process.process_report, cmd_process.process_show, cmd_process.process_status))
def test_process_most_recent_node_exclusive(run_cli_command, process_nodes, command):
"""Test command raises if ``-M`` is specified as well as explicit process nodes."""
result = run_cli_command(command, ['-M', str(process_nodes[0].pk)], raises=True)
assert 'cannot specify individual processes and the `-M/--most-recent-node` flag at the same time.' in result.output

0 comments on commit 5aae874

Please sign in to comment.