-
Notifications
You must be signed in to change notification settings - Fork 5
feat: pass a bash script to srun rather than the command directly #37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,12 +7,17 @@ | |||||||||||||||||||||||||||||||||||||||
| import socket | ||||||||||||||||||||||||||||||||||||||||
| import subprocess | ||||||||||||||||||||||||||||||||||||||||
| import sys | ||||||||||||||||||||||||||||||||||||||||
| from dataclasses import dataclass, field | ||||||||||||||||||||||||||||||||||||||||
| from snakemake_interface_executor_plugins.executors.base import SubmittedJobInfo | ||||||||||||||||||||||||||||||||||||||||
| from snakemake_interface_executor_plugins.executors.real import RealExecutor | ||||||||||||||||||||||||||||||||||||||||
| from snakemake_interface_executor_plugins.jobs import ( | ||||||||||||||||||||||||||||||||||||||||
| JobExecutorInterface, | ||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||
| from snakemake_interface_executor_plugins.settings import ExecMode, CommonSettings | ||||||||||||||||||||||||||||||||||||||||
| from snakemake_interface_executor_plugins.settings import ( | ||||||||||||||||||||||||||||||||||||||||
| CommonSettings, | ||||||||||||||||||||||||||||||||||||||||
| ExecMode, | ||||||||||||||||||||||||||||||||||||||||
| ExecutorSettingsBase, | ||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||
| from snakemake_interface_common.exceptions import WorkflowError | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
@@ -38,6 +43,25 @@ | |||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| @dataclass | ||||||||||||||||||||||||||||||||||||||||
| class ExecutorSettings(ExecutorSettingsBase): | ||||||||||||||||||||||||||||||||||||||||
| """Settings for the SLURM jobstep executor plugin.""" | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| pass_command_as_script: bool = field( | ||||||||||||||||||||||||||||||||||||||||
| default=False, | ||||||||||||||||||||||||||||||||||||||||
| metadata={ | ||||||||||||||||||||||||||||||||||||||||
| "help": ( | ||||||||||||||||||||||||||||||||||||||||
| "Pass to srun the command to be executed as a shell script " | ||||||||||||||||||||||||||||||||||||||||
| "(fed through stdin) instead of wrapping it in the command line " | ||||||||||||||||||||||||||||||||||||||||
| "call. Useful when a limit exists on SLURM command line length (ie. " | ||||||||||||||||||||||||||||||||||||||||
| "max_submit_line_size)." | ||||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||||
| "env_var": False, | ||||||||||||||||||||||||||||||||||||||||
| "required": False, | ||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| # Required: | ||||||||||||||||||||||||||||||||||||||||
| # Implementation of your executor | ||||||||||||||||||||||||||||||||||||||||
| class Executor(RealExecutor): | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -58,6 +82,7 @@ def run_job(self, job: JobExecutorInterface): | |||||||||||||||||||||||||||||||||||||||
| # snakemake_interface_executor_plugins.executors.base.SubmittedJobInfo. | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| jobsteps = dict() | ||||||||||||||||||||||||||||||||||||||||
| srun_script = None | ||||||||||||||||||||||||||||||||||||||||
| # TODO revisit special handling for group job levels via srun at a later stage | ||||||||||||||||||||||||||||||||||||||||
| # if job.is_group(): | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
|
|
@@ -118,14 +143,29 @@ def run_job(self, job: JobExecutorInterface): | |||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| call = "srun -n1 --cpu-bind=q " | ||||||||||||||||||||||||||||||||||||||||
| call += f" {get_cpu_setting(job, self.gpu_job)} " | ||||||||||||||||||||||||||||||||||||||||
| call += f" {self.format_job_exec(job)}" | ||||||||||||||||||||||||||||||||||||||||
| if self.workflow.executor_settings.pass_command_as_script: | ||||||||||||||||||||||||||||||||||||||||
| # format the job to execute with all the snakemake parameters | ||||||||||||||||||||||||||||||||||||||||
| # into a script | ||||||||||||||||||||||||||||||||||||||||
| srun_script = self.format_job_exec(job) | ||||||||||||||||||||||||||||||||||||||||
| # the process will read the srun script from stdin | ||||||||||||||||||||||||||||||||||||||||
| call += " sh -s" | ||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||
| call += f" {self.format_job_exec(job)}" | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| self.logger.debug(f"This job is a group job: {job.is_group()}") | ||||||||||||||||||||||||||||||||||||||||
| self.logger.debug(f"The call for this job is: {call}") | ||||||||||||||||||||||||||||||||||||||||
| self.logger.debug(f"Job is running on host: {socket.gethostname()}") | ||||||||||||||||||||||||||||||||||||||||
| if srun_script is not None: | ||||||||||||||||||||||||||||||||||||||||
| self.logger.debug(f"The script for this job is: \n{srun_script}") | ||||||||||||||||||||||||||||||||||||||||
| # this dict is to support the to be implemented feature of oversubscription in | ||||||||||||||||||||||||||||||||||||||||
| # "ordinary" group jobs. | ||||||||||||||||||||||||||||||||||||||||
| jobsteps[job] = subprocess.Popen(call, shell=True) | ||||||||||||||||||||||||||||||||||||||||
| jobsteps[job] = subprocess.Popen( | ||||||||||||||||||||||||||||||||||||||||
| call, shell=True, text=True, stdin=subprocess.PIPE | ||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||
| if srun_script is not None: | ||||||||||||||||||||||||||||||||||||||||
| # pass the srun bash script via stdin | ||||||||||||||||||||||||||||||||||||||||
| jobsteps[job].stdin.write(srun_script) | ||||||||||||||||||||||||||||||||||||||||
| jobsteps[job].stdin.close() | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+165
to
+168
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add error handling for stdin write operation. Writing to stdin can raise Apply this diff to add error handling: if srun_script is not None:
- # pass the srun bash script via stdin
- jobsteps[job].stdin.write(srun_script)
- jobsteps[job].stdin.close()
+ try:
+ # pass the srun bash script via stdin
+ jobsteps[job].stdin.write(srun_script)
+ jobsteps[job].stdin.close()
+ except BrokenPipeError:
+ # subprocess terminated before reading stdin
+ self.logger.error(
+ f"Failed to write script to stdin for job {job}. "
+ "Subprocess may have terminated prematurely."
+ )
+ self.report_job_error(SubmittedJobInfo(job))
+ raise WorkflowError(
+ f"Job {job} failed: subprocess terminated before reading script"
+ )📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| job_info = SubmittedJobInfo(job) | ||||||||||||||||||||||||||||||||||||||||
| self.report_job_submission(job_info) | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.