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.

@@ -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
- # here, we check whether the given or guessed account is valid
672
- # if not, a WorkflowError is raised
673
- self.test_account(job.resources.slurm_account)
674
- return f" -A '{job.resources.slurm_account}'"
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
- return self._fallback_account_arg
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
- return f" -p {partition}"
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
- cmd = "sshare -U --format Account%256 --noheader"
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
- sshare_report = (
755
+ sacctmgr_report = (
740
756
  "Unable to test the validity of the given or guessed"
741
- f" SLURM account '{account}' with sshare: {e.stderr}."
757
+ f" SLURM account '{account}' with sacctmgr: {e.stderr}."
742
758
  )
743
759
  accounts = ""
744
760
 
745
761
  if not accounts.strip():
746
- cmd = f'sacctmgr -n -s list user "{os.environ["USER"]}" format=account%256'
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
- sacctmgr_report = (
768
+ sshare_report = (
753
769
  "Unable to test the validity of the given or guessed "
754
- f"SLURM account '{account}' with sacctmgr: {e.stderr}."
770
+ f"SLURM account '{account}' with sshare: {e.stderr}."
755
771
  )
756
772
  raise WorkflowError(
757
- f"The 'sshare' reported: '{sshare_report}' "
758
- f"and likewise 'sacctmgr' reported: '{sacctmgr_report}'."
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 'sshare' and 'sacctmgr' returned empty results for account "
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
- f"sbatch "
14
- f"--parsable "
15
- f"--job-name {params.run_uuid} "
16
- f'--output "{params.slurm_logfile}" '
17
- f"--export=ALL "
18
- f'--comment "{params.comment_str}"'
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
- # '-A {account_name}'
38
+ # "-A '{account_name}'"
26
39
  call += f" {params.account}"
27
40
  # here, only the string is used, as it already contains
28
- # '- p {partition_name}'
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
- if job.resources.get("constraint") or isinstance(
38
- job.resources.get("constraint"), str
39
- ):
40
- call += f" -C '{job.resources.get('constraint')}'"
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
- if job.resources.get("qos") or isinstance(job.resources.get("qos"), str):
43
- call += f" --qos='{job.resources.qos}'"
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
- call += f" -D '{params.workdir}'"
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.3
1
+ Metadata-Version: 2.4
2
2
  Name: snakemake-executor-plugin-slurm
3
- Version: 1.7.0
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,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.3
2
+ Generator: poetry-core 2.2.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -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,,