cmd-queue 0.1.20__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.
Potentially problematic release.
This version of cmd-queue might be problematic. Click here for more details.
- cmd_queue/__init__.py +1 -1
- cmd_queue/base_queue.py +5 -0
- cmd_queue/main.py +4 -1
- cmd_queue/serial_queue.py +56 -24
- cmd_queue/slurm_queue.py +114 -22
- cmd_queue/slurmify.py +116 -0
- cmd_queue/tmux_queue.py +34 -0
- cmd_queue-0.2.1.dist-info/METADATA +746 -0
- {cmd_queue-0.1.20.dist-info → cmd_queue-0.2.1.dist-info}/RECORD +13 -12
- {cmd_queue-0.1.20.dist-info → cmd_queue-0.2.1.dist-info}/WHEEL +1 -1
- cmd_queue-0.1.20.dist-info/METADATA +0 -652
- {cmd_queue-0.1.20.dist-info → cmd_queue-0.2.1.dist-info}/LICENSE +0 -0
- {cmd_queue-0.1.20.dist-info → cmd_queue-0.2.1.dist-info}/entry_points.txt +0 -0
- {cmd_queue-0.1.20.dist-info → cmd_queue-0.2.1.dist-info}/top_level.txt +0 -0
cmd_queue/__init__.py
CHANGED
cmd_queue/base_queue.py
CHANGED
|
@@ -135,6 +135,11 @@ class Queue(ub.NiceRepr):
|
|
|
135
135
|
name = kwargs.get('name', None)
|
|
136
136
|
if name is None:
|
|
137
137
|
name = kwargs['name'] = self.name + '-job-{}'.format(self.num_real_jobs)
|
|
138
|
+
|
|
139
|
+
# TODO: make sure name is path safe.
|
|
140
|
+
if ':' in name:
|
|
141
|
+
raise ValueError('Name must be path-safe')
|
|
142
|
+
|
|
138
143
|
if self.all_depends:
|
|
139
144
|
depends = kwargs.get('depends', None)
|
|
140
145
|
if depends is None:
|
cmd_queue/main.py
CHANGED
|
@@ -94,12 +94,15 @@ class CommonShowRun(CommonConfig):
|
|
|
94
94
|
|
|
95
95
|
backend = scfg.Value('tmux', help='the execution backend to use', choices=['tmux', 'slurm', 'serial', 'airflow'])
|
|
96
96
|
|
|
97
|
+
gpus = scfg.Value(None, help='a comma separated list of the gpu numbers to spread across. tmux backend only.')
|
|
98
|
+
|
|
97
99
|
def _build_queue(config):
|
|
98
100
|
import cmd_queue
|
|
99
101
|
import json
|
|
100
102
|
queue = cmd_queue.Queue.create(size=max(1, config['workers']),
|
|
101
103
|
backend=config['backend'],
|
|
102
|
-
name=config['qname']
|
|
104
|
+
name=config['qname'],
|
|
105
|
+
gpus=config['gpus'])
|
|
103
106
|
# Run a new CLI queue
|
|
104
107
|
data = json.loads(config.cli_queue_fpath.read_text())
|
|
105
108
|
print('data = {}'.format(ub.urepr(data, nl=1)))
|
cmd_queue/serial_queue.py
CHANGED
|
@@ -9,30 +9,6 @@ from cmd_queue import base_queue
|
|
|
9
9
|
from cmd_queue.util import util_tags
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
def indent(text, prefix=' '):
|
|
13
|
-
r"""
|
|
14
|
-
Indents a block of text
|
|
15
|
-
|
|
16
|
-
Args:
|
|
17
|
-
text (str): text to indent
|
|
18
|
-
prefix (str, default = ' '): prefix to add to each line
|
|
19
|
-
|
|
20
|
-
Returns:
|
|
21
|
-
str: indented text
|
|
22
|
-
|
|
23
|
-
>>> from cmd_queue.serial_queue import * # NOQA
|
|
24
|
-
>>> text = ['aaaa', 'bb', 'cc\n dddd\n ef\n']
|
|
25
|
-
>>> text = indent(text)
|
|
26
|
-
>>> print(text)
|
|
27
|
-
>>> text = indent(text)
|
|
28
|
-
>>> print(text)
|
|
29
|
-
"""
|
|
30
|
-
if isinstance(text, (list, tuple)):
|
|
31
|
-
return indent('\n'.join(text), prefix)
|
|
32
|
-
else:
|
|
33
|
-
return prefix + text.replace('\n', '\n' + prefix)
|
|
34
|
-
|
|
35
|
-
|
|
36
12
|
class BashJob(base_queue.Job):
|
|
37
13
|
r"""
|
|
38
14
|
A job meant to run inside of a larger bash file. Analog of SlurmJob
|
|
@@ -115,6 +91,21 @@ class BashJob(base_queue.Job):
|
|
|
115
91
|
self.tags = util_tags.Tags.coerce(tags)
|
|
116
92
|
self.allow_indent = allow_indent
|
|
117
93
|
|
|
94
|
+
def _test_bash_syntax_errors(self):
|
|
95
|
+
"""
|
|
96
|
+
Check for bash syntax errors
|
|
97
|
+
|
|
98
|
+
Example:
|
|
99
|
+
>>> from cmd_queue.serial_queue import * # NOQA
|
|
100
|
+
>>> # Demo full boilerplate for a job with dependencies
|
|
101
|
+
>>> self = BashJob('basd syhi(', name='job1')
|
|
102
|
+
>>> import pytest
|
|
103
|
+
>>> with pytest.raises(SyntaxError):
|
|
104
|
+
>>> self._test_bash_syntax_errors()
|
|
105
|
+
"""
|
|
106
|
+
bash_text = self.finalize_text()
|
|
107
|
+
_check_bash_text_for_syntax_errors(bash_text)
|
|
108
|
+
|
|
118
109
|
def finalize_text(self, with_status=True, with_gaurds=True,
|
|
119
110
|
conditionals=None, **kwargs):
|
|
120
111
|
script = []
|
|
@@ -575,6 +566,10 @@ class SerialQueue(base_queue.Queue):
|
|
|
575
566
|
r"""
|
|
576
567
|
Print info about the commands, optionally with rich
|
|
577
568
|
|
|
569
|
+
Args:
|
|
570
|
+
*args: see :func:`cmd_queue.base_queue.Queue.print_commands`.
|
|
571
|
+
**kwargs: see :func:`cmd_queue.base_queue.Queue.print_commands`.
|
|
572
|
+
|
|
578
573
|
CommandLine:
|
|
579
574
|
xdoctest -m cmd_queue.serial_queue SerialQueue.print_commands
|
|
580
575
|
|
|
@@ -713,3 +708,40 @@ def _bash_json_dump(json_fmt_parts, fpath):
|
|
|
713
708
|
printf_part = 'printf ' + printf_body + ' \\\n ' + printf_args
|
|
714
709
|
dump_code = printf_part + ' \\\n ' + redirect_part
|
|
715
710
|
return dump_code
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
def indent(text, prefix=' '):
|
|
714
|
+
r"""
|
|
715
|
+
Indents a block of text
|
|
716
|
+
|
|
717
|
+
Args:
|
|
718
|
+
text (str): text to indent
|
|
719
|
+
prefix (str, default = ' '): prefix to add to each line
|
|
720
|
+
|
|
721
|
+
Returns:
|
|
722
|
+
str: indented text
|
|
723
|
+
|
|
724
|
+
>>> from cmd_queue.serial_queue import * # NOQA
|
|
725
|
+
>>> text = ['aaaa', 'bb', 'cc\n dddd\n ef\n']
|
|
726
|
+
>>> text = indent(text)
|
|
727
|
+
>>> print(text)
|
|
728
|
+
>>> text = indent(text)
|
|
729
|
+
>>> print(text)
|
|
730
|
+
"""
|
|
731
|
+
if isinstance(text, (list, tuple)):
|
|
732
|
+
return indent('\n'.join(text), prefix)
|
|
733
|
+
else:
|
|
734
|
+
return prefix + text.replace('\n', '\n' + prefix)
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
def _check_bash_text_for_syntax_errors(bash_text):
|
|
738
|
+
import tempfile
|
|
739
|
+
tmpdir = tempfile.TemporaryDirectory()
|
|
740
|
+
with tmpdir:
|
|
741
|
+
dpath = ub.Path(tmpdir.name)
|
|
742
|
+
fpath = dpath / 'job_text.sh'
|
|
743
|
+
fpath.write_text(bash_text)
|
|
744
|
+
info = ub.cmd(['bash', '-nv', fpath])
|
|
745
|
+
if info.returncode != 0:
|
|
746
|
+
print(info.stderr)
|
|
747
|
+
raise SyntaxError('bash syntax error')
|
cmd_queue/slurm_queue.py
CHANGED
|
@@ -41,23 +41,51 @@ from cmd_queue import base_queue # NOQA
|
|
|
41
41
|
from cmd_queue.util import util_tags
|
|
42
42
|
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
try:
|
|
45
|
+
from functools import cache # Python 3.9+ only
|
|
46
|
+
except ImportError:
|
|
47
|
+
from ubelt import memoize as cache
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@cache
|
|
51
|
+
def _unit_registery():
|
|
52
|
+
import sys
|
|
53
|
+
if sys.version_info[0:2] == (3, 9):
|
|
54
|
+
# backwards compatability support for numpy 2.0 and pint on cp39
|
|
55
|
+
try:
|
|
56
|
+
import numpy as np
|
|
57
|
+
except ImportError:
|
|
58
|
+
...
|
|
59
|
+
else:
|
|
60
|
+
if not np.__version__.startswith('1.'):
|
|
61
|
+
np.cumproduct = np.cumprod
|
|
62
|
+
import pint
|
|
63
|
+
reg = pint.UnitRegistry()
|
|
64
|
+
return reg
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _coerce_mem_megabytes(mem):
|
|
45
68
|
"""
|
|
69
|
+
Transform input into an integer representing amount of megabytes.
|
|
70
|
+
|
|
46
71
|
Args:
|
|
47
72
|
mem (int | str): integer number of megabytes or a parseable string
|
|
48
73
|
|
|
74
|
+
Returns:
|
|
75
|
+
int: number of megabytes
|
|
76
|
+
|
|
49
77
|
Example:
|
|
78
|
+
>>> # xdoctest: +REQUIRES(module:pint)
|
|
50
79
|
>>> from cmd_queue.slurm_queue import * # NOQA
|
|
51
|
-
>>> print(
|
|
52
|
-
>>> print(
|
|
53
|
-
>>> print(
|
|
54
|
-
>>> print(
|
|
80
|
+
>>> print(_coerce_mem_megabytes(30602))
|
|
81
|
+
>>> print(_coerce_mem_megabytes('4GB'))
|
|
82
|
+
>>> print(_coerce_mem_megabytes('32GB'))
|
|
83
|
+
>>> print(_coerce_mem_megabytes('300000000 bytes'))
|
|
55
84
|
"""
|
|
56
85
|
if isinstance(mem, int):
|
|
57
86
|
assert mem > 0
|
|
58
87
|
elif isinstance(mem, str):
|
|
59
|
-
|
|
60
|
-
reg = pint.UnitRegistry()
|
|
88
|
+
reg = _unit_registery()
|
|
61
89
|
mem = reg.parse_expression(mem)
|
|
62
90
|
mem = int(mem.to('megabytes').m)
|
|
63
91
|
else:
|
|
@@ -190,6 +218,7 @@ class SlurmJob(base_queue.Job):
|
|
|
190
218
|
Represents a slurm job that hasn't been submitted yet
|
|
191
219
|
|
|
192
220
|
Example:
|
|
221
|
+
>>> # xdoctest: +REQUIRES(module:pint)
|
|
193
222
|
>>> from cmd_queue.slurm_queue import * # NOQA
|
|
194
223
|
>>> self = SlurmJob('python -c print("hello world")', 'hi', cpus=5, gpus=1, mem='10GB')
|
|
195
224
|
>>> command = self._build_sbatch_args()
|
|
@@ -235,17 +264,13 @@ class SlurmJob(base_queue.Job):
|
|
|
235
264
|
return ' \\\n '.join(args)
|
|
236
265
|
|
|
237
266
|
def _build_sbatch_args(self, jobname_to_varname=None):
|
|
238
|
-
# job_name = 'todo'
|
|
239
|
-
# output_fpath = '$HOME/.cache/slurm/logs/job-%j-%x.out'
|
|
240
|
-
# command = "python -c 'import sys; sys.exit(1)'"
|
|
241
|
-
# -c 2 -p priority --gres=gpu:1
|
|
242
267
|
sbatch_args = ['sbatch']
|
|
243
268
|
if self.name:
|
|
244
269
|
sbatch_args.append(f'--job-name="{self.name}"')
|
|
245
270
|
if self.cpus:
|
|
246
271
|
sbatch_args.append(f'--cpus-per-task={self.cpus}')
|
|
247
272
|
if self.mem:
|
|
248
|
-
mem =
|
|
273
|
+
mem = _coerce_mem_megabytes(self.mem)
|
|
249
274
|
sbatch_args.append(f'--mem={mem}')
|
|
250
275
|
if self.gpus and 'gres' not in self._sbatch_kvargs:
|
|
251
276
|
ub.schedule_deprecation(
|
|
@@ -277,7 +302,8 @@ class SlurmJob(base_queue.Job):
|
|
|
277
302
|
|
|
278
303
|
for key, value in self._sbatch_kvargs.items():
|
|
279
304
|
key = key.replace('_', '-')
|
|
280
|
-
|
|
305
|
+
if value is not None:
|
|
306
|
+
sbatch_args.append(f'--{key}="{value}"')
|
|
281
307
|
|
|
282
308
|
for key, flag in self._sbatch_flags.items():
|
|
283
309
|
if flag:
|
|
@@ -342,6 +368,19 @@ class SlurmQueue(base_queue.Queue):
|
|
|
342
368
|
CommandLine:
|
|
343
369
|
xdoctest -m cmd_queue.slurm_queue SlurmQueue
|
|
344
370
|
|
|
371
|
+
Example:
|
|
372
|
+
>>> from cmd_queue.slurm_queue import * # NOQA
|
|
373
|
+
>>> self = SlurmQueue()
|
|
374
|
+
>>> job0 = self.submit('echo "hi from $SLURM_JOBID"')
|
|
375
|
+
>>> job1 = self.submit('echo "hi from $SLURM_JOBID"', depends=[job0])
|
|
376
|
+
>>> job2 = self.submit('echo "hi from $SLURM_JOBID"', depends=[job1])
|
|
377
|
+
>>> job3 = self.submit('echo "hi from $SLURM_JOBID"', depends=[job1, job2])
|
|
378
|
+
>>> self.write()
|
|
379
|
+
>>> self.print_commands()
|
|
380
|
+
>>> # xdoctest: +REQUIRES(--run)
|
|
381
|
+
>>> if not self.is_available():
|
|
382
|
+
>>> self.run()
|
|
383
|
+
|
|
345
384
|
Example:
|
|
346
385
|
>>> from cmd_queue.slurm_queue import * # NOQA
|
|
347
386
|
>>> self = SlurmQueue()
|
|
@@ -384,6 +423,11 @@ class SlurmQueue(base_queue.Queue):
|
|
|
384
423
|
self.unused_kwargs = kwargs
|
|
385
424
|
self.queue_id = name + '-' + stamp + '-' + ub.hash_data(uuid.uuid4())[0:8]
|
|
386
425
|
self.dpath = ub.Path.appdir('cmd_queue/slurm') / self.queue_id
|
|
426
|
+
if 0:
|
|
427
|
+
# hack for submission on different systems, probably dont want to
|
|
428
|
+
# do this.
|
|
429
|
+
self.dpath = self.dpath.shrinkuser(home='$HOME')
|
|
430
|
+
|
|
387
431
|
self.log_dpath = self.dpath / 'logs'
|
|
388
432
|
self.fpath = self.dpath / (self.queue_id + '.sh')
|
|
389
433
|
self.shell = shell
|
|
@@ -395,6 +439,37 @@ class SlurmQueue(base_queue.Queue):
|
|
|
395
439
|
def __nice__(self):
|
|
396
440
|
return self.queue_id
|
|
397
441
|
|
|
442
|
+
@classmethod
|
|
443
|
+
def _slurm_checks(cls):
|
|
444
|
+
status = {}
|
|
445
|
+
info = {}
|
|
446
|
+
info['squeue_fpath'] = ub.find_exe('squeue')
|
|
447
|
+
status['has_squeue'] = bool(info['squeue_fpath'])
|
|
448
|
+
status['slurmd_running'] = False
|
|
449
|
+
import psutil
|
|
450
|
+
for p in psutil.process_iter():
|
|
451
|
+
if p.name() == 'slurmd':
|
|
452
|
+
status['slurmd_running'] = True
|
|
453
|
+
info['slurmd_info'] = {
|
|
454
|
+
'pid': p.pid,
|
|
455
|
+
'name': p.name(),
|
|
456
|
+
'status': p.status(),
|
|
457
|
+
'create_time': p.create_time(),
|
|
458
|
+
}
|
|
459
|
+
break
|
|
460
|
+
status['squeue_working'] = (ub.cmd('squeue')['ret'] == 0)
|
|
461
|
+
|
|
462
|
+
sinfo = ub.cmd('sinfo --json')
|
|
463
|
+
status['sinfo_working'] = False
|
|
464
|
+
if sinfo['ret'] == 0:
|
|
465
|
+
status['sinfo_working'] = True
|
|
466
|
+
import json
|
|
467
|
+
sinfo_out = json.loads(sinfo['out'])
|
|
468
|
+
has_working_nodes = not all(
|
|
469
|
+
node['state'] == 'down'
|
|
470
|
+
for node in sinfo_out['nodes'])
|
|
471
|
+
status['has_working_nodes'] = has_working_nodes
|
|
472
|
+
|
|
398
473
|
@classmethod
|
|
399
474
|
def is_available(cls):
|
|
400
475
|
"""
|
|
@@ -407,15 +482,23 @@ class SlurmQueue(base_queue.Queue):
|
|
|
407
482
|
squeue_working = (ub.cmd('squeue')['ret'] == 0)
|
|
408
483
|
if squeue_working:
|
|
409
484
|
# Check if nodes are available or down
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
485
|
+
# note: the --json command is not available in
|
|
486
|
+
# slurm-wlm 19.05.5, but it is in slurm-wlm 21.08.5
|
|
487
|
+
sinfo_version_str = ub.cmd('sinfo --version').stdout.strip().split(' ')[1]
|
|
488
|
+
sinfo_major_version = int(sinfo_version_str.split('.')[0])
|
|
489
|
+
if sinfo_major_version < 21:
|
|
490
|
+
# Dont check in this case
|
|
491
|
+
return True
|
|
492
|
+
else:
|
|
493
|
+
sinfo = ub.cmd('sinfo --json')
|
|
494
|
+
if sinfo['ret'] == 0:
|
|
495
|
+
import json
|
|
496
|
+
sinfo_out = json.loads(sinfo['out'])
|
|
497
|
+
has_working_nodes = not all(
|
|
498
|
+
node['state'] == 'down'
|
|
499
|
+
for node in sinfo_out['nodes'])
|
|
500
|
+
if has_working_nodes:
|
|
501
|
+
return True
|
|
419
502
|
return False
|
|
420
503
|
|
|
421
504
|
def submit(self, command, **kwargs):
|
|
@@ -520,6 +603,10 @@ class SlurmQueue(base_queue.Queue):
|
|
|
520
603
|
info = ub.cmd('squeue --format="%i %P %j %u %t %M %D %R"')
|
|
521
604
|
stream = io.StringIO(info['out'])
|
|
522
605
|
df = pd.read_csv(stream, sep=' ')
|
|
606
|
+
|
|
607
|
+
# Only include job names that this queue created
|
|
608
|
+
job_names = [job.name for job in self.jobs]
|
|
609
|
+
df = df[df['NAME'].isin(job_names)]
|
|
523
610
|
jobid_history.update(df['JOBID'])
|
|
524
611
|
|
|
525
612
|
num_running = (df['ST'] == 'R').sum()
|
|
@@ -700,4 +787,9 @@ sbatch \
|
|
|
700
787
|
squeue
|
|
701
788
|
|
|
702
789
|
|
|
790
|
+
|
|
791
|
+
References:
|
|
792
|
+
https://stackoverflow.com/questions/74164136/slurm-accessing-stdout-stderr-location-of-a-completed-job
|
|
793
|
+
|
|
794
|
+
|
|
703
795
|
"""
|
cmd_queue/slurmify.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
Helper script to wrap a command with sbatch, but using a more srun like syntax.
|
|
3
|
+
|
|
4
|
+
.. code:: bash
|
|
5
|
+
|
|
6
|
+
python -m cmd_queue.slurmify \
|
|
7
|
+
--jobname="my_job" \
|
|
8
|
+
--depends=None \
|
|
9
|
+
--gpus=1 \
|
|
10
|
+
--mem=16GB \
|
|
11
|
+
--cpus_per_task=5 \
|
|
12
|
+
--ntasks=1 \
|
|
13
|
+
--ntasks-per-node=1 \
|
|
14
|
+
--partition=community \
|
|
15
|
+
-- \
|
|
16
|
+
python -c 'import sys; print("hello world"); sys.exit(0)'
|
|
17
|
+
"""
|
|
18
|
+
#!/usr/bin/env python3
|
|
19
|
+
import scriptconfig as scfg
|
|
20
|
+
import ubelt as ub
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class SlurmifyCLI(scfg.DataConfig):
|
|
24
|
+
__command__ = 'slurmify'
|
|
25
|
+
|
|
26
|
+
jobname = scfg.Value(None, help='for submit, this is the name of the new job')
|
|
27
|
+
depends = scfg.Value(None, help='comma separated jobnames to depend on')
|
|
28
|
+
|
|
29
|
+
command = scfg.Value(None, type=str, position=1, nargs='*', help=ub.paragraph(
|
|
30
|
+
'''
|
|
31
|
+
Specifies the bash command to queue.
|
|
32
|
+
Care must be taken when specifying this argument. If specifying as a
|
|
33
|
+
key/value pair argument, it is important to quote and escape the bash
|
|
34
|
+
command properly. A more convinient way to specify this command is as
|
|
35
|
+
a positional argument. End all of the options to this CLI with `--` and
|
|
36
|
+
then specify your full command.
|
|
37
|
+
'''))
|
|
38
|
+
|
|
39
|
+
gpus = scfg.Value(None, help='a comma separated list of the gpu numbers to spread across. tmux backend only.')
|
|
40
|
+
workers = scfg.Value(1, help='number of concurrent queues for the tmux backend.')
|
|
41
|
+
|
|
42
|
+
mem = scfg.Value(None, help='')
|
|
43
|
+
partition = scfg.Value(1, help='slurm partition')
|
|
44
|
+
|
|
45
|
+
ntasks = scfg.Value(None, help='')
|
|
46
|
+
ntasks_per_node = scfg.Value(None, help='')
|
|
47
|
+
cpus_per_task = scfg.Value(None, help='')
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
def main(cls, cmdline=1, **kwargs):
|
|
51
|
+
"""
|
|
52
|
+
Example:
|
|
53
|
+
>>> # xdoctest: +SKIP
|
|
54
|
+
>>> from cmd_queue.slurmify import * # NOQA
|
|
55
|
+
>>> cmdline = 0
|
|
56
|
+
>>> kwargs = dict()
|
|
57
|
+
>>> cls = SlurmifyCLI
|
|
58
|
+
>>> cls.main(cmdline=cmdline, **kwargs)
|
|
59
|
+
"""
|
|
60
|
+
import rich
|
|
61
|
+
from rich.markup import escape
|
|
62
|
+
config = cls.cli(cmdline=cmdline, data=kwargs, strict=True)
|
|
63
|
+
rich.print('config = ' + escape(ub.urepr(config, nl=1)))
|
|
64
|
+
|
|
65
|
+
# import json
|
|
66
|
+
# Run a new CLI queue
|
|
67
|
+
row = {'type': 'command', 'command': config['command']}
|
|
68
|
+
if config.jobname:
|
|
69
|
+
row['name'] = config.jobname
|
|
70
|
+
if config.depends:
|
|
71
|
+
row['depends'] = config.depends
|
|
72
|
+
|
|
73
|
+
import cmd_queue
|
|
74
|
+
queue = cmd_queue.Queue.create(
|
|
75
|
+
size=max(1, config['workers']),
|
|
76
|
+
backend='slurm',
|
|
77
|
+
name='slurmified',
|
|
78
|
+
gpus=config['gpus'],
|
|
79
|
+
mem=config['mem'],
|
|
80
|
+
partition=config['partition'],
|
|
81
|
+
ntasks=config['ntasks'],
|
|
82
|
+
ntasks_per_node=config['ntasks_per_node'],
|
|
83
|
+
)
|
|
84
|
+
try:
|
|
85
|
+
bash_command = row['command']
|
|
86
|
+
if isinstance(bash_command, list):
|
|
87
|
+
if len(bash_command) == 1:
|
|
88
|
+
# hack
|
|
89
|
+
import shlex
|
|
90
|
+
if shlex.quote(bash_command[0]) == bash_command[0]:
|
|
91
|
+
bash_command = bash_command[0]
|
|
92
|
+
else:
|
|
93
|
+
bash_command = shlex.quote(bash_command[0])
|
|
94
|
+
else:
|
|
95
|
+
import shlex
|
|
96
|
+
bash_command = ' '.join([shlex.quote(str(p)) for p in bash_command])
|
|
97
|
+
submitkw = ub.udict(row) & {'name', 'depends'}
|
|
98
|
+
queue.submit(bash_command, log=False, **submitkw)
|
|
99
|
+
except Exception:
|
|
100
|
+
print('row = {}'.format(ub.urepr(row, nl=1)))
|
|
101
|
+
raise
|
|
102
|
+
queue.print_commands()
|
|
103
|
+
|
|
104
|
+
# config.cli_queue_fpath.write_text(json.dumps(row))
|
|
105
|
+
# 'sbatch --job-name="test_job1" --output="$HOME/.cache/slurm/logs/job-%j-%x.out" --wrap=""
|
|
106
|
+
|
|
107
|
+
__cli__ = SlurmifyCLI
|
|
108
|
+
|
|
109
|
+
if __name__ == '__main__':
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
CommandLine:
|
|
113
|
+
python ~/code/cmd_queue/cmd_queue/slurmify.py
|
|
114
|
+
python -m cmd_queue.slurmify
|
|
115
|
+
"""
|
|
116
|
+
__cli__.main()
|
cmd_queue/tmux_queue.py
CHANGED
|
@@ -1055,4 +1055,38 @@ if 0:
|
|
|
1055
1055
|
tmux kill-session -t my_session_id
|
|
1056
1056
|
|
|
1057
1057
|
tmux new-session -d -s my_session_id -e "MYVAR1" -- "bash"
|
|
1058
|
+
|
|
1059
|
+
|
|
1060
|
+
|
|
1061
|
+
#### to start a tmux session with 4 panes
|
|
1062
|
+
tmux new-session -d -s my_session_id1 "bash"
|
|
1063
|
+
tmux send -t my_session_id1 "tmux split-window -h -t 0" Enter
|
|
1064
|
+
tmux send -t my_session_id1 "tmux split-window -v -t 0" Enter
|
|
1065
|
+
tmux send -t my_session_id1 "tmux split-window -v -t 2" Enter
|
|
1066
|
+
|
|
1067
|
+
# Now send a command to each pane
|
|
1068
|
+
tmux send -t my_session_id1 "tmux select-pane -t 0" Enter
|
|
1069
|
+
tmux send -t my_session_id1 "echo pane0" Enter
|
|
1070
|
+
tmux send -t my_session_id1 "tmux select-pane -t 1" Enter
|
|
1071
|
+
tmux send -t my_session_id1 "echo pane1" Enter
|
|
1072
|
+
tmux send -t my_session_id1 "tmux select-pane -t 2" Enter
|
|
1073
|
+
tmux send -t my_session_id1 "echo pane2" Enter
|
|
1074
|
+
tmux send -t my_session_id1 "tmux select-pane -t 3" Enter
|
|
1075
|
+
tmux send -t my_session_id1 "echo pane3" Enter
|
|
1076
|
+
|
|
1077
|
+
# https://stackoverflow.com/questions/54954177/how-to-write-a-tmux-script-so-that-it-automatically-split-windows-and-opens-a-se
|
|
1078
|
+
# https://tmuxcheatsheet.com/
|
|
1079
|
+
# https://gist.github.com/Starefossen/5955406
|
|
1080
|
+
|
|
1081
|
+
# List the bindings
|
|
1082
|
+
tmux list-keys
|
|
1083
|
+
|
|
1084
|
+
# Can arange the splits in a session via a preset layout
|
|
1085
|
+
# Preset layouts are:
|
|
1086
|
+
# even-horizontal, even-vertical, main-horizontal, main-vertical, or tiled.
|
|
1087
|
+
tmux select-layout -t "${SESSION_NAME}" even-vertical
|
|
1088
|
+
|
|
1089
|
+
# switch to an existing session
|
|
1090
|
+
tmux switch -t "${SESSION_NAME}"
|
|
1091
|
+
|
|
1058
1092
|
"""
|