Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds output verdi process [show|report|status|watch|call-root] subcommands #6428

Merged
merged 3 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions docs/source/reference/command_line.rst
Original file line number Diff line number Diff line change
Expand Up @@ -363,17 +363,17 @@ Below is a list with all available subcommands.
--help Show this message and exit.

Commands:
call-root Show root process of the call stack for the given processes.
call-root Show root process of processes.
dump Dump process input and output files to disk.
kill Kill running processes.
list Show a list of running or terminated processes.
list Show a list of processes.
pause Pause running processes.
play Play (unpause) paused processes.
repair Automatically repair all stuck processes.
report Show the log report for one or multiple processes.
show Show details for one or multiple processes.
status Print the status of one or multiple processes.
watch Watch the state transitions for a process.
report Show the log report of processes.
show Show details of processes.
status Show the status of processes.
watch Watch the state transitions of processes.


.. _reference:command-line:verdi-profile:
Expand Down
86 changes: 75 additions & 11 deletions src/aiida/cmdline/commands/cmd_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@
order_by,
order_dir,
):
"""Show a list of running or terminated processes.
"""Show a list of processes.

By default, only those that are still running are shown, but there are options to show also the finished ones.
By default, only processes that are still running are shown, but there are options to show also the finished ones.
"""
from tabulate import tabulate

Expand Down Expand Up @@ -185,9 +185,16 @@
@options.MOST_RECENT_NODE()
@decorators.with_dbenv()
def process_show(processes, most_recent_node):
"""Show details for one or multiple processes."""
"""Show details of processes.

Show details for one or multiple processes."""
from aiida.cmdline.utils.common import get_node_info

if not processes and not most_recent_node:
raise click.UsageError(
'Please specify one or multiple processes by their identifier (PK, UUID or label) or use an option.'
)

if processes and most_recent_node:
raise click.BadOptionUsage(
'most_recent_node',
Expand All @@ -205,7 +212,11 @@
@arguments.PROCESSES()
@decorators.with_dbenv()
def process_call_root(processes):
"""Show root process of the call stack for the given processes."""
"""Show root process of processes.

Show root process(es) of the call stack for one or multiple processes."""
if not processes:
raise click.UsageError('Please specify one or multiple processes by their identifier (PK, UUID or label).')
for process in processes:
caller = process.caller

Expand Down Expand Up @@ -240,10 +251,17 @@
)
@decorators.with_dbenv()
def process_report(processes, most_recent_node, levelname, indent_size, max_depth):
"""Show the log report for one or multiple processes."""
"""Show the log report of processes.

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 not processes and not most_recent_node:
raise click.UsageError(
'Please specify one or multiple processes by their identifier (PK, UUID or label) or use an option.'
)

if processes and most_recent_node:
raise click.BadOptionUsage(
'most_recent_node',
Expand Down Expand Up @@ -272,9 +290,16 @@
)
@arguments.PROCESSES()
def process_status(call_link_label, most_recent_node, max_depth, processes):
"""Print the status of one or multiple processes."""
"""Show the status of processes.

Show the status of one or multiple processes."""
from aiida.cmdline.utils.ascii_vis import format_call_graph

if not processes and not most_recent_node:
raise click.UsageError(
'Please specify one or multiple processes by their identifier (PK, UUID or label) or use an option.'
)

