Skip to content

Commit 5c70ea3

Browse files
committed
refactor; change debug lines to print full expanded hostname instead of alias
1 parent 42f1d21 commit 5c70ea3

File tree

6 files changed

+130
-128
lines changed

6 files changed

+130
-128
lines changed

pybs/console/__init__.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
2+
from rich.theme import Theme
3+
custom_theme = Theme(
4+
{
5+
"progress.description": "yellow bold",
6+
}
7+
)
8+
9+
10+
def _log_formatter(
11+
record: dict,
12+
icon: bool = False,
13+
) -> str:
14+
"""Log message formatter"""
15+
color_map = {
16+
"TRACE": "dim blue",
17+
"DEBUG": "cyan",
18+
"INFO": "bold",
19+
"SUCCESS": "bold green",
20+
"WARNING": "yellow",
21+
"ERROR": "bold red",
22+
"CRITICAL": "bold white on red",
23+
}
24+
lvl_color = color_map.get(record["level"].name, "cyan")
25+
26+
if icon:
27+
icon = "{level.icon}"
28+
else:
29+
icon = ""
30+
return (
31+
"[not bold green]{time:YYYY/MM/DD HH:mm:ss}[/not bold green] |"
32+
+ f"{icon} - [{lvl_color}]{{message}}[/{lvl_color}]"
33+
# Right-align code location:
34+
+ " [dim]{name}:{function}:{line}[/dim]"
35+
)

