We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Cannot Instantiate SlurmProvider in parsl-2023.12.11
**Describe the bug**
When I attempt to instantiate a SlurmProvider in parsl-2023.12.11 I get the following error:
`AttributeError: class SlurmProvider uses qos in the constructor, but does not define it as an attribute`

**To Reproduce**
Steps to reproduce the behavior:
1. Install parsl-2023.12.11
2. Attempt to instantiate SlurmProvider
3. See error

**Expected behavior**
The provider should be instantiated

**Environment**
 - OS: Seen on Ubuntu and MacOS github workers and Linux Mint 21.2
 - Python version: 3.8 and 3.11
 - Parsl version: 2023.12.11

**Distributed Environment**
- I'm not actually running a script at all just trying to instantiate the provider/a config with the provider


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `parsl/providers/slurm/slurm.py`
Content:
```
1 import os
2 import math
3 import time
4 import logging
5 import re
6 import typeguard
7 
8 from typing import Optional
9 
10 from parsl.channels import LocalChannel
11 from parsl.channels.base import Channel
12 from parsl.jobs.states import JobState, JobStatus
13 from parsl.launchers import SingleNodeLauncher
14 from parsl.launchers.base import Launcher
15 from parsl.providers.cluster_provider import ClusterProvider
16 from parsl.providers.slurm.template import template_string
17 from parsl.utils import RepresentationMixin, wtime_to_minutes
18 
19 logger = logging.getLogger(__name__)
20 
21 translate_table = {
22     'PD': JobState.PENDING,
23     'R': JobState.RUNNING,
24     'CA': JobState.CANCELLED,
25     'CF': JobState.PENDING,  # (configuring),
26     'CG': JobState.RUNNING,  # (completing),
27     'CD': JobState.COMPLETED,
28     'F': JobState.FAILED,  # (failed),
29     'TO': JobState.TIMEOUT,  # (timeout),
30     'NF': JobState.FAILED,  # (node failure),
31     'RV': JobState.FAILED,  # (revoked) and
32     'SE': JobState.FAILED   # (special exit state)
33 }
34 
35 
36 class SlurmProvider(ClusterProvider, RepresentationMixin):
37     """Slurm Execution Provider
38 
39     This provider uses sbatch to submit, squeue for status and scancel to cancel
40     jobs. The sbatch script to be used is created from a template file in this
41     same module.
42 
43     Parameters
44     ----------
45     partition : str
46         Slurm partition to request blocks from. If unspecified or ``None``, no partition slurm directive will be specified.
47     account : str
48         Slurm account to which to charge resources used by the job. If unspecified or ``None``, the job will use the
49         user's default account.
50     qos : str
51         Slurm queue to place job in. If unspecified or ``None``, no queue slurm directive will be specified.
52     constraint : str
53         Slurm job constraint, often used to choose cpu or gpu type. If unspecified or ``None``, no constraint slurm directive will be added.
54     channel : Channel
55         Channel for accessing this provider. Possible channels include
56         :class:`~parsl.channels.LocalChannel` (the default),
57         :class:`~parsl.channels.SSHChannel`, or
58         :class:`~parsl.channels.SSHInteractiveLoginChannel`.
59     nodes_per_block : int
60         Nodes to provision per block.
61     cores_per_node : int
62         Specify the number of cores to provision per node. If set to None, executors
63         will assume all cores on the node are available for computation. Default is None.
64     mem_per_node : int
65         Specify the real memory to provision per node in GB. If set to None, no
66         explicit request to the scheduler will be made. Default is None.
67     min_blocks : int
68         Minimum number of blocks to maintain.
69     max_blocks : int
70         Maximum number of blocks to maintain.
71     parallelism : float
72         Ratio of provisioned task slots to active tasks. A parallelism value of 1 represents aggressive
73         scaling where as many resources as possible are used; parallelism close to 0 represents
74         the opposite situation in which as few resources as possible (i.e., min_blocks) are used.
75     walltime : str
76         Walltime requested per block in HH:MM:SS.
77     scheduler_options : str
78         String to prepend to the #SBATCH blocks in the submit script to the scheduler.
79     regex_job_id : str
80         The regular expression used to extract the job ID from the ``sbatch`` standard output.
81         The default is ``r"Submitted batch job (?P<id>\\S*)"``, where ``id`` is the regular expression
82         symbolic group for the job ID.
83     worker_init : str
84         Command to be run before starting a worker, such as 'module load Anaconda; source activate env'.
85     exclusive : bool (Default = True)
86         Requests nodes which are not shared with other running jobs.
87     launcher : Launcher
88         Launcher for this provider. Possible launchers include
89         :class:`~parsl.launchers.SingleNodeLauncher` (the default),
90         :class:`~parsl.launchers.SrunLauncher`, or
91         :class:`~parsl.launchers.AprunLauncher`
92     move_files : Optional[Bool]: should files be moved? by default, Parsl will try to move files.
93     """
94 
95     @typeguard.typechecked
96     def __init__(self,
97                  partition: Optional[str] = None,
98                  account: Optional[str] = None,
99                  qos: Optional[str] = None,
100                  constraint: Optional[str] = None,
101                  channel: Channel = LocalChannel(),
102                  nodes_per_block: int = 1,
103                  cores_per_node: Optional[int] = None,
104                  mem_per_node: Optional[int] = None,
105                  init_blocks: int = 1,
106                  min_blocks: int = 0,
107                  max_blocks: int = 1,
108                  parallelism: float = 1,
109                  walltime: str = "00:10:00",
110                  scheduler_options: str = '',
111                  regex_job_id: str = r"Submitted batch job (?P<id>\S*)",
112                  worker_init: str = '',
113                  cmd_timeout: int = 10,
114                  exclusive: bool = True,
115                  move_files: bool = True,
116                  launcher: Launcher = SingleNodeLauncher()):
117         label = 'slurm'
118         super().__init__(label,
119                          channel,
120                          nodes_per_block,
121                          init_blocks,
122                          min_blocks,
123                          max_blocks,
124                          parallelism,
125                          walltime,
126                          cmd_timeout=cmd_timeout,
127                          launcher=launcher)
128 
129         self.partition = partition
130         self.cores_per_node = cores_per_node
131         self.mem_per_node = mem_per_node
132         self.exclusive = exclusive
133         self.move_files = move_files
134         self.account = account
135         self.scheduler_options = scheduler_options + '\n'
136         if exclusive:
137             self.scheduler_options += "#SBATCH --exclusive\n"
138         if partition:
139             self.scheduler_options += "#SBATCH --partition={}\n".format(partition)
140         if account:
141             self.scheduler_options += "#SBATCH --account={}\n".format(account)
142         if qos:
143             self.scheduler_options += "#SBATCH --qos={}\n".format(qos)
144         if constraint:
145             self.scheduler_options += "#SBATCH --constraint={}\n".format(constraint)
146 
147         self.regex_job_id = regex_job_id
148         self.worker_init = worker_init + '\n'
149 
150     def _status(self):
151         '''Returns the status list for a list of job_ids
152 
153         Args:
154               self
155 
156         Returns:
157               [status...] : Status list of all jobs
158         '''
159         job_id_list = ','.join(
160             [jid for jid, job in self.resources.items() if not job['status'].terminal]
161         )
162         if not job_id_list:
163             logger.debug('No active jobs, skipping status update')
164             return
165 
166         cmd = "squeue --noheader --format='%i %t' --job '{0}'".format(job_id_list)
167         logger.debug("Executing %s", cmd)
168         retcode, stdout, stderr = self.execute_wait(cmd)
169         logger.debug("squeue returned %s %s", stdout, stderr)
170 
171         # Execute_wait failed. Do no update
172         if retcode != 0:
173             logger.warning("squeue failed with non-zero exit code {}".format(retcode))
174             return
175 
176         jobs_missing = set(self.resources.keys())
177         for line in stdout.split('\n'):
178             if not line:
179                 # Blank line
180                 continue
181             job_id, slurm_state = line.split()
182             if slurm_state not in translate_table:
183                 logger.warning(f"Slurm status {slurm_state} is not recognized")
184             status = translate_table.get(slurm_state, JobState.UNKNOWN)
185             logger.debug("Updating job {} with slurm status {} to parsl state {!s}".format(job_id, slurm_state, status))
186             self.resources[job_id]['status'] = JobStatus(status)
187             jobs_missing.remove(job_id)
188 
189         # squeue does not report on jobs that are not running. So we are filling in the
190         # blanks for missing jobs, we might lose some information about why the jobs failed.
191         for missing_job in jobs_missing:
192             logger.debug("Updating missing job {} to completed status".format(missing_job))
193             self.resources[missing_job]['status'] = JobStatus(JobState.COMPLETED)
194 
195     def submit(self, command, tasks_per_node, job_name="parsl.slurm"):
196         """Submit the command as a slurm job.
197 
198         Parameters
199         ----------
200         command : str
201             Command to be made on the remote side.
202         tasks_per_node : int
203             Command invocations to be launched per node
204         job_name : str
205             Name for the job
206         Returns
207         -------
208         None or str
209             If at capacity, returns None; otherwise, a string identifier for the job
210         """
211 
212         scheduler_options = self.scheduler_options
213         worker_init = self.worker_init
214         if self.mem_per_node is not None:
215             scheduler_options += '#SBATCH --mem={}g\n'.format(self.mem_per_node)
216             worker_init += 'export PARSL_MEMORY_GB={}\n'.format(self.mem_per_node)
217         if self.cores_per_node is not None:
218             cpus_per_task = math.floor(self.cores_per_node / tasks_per_node)
219             scheduler_options += '#SBATCH --cpus-per-task={}'.format(cpus_per_task)
220             worker_init += 'export PARSL_CORES={}\n'.format(cpus_per_task)
221 
222         job_name = "{0}.{1}".format(job_name, time.time())
223 
224         script_path = "{0}/{1}.submit".format(self.script_dir, job_name)
225         script_path = os.path.abspath(script_path)
226 
227         logger.debug("Requesting one block with {} nodes".format(self.nodes_per_block))
228 
229         job_config = {}
230         job_config["submit_script_dir"] = self.channel.script_dir
231         job_config["nodes"] = self.nodes_per_block
232         job_config["tasks_per_node"] = tasks_per_node
233         job_config["walltime"] = wtime_to_minutes(self.walltime)
234         job_config["scheduler_options"] = scheduler_options
235         job_config["worker_init"] = worker_init
236         job_config["user_script"] = command
237 
238         # Wrap the command
239         job_config["user_script"] = self.launcher(command,
240                                                   tasks_per_node,
241                                                   self.nodes_per_block)
242 
243         logger.debug("Writing submit script")
244         self._write_submit_script(template_string, script_path, job_name, job_config)
245 
246         if self.move_files:
247             logger.debug("moving files")
248             channel_script_path = self.channel.push_file(script_path, self.channel.script_dir)
249         else:
250             logger.debug("not moving files")
251             channel_script_path = script_path
252 
253         retcode, stdout, stderr = self.execute_wait("sbatch {0}".format(channel_script_path))
254 
255         job_id = None
256         if retcode == 0:
257             for line in stdout.split('\n'):
258                 match = re.match(self.regex_job_id, line)
259                 if match:
260                     job_id = match.group("id")
261                     self.resources[job_id] = {'job_id': job_id, 'status': JobStatus(JobState.PENDING)}
262                     break
263             else:
264                 logger.error("Could not read job ID from submit command standard output.")
265                 logger.error("Retcode:%s STDOUT:%s STDERR:%s", retcode, stdout.strip(), stderr.strip())
266         else:
267             logger.error("Submit command failed")
268             logger.error("Retcode:%s STDOUT:%s STDERR:%s", retcode, stdout.strip(), stderr.strip())
269         return job_id
270 
271     def cancel(self, job_ids):
272         ''' Cancels the jobs specified by a list of job ids
273 
274         Args:
275         job_ids : [<job_id> ...]
276 
277         Returns :
278         [True/False...] : If the cancel operation fails the entire list will be False.
279         '''
280 
281         job_id_list = ' '.join(job_ids)
282         retcode, stdout, stderr = self.execute_wait("scancel {0}".format(job_id_list))
283         rets = None
284         if retcode == 0:
285             for jid in job_ids:
286                 self.resources[jid]['status'] = JobStatus(JobState.CANCELLED)  # Setting state to cancelled
287             rets = [True for i in job_ids]
288         else:
289             rets = [False for i in job_ids]
290 
291         return rets
292 
293     @property
294     def status_polling_interval(self):
295         return 60
296
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/parsl/providers/slurm/slurm.py b/parsl/providers/slurm/slurm.py
--- a/parsl/providers/slurm/slurm.py
+++ b/parsl/providers/slurm/slurm.py
@@ -132,6 +132,8 @@
         self.exclusive = exclusive
         self.move_files = move_files
         self.account = account
+        self.qos = qos
+        self.constraint = constraint
         self.scheduler_options = scheduler_options + '\n'
         if exclusive:
             self.scheduler_options += "#SBATCH --exclusive\n"

{"golden_diff": "diff --git a/parsl/providers/slurm/slurm.py b/parsl/providers/slurm/slurm.py\n--- a/parsl/providers/slurm/slurm.py\n+++ b/parsl/providers/slurm/slurm.py\n@@ -132,6 +132,8 @@\n         self.exclusive = exclusive\n         self.move_files = move_files\n         self.account = account\n+        self.qos = qos\n+        self.constraint = constraint\n         self.scheduler_options = scheduler_options + '\\n'\n         if exclusive:\n             self.scheduler_options += \"#SBATCH --exclusive\\n\"\n", "issue": "Cannot Instantiate SlurmProvider in parsl-2023.12.11\n**Describe the bug**\r\nWhen I attempt to instantiate a SlurmProvider in parsl-2023.12.11 I get the following error:\r\n`AttributeError: class SlurmProvider uses qos in the constructor, but does not define it as an attribute`\r\n\r\n**To Reproduce**\r\nSteps to reproduce the behavior:\r\n1. Install parsl-2023.12.11\r\n2. Attempt to instantiate SlurmProvider\r\n3. See error\r\n\r\n**Expected behavior**\r\nThe provider should be instantiated\r\n\r\n**Environment**\r\n - OS: Seen on Ubuntu and MacOS github workers and Linux Mint 21.2\r\n - Python version: 3.8 and 3.11\r\n - Parsl version: 2023.12.11\r\n\r\n**Distributed Environment**\r\n- I'm not actually running a script at all just trying to instantiate the provider/a config with the provider\r\n\n", "before_files": [{"content": "import os\nimport math\nimport time\nimport logging\nimport re\nimport typeguard\n\nfrom typing import Optional\n\nfrom parsl.channels import LocalChannel\nfrom parsl.channels.base import Channel\nfrom parsl.jobs.states import JobState, JobStatus\nfrom parsl.launchers import SingleNodeLauncher\nfrom parsl.launchers.base import Launcher\nfrom parsl.providers.cluster_provider import ClusterProvider\nfrom parsl.providers.slurm.template import template_string\nfrom parsl.utils import RepresentationMixin, wtime_to_minutes\n\nlogger = logging.getLogger(__name__)\n\ntranslate_table = {\n    'PD': JobState.PENDING,\n    'R': JobState.RUNNING,\n    'CA': JobState.CANCELLED,\n    'CF': JobState.PENDING,  # (configuring),\n    'CG': JobState.RUNNING,  # (completing),\n    'CD': JobState.COMPLETED,\n    'F': JobState.FAILED,  # (failed),\n    'TO': JobState.TIMEOUT,  # (timeout),\n    'NF': JobState.FAILED,  # (node failure),\n    'RV': JobState.FAILED,  # (revoked) and\n    'SE': JobState.FAILED   # (special exit state)\n}\n\n\nclass SlurmProvider(ClusterProvider, RepresentationMixin):\n    \"\"\"Slurm Execution Provider\n\n    This provider uses sbatch to submit, squeue for status and scancel to cancel\n    jobs. The sbatch script to be used is created from a template file in this\n    same module.\n\n    Parameters\n    ----------\n    partition : str\n        Slurm partition to request blocks from. If unspecified or ``None``, no partition slurm directive will be specified.\n    account : str\n        Slurm account to which to charge resources used by the job. If unspecified or ``None``, the job will use the\n        user's default account.\n    qos : str\n        Slurm queue to place job in. If unspecified or ``None``, no queue slurm directive will be specified.\n    constraint : str\n        Slurm job constraint, often used to choose cpu or gpu type. If unspecified or ``None``, no constraint slurm directive will be added.\n    channel : Channel\n        Channel for accessing this provider. Possible channels include\n        :class:`~parsl.channels.LocalChannel` (the default),\n        :class:`~parsl.channels.SSHChannel`, or\n        :class:`~parsl.channels.SSHInteractiveLoginChannel`.\n    nodes_per_block : int\n        Nodes to provision per block.\n    cores_per_node : int\n        Specify the number of cores to provision per node. If set to None, executors\n        will assume all cores on the node are available for computation. Default is None.\n    mem_per_node : int\n        Specify the real memory to provision per node in GB. If set to None, no\n        explicit request to the scheduler will be made. Default is None.\n    min_blocks : int\n        Minimum number of blocks to maintain.\n    max_blocks : int\n        Maximum number of blocks to maintain.\n    parallelism : float\n        Ratio of provisioned task slots to active tasks. A parallelism value of 1 represents aggressive\n        scaling where as many resources as possible are used; parallelism close to 0 represents\n        the opposite situation in which as few resources as possible (i.e., min_blocks) are used.\n    walltime : str\n        Walltime requested per block in HH:MM:SS.\n    scheduler_options : str\n        String to prepend to the #SBATCH blocks in the submit script to the scheduler.\n    regex_job_id : str\n        The regular expression used to extract the job ID from the ``sbatch`` standard output.\n        The default is ``r\"Submitted batch job (?P<id>\\\\S*)\"``, where ``id`` is the regular expression\n        symbolic group for the job ID.\n    worker_init : str\n        Command to be run before starting a worker, such as 'module load Anaconda; source activate env'.\n    exclusive : bool (Default = True)\n        Requests nodes which are not shared with other running jobs.\n    launcher : Launcher\n        Launcher for this provider. Possible launchers include\n        :class:`~parsl.launchers.SingleNodeLauncher` (the default),\n        :class:`~parsl.launchers.SrunLauncher`, or\n        :class:`~parsl.launchers.AprunLauncher`\n    move_files : Optional[Bool]: should files be moved? by default, Parsl will try to move files.\n    \"\"\"\n\n    @typeguard.typechecked\n    def __init__(self,\n                 partition: Optional[str] = None,\n                 account: Optional[str] = None,\n                 qos: Optional[str] = None,\n                 constraint: Optional[str] = None,\n                 channel: Channel = LocalChannel(),\n                 nodes_per_block: int = 1,\n                 cores_per_node: Optional[int] = None,\n                 mem_per_node: Optional[int] = None,\n                 init_blocks: int = 1,\n                 min_blocks: int = 0,\n                 max_blocks: int = 1,\n                 parallelism: float = 1,\n                 walltime: str = \"00:10:00\",\n                 scheduler_options: str = '',\n                 regex_job_id: str = r\"Submitted batch job (?P<id>\\S*)\",\n                 worker_init: str = '',\n                 cmd_timeout: int = 10,\n                 exclusive: bool = True,\n                 move_files: bool = True,\n                 launcher: Launcher = SingleNodeLauncher()):\n        label = 'slurm'\n        super().__init__(label,\n                         channel,\n                         nodes_per_block,\n                         init_blocks,\n                         min_blocks,\n                         max_blocks,\n                         parallelism,\n                         walltime,\n                         cmd_timeout=cmd_timeout,\n                         launcher=launcher)\n\n        self.partition = partition\n        self.cores_per_node = cores_per_node\n        self.mem_per_node = mem_per_node\n        self.exclusive = exclusive\n        self.move_files = move_files\n        self.account = account\n        self.scheduler_options = scheduler_options + '\\n'\n        if exclusive:\n            self.scheduler_options += \"#SBATCH --exclusive\\n\"\n        if partition:\n            self.scheduler_options += \"#SBATCH --partition={}\\n\".format(partition)\n        if account:\n            self.scheduler_options += \"#SBATCH --account={}\\n\".format(account)\n        if qos:\n            self.scheduler_options += \"#SBATCH --qos={}\\n\".format(qos)\n        if constraint:\n            self.scheduler_options += \"#SBATCH --constraint={}\\n\".format(constraint)\n\n        self.regex_job_id = regex_job_id\n        self.worker_init = worker_init + '\\n'\n\n    def _status(self):\n        '''Returns the status list for a list of job_ids\n\n        Args:\n              self\n\n        Returns:\n              [status...] : Status list of all jobs\n        '''\n        job_id_list = ','.join(\n            [jid for jid, job in self.resources.items() if not job['status'].terminal]\n        )\n        if not job_id_list:\n            logger.debug('No active jobs, skipping status update')\n            return\n\n        cmd = \"squeue --noheader --format='%i %t' --job '{0}'\".format(job_id_list)\n        logger.debug(\"Executing %s\", cmd)\n        retcode, stdout, stderr = self.execute_wait(cmd)\n        logger.debug(\"squeue returned %s %s\", stdout, stderr)\n\n        # Execute_wait failed. Do no update\n        if retcode != 0:\n            logger.warning(\"squeue failed with non-zero exit code {}\".format(retcode))\n            return\n\n        jobs_missing = set(self.resources.keys())\n        for line in stdout.split('\\n'):\n            if not line:\n                # Blank line\n                continue\n            job_id, slurm_state = line.split()\n            if slurm_state not in translate_table:\n                logger.warning(f\"Slurm status {slurm_state} is not recognized\")\n            status = translate_table.get(slurm_state, JobState.UNKNOWN)\n            logger.debug(\"Updating job {} with slurm status {} to parsl state {!s}\".format(job_id, slurm_state, status))\n            self.resources[job_id]['status'] = JobStatus(status)\n            jobs_missing.remove(job_id)\n\n        # squeue does not report on jobs that are not running. So we are filling in the\n        # blanks for missing jobs, we might lose some information about why the jobs failed.\n        for missing_job in jobs_missing:\n            logger.debug(\"Updating missing job {} to completed status\".format(missing_job))\n            self.resources[missing_job]['status'] = JobStatus(JobState.COMPLETED)\n\n    def submit(self, command, tasks_per_node, job_name=\"parsl.slurm\"):\n        \"\"\"Submit the command as a slurm job.\n\n        Parameters\n        ----------\n        command : str\n            Command to be made on the remote side.\n        tasks_per_node : int\n            Command invocations to be launched per node\n        job_name : str\n            Name for the job\n        Returns\n        -------\n        None or str\n            If at capacity, returns None; otherwise, a string identifier for the job\n        \"\"\"\n\n        scheduler_options = self.scheduler_options\n        worker_init = self.worker_init\n        if self.mem_per_node is not None:\n            scheduler_options += '#SBATCH --mem={}g\\n'.format(self.mem_per_node)\n            worker_init += 'export PARSL_MEMORY_GB={}\\n'.format(self.mem_per_node)\n        if self.cores_per_node is not None:\n            cpus_per_task = math.floor(self.cores_per_node / tasks_per_node)\n            scheduler_options += '#SBATCH --cpus-per-task={}'.format(cpus_per_task)\n            worker_init += 'export PARSL_CORES={}\\n'.format(cpus_per_task)\n\n        job_name = \"{0}.{1}\".format(job_name, time.time())\n\n        script_path = \"{0}/{1}.submit\".format(self.script_dir, job_name)\n        script_path = os.path.abspath(script_path)\n\n        logger.debug(\"Requesting one block with {} nodes\".format(self.nodes_per_block))\n\n        job_config = {}\n        job_config[\"submit_script_dir\"] = self.channel.script_dir\n        job_config[\"nodes\"] = self.nodes_per_block\n        job_config[\"tasks_per_node\"] = tasks_per_node\n        job_config[\"walltime\"] = wtime_to_minutes(self.walltime)\n        job_config[\"scheduler_options\"] = scheduler_options\n        job_config[\"worker_init\"] = worker_init\n        job_config[\"user_script\"] = command\n\n        # Wrap the command\n        job_config[\"user_script\"] = self.launcher(command,\n                                                  tasks_per_node,\n                                                  self.nodes_per_block)\n\n        logger.debug(\"Writing submit script\")\n        self._write_submit_script(template_string, script_path, job_name, job_config)\n\n        if self.move_files:\n            logger.debug(\"moving files\")\n            channel_script_path = self.channel.push_file(script_path, self.channel.script_dir)\n        else:\n            logger.debug(\"not moving files\")\n            channel_script_path = script_path\n\n        retcode, stdout, stderr = self.execute_wait(\"sbatch {0}\".format(channel_script_path))\n\n        job_id = None\n        if retcode == 0:\n            for line in stdout.split('\\n'):\n                match = re.match(self.regex_job_id, line)\n                if match:\n                    job_id = match.group(\"id\")\n                    self.resources[job_id] = {'job_id': job_id, 'status': JobStatus(JobState.PENDING)}\n                    break\n            else:\n                logger.error(\"Could not read job ID from submit command standard output.\")\n                logger.error(\"Retcode:%s STDOUT:%s STDERR:%s\", retcode, stdout.strip(), stderr.strip())\n        else:\n            logger.error(\"Submit command failed\")\n            logger.error(\"Retcode:%s STDOUT:%s STDERR:%s\", retcode, stdout.strip(), stderr.strip())\n        return job_id\n\n    def cancel(self, job_ids):\n        ''' Cancels the jobs specified by a list of job ids\n\n        Args:\n        job_ids : [<job_id> ...]\n\n        Returns :\n        [True/False...] : If the cancel operation fails the entire list will be False.\n        '''\n\n        job_id_list = ' '.join(job_ids)\n        retcode, stdout, stderr = self.execute_wait(\"scancel {0}\".format(job_id_list))\n        rets = None\n        if retcode == 0:\n            for jid in job_ids:\n                self.resources[jid]['status'] = JobStatus(JobState.CANCELLED)  # Setting state to cancelled\n            rets = [True for i in job_ids]\n        else:\n            rets = [False for i in job_ids]\n\n        return rets\n\n    @property\n    def status_polling_interval(self):\n        return 60\n", "path": "parsl/providers/slurm/slurm.py"}], "after_files": [{"content": "import os\nimport math\nimport time\nimport logging\nimport re\nimport typeguard\n\nfrom typing import Optional\n\nfrom parsl.channels import LocalChannel\nfrom parsl.channels.base import Channel\nfrom parsl.jobs.states import JobState, JobStatus\nfrom parsl.launchers import SingleNodeLauncher\nfrom parsl.launchers.base import Launcher\nfrom parsl.providers.cluster_provider import ClusterProvider\nfrom parsl.providers.slurm.template import template_string\nfrom parsl.utils import RepresentationMixin, wtime_to_minutes\n\nlogger = logging.getLogger(__name__)\n\ntranslate_table = {\n    'PD': JobState.PENDING,\n    'R': JobState.RUNNING,\n    'CA': JobState.CANCELLED,\n    'CF': JobState.PENDING,  # (configuring),\n    'CG': JobState.RUNNING,  # (completing),\n    'CD': JobState.COMPLETED,\n    'F': JobState.FAILED,  # (failed),\n    'TO': JobState.TIMEOUT,  # (timeout),\n    'NF': JobState.FAILED,  # (node failure),\n    'RV': JobState.FAILED,  # (revoked) and\n    'SE': JobState.FAILED   # (special exit state)\n}\n\n\nclass SlurmProvider(ClusterProvider, RepresentationMixin):\n    \"\"\"Slurm Execution Provider\n\n    This provider uses sbatch to submit, squeue for status and scancel to cancel\n    jobs. The sbatch script to be used is created from a template file in this\n    same module.\n\n    Parameters\n    ----------\n    partition : str\n        Slurm partition to request blocks from. If unspecified or ``None``, no partition slurm directive will be specified.\n    account : str\n        Slurm account to which to charge resources used by the job. If unspecified or ``None``, the job will use the\n        user's default account.\n    qos : str\n        Slurm queue to place job in. If unspecified or ``None``, no queue slurm directive will be specified.\n    constraint : str\n        Slurm job constraint, often used to choose cpu or gpu type. If unspecified or ``None``, no constraint slurm directive will be added.\n    channel : Channel\n        Channel for accessing this provider. Possible channels include\n        :class:`~parsl.channels.LocalChannel` (the default),\n        :class:`~parsl.channels.SSHChannel`, or\n        :class:`~parsl.channels.SSHInteractiveLoginChannel`.\n    nodes_per_block : int\n        Nodes to provision per block.\n    cores_per_node : int\n        Specify the number of cores to provision per node. If set to None, executors\n        will assume all cores on the node are available for computation. Default is None.\n    mem_per_node : int\n        Specify the real memory to provision per node in GB. If set to None, no\n        explicit request to the scheduler will be made. Default is None.\n    min_blocks : int\n        Minimum number of blocks to maintain.\n    max_blocks : int\n        Maximum number of blocks to maintain.\n    parallelism : float\n        Ratio of provisioned task slots to active tasks. A parallelism value of 1 represents aggressive\n        scaling where as many resources as possible are used; parallelism close to 0 represents\n        the opposite situation in which as few resources as possible (i.e., min_blocks) are used.\n    walltime : str\n        Walltime requested per block in HH:MM:SS.\n    scheduler_options : str\n        String to prepend to the #SBATCH blocks in the submit script to the scheduler.\n    regex_job_id : str\n        The regular expression used to extract the job ID from the ``sbatch`` standard output.\n        The default is ``r\"Submitted batch job (?P<id>\\\\S*)\"``, where ``id`` is the regular expression\n        symbolic group for the job ID.\n    worker_init : str\n        Command to be run before starting a worker, such as 'module load Anaconda; source activate env'.\n    exclusive : bool (Default = True)\n        Requests nodes which are not shared with other running jobs.\n    launcher : Launcher\n        Launcher for this provider. Possible launchers include\n        :class:`~parsl.launchers.SingleNodeLauncher` (the default),\n        :class:`~parsl.launchers.SrunLauncher`, or\n        :class:`~parsl.launchers.AprunLauncher`\n    move_files : Optional[Bool]: should files be moved? by default, Parsl will try to move files.\n    \"\"\"\n\n    @typeguard.typechecked\n    def __init__(self,\n                 partition: Optional[str] = None,\n                 account: Optional[str] = None,\n                 qos: Optional[str] = None,\n                 constraint: Optional[str] = None,\n                 channel: Channel = LocalChannel(),\n                 nodes_per_block: int = 1,\n                 cores_per_node: Optional[int] = None,\n                 mem_per_node: Optional[int] = None,\n                 init_blocks: int = 1,\n                 min_blocks: int = 0,\n                 max_blocks: int = 1,\n                 parallelism: float = 1,\n                 walltime: str = \"00:10:00\",\n                 scheduler_options: str = '',\n                 regex_job_id: str = r\"Submitted batch job (?P<id>\\S*)\",\n                 worker_init: str = '',\n                 cmd_timeout: int = 10,\n                 exclusive: bool = True,\n                 move_files: bool = True,\n                 launcher: Launcher = SingleNodeLauncher()):\n        label = 'slurm'\n        super().__init__(label,\n                         channel,\n                         nodes_per_block,\n                         init_blocks,\n                         min_blocks,\n                         max_blocks,\n                         parallelism,\n                         walltime,\n                         cmd_timeout=cmd_timeout,\n                         launcher=launcher)\n\n        self.partition = partition\n        self.cores_per_node = cores_per_node\n        self.mem_per_node = mem_per_node\n        self.exclusive = exclusive\n        self.move_files = move_files\n        self.account = account\n        self.qos = qos\n        self.constraint = constraint\n        self.scheduler_options = scheduler_options + '\\n'\n        if exclusive:\n            self.scheduler_options += \"#SBATCH --exclusive\\n\"\n        if partition:\n            self.scheduler_options += \"#SBATCH --partition={}\\n\".format(partition)\n        if account:\n            self.scheduler_options += \"#SBATCH --account={}\\n\".format(account)\n        if qos:\n            self.scheduler_options += \"#SBATCH --qos={}\\n\".format(qos)\n        if constraint:\n            self.scheduler_options += \"#SBATCH --constraint={}\\n\".format(constraint)\n\n        self.regex_job_id = regex_job_id\n        self.worker_init = worker_init + '\\n'\n\n    def _status(self):\n        '''Returns the status list for a list of job_ids\n\n        Args:\n              self\n\n        Returns:\n              [status...] : Status list of all jobs\n        '''\n        job_id_list = ','.join(\n            [jid for jid, job in self.resources.items() if not job['status'].terminal]\n        )\n        if not job_id_list:\n            logger.debug('No active jobs, skipping status update')\n            return\n\n        cmd = \"squeue --noheader --format='%i %t' --job '{0}'\".format(job_id_list)\n        logger.debug(\"Executing %s\", cmd)\n        retcode, stdout, stderr = self.execute_wait(cmd)\n        logger.debug(\"squeue returned %s %s\", stdout, stderr)\n\n        # Execute_wait failed. Do no update\n        if retcode != 0:\n            logger.warning(\"squeue failed with non-zero exit code {}\".format(retcode))\n            return\n\n        jobs_missing = set(self.resources.keys())\n        for line in stdout.split('\\n'):\n            if not line:\n                # Blank line\n                continue\n            job_id, slurm_state = line.split()\n            if slurm_state not in translate_table:\n                logger.warning(f\"Slurm status {slurm_state} is not recognized\")\n            status = translate_table.get(slurm_state, JobState.UNKNOWN)\n            logger.debug(\"Updating job {} with slurm status {} to parsl state {!s}\".format(job_id, slurm_state, status))\n            self.resources[job_id]['status'] = JobStatus(status)\n            jobs_missing.remove(job_id)\n\n        # squeue does not report on jobs that are not running. So we are filling in the\n        # blanks for missing jobs, we might lose some information about why the jobs failed.\n        for missing_job in jobs_missing:\n            logger.debug(\"Updating missing job {} to completed status\".format(missing_job))\n            self.resources[missing_job]['status'] = JobStatus(JobState.COMPLETED)\n\n    def submit(self, command, tasks_per_node, job_name=\"parsl.slurm\"):\n        \"\"\"Submit the command as a slurm job.\n\n        Parameters\n        ----------\n        command : str\n            Command to be made on the remote side.\n        tasks_per_node : int\n            Command invocations to be launched per node\n        job_name : str\n            Name for the job\n        Returns\n        -------\n        None or str\n            If at capacity, returns None; otherwise, a string identifier for the job\n        \"\"\"\n\n        scheduler_options = self.scheduler_options\n        worker_init = self.worker_init\n        if self.mem_per_node is not None:\n            scheduler_options += '#SBATCH --mem={}g\\n'.format(self.mem_per_node)\n            worker_init += 'export PARSL_MEMORY_GB={}\\n'.format(self.mem_per_node)\n        if self.cores_per_node is not None:\n            cpus_per_task = math.floor(self.cores_per_node / tasks_per_node)\n            scheduler_options += '#SBATCH --cpus-per-task={}'.format(cpus_per_task)\n            worker_init += 'export PARSL_CORES={}\\n'.format(cpus_per_task)\n\n        job_name = \"{0}.{1}\".format(job_name, time.time())\n\n        script_path = \"{0}/{1}.submit\".format(self.script_dir, job_name)\n        script_path = os.path.abspath(script_path)\n\n        logger.debug(\"Requesting one block with {} nodes\".format(self.nodes_per_block))\n\n        job_config = {}\n        job_config[\"submit_script_dir\"] = self.channel.script_dir\n        job_config[\"nodes\"] = self.nodes_per_block\n        job_config[\"tasks_per_node\"] = tasks_per_node\n        job_config[\"walltime\"] = wtime_to_minutes(self.walltime)\n        job_config[\"scheduler_options\"] = scheduler_options\n        job_config[\"worker_init\"] = worker_init\n        job_config[\"user_script\"] = command\n\n        # Wrap the command\n        job_config[\"user_script\"] = self.launcher(command,\n                                                  tasks_per_node,\n                                                  self.nodes_per_block)\n\n        logger.debug(\"Writing submit script\")\n        self._write_submit_script(template_string, script_path, job_name, job_config)\n\n        if self.move_files:\n            logger.debug(\"moving files\")\n            channel_script_path = self.channel.push_file(script_path, self.channel.script_dir)\n        else:\n            logger.debug(\"not moving files\")\n            channel_script_path = script_path\n\n        retcode, stdout, stderr = self.execute_wait(\"sbatch {0}\".format(channel_script_path))\n\n        job_id = None\n        if retcode == 0:\n            for line in stdout.split('\\n'):\n                match = re.match(self.regex_job_id, line)\n                if match:\n                    job_id = match.group(\"id\")\n                    self.resources[job_id] = {'job_id': job_id, 'status': JobStatus(JobState.PENDING)}\n                    break\n            else:\n                logger.error(\"Could not read job ID from submit command standard output.\")\n                logger.error(\"Retcode:%s STDOUT:%s STDERR:%s\", retcode, stdout.strip(), stderr.strip())\n        else:\n            logger.error(\"Submit command failed\")\n            logger.error(\"Retcode:%s STDOUT:%s STDERR:%s\", retcode, stdout.strip(), stderr.strip())\n        return job_id\n\n    def cancel(self, job_ids):\n        ''' Cancels the jobs specified by a list of job ids\n\n        Args:\n        job_ids : [<job_id> ...]\n\n        Returns :\n        [True/False...] : If the cancel operation fails the entire list will be False.\n        '''\n\n        job_id_list = ' '.join(job_ids)\n        retcode, stdout, stderr = self.execute_wait(\"scancel {0}\".format(job_id_list))\n        rets = None\n        if retcode == 0:\n            for jid in job_ids:\n                self.resources[jid]['status'] = JobStatus(JobState.CANCELLED)  # Setting state to cancelled\n            rets = [True for i in job_ids]\n        else:\n            rets = [False for i in job_ids]\n\n        return rets\n\n    @property\n    def status_polling_interval(self):\n        return 60\n", "path": "parsl/providers/slurm/slurm.py"}]}