if processes and most_recent_node:
raise click.BadOptionUsage(
'most_recent_node',
Expand All @@ -296,9 +321,16 @@
@options.WAIT()
@decorators.with_dbenv()
def process_kill(processes, all_entries, timeout, wait):
"""Kill running processes."""
"""Kill running processes.

Kill one or multiple running processes."""
from aiida.engine.processes import control

if not processes and not all_entries:
raise click.UsageError(
'Please specify one or multiple processes by their identifier (PK, UUID or label) or use an option.'
)

if processes and all_entries:
raise click.BadOptionUsage('all', 'cannot specify individual processes and the `--all` flag at the same time.')

Expand All @@ -323,9 +355,16 @@
@options.WAIT()
@decorators.with_dbenv()
def process_pause(processes, all_entries, timeout, wait):
"""Pause running processes."""
"""Pause running processes.

Pause one or multiple running processes."""
from aiida.engine.processes import control

if not processes and not all_entries:
raise click.UsageError(
'Please specify one or multiple processes by their identifier (PK, UUID or label) or use an option.'
)

if processes and all_entries:
raise click.BadOptionUsage('all', 'cannot specify individual processes and the `--all` flag at the same time.')

Expand All @@ -347,9 +386,16 @@
@options.WAIT()
@decorators.with_dbenv()
def process_play(processes, all_entries, timeout, wait):
"""Play (unpause) paused processes."""
"""Play (unpause) paused processes.

Play (unpause) one or multiple paused processes."""
from aiida.engine.processes import control

if not processes and not all_entries:
raise click.UsageError(
'Please specify one or multiple processes by their identifier (PK, UUID or label) or use an option.'
)

if processes and all_entries:
raise click.BadOptionUsage('all', 'cannot specify individual processes and the `--all` flag at the same time.')

Expand All @@ -365,11 +411,26 @@

@verdi_process.command('watch')
@arguments.PROCESSES()
@options.MOST_RECENT_NODE()
@decorators.with_dbenv()
@decorators.with_broker
@decorators.only_if_daemon_running(echo.echo_warning, 'daemon is not running, so process may not be reachable')
def process_watch(broker, processes):
"""Watch the state transitions for a process."""
def process_watch(broker, processes, most_recent_node):
"""Watch the state transitions of processes.

Watch the state transitions for one or multiple running processes."""

if not processes and not most_recent_node:
raise click.UsageError(
'Please specify one or multiple processes by their identifier (PK, UUID or label) or use an option.'
)

if processes and most_recent_node:
raise click.BadOptionUsage(

Check warning on line 429 in src/aiida/cmdline/commands/cmd_process.py

View check run for this annotation

Codecov / codecov/patch

src/aiida/cmdline/commands/cmd_process.py#L428-L429

Added lines #L428 - L429 were not covered by tests
'most_recent_node',
'cannot specify individual processes and the `-M/--most-recent-node` flag at the same time.',
)

from time import sleep

from kiwipy import BroadcastFilter
Expand All @@ -387,6 +448,9 @@
communicator = broker.get_communicator()
echo.echo_report('watching for broadcasted messages, press CTRL+C to stop...')

if most_recent_node:
processes = [get_most_recent_node()]

Check warning on line 452 in src/aiida/cmdline/commands/cmd_process.py

View check run for this annotation

Codecov / codecov/patch

src/aiida/cmdline/commands/cmd_process.py#L451-L452

Added lines #L451 - L452 were not covered by tests

for process in processes:
if process.is_terminated:
echo.echo_error(f'Process<{process.pk}> is already terminated')
Expand Down
1 change: 1 addition & 0 deletions src/aiida/cmdline/utils/echo.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class ExitCode(enum.IntEnum):
"""Exit codes for the verdi command line."""

CRITICAL = 1
USAGE_ERROR = 2
DEPRECATED = 80
UNKNOWN = 99
SUCCESS = 0
Expand Down
64 changes: 52 additions & 12 deletions tests/cmdline/commands/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,11 +206,11 @@ def test_process_show(self, run_cli_command):
calcjob_one.store()
calcjob_two.store()

# Running without identifiers should not except and not print anything
# Running without identifiers should except and print something
options = []
result = run_cli_command(cmd_process.process_show, options)

assert len(result.output_lines) == 0
result = run_cli_command(cmd_process.process_show, options, raises=True)
assert result.exit_code == ExitCode.USAGE_ERROR
assert len(result.output_lines) > 0

# Giving a single identifier should print a non empty string message
options = [str(workchain_one.pk)]
Expand All @@ -232,11 +232,11 @@ def test_process_report(self, run_cli_command):
"""Test verdi process report"""
node = WorkflowNode().store()

# Running without identifiers should not except and not print anything
# Running without identifiers should except and print something
options = []
result = run_cli_command(cmd_process.process_report, options)

assert len(result.output_lines) == 0
result = run_cli_command(cmd_process.process_report, options, raises=True)
assert result.exit_code == ExitCode.USAGE_ERROR
assert len(result.output_lines) > 0

# Giving a single identifier should print a non empty string message
options = [str(node.pk)]
Expand All @@ -255,11 +255,11 @@ def test_process_status(self, run_cli_command):
node = WorkflowNode().store()
node.set_process_state(ProcessState.RUNNING)

# Running without identifiers should not except and not print anything
# Running without identifiers should except and print something
options = []
result = run_cli_command(cmd_process.process_status, options)
assert result.exception is None, result.output
assert len(result.output_lines) == 0
result = run_cli_command(cmd_process.process_status, options, raises=True)
assert result.exit_code == ExitCode.USAGE_ERROR
assert len(result.output_lines) > 0

# Giving a single identifier should print a non empty string message
options = [str(node.pk)]
Expand All @@ -273,6 +273,21 @@ def test_process_status(self, run_cli_command):
assert result.exception is None, result.output
assert len(result.output_lines) == 0

@pytest.mark.requires_rmq
def test_process_watch(self, run_cli_command):
"""Test verdi process watch"""
# Running without identifiers should except and print something
options = []
result = run_cli_command(cmd_process.process_watch, options, raises=True)
assert result.exit_code == ExitCode.USAGE_ERROR
assert len(result.output_lines) > 0

# Running with both identifiers should raise an error and print something
options = ['--most-recent-node', '1']
result = run_cli_command(cmd_process.process_watch, options, raises=True)
assert result.exit_code == ExitCode.USAGE_ERROR
assert len(result.output_lines) > 0

def test_process_status_call_link_label(self, run_cli_command):
"""Test ``verdi process status --call-link-label``."""
node = WorkflowNode().store()
Expand Down Expand Up @@ -460,6 +475,13 @@ def test_multiple_processes(self, run_cli_command):
assert str(self.node_root.pk) in result.output_lines[1]
assert str(self.node_root.pk) in result.output_lines[2]

def test_no_process_argument(self, run_cli_command):
# Running without identifiers should except and print something
options = []
result = run_cli_command(cmd_process.process_call_root, options, raises=True)
assert result.exit_code == ExitCode.USAGE_ERROR
assert len(result.output_lines) > 0


@pytest.mark.requires_rmq
@pytest.mark.usefixtures('started_daemon_client')
Expand All @@ -471,6 +493,12 @@ def test_process_pause(submit_and_await, run_cli_command):
run_cli_command(cmd_process.process_pause, [str(node.pk), '--wait'])
await_condition(lambda: node.paused)

# Running without identifiers should except and print something
options = []
result = run_cli_command(cmd_process.process_pause, options, raises=True)
assert result.exit_code == ExitCode.USAGE_ERROR
assert len(result.output_lines) > 0


@pytest.mark.requires_rmq
@pytest.mark.usefixtures('started_daemon_client')
Expand All @@ -484,6 +512,12 @@ def test_process_play(submit_and_await, run_cli_command):
run_cli_command(cmd_process.process_play, [str(node.pk), '--wait'])
await_condition(lambda: not node.paused)

# Running without identifiers should except and print something
options = []
result = run_cli_command(cmd_process.process_play, options, raises=True)
assert result.exit_code == ExitCode.USAGE_ERROR
assert len(result.output_lines) > 0


@pytest.mark.requires_rmq
@pytest.mark.usefixtures('started_daemon_client')
Expand Down Expand Up @@ -515,6 +549,12 @@ def test_process_kill(submit_and_await, run_cli_command):
await_condition(lambda: node.is_killed)
assert node.process_status == 'Killed through `verdi process kill`'

# Running without identifiers should except and print something
options = []
result = run_cli_command(cmd_process.process_kill, options, raises=True)
assert result.exit_code == ExitCode.USAGE_ERROR
assert len(result.output_lines) > 0


@pytest.mark.requires_rmq
@pytest.mark.usefixtures('started_daemon_client')
Expand Down
Loading