pybs/console/remote/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,35 @@
1111
[09:26:12.364] stderr> OpenSSH_9.8p1, LibreSSL 3.3.6
1212
1313
```
14+
15+
16+
DONE:
17+
- add tab autocompletion scripts
18+
- add hostname tab completion (use ~/.ssh/config)
19+
- fix logging for qsub wait
20+
- add TUI timer for job submission
21+
- add intelligent remote server expansion of paths example $SCRATCH
22+
to avoid issue where VS code does not evaluate $ variables correctly.
23+
- add support for local job scripts
24+
25+
TODO:
26+
- change character width on subcommand tab complete suggestions
27+
- automatically close VS code window when Ctrl+C is used to kill job
28+
29+
- add help to ck args
30+
- add tab complete for remote paths similar to `scp```
31+
- add auto install of ssh config required hostname alias
32+
- add arbitrary command execution for any method (with certain decorator)
33+
from PBSServer class
34+
e.g. write `qsub` and this will call the `qsub` method of the PBSServer class
35+
if a method with that name exists.
36+
- refactoring of PBSServer class to use `ssh_command` decorator
37+
- add `config` command to add config items to a config file
38+
such as turning debug on/off
39+
40+
Future TODO:
41+
- add db for currently running jobs, able to login to
42+
any server and see resources, walltime etc.
43+
- add "autorefresh" or "keepalive" option to remember when the walltime will
44+
expire, and request another GPU node that overlaps so we can keep the session logged
45+
in on the same node.

pybs/console/remote/code.py

Lines changed: 20 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -12,57 +12,14 @@
1212
from rich.progress import Progress, TimeElapsedColumn, SpinnerColumn, TextColumn
1313
from rich.console import Console, Group
1414
from rich.logging import RichHandler
15-
import rich.style
1615
from rich.live import Live
17-
from rich.theme import Theme
18-
19-
custom_theme = Theme(
20-
{
21-
"progress.description": "yellow bold",
22-
}
23-
)
24-
25-
26-
JOB_STATUS_DICT = {
27-
"C": "Completed",
28-
"E": "Exiting",
29-
"H": "Held",
30-
"Q": "Queued",
31-
"R": "Running",
32-
"T": "Moving",
33-
"W": "Waiting",
34-
"S": "Suspended",
35-
"B": "Batch",
36-
}
37-
38-
39-
def _log_formatter(
40-
record: dict,
41-
icon: bool = False,
42-
) -> str:
43-
"""Log message formatter"""
44-
color_map = {
45-
"TRACE": "dim blue",
46-
"DEBUG": "cyan",
47-
"INFO": "bold",
48-
"SUCCESS": "bold green",
49-
"WARNING": "yellow",
50-
"ERROR": "bold red",
51-
"CRITICAL": "bold white on red",
52-
}
53-
lvl_color = color_map.get(record["level"].name, "cyan")
54-
55-
if icon:
56-
icon = "{level.icon}"
57-
else:
58-
icon = ""
59-
return (
60-
"[not bold green]{time:YYYY/MM/DD HH:mm:ss}[/not bold green] |"
61-
+ f"{icon} - [{lvl_color}]{{message}}[/{lvl_color}]"
62-
# Right-align code location:
63-
+ " [dim]{name}:{function}:{line}[/dim]"
64-
)
16+
from rich.progress import Progress, ProgressColumn, Text
6517

18+
from pybs.constants import JOB_STATUS_DICT, POLL_INTERVAL
19+
from pybs.console import custom_theme, _log_formatter
20+
from pybs.server import PBSServer
21+
from pybs.console.tabcomplete import complete_remote_path, complete_hostname, complete_job_script
22+
from pybs.console.ui import CompactTimeColumn
6623

6724
console = Console(
6825
theme=custom_theme,
@@ -86,66 +43,6 @@ def _log_formatter(
8643
level=level,
8744
)
8845

89-
90-
from pybs.server import PBSServer
91-
from pybs.console.tabcomplete import complete_remote_path, complete_hostname, complete_job_script
92-
93-
POLL_INTERVAL = 0.5
94-
95-
96-
# DONE:
97-
# - add tab autocompletion scripts
98-
# - add hostname tab completion (use ~/.ssh/config)
99-
# - fix logging for qsub wait
100-
# - add TUI timer for job submission
101-
# - add intelligent remote server expansion of paths example $SCRATCH
102-
# to avoid issue where VS code does not evaluate $ variables correctly.
103-
# - add support for local job scripts
104-
105-
# TODO:
106-
# - change character width on subcommand tab complete suggestions
107-
108-
# - add help to ck args
109-
# - add tab complete for remote paths similar to `scp```
110-
# - add auto install of ssh config required hostname alias
111-
# - add arbitrary command execution for any method (with certain decorator)
112-
# from PBSServer class
113-
# e.g. write `qsub` and this will call the `qsub` method of the PBSServer class
114-
# if a method with that name exists.
115-
# - refactoring of PBSServer class to use `ssh_command` decorator
116-
# - add `config` command to add config items to a config file
117-
# such as turning debug on/off
118-
119-
# Future TODO:
120-
# - add db for currently running jobs, able to login to
121-
# any server and see resources, walltime etc.
122-
# - add "autorefresh" or "keepalive" option to remember when the walltime will
123-
# expire, and request another GPU node that overlaps so we can keep the session logged
124-
# in on the same node.
125-
126-
from rich.progress import Progress, ProgressColumn, Text
127-
from datetime import timedelta
128-
129-
130-
# TODO: make PR for this?
131-
class CompactTimeColumn(ProgressColumn):
132-
"""Renders time elapsed."""
133-
134-
def render(self, task: "Task") -> Text:
135-
"""Show time elapsed."""
136-
elapsed = task.finished_time if task.finished else task.elapsed
137-
if elapsed is None:
138-
return Text("-:--:--", style="progress.elapsed")
139-
delta = timedelta(seconds=max(0, elapsed))
140-
# get number of seconds
141-
n_seconds = delta.total_seconds()
142-
# customise progress.elapsed to be gray text
143-
# style = "progress.elapsed"
144-
# style = "grey58"
145-
style = "white"
146-
return Text(f"({n_seconds:.1f}s)", style=style)
147-
148-
14946
@ck.command()
15047
@ck.argument(
15148
"hostname",
@@ -204,9 +101,11 @@ def code(
204101
log.debug(f"Launching job on {hostname} with remote path {remote_path}")
205102
log.debug(f"Job script location: {job_script_location}")
206103

104+
207105
if job_script_location is None:
208106
log.info(f"Checking if job script {job_script} exists...")
209107
if job_script.is_file():
108+
job_script = job_script.resolve()
210109
log.info(f"Using local job script: {job_script}")
211110
job_script_location = "local"
212111
else:
@@ -218,18 +117,11 @@ def code(
218117
progress = Progress(
219118
SpinnerColumn(
220119
spinner_name="line",
221-
# spinner_name="simpleDots",
222-
# spinner_name="simpleDotsScrolling",
223120
style="blue",
224121
),
225122
TextColumn("[progress.description]{task.description}", style="blue"),
226-
# SpinnerColumn(
227-
# spinner_name="simpleDotsScrolling",
228-
# style="blue",
229-
# ),
230123
CompactTimeColumn(),
231124
)
232-
233125
monitor_job_status = Progress(
234126
SpinnerColumn(spinner_name="dots", style="white"),
235127
TextColumn(
@@ -239,30 +131,27 @@ def code(
239131
""",
240132
#style="blink bold black on yellow",
241133
),
242-
# TextColumn("[progress.description]{task.fields[job_status]}"),
243-
# TextColumn("[progress.description]Node: {task.fields[node]}"),
244-
# CompactTimeColumn(), # show 1dp of seconds (elapsed time)
245134
)
246135

247136
import time
248-
249137
# If remote, check if the file exists on the remote server
250138
server = PBSServer(hostname, verbose=verbose)
139+
hostname_expanded = server.full_remotehost
251140
if job_script_location == "remote":
252141
with progress:
253142
task1 = progress.add_task(
254-
f"Checking job script on [bold][white]{hostname}[/white][/bold] exists... ",
143+
f"Checking job script on [bold][white]{hostname_expanded}[/white][/bold] exists... ",
255144
total=1,
256145
)
257146
# expand remote path
258147
log.info(f"Expanding remote path {job_script}")
259148
job_script = server.expand_remote_path(job_script)
260149
log.info(f"--> {job_script}")
261150
if not server.check_file_exists(job_script):
262-
log.error(f"Job script {job_script} not found on {hostname}. Exiting.")
151+
log.error(f"Job script {job_script} not found on {hostname_expanded}. Exiting.")
263152
return
264153
else:
265-
log.info(f"Job script found on {hostname}.")
154+
log.info(f"Job script found on {hostname_expanded}.")
266155
# mark progress as complete
267156
progress.update(task1, completed=True)
268157

@@ -285,7 +174,7 @@ def code(
285174
# Expand path
286175
with progress:
287176
task = progress.add_task(
288-
f"Expanding remote path on [bold][white]{hostname}[/white][/bold]... ",
177+
f"Expanding remote path on [bold][white]{hostname_expanded}[/white][/bold]... ",
289178
total=1,
290179
)
291180
log.info(f"Expanding remote path {remote_path}")
@@ -301,16 +190,16 @@ def code(
301190
else:
302191
with progress:
303192
task1 = progress.add_task(
304-
f"Checking that workspace directory on [bold][white]{hostname}[/white][/bold] exists... ",
193+
f"Checking that workspace directory on [bold][white]{hostname_expanded}[/white][/bold] exists... ",
305194
total=1,
306195
)
307196
if not server.check_dir_exists(remote_path):
308197
log.error(
309-
f"Remote path {remote_path} not found on {hostname}. Exiting."
198+
f"Remote path {remote_path} not found on {hostname_expanded}. Exiting."
310199
)
311200
return
312201
else:
313-
log.info(f"Remote path {remote_path} found on {hostname}.")
202+
log.info(f"Remote path {remote_path} found on {hostname_expanded}.")
314203
# mark progress as complete
315204
progress.update(task1, completed=True)
316205

@@ -439,7 +328,10 @@ def code(
439328
# Clear all tasks from progress
440329
ids = progress.task_ids
441330
for task_id in ids:
442-
progress.remove_task(task_id)
331+
progress.remove_task(task_id)
332+
333+
for task_id in monitor_job_status.task_ids:
334+
monitor_job_status.remove_task(task_id)
443335

444336
progress_group = Group(
445337
progress,

pybs/console/ui/__init__.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""Custom UI elements for CLI console."""
2+
3+
from rich.console import Console
4+
from rich.progress import Progress, ProgressColumn, Text
5+
from rich.text import Text
6+
from datetime import timedelta
7+
8+
9+
# TODO: make PR for this?
10+
class CompactTimeColumn(ProgressColumn):
11+
"""Renders time elapsed."""
12+
13+
def render(self, task: "Task") -> Text:
14+
"""Show time elapsed."""
15+
elapsed = task.finished_time if task.finished else task.elapsed
16+
if elapsed is None:
17+
return Text("-:--:--", style="progress.elapsed")
18+
delta = timedelta(seconds=max(0, elapsed))
19+
# get number of seconds
20+
n_seconds = delta.total_seconds()
21+
# customise progress.elapsed to be gray text
22+
# style = "progress.elapsed"
23+
# style = "grey58"
24+
style = "white"
25+
return Text(f"({n_seconds:.1f}s)", style=style)

pybs/constants/__init__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"""Constants for PyBS."""
2+
3+
POLL_INTERVAL = 0.5
4+
5+
JOB_STATUS_DICT = {
6+
"C": "Completed",
7+
"E": "Exiting",
8+
"H": "Held",
9+
"Q": "Queued",
10+
"R": "Running",
11+
"T": "Moving",
12+
"W": "Waiting",
13+
"S": "Suspended",
14+
"B": "Batch",
15+
}

pybs/server/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ def __init__(
5454
), f"Specified hostname '{remotehost}' not found in ssh config"
5555
username = c.host(remotehost)["user"]
5656
self.username = username
57+
self.remotehost = remotehost
58+
self.address = c.host(remotehost)["hostname"]
59+
self.full_remotehost = f"{self.username}@{self.address}"
5760

5861
# log info using pretty colours for username
5962

0 commit comments

Comments
 (0)