hpc-runner 0.1.1__py3-none-any.whl → 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. hpc_runner/_version.py +2 -2
  2. hpc_runner/cli/cancel.py +1 -1
  3. hpc_runner/cli/config.py +2 -2
  4. hpc_runner/cli/main.py +17 -13
  5. hpc_runner/cli/monitor.py +30 -0
  6. hpc_runner/cli/run.py +223 -67
  7. hpc_runner/cli/status.py +6 -5
  8. hpc_runner/core/__init__.py +30 -0
  9. hpc_runner/core/descriptors.py +87 -33
  10. hpc_runner/core/exceptions.py +9 -0
  11. hpc_runner/core/job.py +272 -93
  12. hpc_runner/core/job_info.py +104 -0
  13. hpc_runner/core/result.py +4 -0
  14. hpc_runner/schedulers/base.py +148 -30
  15. hpc_runner/schedulers/detection.py +22 -4
  16. hpc_runner/schedulers/local/scheduler.py +119 -2
  17. hpc_runner/schedulers/sge/args.py +161 -94
  18. hpc_runner/schedulers/sge/parser.py +106 -13
  19. hpc_runner/schedulers/sge/scheduler.py +727 -171
  20. hpc_runner/schedulers/sge/templates/batch.sh.j2 +82 -0
  21. hpc_runner/schedulers/sge/templates/interactive.sh.j2 +78 -0
  22. hpc_runner/tui/__init__.py +5 -0
  23. hpc_runner/tui/app.py +436 -0
  24. hpc_runner/tui/components/__init__.py +17 -0
  25. hpc_runner/tui/components/detail_panel.py +187 -0
  26. hpc_runner/tui/components/filter_bar.py +174 -0
  27. hpc_runner/tui/components/filter_popup.py +345 -0
  28. hpc_runner/tui/components/job_table.py +260 -0
  29. hpc_runner/tui/providers/__init__.py +5 -0
  30. hpc_runner/tui/providers/jobs.py +197 -0
  31. hpc_runner/tui/screens/__init__.py +7 -0
  32. hpc_runner/tui/screens/confirm.py +67 -0
  33. hpc_runner/tui/screens/job_details.py +210 -0
  34. hpc_runner/tui/screens/log_viewer.py +170 -0
  35. hpc_runner/tui/snapshot.py +153 -0
  36. hpc_runner/tui/styles/monitor.tcss +567 -0
  37. hpc_runner-0.2.1.dist-info/METADATA +285 -0
  38. hpc_runner-0.2.1.dist-info/RECORD +56 -0
  39. hpc_runner/schedulers/sge/templates/job.sh.j2 +0 -39
  40. hpc_runner-0.1.1.dist-info/METADATA +0 -46
  41. hpc_runner-0.1.1.dist-info/RECORD +0 -38
  42. {hpc_runner-0.1.1.dist-info → hpc_runner-0.2.1.dist-info}/WHEEL +0 -0
  43. {hpc_runner-0.1.1.dist-info → hpc_runner-0.2.1.dist-info}/entry_points.txt +0 -0
hpc_runner/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.1.1'
32
- __version_tuple__ = version_tuple = (0, 1, 1)
31
+ __version__ = version = '0.2.1'
32
+ __version_tuple__ = version_tuple = (0, 2, 1)
33
33
 
34
34
  __commit_id__ = commit_id = None
hpc_runner/cli/cancel.py CHANGED
@@ -10,7 +10,7 @@ console = Console()
10
10
 
11
11
  @click.command()
12
12
  @click.argument("job_id")
13
- @click.option("--force", "-f", is_flag=True, help="Force cancel without confirmation")
13
+ @click.option("--force", is_flag=True, help="Cancel without confirmation")
14
14
  @pass_context
