Skip to content

Commit ccde8ca

Browse files
dmwnzcmeesters
andauthored
feat: pass a shell script to sbatch rather than the command directly (#380)
Fixes #379 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a user-facing setting to choose whether job commands are submitted as a shell script (on/off). * **Refactor** * When enabled, job scripts are streamed to the scheduler via stdin instead of being embedded on the command line, improving handling of complex commands. * **Bug Fixes / Reliability** * Enhanced logging and error messages now include submitted script content to aid debugging. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Christian Meesters <cmeesters@users.noreply.github.com>
1 parent 27607e5 commit ccde8ca

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
@@ -283,6 +283,20 @@ class ExecutorSettings(ExecutorSettingsBase):
283283
},
284284
)
285285

286+
pass_command_as_script: bool = field(
287+
default=False,
288+
metadata={
289+
"help": (
290+
"Pass to sbatch and srun the command to be executed as a shell script"
291+
" (fed through stdin) instead of wrapping it in the command line "
292+
"call. Useful when a limit exists on SLURM command line length (ie. "
293+
"max_submit_line_size)."
294+
),
295+
"env_var": False,
296+
"required": False,
297+
},
298+
)
299+
286300
def __post_init__(self):
287301
"""Validate settings after initialization."""
288302
validate_executor_settings(self)
@@ -427,7 +441,10 @@ def warn_on_jobcontext(self, done=None):
427441
done = True
428442

429443
def additional_general_args(self):
430-
return "--executor slurm-jobstep --jobs 1"
444+
general_args = "--executor slurm-jobstep --jobs 1"
445+
if self.workflow.executor_settings.pass_command_as_script:
446+
general_args += " --slurm-jobstep-pass-command-as-script"
447+
return general_args
431448

432449
def run_job(self, job: JobExecutorInterface):
433450
# Implement here how to run a job.
@@ -514,19 +531,31 @@ def run_job(self, job: JobExecutorInterface):
514531

515532
exec_job = self.format_job_exec(job)
516533

517-
# and finally the job to execute with all the snakemake parameters
518-
call += f' --wrap="{exec_job}"'
534+
if not self.workflow.executor_settings.pass_command_as_script:
535+
# and finally wrap the job to execute with all the snakemake parameters
536+
call += f' --wrap="{exec_job}"'
537+
subprocess_stdin = None
538+
else:
539+
# format the job to execute with all the snakemake parameters into a script
540+
sbatch_script = "\n".join(["#!/bin/sh", exec_job])
541+
self.logger.debug(f"sbatch script:\n{sbatch_script}")
542+
# feed the shell script to sbatch via stdin
543+
call += " /dev/stdin"
544+
subprocess_stdin = sbatch_script
519545

520546
self.logger.debug(f"sbatch call: {call}")
521547
try:
522548
process = subprocess.Popen(
523549
call,
524550
shell=True,
525551
text=True,
552+
stdin=subprocess.PIPE,
526553
stdout=subprocess.PIPE,
527554
stderr=subprocess.PIPE,
528555
)
529-
out, err = process.communicate()
556+
out, err = process.communicate(
557+
input=subprocess_stdin # feed the sbatch shell script through stdin
558+
)
530559
if process.returncode != 0:
531560
raise subprocess.CalledProcessError(
532561
process.returncode, call, output=err
@@ -538,6 +567,11 @@ def run_job(self, job: JobExecutorInterface):
538567
"SLURM sbatch failed. "
539568
f"The error message was '{e.output.strip()}'.\n"
540569
f" sbatch call:\n {call}\n"
570+
+ (
571+
f" sbatch script:\n{sbatch_script}\n"
572+
if subprocess_stdin is not None
573+
else ""
574+
)
541575
),
542576
)
543577
return

0 commit comments

Comments
 (0)