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

Adjust gridtk list output to fit terminal width #15

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
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
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,3 +251,29 @@ or for `zsh` add the following line to your `~/.zshrc` file:
```bash
eval "$(_GRIDTK_COMPLETE=zsh_source gridtk)"
```

### Adjusting `gridtk list` Output

By default, `gridtk list` outputs a table which migh not fit the terminal width.
You can adjust the output using the `--wrap` and `--truncate` flags. The `--wrap`
flag wraps the output to fit the terminal width, while the `--truncate` flag
truncates the output to fit the terminal width.

```bash
$ gridtk list
job-id slurm-id nodes state job-name output dependencies command
-------- ---------- ------- ------------- ---------- ---------------------- -------------- --------------------
1 506994 hcne01 COMPLETED (0) gridtk logs/gridtk.506994.out gridtk submit job.sh

$ gridtk list --wrap # --wrap or -w
job-id slurm- nodes state job-name output depende command
id ncies
-------- -------- ------- -------- ---------- ----------------- --------- -------------
1 506994 hcne0 COMPLETE gridtk logs/gridtk.50699 gridtk submit
1 D (0) 4.out job.sh

$ gridtk list --truncate # --truncate or -t
job-id slur.. nodes state job-name output depe.. command
-------- -------- ------- ------- ---------- ---------------- -------- -------------
1 506994 hc.. COMPL.. gridtk logs/gridtk.50.. gridtk subm..
```
76 changes: 73 additions & 3 deletions src/gridtk/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later

import pydoc
import shutil
import tempfile

from collections import defaultdict
Expand Down Expand Up @@ -392,29 +393,50 @@ def resubmit(

@cli.command(name="list")
@job_filters
@click.option(
"-w",
"--wrap",
is_flag=True,
default=False,
help="Wrap the output to the terminal width",
)
@click.option(
"-t",
"--truncate",
is_flag=True,
default=False,
help="Truncate the output to the terminal width",
)
@click.pass_context
def list_jobs(
ctx: click.Context,
job_ids: list[int],
states: list[str],
names: list[str],
dependents: bool,
wrap: bool,
truncate: bool,
):
"""List jobs in the queue, similar to sacct and squeue."""
from tabulate import tabulate

from .manager import JobManager

def truncate_str(content: str, max_width: int) -> str:
if len(content) > max_width:
return content[: max_width - 3] + ".."
return content

job_manager: JobManager = ctx.meta["job_manager"]
with job_manager as session:
jobs = job_manager.list_jobs(
job_ids=job_ids, states=states, names=names, dependents=dependents
)
table = defaultdict(list)
table: dict[str, list[str]] = defaultdict(list)
for job in jobs:
table["job-id"].append(job.id)
table["slurm-id"].append(job.grid_id)
table["nodes"].append(job.nodes)
table["nodes"].append(str(job.nodes))
table["state"].append(f"{job.state} ({job.exit_code})")
table["job-name"].append(job.name)
output = job.output_files[0].resolve()
Expand All @@ -428,7 +450,55 @@ def list_jobs(
",".join([str(dep_job) for dep_job in job.dependencies_ids])
)
table["command"].append("gridtk submit " + " ".join(job.command))
click.echo(tabulate(table, headers="keys"))

maxcolwidths = None
full_output = not wrap and not truncate
if not full_output and table:
minimum_column_width = 7
width_of_spaces = (len(table) - 1) * 2
terminal_width = max(
len(table) * minimum_column_width + width_of_spaces,
shutil.get_terminal_size().columns,
)
max_widths = {
"job-id": minimum_column_width,
"slurm-id": minimum_column_width,
"nodes": 0.1,
"state": 0.15,
"job-name": 0.2,
"output": 0.3,
"dependencies": minimum_column_width,
"command": 0.25,
}
left_over_width = (
terminal_width
- width_of_spaces
- sum(v for v in max_widths.values() if isinstance(v, int))
)
for key, value in max_widths.items():
if isinstance(value, float):
max_widths[key] = int(left_over_width * value)
maxcolwidths = [int(max_widths[key]) for key in table]
if truncate:
for key, rows in table.items():
table[key] = [
truncate_str(str(row), int(max_widths[key])) for row in rows
]
# truncate column names
table = {
truncate_str(key, int(max_widths[key])): value
for key, value in table.items()
}

if table:
click.echo(
tabulate(
table,
headers="keys",
maxcolwidths=maxcolwidths,
maxheadercolwidths=maxcolwidths,
)
)
session.commit()


Expand Down
41 changes: 40 additions & 1 deletion tests/test_gridtk.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,14 @@ def test_submit_triple_dash(mock_check_output: Mock, runner):

@patch("subprocess.check_output")
def test_list_jobs(mock_check_output, runner):
with runner.isolated_filesystem():
# override shutil.get_terminal_size to return a fixed size with COLUMNS=80
with runner.isolated_filesystem(), runner.isolation(env={"COLUMNS": "80"}):
# test when there are no jobs
result = runner.invoke(cli, ["list"])
assert_click_runner_result(result)
assert result.output == ""

# test when there are jobs
submit_job_id = 9876543
_submit_job(
runner=runner, mock_check_output=mock_check_output, job_id=submit_job_id
Expand All @@ -240,6 +247,38 @@ def test_list_jobs(mock_check_output, runner):
mock_check_output.assert_called_with(
["sacct", "-j", str(submit_job_id), "--json"], text=True
)
# full command
assert "gridtk submit --wrap sleep\n" in result.output
# full log file name
assert "logs/gridtk.9876543.out " in result.output

# test gridtk list --truncate
result = runner.invoke(cli, ["list", "--truncate"])
assert_click_runner_result(result)
assert str(submit_job_id) in result.output
mock_check_output.assert_called_with(
["sacct", "-j", str(submit_job_id), "--json"], text=True
)
# truncated command
assert "gridtk s..\n" in result.output
# truncated log file name
assert "logs/gridt.. " in result.output

# test gridtk list --wrap
result = runner.invoke(cli, ["list", "--wrap"])
assert_click_runner_result(result)
assert str(submit_job_id) in result.output
mock_check_output.assert_called_with(
["sacct", "-j", str(submit_job_id), "--json"], text=True
)
# wraped command
assert "gridtk" in result.output
assert "submit" in result.output
assert "--wrap" in result.output
assert "sleep" in result.output
# wraped log file name
assert "logs/gridtk.9 " in result.output
assert "876543.out " in result.output


@patch("subprocess.check_output")
Expand Down
Loading