Skip to content

Commit 144c300

Browse files
committed
Optionally pass a shell script to sbatch rather than wrapping the command directly to circumvent SLURM command-line character limit
1 parent a3e91cd commit 144c300

File tree

1 file changed

+38
-4
lines changed

1 file changed

+38
-4
lines changed

snakemake_executor_plugin_slurm/__init__.py

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,20 @@ class ExecutorSettings(ExecutorSettingsBase):
269269
},
270270
)
271271

272+
pass_command_as_script: bool = field(
273+
default=False,
274+
metadata={
275+
"help": (
276+
"Pass to sbatch and srun the command to be executed as a shell script"
277+
" (fed through stdin) instead of wrapping it in the command line "
278+
"call. Useful when a limit exists on SLURM command line length (ie. "
279+
"max_submit_line_size)."
280+
),
281+
"env_var": False,
282+
"required": False,
283+
},
284+
)
285+
272286
def __post_init__(self):
273287
"""Validate settings after initialization."""
274288
validate_executor_settings(self)
@@ -413,7 +427,10 @@ def warn_on_jobcontext(self, done=None):
413427
done = True
414428

415429
def additional_general_args(self):
416-
return "--executor slurm-jobstep --jobs 1"
430+
general_args = "--executor slurm-jobstep --jobs 1"
431+
if self.workflow.executor_settings.pass_command_as_script:
432+
general_args += " --slurm-jobstep-pass-command-as-script"
433+
return general_args
417434

418435
def run_job(self, job: JobExecutorInterface):
419436
# Implement here how to run a job.
@@ -500,19 +517,31 @@ def run_job(self, job: JobExecutorInterface):
500517

501518
exec_job = self.format_job_exec(job)
502519

503-
# and finally the job to execute with all the snakemake parameters
504-
call += f' --wrap="{exec_job}"'
520+
if not self.workflow.executor_settings.pass_command_as_script:
521+
# and finally wrap the job to execute with all the snakemake parameters
522+
call += f' --wrap="{exec_job}"'
523+
subprocess_stdin = None
524+
else:
525+
# format the job to execute with all the snakemake parameters into a script
526+
sbatch_script = "\n".join(["#!/bin/sh", exec_job])
527+
self.logger.debug(f"sbatch script:\n{sbatch_script}")
528+
# feed the shell script to sbatch via stdin
529+
call += " /dev/stdin"
530+
subprocess_stdin = sbatch_script
505531

506532
self.logger.debug(f"sbatch call: {call}")
507533
try:
508534
process = subprocess.Popen(
509535
call,
510536
shell=True,
511537
text=True,
538+
stdin=subprocess.PIPE,
512539
stdout=subprocess.PIPE,
513540
stderr=subprocess.PIPE,
514541
)
515-
out, err = process.communicate()
542+
out, err = process.communicate(
543+
input=subprocess_stdin # feed the sbatch shell script through stdin
544+
)
516545
if process.returncode != 0:
517546
raise subprocess.CalledProcessError(
518547
process.returncode, call, output=err
@@ -524,6 +553,11 @@ def run_job(self, job: JobExecutorInterface):
524553
"SLURM sbatch failed. "
525554
f"The error message was '{e.output.strip()}'.\n"
526555
f" sbatch call:\n {call}\n"
556+
+ (
557+
f" sbatch script:\n{sbatch_script}\n"
558+
if subprocess_stdin is not None
559+
else ""
560+
)
527561
),
528562
)
529563
return

0 commit comments

Comments
 (0)