15
15
  def cancel(
16
16
  ctx: Context,
hpc_runner/cli/config.py CHANGED
@@ -21,7 +21,7 @@ def config_cmd() -> None:
21
21
  @pass_context
22
22
  def show(ctx: Context) -> None:
23
23
  """Show current configuration."""
24
- from hpc_runner.core.config import find_config_file, load_config
24
+ from hpc_runner.core.config import find_config_file
25
25
 
26
26
  config_path = ctx.config_path or find_config_file()
27
27
 
@@ -45,7 +45,7 @@ def show(ctx: Context) -> None:
45
45
 
46
46
 
47
47
  @config_cmd.command("init")
48
- @click.option("--global", "-g", "global_config", is_flag=True, help="Create global config")
48
+ @click.option("--global", "global_config", is_flag=True, help="Create in ~/.config/hpc-tools/")
49
49
  @pass_context
50
50
  def init(ctx: Context, global_config: bool) -> None:
51
51
  """Create a new configuration file."""
hpc_runner/cli/main.py CHANGED
@@ -1,7 +1,6 @@
1
1
  """Main CLI entry point using rich-click."""
2
2
 
3
3
  from pathlib import Path
4
- from typing import Optional
5
4
 
6
5
  import rich_click as click
7
6
  from rich.console import Console
@@ -15,52 +14,57 @@ console = Console()
15
14
  # Context object to pass state between commands
16
15
  class Context:
17
16
  def __init__(self) -> None:
18
- self.config_path: Optional[Path] = None
19
- self.scheduler: Optional[str] = None
17
+ self.config_path: Path | None = None
18
+ self.scheduler: str | None = None
20
19
  self.verbose: bool = False
21
20
 
22
21
  pass_context = click.make_pass_decorator(Context, ensure=True)
23
22
 
24
23
 
25
- @click.group()
24
+ @click.group(context_settings={"help_option_names": ["-h", "--help"]})
26
25
  @click.option(
27
- "--config", "-c",
26
+ "--config",
28
27
  type=click.Path(exists=True, path_type=Path),
29
28
  help="Path to configuration file",
30
29
  )
31
30
  @click.option(
32
- "--scheduler", "-s",
31
+ "--scheduler",
33
32
  type=str,
34
33
  help="Force scheduler (sge, slurm, pbs, local)",
35
34
  )
36
35
  @click.option(
37
- "--verbose", "-v",
36
+ "--verbose",
38
37
  is_flag=True,
39
38
  help="Enable verbose output",
40
39
  )
41
40
  @click.version_option(package_name="hpc-runner")
42
41
  @pass_context
43
- def cli(ctx: Context, config: Optional[Path], scheduler: Optional[str], verbose: bool) -> None:
42
+ def cli(ctx: Context, config: Path | None, scheduler: str | None, verbose: bool) -> None:
44
43
  """HPC job submission tool.
45
44
 
46
45
  Submit and manage jobs across different HPC schedulers (SGE, Slurm, PBS)
47
46
  with a unified interface.
47
+
48
+ Any unrecognized short options are passed directly to the underlying
49
+ scheduler, allowing use of native flags like -N, -n, -q, etc.
48
50
  """
49
51
  ctx.config_path = config
50
52
  ctx.scheduler = scheduler
51
53
  ctx.verbose = verbose
52
54
 
53
55
 
54
- # Import and register subcommands
55
- from hpc_runner.cli.run import run
56
- from hpc_runner.cli.status import status
57
- from hpc_runner.cli.cancel import cancel
58
- from hpc_runner.cli.config import config_cmd
56
+ # Import and register subcommands (must be after cli is defined to avoid circular imports)
57
+ from hpc_runner.cli.cancel import cancel # noqa: E402
58
+ from hpc_runner.cli.config import config_cmd # noqa: E402
59
+ from hpc_runner.cli.monitor import monitor # noqa: E402
60
+ from hpc_runner.cli.run import run # noqa: E402
61
+ from hpc_runner.cli.status import status # noqa: E402
59
62
 
60
63
  cli.add_command(run)
61
64
  cli.add_command(status)
62
65
  cli.add_command(cancel)
63
66
  cli.add_command(config_cmd, name="config")
67
+ cli.add_command(monitor)
64
68
 
65
69
 
66
70
  def main() -> None:
@@ -0,0 +1,30 @@
1
+ """CLI command for launching the interactive job monitor."""
2
+
3
+ import rich_click as click
4
+
5
+
6
+ @click.command()
7
+ @click.option(
8
+ "--refresh",
9
+ "-r",
10
+ default=10,
11
+ type=int,
12
+ help="Auto-refresh interval in seconds",
13
+ )
14
+ def monitor(refresh: int) -> None:
15
+ """Launch interactive job monitor TUI.
16
+
17
+ Opens a terminal UI for monitoring HPC jobs across schedulers.
18
+ Shows active and completed jobs with filtering and search.
19
+
20
+ \b
21
+ Keyboard shortcuts:
22
+ q Quit
23
+ r Manual refresh
24
+ u Toggle user filter (me/all)
25
+ Tab Switch tabs
26
+ """
27
+ from hpc_runner.tui import HpcMonitorApp
28
+
29
+ app = HpcMonitorApp(refresh_interval=refresh)
30
+ app.run()
hpc_runner/cli/run.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """Run command - submit jobs to the scheduler."""
2
2
 
3
- from typing import Optional, Tuple
3
+ from typing import TYPE_CHECKING
4
4
 
5
5
  import rich_click as click
6
6
  from rich.console import Console
@@ -9,128 +9,284 @@ from rich.syntax import Syntax
9
9
 
10
10
  from hpc_runner.cli.main import Context, pass_context
11
11
 
12
+ if TYPE_CHECKING:
13
+ from hpc_runner.core.job import Job
14
+
12
15
  console = Console()
13
16
 
14
17
 
15
- @click.command()
16
- @click.argument("command", nargs=-1, required=True)
17
- @click.option("--name", "-N", help="Job name")
18
- @click.option("--cpu", "-c", type=int, help="Number of CPUs")
19
- @click.option("--mem", "-m", help="Memory requirement (e.g., 16G)")
20
- @click.option("--time", "-t", help="Time limit (e.g., 4:00:00)")
21
- @click.option("--queue", "-q", help="Queue/partition name")
22
- @click.option("--interactive", "-I", is_flag=True, help="Run interactively (blocking)")
23
- @click.option("--local", "-L", is_flag=True, help="Run locally (no scheduler)")
24
- @click.option("--type", "-T", "job_type", help="Job type from config")
25
- @click.option("--module", "-M", multiple=True, help="Modules to load (can be repeated)")
26
- @click.option("--raw", "-R", multiple=True, help="Raw scheduler args (can be repeated)")
27
- @click.option("--dry-run", "-n", is_flag=True, help="Show what would be submitted")
28
- @click.option("--stderr", "-e", help="Separate stderr file (default: merged with stdout)")
18
+ @click.command(
19
+ context_settings={
20
+ "ignore_unknown_options": True,
21
+ "allow_interspersed_args": True,
22
+ }
23
+ )
24
+ @click.argument("args", nargs=-1, type=click.UNPROCESSED)
25
+ # All hpc-runner options are long-form only
26
+ @click.option("--job-name", "job_name", help="Job name")
27
+ @click.option("--cpu", type=int, help="Number of CPUs")
28
+ @click.option("--mem", help="Memory requirement (e.g., 16G)")
29
+ @click.option("--time", "time_limit", help="Time limit (e.g., 4:00:00)")
30
+ @click.option("--queue", help="Queue/partition name")
31
+ @click.option("--nodes", type=int, help="Number of nodes (MPI jobs)")
32
+ @click.option("--ntasks", type=int, help="Number of tasks (MPI jobs)")
33
+ @click.option("--directory", type=click.Path(exists=True), help="Working directory")
34
+ @click.option("--job-type", "job_type", help="Job type from config")
35
+ @click.option("--module", "modules", multiple=True, help="Modules to load (repeatable)")
36
+ @click.option("--stderr", help="Separate stderr file (default: merged)")
37
+ @click.option("--output", help="Stdout file path pattern")
38
+ @click.option("--array", help="Array job specification (e.g., 1-100)")
39
+ @click.option("--depend", help="Job dependency specification")
40
+ @click.option("--inherit-env/--no-inherit-env", "inherit_env", default=True, help="Inherit environment variables")
41
+ @click.option("--interactive", is_flag=True, help="Run interactively (srun/qrsh)")
42
+ @click.option("--local", is_flag=True, help="Run locally (no scheduler)")
43
+ @click.option("--dry-run", "dry_run", is_flag=True, help="Show what would be submitted")
44
+ @click.option("--wait", is_flag=True, help="Wait for job completion")
45
+ @click.option("--keep-script", "keep_script", is_flag=True, help="Keep job script for debugging")
29
46
  @pass_context
30
47
  def run(
31
48
  ctx: Context,
32
- command: Tuple[str, ...],
33
- name: Optional[str],
34
- cpu: Optional[int],
35
- mem: Optional[str],
36
- time: Optional[str],
37
- queue: Optional[str],
49
+ args: tuple[str, ...],
50
+ job_name: str | None,
51
+ cpu: int | None,
52
+ mem: str | None,
53
+ time_limit: str | None,
54
+ queue: str | None,
55
+ nodes: int | None,
56
+ ntasks: int | None,
57
+ directory: str | None,
58
+ job_type: str | None,
59
+ modules: tuple[str, ...],
60
+ stderr: str | None,
61
+ output: str | None,
62
+ array: str | None,
63
+ depend: str | None,
64
+ inherit_env: bool,
38
65
  interactive: bool,
39
66
  local: bool,
40
- job_type: Optional[str],
41
- module: Tuple[str, ...],
42
- raw: Tuple[str, ...],
43
67
  dry_run: bool,
44
- stderr: Optional[str],
68
+ wait: bool,
69
+ keep_script: bool,
45
70
  ) -> None:
46
71
  """Submit a job to the scheduler.
47
72
 
48
73
  COMMAND is the command to execute. Use quotes for complex commands:
49
74
 
75
+ \b
50
76
  hpc run "make -j8 all"
51
-
52
77
  hpc run python script.py --arg value
78
+
79
+ Any unrecognized options starting with '-' are passed directly to the
80
+ underlying scheduler. This allows using native flags:
81
+
82
+ \b
83
+ hpc run -N 4 -n 16 "mpirun ./sim" # Slurm nodes/tasks
84
+ hpc run -q batch.q -l gpu=2 "train" # SGE queue/resources
53
85
  """
86
+ import shlex
87
+
54
88
  from hpc_runner.core.job import Job
55
89
  from hpc_runner.schedulers import get_scheduler
56
90
 
91
+ # Parse args into command and scheduler passthrough args
92
+ command_parts, scheduler_args = _parse_args(args)
93
+
94
+ if not command_parts:
95
+ raise click.UsageError("Command is required")
96
+
97
+ # Use shlex.join to preserve quoting for args with spaces/special chars
98
+ cmd_str = shlex.join(command_parts)
99
+
57
100
  # Get scheduler
58
101
  scheduler_name = "local" if local else ctx.scheduler
59
102
  scheduler = get_scheduler(scheduler_name)
60
103
 
61
- # Build command string
62
- cmd_str = " ".join(command)
63
-
64
104
  # Create job from config or parameters
65
105
  if job_type:
66
106
  job = Job.from_config(job_type, command=cmd_str)
67
107
  else:
68
108
  job = Job(command=cmd_str)
69
109
 
70
- # Override with CLI arguments
71
- if name:
72
- job.name = name
110
+ # Apply CLI overrides
111
+ if job_name:
112
+ job.name = job_name
73
113
  if cpu:
74
114
  job.cpu = cpu
75
115
  if mem:
76
116
  job.mem = mem
77
- if time:
78
- job.time = time
117
+ if time_limit:
118
+ job.time = time_limit
79
119
  if queue:
80
120
  job.queue = queue
81
- if module:
82
- job.modules = list(module)
83
- if raw:
84
- job.raw_args = list(raw)
121
+ if nodes:
122
+ job.nodes = nodes
123
+ if ntasks:
124
+ job.tasks = ntasks
125
+ if directory:
126
+ job.workdir = directory
127
+ if modules:
128
+ job.modules = list(modules)
85
129
  if stderr:
86
130
  job.stderr = stderr
131
+ if output:
132
+ job.stdout = output
133
+ if depend:
134
+ job.dependency = depend
135
+
136
+ # inherit_env is always set (has a default), so always apply it
137
+ job.inherit_env = inherit_env
138
+
139
+ # Add scheduler passthrough args
140
+ if scheduler_args:
141
+ job.raw_args = scheduler_args
142
+ if ctx.verbose:
143
+ console.print(f"[dim]Scheduler passthrough: {' '.join(scheduler_args)}[/dim]")
144
+
145
+ # Handle array jobs
146
+ if array:
147
+ _handle_array_job(job, array, scheduler, dry_run, ctx.verbose)
148
+ return
87
149
 
88
150
  if dry_run:
89
- _show_dry_run(job, scheduler)
151
+ _show_dry_run(job, scheduler, scheduler_args, interactive=interactive)
90
152
  return
91
153
 
92
- # Submit
93
- result = scheduler.submit(job, interactive=interactive)
154
+ # Submit the job
155
+ result = scheduler.submit(job, interactive=interactive, keep_script=keep_script)
94
156
 
95
157
  if interactive:
96
158
  if result.returncode == 0:
97
- console.print(f"[green]Job completed successfully[/green]")
159
+ console.print("[green]Job completed successfully[/green]")
98
160
  else:
99
161
  console.print(f"[red]Job failed with exit code: {result.returncode}[/red]")
100
162
  else:
101
163
  console.print(f"Submitted job [bold cyan]{result.job_id}[/bold cyan]")
164
+
102
165
  if ctx.verbose:
103
166
  console.print(f" Scheduler: {scheduler.name}")
104
167
  console.print(f" Job name: {job.name}")
105
168
  console.print(f" Command: {job.command}")
106
169
 
170
+ if wait:
171
+ console.print("[dim]Waiting for job completion...[/dim]")
172
+ final_status = result.wait()
173
+ console.print(f"Job completed with status: [bold]{final_status.name}[/bold]")
174
+
175
+
176
+ def _parse_args(args: tuple[str, ...]) -> tuple[list[str], list[str]]:
177
+ """Parse args into command parts and scheduler passthrough args.
178
+
179
+ Scheduler args are any args that:
180
+ - Start with '-' and are not recognized hpc-runner options
181
+ - Include their values (e.g., "-N 4" becomes ["-N", "4"])
182
+
183
+ The command is everything after the first non-option arg or after '--'.
184
+
185
+ Args:
186
+ args: Raw arguments from click
187
+
188
+ Returns:
189
+ Tuple of (command_parts, scheduler_args)
190
+ """
191
+ command_parts: list[str] = []
192
+ scheduler_args: list[str] = []
107
193
 
108
- def _show_dry_run(job: "Job", scheduler: "BaseScheduler") -> None:
194
+ args_list = list(args)
195
+ i = 0
196
+ in_command = False
197
+
198
+ while i < len(args_list):
199
+ arg = args_list[i]
200
+
201
+ # '--' signals end of options
202
+ if arg == "--":
203
+ in_command = True
204
+ i += 1
205
+ continue
206
+
207
+ if in_command:
208
+ command_parts.append(arg)
209
+ i += 1
210
+ continue
211
+
212
+ # Check if this looks like an option
213
+ if arg.startswith("-"):
214
+ # This is a scheduler passthrough option
215
+ scheduler_args.append(arg)
216
+
217
+ # Check if next arg is the value (not another option)
218
+ if i + 1 < len(args_list) and not args_list[i + 1].startswith("-"):
219
+ # Handle special case: is this a flag or does it take a value?
220
+ # Heuristic: if next arg doesn't start with '-', treat as value
221
+ # unless the current arg uses '=' syntax
222
+ if "=" not in arg:
223
+ i += 1
224
+ scheduler_args.append(args_list[i])
225
+ i += 1
226
+ else:
227
+ # First non-option arg starts the command
228
+ in_command = True
229
+ command_parts.append(arg)
230
+ i += 1
231
+
232
+ return command_parts, scheduler_args
233
+
234
+
235
+ def _show_dry_run(
236
+ job: "Job", scheduler, scheduler_args: list[str], interactive: bool = False
237
+ ) -> None:
109
238
  """Display what would be submitted."""
110
- from hpc_runner.schedulers.base import BaseScheduler
111
-
112
- console.print(Panel.fit("[bold]Dry Run[/bold]", border_style="yellow"))
113
- console.print(f"[bold]Scheduler:[/bold] {scheduler.name}")
114
- console.print(f"[bold]Job Name:[/bold] {job.name}")
115
- console.print(f"[bold]Command:[/bold] {job.command}")
116
-
117
- if job.cpu:
118
- console.print(f"[bold]CPU:[/bold] {job.cpu}")
119
- if job.mem:
120
- console.print(f"[bold]Memory:[/bold] {job.mem}")
121
- if job.time:
122
- console.print(f"[bold]Time:[/bold] {job.time}")
123
- if job.queue:
124
- console.print(f"[bold]Queue:[/bold] {job.queue}")
125
- if job.modules:
126
- console.print(f"[bold]Modules:[/bold] {', '.join(job.modules)}")
127
- if job.merge_output:
128
- console.print(f"[bold]Output:[/bold] merged (stdout only)")
129
- else:
130
- console.print(f"[bold]Stderr:[/bold] {job.stderr}")
239
+ mode = "interactive" if interactive else "batch"
240
+ console.print(
241
+ Panel.fit(
242
+ f"[bold]Scheduler:[/bold] {scheduler.name}\n"
243
+ f"[bold]Mode:[/bold] {mode}\n"
244
+ f"[bold]Job name:[/bold] {job.name}\n"
245
+ f"[bold]Command:[/bold] {job.command}",
246
+ title="Dry Run",
247
+ border_style="blue",
248
+ )
249
+ )
131
250
 
132
- console.print()
133
- console.print("[bold]Generated Script:[/bold]")
134
- script = scheduler.generate_script(job)
251
+ if scheduler_args:
252
+ console.print(f"\n[bold]Scheduler passthrough args:[/bold] {' '.join(scheduler_args)}")
253
+
254
+ console.print("\n[bold]Generated script:[/bold]")
255
+ if interactive and hasattr(scheduler, "_generate_interactive_script"):
256
+ script = scheduler._generate_interactive_script(job, "/tmp/example_script.sh")
257
+ else:
258
+ script = scheduler.generate_script(job)
135
259
  syntax = Syntax(script, "bash", theme="monokai", line_numbers=True)
136
260
  console.print(syntax)
261
+
262
+
263
+ def _handle_array_job(job, array_spec: str, scheduler, dry_run: bool, verbose: bool) -> None:
264
+ """Handle array job submission."""
265
+ from hpc_runner.core.job_array import JobArray
266
+
267
+ # Parse array spec (e.g., "1-100", "1-100:10", "1-100%5")
268
+ # Basic parsing - could be enhanced
269
+ parts = array_spec.replace("%", ":").split(":")
270
+ range_parts = parts[0].split("-")
271
+
272
+ start = int(range_parts[0])
273
+ end = int(range_parts[1]) if len(range_parts) > 1 else start
274
+ step = int(parts[1]) if len(parts) > 1 else 1
275
+ max_concurrent = int(parts[2]) if len(parts) > 2 else None
276
+
277
+ array_job = JobArray(
278
+ job=job,
279
+ start=start,
280
+ end=end,
281
+ step=step,
282
+ max_concurrent=max_concurrent,
283
+ )
284
+
285
+ if dry_run:
286
+ console.print(f"[bold]Array job:[/bold] {array_job.range_str} ({array_job.count} tasks)")
287
+ _show_dry_run(job, scheduler, [])
288
+ return
289
+
290
+ result = array_job.submit(scheduler)
291
+ console.print(f"Submitted array job [bold cyan]{result.base_job_id}[/bold cyan]")
292
+ console.print(f" Tasks: {array_job.count} ({array_job.range_str})")
hpc_runner/cli/status.py CHANGED
@@ -1,6 +1,5 @@
1
1
  """Status command - check job status."""
2
2
 
3
- from typing import Optional
4
3
 
5
4
  import rich_click as click
6
5
  from rich.console import Console
@@ -13,12 +12,12 @@ console = Console()
13
12
 
14
13
  @click.command()
15
14
  @click.argument("job_id", required=False)
16
- @click.option("--all", "-a", "all_users", is_flag=True, help="Show all users' jobs")
17
- @click.option("--watch", "-w", is_flag=True, help="Watch mode (refresh periodically)")
15
+ @click.option("--all", "all_users", is_flag=True, help="Show all users' jobs")
16
+ @click.option("--watch", is_flag=True, help="Watch mode (refresh periodically)")
18
17
  @pass_context
19
18
  def status(
20
19
  ctx: Context,
21
- job_id: Optional[str],
20
+ job_id: str | None,
22
21
  all_users: bool,
23
22
  watch: bool,
24
23
  ) -> None:
@@ -47,7 +46,9 @@ def status(
47
46
  console.print(table)
48
47
  else:
49
48
  # List all jobs (not implemented for all schedulers)
50
- console.print("[yellow]Listing all jobs requires scheduler-specific implementation[/yellow]")
49
+ console.print(
50
+ "[yellow]Listing all jobs requires scheduler-specific implementation[/yellow]"
51
+ )
51
52
  console.print("Use 'hpc status <job_id>' to check a specific job")
52
53
 
53
54
 
@@ -1 +1,31 @@
1
1
  """Core models and abstractions for hpc-tools."""
2
+
3
+ from .exceptions import (
4
+ AccountingNotAvailable,
5
+ ConfigError,
6
+ ConfigNotFoundError,
7
+ HPCToolsError,
8
+ JobNotFoundError,
9
+ SchedulerError,
10
+ SubmissionError,
11
+ ValidationError,
12
+ )
13
+ from .job_info import JobInfo
14
+ from .result import ArrayJobResult, JobResult, JobStatus
15
+
16
+ __all__ = [
17
+ # Exceptions
18
+ "AccountingNotAvailable",
19
+ "ConfigError",
20
+ "ConfigNotFoundError",
21
+ "HPCToolsError",
22
+ "JobNotFoundError",
23
+ "SchedulerError",
24
+ "SubmissionError",
25
+ "ValidationError",
26
+ # Types
27
+ "JobInfo",
28
+ "JobResult",
29
+ "ArrayJobResult",
30
+ "JobStatus",
31
+ ]