snakemake-executor-plugin-slurm 1.7.0__py3-none-any.whl → 1.8.0__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 snakemake-executor-plugin-slurm might be problematic. Click here for more details.
- snakemake_executor_plugin_slurm/__init__.py +33 -17
- snakemake_executor_plugin_slurm/submit_string.py +35 -17
- {snakemake_executor_plugin_slurm-1.7.0.dist-info → snakemake_executor_plugin_slurm-1.8.0.dist-info}/METADATA +4 -2
- snakemake_executor_plugin_slurm-1.8.0.dist-info/RECORD +8 -0
- {snakemake_executor_plugin_slurm-1.7.0.dist-info → snakemake_executor_plugin_slurm-1.8.0.dist-info}/WHEEL +1 -1
- snakemake_executor_plugin_slurm-1.7.0.dist-info/RECORD +0 -8
- {snakemake_executor_plugin_slurm-1.7.0.dist-info → snakemake_executor_plugin_slurm-1.8.0.dist-info/licenses}/LICENSE +0 -0
|
@@ -307,7 +307,7 @@ class Executor(RemoteExecutor):
|
|
|
307
307
|
"run_uuid": self.run_uuid,
|
|
308
308
|
"slurm_logfile": slurm_logfile,
|
|
309
309
|
"comment_str": comment_str,
|
|
310
|
-
"account": self.get_account_arg(job),
|
|
310
|
+
"account": next(self.get_account_arg(job)),
|
|
311
311
|
"partition": self.get_partition_arg(job),
|
|
312
312
|
"workdir": self.workflow.workdir_init,
|
|
313
313
|
}
|
|
@@ -668,10 +668,21 @@ We leave it to SLURM to resume your job(s)"""
|
|
|
668
668
|
else raises an error - implicetly.
|
|
669
669
|
"""
|
|
670
670
|
if job.resources.get("slurm_account"):
|
|
671
|
-
#
|
|
672
|
-
#
|
|
673
|
-
|
|
674
|
-
|
|
671
|
+
# split the account upon ',' and whitespace, to allow
|
|
672
|
+
# multiple accounts being given
|
|
673
|
+
accounts = [
|
|
674
|
+
a for a in re.split(r"[,\s]+", job.resources.slurm_account) if a
|
|
675
|
+
]
|
|
676
|
+
for account in accounts:
|
|
677
|
+
# here, we check whether the given or guessed account is valid
|
|
678
|
+
# if not, a WorkflowError is raised
|
|
679
|
+
self.test_account(account)
|
|
680
|
+
# sbatch only allows one account per submission
|
|
681
|
+
# yield one after the other, if multiple were given
|
|
682
|
+
# we have to quote the account, because it might
|
|
683
|
+
# contain build-in shell commands - see issue #354
|
|
684
|
+
for account in accounts:
|
|
685
|
+
yield f" -A {shlex.quote(account)}"
|
|
675
686
|
else:
|
|
676
687
|
if self._fallback_account_arg is None:
|
|
677
688
|
self.logger.warning("No SLURM account given, trying to guess.")
|
|
@@ -679,7 +690,7 @@ We leave it to SLURM to resume your job(s)"""
|
|
|
679
690
|
if account:
|
|
680
691
|
self.logger.warning(f"Guessed SLURM account: {account}")
|
|
681
692
|
self.test_account(f"{account}")
|
|
682
|
-
self._fallback_account_arg = f" -A {account}"
|
|
693
|
+
self._fallback_account_arg = f" -A {shlex.quote(account)}"
|
|
683
694
|
else:
|
|
684
695
|
self.logger.warning(
|
|
685
696
|
"Unable to guess SLURM account. Trying to proceed without."
|
|
@@ -687,7 +698,7 @@ We leave it to SLURM to resume your job(s)"""
|
|
|
687
698
|
self._fallback_account_arg = (
|
|
688
699
|
"" # no account specific args for sbatch
|
|
689
700
|
)
|
|
690
|
-
|
|
701
|
+
yield self._fallback_account_arg
|
|
691
702
|
|
|
692
703
|
def get_partition_arg(self, job: JobExecutorInterface):
|
|
693
704
|
"""
|
|
@@ -702,7 +713,9 @@ We leave it to SLURM to resume your job(s)"""
|
|
|
702
713
|
self._fallback_partition = self.get_default_partition(job)
|
|
703
714
|
partition = self._fallback_partition
|
|
704
715
|
if partition:
|
|
705
|
-
|
|
716
|
+
# we have to quote the partition, because it might
|
|
717
|
+
# contain build-in shell commands
|
|
718
|
+
return f" -p {shlex.quote(partition)}"
|
|
706
719
|
else:
|
|
707
720
|
return ""
|
|
708
721
|
|
|
@@ -730,32 +743,35 @@ We leave it to SLURM to resume your job(s)"""
|
|
|
730
743
|
"""
|
|
731
744
|
tests whether the given account is registered, raises an error, if not
|
|
732
745
|
"""
|
|
733
|
-
|
|
746
|
+
# first we need to test with sacctmgr because sshare might not
|
|
747
|
+
# work in a multicluster environment
|
|
748
|
+
cmd = f'sacctmgr -n -s list user "{os.environ["USER"]}" format=account%256'
|
|
749
|
+
sacctmgr_report = sshare_report = ""
|
|
734
750
|
try:
|
|
735
751
|
accounts = subprocess.check_output(
|
|
736
752
|
cmd, shell=True, text=True, stderr=subprocess.PIPE
|
|
737
753
|
)
|
|
738
754
|
except subprocess.CalledProcessError as e:
|
|
739
|
-
|
|
755
|
+
sacctmgr_report = (
|
|
740
756
|
"Unable to test the validity of the given or guessed"
|
|
741
|
-
f" SLURM account '{account}' with
|
|
757
|
+
f" SLURM account '{account}' with sacctmgr: {e.stderr}."
|
|
742
758
|
)
|
|
743
759
|
accounts = ""
|
|
744
760
|
|
|
745
761
|
if not accounts.strip():
|
|
746
|
-
cmd =
|
|
762
|
+
cmd = "sshare -U --format Account%256 --noheader"
|
|
747
763
|
try:
|
|
748
764
|
accounts = subprocess.check_output(
|
|
749
765
|
cmd, shell=True, text=True, stderr=subprocess.PIPE
|
|
750
766
|
)
|
|
751
767
|
except subprocess.CalledProcessError as e:
|
|
752
|
-
|
|
768
|
+
sshare_report = (
|
|
753
769
|
"Unable to test the validity of the given or guessed "
|
|
754
|
-
f"SLURM account '{account}' with
|
|
770
|
+
f"SLURM account '{account}' with sshare: {e.stderr}."
|
|
755
771
|
)
|
|
756
772
|
raise WorkflowError(
|
|
757
|
-
f"The '
|
|
758
|
-
f"and likewise '
|
|
773
|
+
f"The 'sacctmgr' reported: '{sacctmgr_report}' "
|
|
774
|
+
f"and likewise 'sshare' reported: '{sshare_report}'."
|
|
759
775
|
)
|
|
760
776
|
|
|
761
777
|
# The set() has been introduced during review to eliminate
|
|
@@ -764,7 +780,7 @@ We leave it to SLURM to resume your job(s)"""
|
|
|
764
780
|
|
|
765
781
|
if not accounts:
|
|
766
782
|
self.logger.warning(
|
|
767
|
-
f"Both '
|
|
783
|
+
f"Both 'sacctmgr' and 'sshare' returned empty results for account "
|
|
768
784
|
f"'{account}'. Proceeding without account validation."
|
|
769
785
|
)
|
|
770
786
|
return ""
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
from snakemake_executor_plugin_slurm_jobstep import get_cpu_setting
|
|
2
2
|
from types import SimpleNamespace
|
|
3
|
+
import shlex
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def safe_quote(value):
|
|
7
|
+
"""
|
|
8
|
+
Safely quote a parameter value using shlex.quote.
|
|
9
|
+
Handles None values and converts to string if needed.
|
|
10
|
+
Returns empty quotes for empty strings.
|
|
11
|
+
"""
|
|
12
|
+
str_value = str(value)
|
|
13
|
+
if str_value == "":
|
|
14
|
+
return "''"
|
|
15
|
+
return shlex.quote(str_value)
|
|
3
16
|
|
|
4
17
|
|
|
5
18
|
def get_submit_command(job, params):
|
|
@@ -10,37 +23,41 @@ def get_submit_command(job, params):
|
|
|
10
23
|
params = SimpleNamespace(**params)
|
|
11
24
|
|
|
12
25
|
call = (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
f"--job-name {params.run_uuid} "
|
|
16
|
-
f
|
|
17
|
-
|
|
18
|
-
f
|
|
26
|
+
"sbatch "
|
|
27
|
+
"--parsable "
|
|
28
|
+
f"--job-name {safe_quote(params.run_uuid)} "
|
|
29
|
+
f"--output {safe_quote(params.slurm_logfile)} "
|
|
30
|
+
"--export=ALL "
|
|
31
|
+
f"--comment {safe_quote(params.comment_str)}"
|
|
19
32
|
)
|
|
20
33
|
|
|
21
34
|
# No accout or partition checking is required, here.
|
|
22
35
|
# Checking is done in the submit function.
|
|
23
36
|
|
|
24
37
|
# here, only the string is used, as it already contains
|
|
25
|
-
#
|
|
38
|
+
# "-A '{account_name}'"
|
|
26
39
|
call += f" {params.account}"
|
|
27
40
|
# here, only the string is used, as it already contains
|
|
28
|
-
#
|
|
41
|
+
# "- p '{partition_name}'"
|
|
29
42
|
call += f" {params.partition}"
|
|
30
43
|
|
|
31
44
|
if job.resources.get("clusters"):
|
|
32
|
-
call += f" --clusters {job.resources.clusters}"
|
|
45
|
+
call += f" --clusters {safe_quote(job.resources.clusters)}"
|
|
33
46
|
|
|
34
47
|
if job.resources.get("runtime"):
|
|
35
|
-
call += f" -t {job.resources.runtime}"
|
|
48
|
+
call += f" -t {safe_quote(job.resources.runtime)}"
|
|
36
49
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
50
|
+
# Both, constraint and qos are optional.
|
|
51
|
+
# If not set, they will not be added to the sbatch call.
|
|
52
|
+
# If explicitly set to an empty string,
|
|
53
|
+
# `--constraint ''` or `--qos ''` will be added.
|
|
54
|
+
constraint = job.resources.get("constraint")
|
|
55
|
+
if constraint is not None:
|
|
56
|
+
call += f" -C {safe_quote(constraint)}"
|
|
41
57
|
|
|
42
|
-
|
|
43
|
-
|
|
58
|
+
qos = job.resources.get("qos")
|
|
59
|
+
if qos is not None:
|
|
60
|
+
call += f" --qos={safe_quote(qos)}"
|
|
44
61
|
|
|
45
62
|
if job.resources.get("mem_mb_per_cpu"):
|
|
46
63
|
call += f" --mem-per-cpu {job.resources.mem_mb_per_cpu}"
|
|
@@ -77,6 +94,7 @@ def get_submit_command(job, params):
|
|
|
77
94
|
# ensure that workdir is set correctly
|
|
78
95
|
# use short argument as this is the same in all slurm versions
|
|
79
96
|
# (see https://github.com/snakemake/snakemake/issues/2014)
|
|
80
|
-
|
|
97
|
+
if params.workdir:
|
|
98
|
+
call += f" -D {safe_quote(params.workdir)}"
|
|
81
99
|
|
|
82
100
|
return call
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: snakemake-executor-plugin-slurm
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.0
|
|
4
4
|
Summary: A Snakemake executor plugin for submitting jobs to a SLURM cluster.
|
|
5
5
|
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
6
7
|
Keywords: snakemake,plugin,executor,cluster,slurm
|
|
7
8
|
Author: Christian Meesters
|
|
8
9
|
Author-email: meesters@uni-mainz.de
|
|
@@ -12,6 +13,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
15
17
|
Requires-Dist: numpy (>=1.26.4,<3)
|
|
16
18
|
Requires-Dist: pandas (>=2.2.3,<3.0.0)
|
|
17
19
|
Requires-Dist: snakemake-executor-plugin-slurm-jobstep (>=0.3.0,<0.4.0)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
snakemake_executor_plugin_slurm/__init__.py,sha256=G40Rbm62GFQF7bo38OJBFEFAxa4BLYeNO66wPQuki-c,34755
|
|
2
|
+
snakemake_executor_plugin_slurm/efficiency_report.py,sha256=crPfJDK4NojfRbu_wEw3ZmC3suMRABr5r-1rO5q3WEo,7429
|
|
3
|
+
snakemake_executor_plugin_slurm/submit_string.py,sha256=12DaJ9BIqEMC19dKuViPU8W5mZRjBRtNDQQu7M-iEjM,3448
|
|
4
|
+
snakemake_executor_plugin_slurm/utils.py,sha256=7XVXtzu7bg_89wWZisW-Zk7TNQyEgK4v_y4Y3F9uOwc,4491
|
|
5
|
+
snakemake_executor_plugin_slurm-1.8.0.dist-info/METADATA,sha256=8-ktC0sGhHDPOAyxpFl1LwWc7wPueZ2PxREKrQguhME,1507
|
|
6
|
+
snakemake_executor_plugin_slurm-1.8.0.dist-info/WHEEL,sha256=M5asmiAlL6HEcOq52Yi5mmk9KmTVjY2RDPtO4p9DMrc,88
|
|
7
|
+
snakemake_executor_plugin_slurm-1.8.0.dist-info/licenses/LICENSE,sha256=YVc4xTLWMqGfFL36120k7rzXtsT6e4RkJsh68VVn12s,1076
|
|
8
|
+
snakemake_executor_plugin_slurm-1.8.0.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
snakemake_executor_plugin_slurm/__init__.py,sha256=BOuvCB954jBaxqoh4ezFBYLStptsrESbPduDXMT0K9Q,33916
|
|
2
|
-
snakemake_executor_plugin_slurm/efficiency_report.py,sha256=crPfJDK4NojfRbu_wEw3ZmC3suMRABr5r-1rO5q3WEo,7429
|
|
3
|
-
snakemake_executor_plugin_slurm/submit_string.py,sha256=Cn9qopyQwBqs1MvZFxSyRR_7mZzCVj8_vO_JNzbiqew,2896
|
|
4
|
-
snakemake_executor_plugin_slurm/utils.py,sha256=7XVXtzu7bg_89wWZisW-Zk7TNQyEgK4v_y4Y3F9uOwc,4491
|
|
5
|
-
snakemake_executor_plugin_slurm-1.7.0.dist-info/LICENSE,sha256=YVc4xTLWMqGfFL36120k7rzXtsT6e4RkJsh68VVn12s,1076
|
|
6
|
-
snakemake_executor_plugin_slurm-1.7.0.dist-info/METADATA,sha256=XiUbU8KRQKmnK55EVkfrEKRYny3I4IF2WpbrfP43Acc,1434
|
|
7
|
-
snakemake_executor_plugin_slurm-1.7.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
8
|
-
snakemake_executor_plugin_slurm-1.7.0.dist-info/RECORD,,
|
|
File without changes
|