Coverage for sparkle/CLI/cancel.py: 47%
62 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-05 14:48 +0000
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-05 14:48 +0000
1#!/usr/bin/env python3
2"""Command to cancel async jobs."""
3import sys
4import argparse
5import pytermgui as ptg
7from runrunner.base import Status
9from sparkle.CLI.help import logging
10from sparkle.CLI.help import argparse_custom as ac
11from sparkle.CLI.help import jobs as jobs_help
12from sparkle.CLI.help import global_variables as gv
15def parser_function() -> argparse.ArgumentParser:
16 """Create parser for the cancel command."""
17 parser = argparse.ArgumentParser(description="Command to cancel running jobs.")
18 parser.add_argument(*ac.JobIDsArgument.names, **ac.JobIDsArgument.kwargs)
19 parser.add_argument(*ac.AllJobsArgument.names, **ac.AllJobsArgument.kwargs)
20 return parser
23def main(argv: list[str]) -> None:
24 """Main function of the cancel command."""
25 # Log command call
26 logging.log_command(sys.argv)
28 # Define command line arguments
29 parser = parser_function()
31 # Process command line arguments
32 args = parser.parse_args(argv)
34 # Filter jobs on relevant status
35 path = gv.settings().DEFAULT_log_output
36 jobs = [run for run in jobs_help.get_runs_from_file(path)
37 if run.status == Status.WAITING or run.status == Status.RUNNING]
38 if args.all or args.job_ids:
39 killed_jobs = []
40 for j in jobs:
41 if args.all or j.run_id in args.job_ids:
42 j.kill()
43 killed_jobs.append(j)
44 if len(killed_jobs) == 0:
45 if args.all:
46 print("No jobs to cancel.")
47 sys.exit(0)
48 else:
49 print(f"ERROR: No jobs with ids {args.job_ids} to cancel.")
50 # NOTE: Should we raise an error here instead?
51 sys.exit(-1)
52 print(f"Canceled {len(killed_jobs)} jobs with IDs: "
53 f"{', '.join([j.run_id for j in killed_jobs])}.")
54 elif len(jobs) == 0:
55 print("No jobs available to cancel.")
56 else:
57 jobs = sorted(jobs, key=lambda j: j.run_id)
58 ptg.Button.chars = {"delimiter": ["", ""]} # Disable padding around buttons
60 def cancel_jobs(self: ptg.Button) -> None:
61 """Cancel jobs based on a button click."""
62 job_id = self.label.split("|")[1].strip()
63 for job in jobs:
64 if job.run_id == job_id:
65 job.kill()
66 # Replace button with label
67 for iw, widget in enumerate(self.parent._widgets):
68 if isinstance(widget, ptg.Button) and \
69 widget.label == self.label:
70 self.parent._widgets[iw] = ptg.Label(self.label)
71 break
72 refresh_data(self.parent)
74 def refresh_data(self: ptg.Window) -> None:
75 """Refresh the table."""
76 data_table = jobs_help.create_jobs_table(jobs, markup=True).splitlines()
77 for iw, widget in enumerate(self._widgets):
78 self._widgets[iw] = type(widget)(data_table[iw], onclick=cancel_jobs)
79 self._widgets[iw].parent = self
81 table = jobs_help.create_jobs_table(jobs, markup=True).splitlines()
82 with ptg.WindowManager() as manager:
83 window = (
84 ptg.Window(
85 width=len(table[0]),
86 box="EMPTY",
87 )
88 .set_title("Running Sparkle Jobs")
89 )
90 for row in table:
91 if "|" not in row or not row.split("|")[1].strip().isnumeric():
92 window._add_widget(ptg.Label(row))
93 else:
94 window._add_widget(ptg.Button(label=row, onclick=cancel_jobs))
96 manager.add(window)
98 sys.exit(0)
101if __name__ == "__main__":
102 main(sys.argv[1:])