Skip to content

Commit

Permalink
fix(options): fix --singularity flag
Browse files Browse the repository at this point in the history
  • Loading branch information
wpbonelli committed Dec 31, 2022
1 parent 5ef343c commit 95b79b0
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 64 deletions.
15 changes: 10 additions & 5 deletions docs/md/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ slappt --image docker://alpine \
--entrypoint "echo 'hello world'"
```

## Parallelism
## Multiple jobs

`slappt` is convenient not only for one-off container jobs, but for running multiple copies of a workflow in parallel (e.g. for Monte Carlo simulations), or mapping a workflow over a list of inputs. These use cases are accomplished with job arrays and can be configured via the `--iterations` and the `--inputs` options.
`slappt` is convenient not only for one-off container jobs, but for running multiple copies of a workflow, possibly in parallel (e.g. for Monte Carlo simulations), or mapping a workflow over a list of inputs. These use cases are accomplished with job arrays and can be configured via the `--iterations` and the `--inputs` options.

**Note:** your job remains limited by the number of nodes allocated to it by the scheduler. To run containers in parallel, you must request multiple nodes.

Expand Down Expand Up @@ -74,10 +74,15 @@ Assuming we have permission to submit to the `batch` partition, we can generate
```shell
slappt --image docker://alpine \
--shell sh \
--nodes 2 \
--partition batch \
--entrypoint "cat \$SLAPPT_INPUT" \
--inputs inputs.txt
--inputs inputs.txt > job.sh
```

This script will request 2 nodes, spawning a container on each with the `SLAPPT_INPUT` environment variable set one of the input files.
This will generate a script to spawn a container, reading the input from the `SLAPPT_INPUT` environment variable.

It can be then submitted with, for instance:

```shell
sbatch --array=1-2 job.sh
```
53 changes: 3 additions & 50 deletions slappt/scripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
)

SHEBANG = "#!/bin/bash"
LAUNCHER_SCRIPT_NAME = "launch" # TODO make configurable


class ScriptGenerator:
Expand Down Expand Up @@ -105,23 +104,11 @@ def gen_job_headers(self) -> List[str]:
return headers

def gen_job_command(self) -> List[str]:
if self.config.parallelism == Parallelism.LAUNCHER:
commands = self.gen_launcher_entrypoint()
elif self.config.parallelism == Parallelism.JOBARRAY:
commands = self.gen_job_array_script()
else:
raise ValueError(
f"Unsupported parallelism strategy {self.config.parallelism}"
)

self.logger.debug(f"Using run commands: {linesep.join(commands)}")
return commands

def gen_job_array_script(self) -> List[str]:
commands = []

if self.config.inputs:
commands.append(
f"file=$(head -n $SLURM_ARRAY_TASK_ID {self.config.inputs} | tail -1)"
f"SLAPPT_INPUT=$(head -n $SLURM_ARRAY_TASK_ID {self.config.inputs} | tail -1)"
)

commands = commands + ScriptGenerator.generate_invocation(
Expand All @@ -135,44 +122,10 @@ def gen_job_array_script(self) -> List[str]:
shell=self.config.shell,
singularity=self.config.singularity,
)
return commands

def gen_launcher_entrypoint(self) -> List[str]:
commands = []
commands.append(f"export LAUNCHER_WORKDIR={self.config.workdir}")
commands.append(
f"export LAUNCHER_JOB_FILE={environ.get(LAUNCHER_SCRIPT_NAME)}"
)
commands.append("$LAUNCHER_DIR/paramrun")
self.logger.debug(f"Using run commands: {linesep.join(commands)}")
return commands

def gen_launcher_script(self) -> List[str]:
lines: List[str] = []
files = [] # TODO: read from inputs.list

for i in range(0, self.config.iterations):
for file_name in files:
env = (
self.config.environment if self.config.environment else []
)
env.append(EnvironmentVariable("SLAPPT_ITERATION", str(i + 1)))
path = join(self.config.workdir, file_name)
lines = lines + ScriptGenerator.generate_invocation(
work_dir=self.config.workdir,
image=self.config.image,
commands=self.config.entrypoint.replace(
"$SLAPPT_INPUT", path
),
env=env,
bind_mounts=self.config.bind_mounts,
no_cache=self.config.no_cache,
gpus=self.config.gpus,
shell=self.config.shell,
singularity=self.config.singularity,
)

return lines

def gen_job_script(self) -> List[str]:
headers = self.gen_job_headers()
command = self.gen_job_command()
Expand Down
2 changes: 1 addition & 1 deletion slappt/slappt.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def generate_script(config: SlapptConfig):
@click.option("--cores", required=False, type=int, default=1)
@click.option("--tasks", required=False, type=int, default=1)
@click.option("--header_skip", required=False)
@click.option("--singularity", required=False, type=bool, default=False)
@click.option("--singularity", is_flag=True, default=False)
def cli(
ctx,
file,
Expand Down
3 changes: 1 addition & 2 deletions slappt/sshlurm.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,6 @@ def read_stderr():
return job_id


# @click.group(invoke_without_command=True)
@click.command()
@click.argument("file", required=True)
@click.option("--host", required=False, type=str)
Expand All @@ -202,7 +201,7 @@ def read_stderr():
@click.option("--pkey", required=False, type=str, default="~/.ssh/id_rsa")
@click.option("--allow_stderr", required=False, type=bool, default=False)
@click.option("--timeout", required=False, type=int, default=15)
@click.option("--verbose", required=False, type=bool, default=False)
@click.option("--verbose", is_flag=True, default=False)
def cli(
file,
workdir,
Expand Down
27 changes: 21 additions & 6 deletions slappt/tests/scripts/test_slappt.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@


# todo parametrize with optional params (e.g. account)
@pytest.mark.parametrize("file", [None, "job.sh"])
def test_script_hello_world(tmp_path, file):
def test_script_hello_world(tmp_path):
config = SlapptConfig(
file=str(tmp_path / file) if file else None,
name=str(uuid4()),
image="alpine",
entrypoint="echo 'hello world'",
Expand All @@ -30,8 +28,7 @@ def test_script_hello_world(tmp_path, file):
assert script[0].startswith("#!/bin/bash")


@pytest.mark.parametrize("file", [None, "job.sh"])
def test_script_with_inputs_file(tmp_path, file):
def test_script_with_inputs_file(tmp_path):
input_file_1 = tmp_path / "input_1.txt"
input_file_2 = tmp_path / "input_2.txt"
inputs_file = tmp_path / "inputs.txt"
Expand All @@ -47,7 +44,6 @@ def test_script_with_inputs_file(tmp_path, file):
f.write(str(input_file_2))

config = SlapptConfig(
file=str(tmp_path / file) if file else None,
name=str(uuid4()),
image="alpine",
entrypoint="cat $SLAPPT_INPUT",
Expand All @@ -60,3 +56,22 @@ def test_script_with_inputs_file(tmp_path, file):
script = generate_script(config)
pprint(script)
assert script[0].startswith("#!/bin/bash")
assert script[-2].startswith("SLAPPT_INPUT=")


def test_singularity_flag(tmp_path):
config = SlapptConfig(
name=str(uuid4()),
image="alpine",
entrypoint="echo 'hello world'",
workdir=str(tmp_path),
email=email,
partition=partition,
singularity=True,
)

script = generate_script(config)
pprint(script)
assert script[0].startswith("#!/bin/bash")
assert any(s.startswith("singularity exec") for s in script)
assert not any(s.startswith("apptainer exec") for s in script)

0 comments on commit 95b79b0

Please sign in to comment.