richqueue 1.10__tar.gz → 1.11__tar.gz

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.
@@ -0,0 +1,116 @@
1
+ Metadata-Version: 2.4
2
+ Name: richqueue
3
+ Version: 1.11
4
+ Summary: RichQueue
5
+ Project-URL: Homepage, https://github.com/mwinokan/RichQueue/issues
6
+ Project-URL: Bug Tracker, https://github.com/mwinokan/RichQueue/issues
7
+ Author-email: Max Winokan <max@winokan.com>
8
+ License-File: LICENSE
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: >=3.10
13
+ Requires-Dist: pandas
14
+ Requires-Dist: rich
15
+ Requires-Dist: typer
16
+ Description-Content-Type: text/markdown
17
+
18
+ # RichQueue
19
+
20
+ > 💰 RichQueue: A colourful and pythonic SLURM queue viewer
21
+
22
+ Installation from a modern python environment (>3.10) should be as simple as:
23
+
24
+ ```shell
25
+
26
+ pip install --upgrade richqueue
27
+ ```
28
+
29
+ To see your live-updating SLURM queue:
30
+
31
+ ```shell
32
+ rq
33
+ ```
34
+
35
+ ![queue_example](https://github.com/user-attachments/assets/d99ca5e9-7675-4853-ab28-2d7b4da855f2)
36
+
37
+ ## Other `rq` options
38
+
39
+ To see more detail:
40
+
41
+ ```rq --long```
42
+
43
+ To see someone else's queue:
44
+
45
+ ```rq --user USER```
46
+
47
+ To see the last `X` weeks history:
48
+
49
+ ```rq --hist X```
50
+
51
+ To see history for a given time period, e.g.:
52
+
53
+ ```rq --hist '3 days'```
54
+
55
+ To list available nodes on the cluster:
56
+
57
+ ```rq --idle```
58
+
59
+ To show a static view:
60
+
61
+ ```rq --no-loop```
62
+
63
+ ## Monitoring log files
64
+
65
+ If you keep your SLURM log files in a specific directory exported as the `LOGS` variable, you can use RichQueue to monitor results as they come in with
66
+
67
+ ```
68
+ res <JOB_ID>
69
+ ```
70
+
71
+ <img width="569" height="737" alt="Screenshot 2025-11-20 at 09 05 48" src="https://github.com/user-attachments/assets/1b0f6457-d769-4a1a-b331-bb7f121b5864" />
72
+
73
+ ## Changing to the working directory of a job
74
+
75
+ To change to the submission/working directory of a job:
76
+
77
+ ```
78
+ cd $(res --dir <JOB_ID>)
79
+ ```
80
+
81
+ You might find it convenient to add a shortcut to this to your login profile, e.g. `.bashrc_user` or `.bash_profile`:
82
+
83
+ ```
84
+ res --install-jd >> ~/.bashrc_user
85
+ source ~/.bashrc_user
86
+ ```
87
+
88
+ This will make the `jd` executable available. Change to the directory of the single active or most recently submitted job:
89
+
90
+ ```
91
+ jd
92
+ ```
93
+
94
+ Change to a specific job's directory:
95
+
96
+ ```
97
+ jd <JOB_ID>
98
+ ```
99
+
100
+ ## Submitting SLURM jobs
101
+
102
+ RichQueue also provides a convenient wrapper to the `sbatch` command which makes sure your log files end up in the directory specified by the `LOGS` variable, and prints a pretty summary of the submitted job. Just replace `sbatch` with `sb`. E.g.:
103
+
104
+ ```
105
+ sb --job-name test script.sh --script-arg1
106
+ ```
107
+
108
+ Would run this under the hood:
109
+
110
+ ```
111
+ sbatch --output=$LOGS/%j.log --error=$LOGS/%j.log --job-name test script.sh --script-arg1
112
+ ```
113
+
114
+ and display the following:
115
+
116
+ <img width="474" height="255" alt="Screenshot 2025-11-20 at 10 37 19" src="https://github.com/user-attachments/assets/4464f813-841a-45bd-864e-00dd3dabf924" />
@@ -0,0 +1,99 @@
1
+ # RichQueue
2
+
3
+ > 💰 RichQueue: A colourful and pythonic SLURM queue viewer
4
+
5
+ Installation from a modern python environment (>3.10) should be as simple as:
6
+
7
+ ```shell
8
+
9
+ pip install --upgrade richqueue
10
+ ```
11
+
12
+ To see your live-updating SLURM queue:
13
+
14
+ ```shell
15
+ rq
16
+ ```
17
+
18
+ ![queue_example](https://github.com/user-attachments/assets/d99ca5e9-7675-4853-ab28-2d7b4da855f2)
19
+
20
+ ## Other `rq` options
21
+
22
+ To see more detail:
23
+
24
+ ```rq --long```
25
+
26
+ To see someone else's queue:
27
+
28
+ ```rq --user USER```
29
+
30
+ To see the last `X` weeks history:
31
+
32
+ ```rq --hist X```
33
+
34
+ To see history for a given time period, e.g.:
35
+
36
+ ```rq --hist '3 days'```
37
+
38
+ To list available nodes on the cluster:
39
+
40
+ ```rq --idle```
41
+
42
+ To show a static view:
43
+
44
+ ```rq --no-loop```
45
+
46
+ ## Monitoring log files
47
+
48
+ If you keep your SLURM log files in a specific directory exported as the `LOGS` variable, you can use RichQueue to monitor results as they come in with
49
+
50
+ ```
51
+ res <JOB_ID>
52
+ ```
53
+
54
+ <img width="569" height="737" alt="Screenshot 2025-11-20 at 09 05 48" src="https://github.com/user-attachments/assets/1b0f6457-d769-4a1a-b331-bb7f121b5864" />
55
+
56
+ ## Changing to the working directory of a job
57
+
58
+ To change to the submission/working directory of a job:
59
+
60
+ ```
61
+ cd $(res --dir <JOB_ID>)
62
+ ```
63
+
64
+ You might find it convenient to add a shortcut to this to your login profile, e.g. `.bashrc_user` or `.bash_profile`:
65
+
66
+ ```
67
+ res --install-jd >> ~/.bashrc_user
68
+ source ~/.bashrc_user
69
+ ```
70
+
71
+ This will make the `jd` executable available. Change to the directory of the single active or most recently submitted job:
72
+
73
+ ```
74
+ jd
75
+ ```
76
+
77
+ Change to a specific job's directory:
78
+
79
+ ```
80
+ jd <JOB_ID>
81
+ ```
82
+
83
+ ## Submitting SLURM jobs
84
+
85
+ RichQueue also provides a convenient wrapper to the `sbatch` command which makes sure your log files end up in the directory specified by the `LOGS` variable, and prints a pretty summary of the submitted job. Just replace `sbatch` with `sb`. E.g.:
86
+
87
+ ```
88
+ sb --job-name test script.sh --script-arg1
89
+ ```
90
+
91
+ Would run this under the hood:
92
+
93
+ ```
94
+ sbatch --output=$LOGS/%j.log --error=$LOGS/%j.log --job-name test script.sh --script-arg1
95
+ ```
96
+
97
+ and display the following:
98
+
99
+ <img width="474" height="255" alt="Screenshot 2025-11-20 at 10 37 19" src="https://github.com/user-attachments/assets/4464f813-841a-45bd-864e-00dd3dabf924" />
@@ -3,7 +3,7 @@ requires = ["hatchling"]
3
3
  build-backend = "hatchling.build"
4
4
  [project]
5
5
  name = "richqueue"
6
- version = "1.10"
6
+ version = "1.11"
7
7
  authors = [
8
8
  { name = "Max Winokan", email = "max@winokan.com" },
9
9
  ]
@@ -32,3 +32,4 @@ include = [
32
32
  [project.scripts]
33
33
  rq = "richqueue.main:main"
34
34
  res = "richqueue.log:main"
35
+ sb = "richqueue.submit:main"
@@ -0,0 +1,59 @@
1
+ from .slurm import combined_df
2
+
3
+
4
+ def guess_current_job(user: str, **kwargs):
5
+
6
+ global JOB_DF
7
+ JOB_DF = combined_df(user=user, **kwargs)
8
+
9
+ assert len(JOB_DF), "No jobs found"
10
+
11
+ pending = JOB_DF[JOB_DF["job_state"] == "PENDING"]
12
+ running = JOB_DF[JOB_DF["job_state"] == "RUNNING"]
13
+ history = JOB_DF[~JOB_DF["job_state"].isin(["PENDING", "RUNNING"])]
14
+
15
+ # pick the single active job
16
+ if len(running) == 1:
17
+ return running.iloc[0]
18
+
19
+ # pick the most recently submitted active job
20
+ elif len(running) > 1:
21
+ return running.sort_values(by="submit_time", ascending=False).iloc[0]
22
+
23
+ # pick the single pending job
24
+ elif len(pending) == 1:
25
+ return pending.iloc[0]
26
+
27
+ # pick the most recently submitted pending job
28
+ elif len(pending) > 1:
29
+ return pending.sort_values(by="submit_time", ascending=False).iloc[0]
30
+
31
+ # pick the most recently submitted pending job
32
+ else:
33
+ return pending.sort_values(by="submit_time", ascending=False).iloc[0]
34
+
35
+ return job
36
+
37
+
38
+ def get_specific_job(job: int, user: str, **kwargs):
39
+
40
+ global JOB_DF
41
+ JOB_DF = combined_df(user=user, **kwargs)
42
+
43
+ assert len(JOB_DF), "No jobs found"
44
+
45
+ matches = JOB_DF[JOB_DF["job_id"] == job]
46
+
47
+ if not len(matches):
48
+ console.print("[red bold]Could not find job with ID {job_id=}")
49
+ return None
50
+
51
+ elif len(matches) > 1:
52
+
53
+ matches = matches[matches["command"].notna()]
54
+
55
+ if len(matches) > 1:
56
+ console.print(matches.to_dict(orient="records"))
57
+ raise ValueError("Multiple job matches")
58
+
59
+ return matches.iloc[0]
@@ -1,3 +1,4 @@
1
+ import sys
1
2
  import time
2
3
 
3
4
  from rich.live import Live
@@ -9,9 +10,10 @@ from typer import Typer, Argument, Option
9
10
 
10
11
  from .console import console
11
12
  from .layout import dual_layout
12
- from .slurm import combined_df, get_user, PANEL_PADDING
13
+ from .slurm import get_user, PANEL_PADDING
13
14
  from .table import job_table, log_table
14
15
  from .tools import curry
16
+ from .job import guess_current_job, get_specific_job
15
17
 
16
18
  # access logs
17
19
 
@@ -31,12 +33,27 @@ def show_log(
31
33
  help="Query jobs for another user",
32
34
  ),
33
35
  ] = None,
34
- long: Annotated[bool, Option("-v", "--long", help="More detailed output")] = False,
35
- wisdom: Annotated[
36
- bool, Option("-w", "--wisdom", help="Show a confucius quote on exit")
36
+ cwd: Annotated[
37
+ bool,
38
+ Option("-d", "--dir", help="Print job's current working directory and exit"),
39
+ ] = False,
40
+ install_jd: Annotated[
41
+ bool, Option("--install-jd", help="Print function to create jd wrapper")
37
42
  ] = False,
43
+ long: Annotated[bool, Option("-v", "--long", help="More detailed output")] = False,
38
44
  ):
39
45
 
46
+ if install_jd:
47
+ print(
48
+ """
49
+ jd() {
50
+ target="$(res --dir "$@")"
51
+ cd "$target"
52
+ }
53
+ """
54
+ )
55
+ sys.exit(0)
56
+
40
57
  if user is None:
41
58
  user = get_user()
42
59
 
@@ -55,8 +72,19 @@ def show_log(
55
72
  job = job_getter()
56
73
 
57
74
  if job is None:
75
+ print("Could not get job")
58
76
  return None
59
77
 
78
+ if cwd:
79
+ cwd = job.get("current_working_directory")
80
+
81
+ if not cwd:
82
+ print("Could not get job's 'current_working_directory'")
83
+ sys.exit(1)
84
+
85
+ print(cwd)
86
+ sys.exit(0)
87
+
60
88
  layout_func = curry(dual_layout, log_layout_pair)
61
89
 
62
90
  layout = layout_func(job_getter=job_getter, long=long)
@@ -76,10 +104,6 @@ def show_log(
76
104
  time.sleep(1)
77
105
  except KeyboardInterrupt:
78
106
  live.stop()
79
- if wisdom:
80
- from .wisdom import print_random_quote
81
-
82
- print_random_quote()
83
107
 
84
108
 
85
109
  def log_layout_pair(job_getter, **kwargs):
@@ -102,64 +126,6 @@ def log_layout_pair(job_getter, **kwargs):
102
126
  return upper, lower
103
127
 
104
128
 
105
- def guess_current_job(user: str, **kwargs):
106
-
107
- global JOB_DF
108
- JOB_DF = combined_df(user=user, **kwargs)
109
-
110
- assert len(JOB_DF), "No jobs found"
111
-
112
- pending = JOB_DF[JOB_DF["job_state"] == "PENDING"]
113
- running = JOB_DF[JOB_DF["job_state"] == "RUNNING"]
114
- history = JOB_DF[~JOB_DF["job_state"].isin(["PENDING", "RUNNING"])]
115
-
116
- # pick the single active job
117
- if len(running) == 1:
118
- return running.iloc[0]
119
-
120
- # pick the most recently submitted active job
121
- elif len(running) > 1:
122
- return running.sort_values(by="submit_time", ascending=False).iloc[0]
123
-
124
- # pick the single pending job
125
- elif len(pending) == 1:
126
- return pending.iloc[0]
127
-
128
- # pick the most recently submitted pending job
129
- elif len(pending) > 1:
130
- return pending.sort_values(by="submit_time", ascending=False).iloc[0]
131
-
132
- # pick the most recently submitted pending job
133
- else:
134
- return pending.sort_values(by="submit_time", ascending=False).iloc[0]
135
-
136
- return job
137
-
138
-
139
- def get_specific_job(job: int, user: str, **kwargs):
140
-
141
- global JOB_DF
142
- JOB_DF = combined_df(user=user, **kwargs)
143
-
144
- assert len(JOB_DF), "No jobs found"
145
-
146
- matches = JOB_DF[JOB_DF["job_id"] == job]
147
-
148
- if not len(matches):
149
- console.print("[red bold]Could not find job with ID {job_id=}")
150
- return None
151
-
152
- elif len(matches) > 1:
153
-
154
- matches = matches[matches["command"].notna()]
155
-
156
- if len(matches) > 1:
157
- console.print(matches.to_dict(orient="records"))
158
- raise ValueError("Multiple job matches")
159
-
160
- return matches.iloc[0]
161
-
162
-
163
129
  def main():
164
130
  app()
165
131
 
@@ -123,21 +123,6 @@ def show(
123
123
  console.print(f"[red bold]{e}[reset]")
124
124
 
125
125
 
126
- @app.command()
127
- def log(job: Annotated[int, Option(help="Show logs from a specific job")]):
128
- raise NotImplementedError
129
-
130
-
131
- @app.command()
132
- def dir(job: Annotated[int, Option(help="Get job directory")]):
133
- raise NotImplementedError
134
-
135
-
136
- @app.command()
137
- def nickname():
138
- raise NotImplementedError
139
-
140
-
141
126
  def parse_history_string(hist: str, unit: str = "week") -> (int, str):
142
127
  """Parse one or two part string into integer value and string unit"""
143
128
 
@@ -0,0 +1,54 @@
1
+ import sys
2
+ import subprocess
3
+ from os import environ
4
+ from pathlib import Path
5
+
6
+ from .main import show
7
+ from .console import console
8
+
9
+
10
+ def submit():
11
+
12
+ log_dir = environ.get("LOGS")
13
+
14
+ if not log_dir:
15
+ console.print("[red bold]LOGS variable not set")
16
+ sys.exit(1)
17
+
18
+ log_dir = Path(log_dir)
19
+
20
+ sbatch_args = sys.argv[1:]
21
+
22
+ commands = [
23
+ "sbatch",
24
+ "--output=" f"{log_dir.resolve()}/%j.log",
25
+ "--error=" f"{log_dir.resolve()}/%j.log",
26
+ *sbatch_args,
27
+ ]
28
+
29
+ print(" ".join(commands))
30
+
31
+ x = subprocess.run(
32
+ commands, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE
33
+ )
34
+
35
+ if x.returncode != 0:
36
+ console.print(f"[red bold]{x.stderr.decode().strip()}")
37
+ sys.exit(1)
38
+
39
+ message = x.stdout.decode().strip()
40
+
41
+ job_id = int(message.split()[-1])
42
+
43
+ console.print(f"[bold green]{message}:")
44
+
45
+ show(job=job_id, no_loop=True, hist=None)
46
+ sys.exit(0)
47
+
48
+
49
+ def main():
50
+ submit()
51
+
52
+
53
+ if __name__ == "__main__":
54
+ main()
richqueue-1.10/PKG-INFO DELETED
@@ -1,61 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: richqueue
3
- Version: 1.10
4
- Summary: RichQueue
5
- Project-URL: Homepage, https://github.com/mwinokan/RichQueue/issues
6
- Project-URL: Bug Tracker, https://github.com/mwinokan/RichQueue/issues
7
- Author-email: Max Winokan <max@winokan.com>
8
- License-File: LICENSE
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Operating System :: OS Independent
11
- Classifier: Programming Language :: Python :: 3
12
- Requires-Python: >=3.10
13
- Requires-Dist: pandas
14
- Requires-Dist: rich
15
- Requires-Dist: typer
16
- Description-Content-Type: text/markdown
17
-
18
- # RichQueue
19
-
20
- > 💰 RichQueue: A colourful and pythonic SLURM queue viewer
21
-
22
- Installation from a modern python environment (>3.10) should be as simple as:
23
-
24
- ```shell
25
-
26
- pip install --upgrade richqueue
27
- ```
28
-
29
- To see your live-updating SLURM queue:
30
-
31
- ```shell
32
- rq
33
- ```
34
-
35
- ![queue_example](https://github.com/user-attachments/assets/d99ca5e9-7675-4853-ab28-2d7b4da855f2)
36
-
37
- ## Other options
38
-
39
- To see more detail:
40
-
41
- ```rq --long```
42
-
43
- To see someone else's queue:
44
-
45
- ```rq --user USER```
46
-
47
- To see the last `X` weeks history:
48
-
49
- ```rq --hist X```
50
-
51
- To see history for a given time period, e.g.:
52
-
53
- ```rq --hist '3 days'```
54
-
55
- To list available nodes on the cluster:
56
-
57
- ```rq --idle```
58
-
59
- To show a static view:
60
-
61
- ```rq --no-loop```
richqueue-1.10/README.md DELETED
@@ -1,44 +0,0 @@
1
- # RichQueue
2
-
3
- > 💰 RichQueue: A colourful and pythonic SLURM queue viewer
4
-
5
- Installation from a modern python environment (>3.10) should be as simple as:
6
-
7
- ```shell
8
-
9
- pip install --upgrade richqueue
10
- ```
11
-
12
- To see your live-updating SLURM queue:
13
-
14
- ```shell
15
- rq
16
- ```
17
-
18
- ![queue_example](https://github.com/user-attachments/assets/d99ca5e9-7675-4853-ab28-2d7b4da855f2)
19
-
20
- ## Other options
21
-
22
- To see more detail:
23
-
24
- ```rq --long```
25
-
26
- To see someone else's queue:
27
-
28
- ```rq --user USER```
29
-
30
- To see the last `X` weeks history:
31
-
32
- ```rq --hist X```
33
-
34
- To see history for a given time period, e.g.:
35
-
36
- ```rq --hist '3 days'```
37
-
38
- To list available nodes on the cluster:
39
-
40
- ```rq --idle```
41
-
42
- To show a static view:
43
-
44
- ```rq --no-loop```
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes