<h1 align="center">Simple Slurm</h1>
<p align="center">A simple Python wrapper for Slurm with flexibility in mind<p>
<p align="center">
<a href="https://github.com/amq92/simple_slurm/actions/workflows/python-publish-pypi.yml">
<img src="https://github.com/amq92/simple_slurm/actions/workflows/python-publish-pypi.yml/badge.svg" alt="Publish to PyPI" />
</a>
<a href="https://github.com/amq92/simple_slurm/actions/workflows/python-package-conda.yml">
<img src="https://github.com/amq92/simple_slurm/actions/workflows/python-package-conda.yml/badge.svg" alt="Publish to Conda" />
</a>
<a href="https://github.com/amq92/simple_slurm/actions/workflows/python-run-tests.yml">
<img src="https://github.com/amq92/simple_slurm/actions/workflows/python-run-tests.yml/badge.svg" alt="Run Python Tests" />
</a>
</p>
```python
import datetime
from simple_slurm import Slurm
slurm = Slurm(
array=range(3, 12),
cpus_per_task=15,
dependency=dict(after=65541, afterok=34987),
gres=['gpu:kepler:2', 'gpu:tesla:2', 'mps:400'],
ignore_pbs=True,
job_name='name',
output=f'{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out',
time=datetime.timedelta(days=1, hours=2, minutes=3, seconds=4),
)
slurm.add_cmd('module load python')
slurm.sbatch('python demo.py', Slurm.SLURM_ARRAY_TASK_ID)
```
The above snippet is equivalent to running the following command:
```bash
sbatch << EOF
#!/bin/sh
#SBATCH --array 3-11
#SBATCH --cpus-per-task 15
#SBATCH --dependency after:65541,afterok:34987
#SBATCH --gres gpu:kepler:2,gpu:tesla:2,mps:400
#SBATCH --ignore-pbs
#SBATCH --job-name name
#SBATCH --output %A_%a.out
#SBATCH --time 1-02:03:04
module load python
python demo.py \$SLURM_ARRAY_TASK_ID
EOF
```
Get it using either one of :
```bash
pip install simple_slurm
conda install -c conda-forge simple_slurm
```
## Contents
+ [Introduction](#introduction)
+ [Many syntaxes available](#many-syntaxes-available)
- [Using configuration files](#using-configuration-files)
- [Using the command line](#using-the-command-line)
+ [Job dependencies](#job-dependencies)
+ [Additional features](#additional-features)
- [Filename Patterns](#filename-patterns)
- [Output Environment Variables](#output-environment-variables)
## Introduction
The [`sbatch`](https://slurm.schedmd.com/sbatch.html) and [`srun`](https://slurm.schedmd.com/srun.html) commands in [Slurm](https://slurm.schedmd.com/overview.html) allow submitting parallel jobs into a Linux cluster in the form of batch scripts that follow a certain structure.
The goal of this library is to provide a simple wrapper for these functions (`sbatch` and `srun`) so that Python code can be used for constructing and launching the aforementioned batch script.
Indeed, the generated batch script can be shown by printing the `Slurm` object:
```python
from simple_slurm import Slurm
slurm = Slurm(array=range(3, 12), job_name='name')
print(slurm)
```
```bash
>> #!/bin/sh
>>
>> #SBATCH --array 3-11
>> #SBATCH --job-name name
```
Then, the job can be launched with either command:
```python
slurm.srun('echo hello!')
slurm.sbatch('echo hello!')
```
```bash
>> Submitted batch job 34987
```
While both commands are quite similar, [`srun`](https://slurm.schedmd.com/srun.html) will wait for the job completion, while [`sbatch`](https://slurm.schedmd.com/sbatch.html) will launch and disconnect from the jobs.
> More information can be found in [Slurm's Quick Start Guide](https://slurm.schedmd.com/quickstart.html) and in [here](https://stackoverflow.com/questions/43767866/slurm-srun-vs-sbatch-and-their-parameters).
Moreover, multi-line commands can be added using `add_cmd` and reset with `reset_cmd`.
The `sbatch` directive will call `add_cmd` before launching the job.
```python
slurm.add_cmd('echo hello for the first time!')
slurm.add_cmd('echo hello for the second time!')
slurm.sbatch('echo hello for the last time!')
slurm.reset_cmd()
slurm.sbatch('echo hello again!')
```
This results in two outputs
```
hello for the first time!
hello for the second time!
hello for the last time!
```
```
hello again!
```
## Many syntaxes available
```python
slurm = Slurm('-a', '3-11')
slurm = Slurm('--array', '3-11')
slurm = Slurm('array', '3-11')
slurm = Slurm(array='3-11')
slurm = Slurm(array=range(3, 12))
slurm.add_arguments(array=range(3, 12))
slurm.set_array(range(3, 12))
```
All these arguments are equivalent!
It's up to you to choose the one(s) that best suits you needs.
> *"With great flexibility comes great responsability"*
You can either keep a command-line-like syntax or a more Python-like one
```python
slurm = Slurm()
slurm.set_dependency('after:65541,afterok:34987')
slurm.set_dependency(['after:65541', 'afterok:34987'])
slurm.set_dependency(dict(after=65541, afterok=34987))
```
All the possible arguments have their own setter methods
(ex. `set_array`, `set_dependency`, `set_job_name`).
Please note that hyphenated arguments, such as `--job-name`, need to be underscored
(so to comply with Python syntax and be coherent).
```python
slurm = Slurm('--job_name', 'name')
slurm = Slurm(job_name='name')
# slurm = Slurm('--job-name', 'name') # NOT VALID
# slurm = Slurm(job-name='name') # NOT VALID
```
Moreover, boolean arguments such as `--contiguous`, `--ignore_pbs` or `--overcommit`
can be activated with `True` or an empty string.
```python
slurm = Slurm('--contiguous', True)
slurm.add_arguments(ignore_pbs='')
slurm.set_wait(False)
print(slurm)
```
```bash
#!/bin/sh
#SBATCH --contiguous
#SBATCH --ignore-pbs
```
### Using configuration files
Let's define the *static* components of a job definition in a YAML file `default.slurm`
```yaml
cpus_per_task: 15
job_name: 'name'
output: '%A_%a.out'
```
Including these options with the using the `yaml` package is very *simple*
```python
import yaml
from simple_slurm import Slurm
slurm = Slurm(**yaml.load(open('default.slurm')))
...
slurm.set_array(range(NUMBER_OF_SIMULATIONS))
```
The job can be updated according to the *dynamic* project needs (ex. `NUMBER_OF_SIMULATIONS`).
### Using the command line
For simpler dispatch jobs, a comand line entry point is also made available.
```bash
simple_slurm [OPTIONS] "COMMAND_TO_RUN_WITH_SBATCH"
```
As such, both of these `python` and `bash` calls are equivalent.
```python
slurm = Slurm(partition='compute.p', output='slurm.log', ignore_pbs=True)
slurm.sbatch('echo \$HOSTNAME')
```
```bash
simple_slurm --partition=compute.p --output slurm.log --ignore_pbs "echo \$HOSTNAME"
```
## Job dependencies
The `sbatch` call prints a message if successful and returns the corresponding `job_id`
```python
job_id = slurm.sbatch('python demo.py ' + Slurm.SLURM_ARRAY_TAKSK_ID)
```
If the job submission was successful, it prints:
```
Submitted batch job 34987
```
And returns the variable `job_id = 34987`, which can be used for setting dependencies on subsequent jobs
```python
slurm_after = Slurm(dependency=dict(afterok=job_id)))
```
## Additional features
For convenience, Filename Patterns and Output Environment Variables are available as attributes of the Simple Slurm object.
See [https://slurm.schedmd.com/sbatch.html](https://slurm.schedmd.com/sbatch.html#lbAH) for details on the commands.
```python
from slurm import Slurm
slurm = Slurm(output=('{}_{}.out'.format(
Slurm.JOB_ARRAY_MASTER_ID,
Slurm.JOB_ARRAY_ID))
slurm.sbatch('python demo.py ' + slurm.SLURM_ARRAY_JOB_ID)
```
This example would result in output files of the form `65541_15.out`.
Here the job submission ID is `65541`, and this output file corresponds to the submission number `15` in the job array. Moreover, this index is passed to the Python code `demo.py` as an argument.
> Note that they can be accessed either as `Slurm.<name>` or `slurm.<name>`, here `slurm` is an instance of the `Slurm` class.
### Filename Patterns
`sbatch` allows for a filename pattern to contain one or more replacement symbols.
They can be accessed with `Slurm.<name>`
name | value | description
:-------------------|------:|:-----------
JOB_ARRAY_MASTER_ID | %A | job array's master job allocation number
JOB_ARRAY_ID | %a | job array id (index) number
JOB_ID_STEP_ID | %J | jobid.stepid of the running job. (e.g. "128.0")
JOB_ID | %j | jobid of the running job
HOSTNAME | %N | short hostname. this will create a separate io file per node
NODE_IDENTIFIER | %n | node identifier relative to current job (e.g. "0" is the first node of the running job) this will create a separate io file per node
STEP_ID | %s | stepid of the running job
TASK_IDENTIFIER | %t | task identifier (rank) relative to current job. this will create a separate io file per task
USER_NAME | %u | user name
JOB_NAME | %x | job name
PERCENTAGE | %% | the character "%"
DO_NOT_PROCESS | \\\\ | do not process any of the replacement symbols
### Output Environment Variables
The Slurm controller will set the following variables in the environment of the batch script.
They can be accessed with `Slurm.<name>`.
name | description
:----------------------|:-----------
SLURM_ARRAY_TASK_COUNT | total number of tasks in a job array
SLURM_ARRAY_TASK_ID | job array id (index) number
SLURM_ARRAY_TASK_MAX | job array's maximum id (index) number
SLURM_ARRAY_TASK_MIN | job array's minimum id (index) number
SLURM_ARRAY_TASK_STEP | job array's index step size
SLURM_ARRAY_JOB_ID | job array's master job id number
... | ...
See [https://slurm.schedmd.com/sbatch.html](https://slurm.schedmd.com/sbatch.html#lbAK) for a complete list.
Raw data
{
"_id": null,
"home_page": "https://github.com/amq92/simple_slurm",
"name": "simple-slurm",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.6",
"maintainer_email": "",
"keywords": "",
"author": "Arturo Mendoza",
"author_email": "",
"download_url": "https://files.pythonhosted.org/packages/4b/be/a6607d1b937d719947943c30b5a79c136d25fa82cb7cebba1ae408d6ba2a/simple_slurm-0.2.7.tar.gz",
"platform": null,
"description": "<h1 align=\"center\">Simple Slurm</h1>\n<p align=\"center\">A simple Python wrapper for Slurm with flexibility in mind<p>\n<p align=\"center\">\n<a href=\"https://github.com/amq92/simple_slurm/actions/workflows/python-publish-pypi.yml\">\n <img src=\"https://github.com/amq92/simple_slurm/actions/workflows/python-publish-pypi.yml/badge.svg\" alt=\"Publish to PyPI\" />\n</a>\n<a href=\"https://github.com/amq92/simple_slurm/actions/workflows/python-package-conda.yml\">\n <img src=\"https://github.com/amq92/simple_slurm/actions/workflows/python-package-conda.yml/badge.svg\" alt=\"Publish to Conda\" />\n</a>\n<a href=\"https://github.com/amq92/simple_slurm/actions/workflows/python-run-tests.yml\">\n <img src=\"https://github.com/amq92/simple_slurm/actions/workflows/python-run-tests.yml/badge.svg\" alt=\"Run Python Tests\" />\n</a>\n</p>\n\n```python\nimport datetime\n\nfrom simple_slurm import Slurm\n\nslurm = Slurm(\n array=range(3, 12),\n cpus_per_task=15,\n dependency=dict(after=65541, afterok=34987),\n gres=['gpu:kepler:2', 'gpu:tesla:2', 'mps:400'],\n ignore_pbs=True,\n job_name='name',\n output=f'{Slurm.JOB_ARRAY_MASTER_ID}_{Slurm.JOB_ARRAY_ID}.out',\n time=datetime.timedelta(days=1, hours=2, minutes=3, seconds=4),\n)\nslurm.add_cmd('module load python')\nslurm.sbatch('python demo.py', Slurm.SLURM_ARRAY_TASK_ID)\n```\nThe above snippet is equivalent to running the following command:\n\n```bash\nsbatch << EOF\n#!/bin/sh\n\n#SBATCH --array 3-11\n#SBATCH --cpus-per-task 15\n#SBATCH --dependency after:65541,afterok:34987\n#SBATCH --gres gpu:kepler:2,gpu:tesla:2,mps:400\n#SBATCH --ignore-pbs\n#SBATCH --job-name name\n#SBATCH --output %A_%a.out\n#SBATCH --time 1-02:03:04\n\nmodule load python\npython demo.py \\$SLURM_ARRAY_TASK_ID\n\nEOF\n```\n\nGet it using either one of :\n```bash\npip install simple_slurm\nconda install -c conda-forge simple_slurm\n```\n\n## Contents\n+ [Introduction](#introduction)\n+ [Many syntaxes available](#many-syntaxes-available)\n - [Using configuration files](#using-configuration-files)\n - [Using the command line](#using-the-command-line)\n+ [Job dependencies](#job-dependencies)\n+ [Additional features](#additional-features)\n - [Filename Patterns](#filename-patterns)\n - [Output Environment Variables](#output-environment-variables)\n\n\n\n## Introduction\n\nThe [`sbatch`](https://slurm.schedmd.com/sbatch.html) and [`srun`](https://slurm.schedmd.com/srun.html) commands in [Slurm](https://slurm.schedmd.com/overview.html) allow submitting parallel jobs into a Linux cluster in the form of batch scripts that follow a certain structure.\n\nThe goal of this library is to provide a simple wrapper for these functions (`sbatch` and `srun`) so that Python code can be used for constructing and launching the aforementioned batch script.\n\nIndeed, the generated batch script can be shown by printing the `Slurm` object:\n\n```python\nfrom simple_slurm import Slurm\n\nslurm = Slurm(array=range(3, 12), job_name='name')\nprint(slurm)\n```\n```bash\n>> #!/bin/sh\n>> \n>> #SBATCH --array 3-11\n>> #SBATCH --job-name name\n```\n\nThen, the job can be launched with either command:\n```python\nslurm.srun('echo hello!')\nslurm.sbatch('echo hello!')\n```\n```bash\n>> Submitted batch job 34987\n```\n\nWhile both commands are quite similar, [`srun`](https://slurm.schedmd.com/srun.html) will wait for the job completion, while [`sbatch`](https://slurm.schedmd.com/sbatch.html) will launch and disconnect from the jobs.\n> More information can be found in [Slurm's Quick Start Guide](https://slurm.schedmd.com/quickstart.html) and in [here](https://stackoverflow.com/questions/43767866/slurm-srun-vs-sbatch-and-their-parameters).\n\n\nMoreover, multi-line commands can be added using `add_cmd` and reset with `reset_cmd`.\nThe `sbatch` directive will call `add_cmd` before launching the job.\n```python\nslurm.add_cmd('echo hello for the first time!')\nslurm.add_cmd('echo hello for the second time!')\nslurm.sbatch('echo hello for the last time!')\nslurm.reset_cmd()\nslurm.sbatch('echo hello again!')\n```\nThis results in two outputs\n```\nhello for the first time!\nhello for the second time!\nhello for the last time!\n```\n```\nhello again!\n```\n\n\n\n## Many syntaxes available\n\n```python\nslurm = Slurm('-a', '3-11')\nslurm = Slurm('--array', '3-11')\nslurm = Slurm('array', '3-11')\nslurm = Slurm(array='3-11')\nslurm = Slurm(array=range(3, 12))\nslurm.add_arguments(array=range(3, 12))\nslurm.set_array(range(3, 12))\n```\n\nAll these arguments are equivalent!\nIt's up to you to choose the one(s) that best suits you needs.\n\n> *\"With great flexibility comes great responsability\"*\n\nYou can either keep a command-line-like syntax or a more Python-like one\n\n```python\nslurm = Slurm()\nslurm.set_dependency('after:65541,afterok:34987')\nslurm.set_dependency(['after:65541', 'afterok:34987'])\nslurm.set_dependency(dict(after=65541, afterok=34987))\n```\n\nAll the possible arguments have their own setter methods\n(ex. `set_array`, `set_dependency`, `set_job_name`).\n\nPlease note that hyphenated arguments, such as `--job-name`, need to be underscored\n(so to comply with Python syntax and be coherent).\n\n```python\nslurm = Slurm('--job_name', 'name')\nslurm = Slurm(job_name='name')\n\n# slurm = Slurm('--job-name', 'name') # NOT VALID\n# slurm = Slurm(job-name='name') # NOT VALID\n```\n\nMoreover, boolean arguments such as `--contiguous`, `--ignore_pbs` or `--overcommit` \ncan be activated with `True` or an empty string.\n\n```python\nslurm = Slurm('--contiguous', True)\nslurm.add_arguments(ignore_pbs='')\nslurm.set_wait(False)\nprint(slurm)\n```\n```bash\n#!/bin/sh\n\n#SBATCH --contiguous\n#SBATCH --ignore-pbs\n```\n\n\n\n\n### Using configuration files\n\nLet's define the *static* components of a job definition in a YAML file `default.slurm`\n\n```yaml\ncpus_per_task: 15\njob_name: 'name'\noutput: '%A_%a.out'\n```\n\nIncluding these options with the using the `yaml` package is very *simple*\n\n```python\nimport yaml\n\nfrom simple_slurm import Slurm\n\nslurm = Slurm(**yaml.load(open('default.slurm')))\n\n...\n\nslurm.set_array(range(NUMBER_OF_SIMULATIONS))\n```\n\nThe job can be updated according to the *dynamic* project needs (ex. `NUMBER_OF_SIMULATIONS`).\n\n\n\n\n### Using the command line\n\nFor simpler dispatch jobs, a comand line entry point is also made available.\n\n```bash\nsimple_slurm [OPTIONS] \"COMMAND_TO_RUN_WITH_SBATCH\"\n```\n\nAs such, both of these `python` and `bash` calls are equivalent.\n\n```python\nslurm = Slurm(partition='compute.p', output='slurm.log', ignore_pbs=True)\nslurm.sbatch('echo \\$HOSTNAME')\n```\n```bash\nsimple_slurm --partition=compute.p --output slurm.log --ignore_pbs \"echo \\$HOSTNAME\"\n```\n\n\n\n\n## Job dependencies\n\nThe `sbatch` call prints a message if successful and returns the corresponding `job_id` \n\n```python\njob_id = slurm.sbatch('python demo.py ' + Slurm.SLURM_ARRAY_TAKSK_ID)\n```\n\nIf the job submission was successful, it prints:\n\n```\nSubmitted batch job 34987\n```\n\nAnd returns the variable `job_id = 34987`, which can be used for setting dependencies on subsequent jobs\n\n```python\nslurm_after = Slurm(dependency=dict(afterok=job_id)))\n```\n\n\n## Additional features\n\nFor convenience, Filename Patterns and Output Environment Variables are available as attributes of the Simple Slurm object.\n\nSee [https://slurm.schedmd.com/sbatch.html](https://slurm.schedmd.com/sbatch.html#lbAH) for details on the commands.\n\n```python\nfrom slurm import Slurm\n\nslurm = Slurm(output=('{}_{}.out'.format(\n Slurm.JOB_ARRAY_MASTER_ID,\n Slurm.JOB_ARRAY_ID))\nslurm.sbatch('python demo.py ' + slurm.SLURM_ARRAY_JOB_ID)\n```\n\nThis example would result in output files of the form `65541_15.out`.\nHere the job submission ID is `65541`, and this output file corresponds to the submission number `15` in the job array. Moreover, this index is passed to the Python code `demo.py` as an argument.\n\n> Note that they can be accessed either as `Slurm.<name>` or `slurm.<name>`, here `slurm` is an instance of the `Slurm` class.\n\n\n\n### Filename Patterns\n\n`sbatch` allows for a filename pattern to contain one or more replacement symbols.\n\nThey can be accessed with `Slurm.<name>`\n\nname | value | description\n:-------------------|------:|:-----------\nJOB_ARRAY_MASTER_ID | %A | job array's master job allocation number\nJOB_ARRAY_ID | %a | job array id (index) number\nJOB_ID_STEP_ID | %J | jobid.stepid of the running job. (e.g. \"128.0\")\nJOB_ID | %j | jobid of the running job\nHOSTNAME | %N | short hostname. this will create a separate io file per node\nNODE_IDENTIFIER | %n | node identifier relative to current job (e.g. \"0\" is the first node of the running job) this will create a separate io file per node\nSTEP_ID | %s | stepid of the running job\nTASK_IDENTIFIER | %t | task identifier (rank) relative to current job. this will create a separate io file per task\nUSER_NAME | %u | user name\nJOB_NAME | %x | job name\nPERCENTAGE | %% | the character \"%\"\nDO_NOT_PROCESS | \\\\\\\\ | do not process any of the replacement symbols\n\n\n\n### Output Environment Variables\n\nThe Slurm controller will set the following variables in the environment of the batch script.\n\nThey can be accessed with `Slurm.<name>`.\n\nname | description\n:----------------------|:-----------\nSLURM_ARRAY_TASK_COUNT | total number of tasks in a job array\nSLURM_ARRAY_TASK_ID | job array id (index) number\nSLURM_ARRAY_TASK_MAX | job array's maximum id (index) number\nSLURM_ARRAY_TASK_MIN | job array's minimum id (index) number\nSLURM_ARRAY_TASK_STEP | job array's index step size\nSLURM_ARRAY_JOB_ID | job array's master job id number\n... | ...\n\nSee [https://slurm.schedmd.com/sbatch.html](https://slurm.schedmd.com/sbatch.html#lbAK) for a complete list.\n",
"bugtrack_url": null,
"license": "GNU Affero General Public License v3",
"summary": "A simple Python wrapper for Slurm with flexibility in mind.",
"version": "0.2.7",
"project_urls": {
"Homepage": "https://github.com/amq92/simple_slurm"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "27babcc1e51ee75fba0b72dfe66b086e935ee3b5342a83c8560ebf0b8c132a16",
"md5": "028ea48a730db6ff6edb6433378a3e58",
"sha256": "8f61ee0a212fb95b2969a0b15ddcead9d2099fcb7e602f3655eab39819f721c3"
},
"downloads": -1,
"filename": "simple_slurm-0.2.7-py3-none-any.whl",
"has_sig": false,
"md5_digest": "028ea48a730db6ff6edb6433378a3e58",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.6",
"size": 26783,
"upload_time": "2023-11-30T22:44:53",
"upload_time_iso_8601": "2023-11-30T22:44:53.550015Z",
"url": "https://files.pythonhosted.org/packages/27/ba/bcc1e51ee75fba0b72dfe66b086e935ee3b5342a83c8560ebf0b8c132a16/simple_slurm-0.2.7-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "4bbea6607d1b937d719947943c30b5a79c136d25fa82cb7cebba1ae408d6ba2a",
"md5": "660c7cfda2dd371791a9a2faabd4317d",
"sha256": "c439e5c756ca9e5f600c4f54cdbf31d44c91330db60b1854a0f244acc6f2d300"
},
"downloads": -1,
"filename": "simple_slurm-0.2.7.tar.gz",
"has_sig": false,
"md5_digest": "660c7cfda2dd371791a9a2faabd4317d",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 27091,
"upload_time": "2023-11-30T22:44:55",
"upload_time_iso_8601": "2023-11-30T22:44:55.151399Z",
"url": "https://files.pythonhosted.org/packages/4b/be/a6607d1b937d719947943c30b5a79c136d25fa82cb7cebba1ae408d6ba2a/simple_slurm-0.2.7.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-11-30 22:44:55",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "amq92",
"github_project": "simple_slurm",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "simple-slurm"
}