We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Condor provider should set default channel
When running the condor provider without explicitly setting the channel:
```
  File "/afs/hep.wisc.edu/home/awoodard/.local/lib/python3.6/site-packages/parsl-0.7.2-py3.6.egg/parsl/dataflow/dflow.py", line 762, in add_executors
    if executor.provider.channel.script_dir is None:
AttributeError: 'NoneType' object has no attribute 'script_dir'
```



--- 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/condor/condor.py`
Content:
```
1 import logging
2 import os
3 import re
4 import time
5 
6 from parsl.utils import RepresentationMixin
7 from parsl.launchers import SingleNodeLauncher
8 from parsl.providers.condor.template import template_string
9 from parsl.providers.cluster_provider import ClusterProvider
10 
11 logger = logging.getLogger(__name__)
12 
13 # See http://pages.cs.wisc.edu/~adesmet/status.html
14 translate_table = {
15     '1': 'PENDING',
16     '2': 'RUNNING',
17     '3': 'CANCELLED',
18     '4': 'COMPLETED',
19     '5': 'FAILED',
20     '6': 'FAILED',
21 }
22 
23 
24 class CondorProvider(RepresentationMixin, ClusterProvider):
25     """HTCondor Execution Provider.
26 
27     Parameters
28     ----------
29     channel : Channel
30         Channel for accessing this provider. Possible channels include
31         :class:`~parsl.channels.LocalChannel` (the default),
32         :class:`~parsl.channels.SSHChannel`, or
33         :class:`~parsl.channels.SSHInteractiveLoginChannel`.
34     nodes_per_block : int
35         Nodes to provision per block.
36     init_blocks : int
37         Number of blocks to provision at time of initialization
38     min_blocks : int
39         Minimum number of blocks to maintain
40     max_blocks : int
41         Maximum number of blocks to maintain.
42     parallelism : float
43         Ratio of provisioned task slots to active tasks. A parallelism value of 1 represents aggressive
44         scaling where as many resources as possible are used; parallelism close to 0 represents
45         the opposite situation in which as few resources as possible (i.e., min_blocks) are used.
46     environment : dict of str
47         A dictionary of environmant variable name and value pairs which will be set before
48         running a task.
49     project : str
50         Project which the job will be charged against
51     scheduler_options : str
52         String to add specific condor attributes to the HTCondor submit script.
53     worker_init : str
54         Command to be run before starting a worker.
55     requirements : str
56         Condor requirements.
57     launcher : Launcher
58         Launcher for this provider. Possible launchers include
59         :class:`~parsl.launchers.SingleNodeLauncher` (the default),
60     """
61     def __init__(self,
62                  channel=None,
63                  nodes_per_block=1,
64                  init_blocks=1,
65                  min_blocks=0,
66                  max_blocks=10,
67                  parallelism=1,
68                  environment=None,
69                  project='',
70                  scheduler_options='',
71                  walltime="00:10:00",
72                  worker_init='',
73                  launcher=SingleNodeLauncher(),
74                  requirements=''):
75 
76         label = 'condor'
77         super().__init__(label,
78                          channel,
79                          nodes_per_block,
80                          init_blocks,
81                          min_blocks,
82                          max_blocks,
83                          parallelism,
84                          walltime,
85                          launcher)
86 
87         self.provisioned_blocks = 0
88 
89         self.environment = environment if environment is not None else {}
90         for key, value in self.environment.items():
91             # To escape literal quote marks, double them
92             # See: http://research.cs.wisc.edu/htcondor/manual/v8.6/condor_submit.html
93             try:
94                 self.environment[key] = "'{}'".format(value.replace("'", '"').replace('"', '""'))
95             except AttributeError:
96                 pass
97 
98         self.project = project
99         self.scheduler_options = scheduler_options
100         self.worker_init = worker_init
101         self.requirements = requirements
102 
103     def _status(self):
104         """Update the resource dictionary with job statuses."""
105 
106         job_id_list = ' '.join(self.resources.keys())
107         cmd = "condor_q {0} -af:jr JobStatus".format(job_id_list)
108         retcode, stdout, stderr = super().execute_wait(cmd)
109         """
110         Example output:
111 
112         $ condor_q 34524642.0 34524643.0 -af:jr JobStatus
113         34524642.0 2
114         34524643.0 1
115         """
116 
117         for line in stdout.strip().split('\n'):
118             parts = line.split()
119             job_id = parts[0]
120             status = translate_table.get(parts[1], 'UNKNOWN')
121             self.resources[job_id]['status'] = status
122 
123     def status(self, job_ids):
124         """Get the status of a list of jobs identified by their ids.
125 
126         Parameters
127         ----------
128         job_ids : list of int
129             Identifiers of jobs for which the status will be returned.
130 
131         Returns
132         -------
133         List of int
134             Status codes for the requested jobs.
135 
136         """
137         self._status()
138         return [self.resources[jid]['status'] for jid in job_ids]
139 
140     def submit(self, command, blocksize, tasks_per_node, job_name="parsl.auto"):
141         """Submits the command onto an Local Resource Manager job of blocksize parallel elements.
142 
143         example file with the complex case of multiple submits per job:
144             Universe =vanilla
145             output = out.$(Cluster).$(Process)
146             error = err.$(Cluster).$(Process)
147             log = log.$(Cluster)
148             leave_in_queue = true
149             executable = test.sh
150             queue 5
151             executable = foo
152             queue 1
153 
154         $ condor_submit test.sub
155         Submitting job(s)......
156         5 job(s) submitted to cluster 118907.
157         1 job(s) submitted to cluster 118908.
158 
159         Parameters
160         ----------
161         command : str
162             Command to execute
163         blocksize : int
164             Number of blocks to request.
165         job_name : str
166             Job name prefix.
167         tasks_per_node : int
168             command invocations to be launched per node
169         Returns
170         -------
171         None or str
172             None if at capacity and cannot provision more; otherwise the identifier for the job.
173         """
174 
175         logger.debug("Attempting to launch with blocksize: {}".format(blocksize))
176         if self.provisioned_blocks >= self.max_blocks:
177             template = "Provider {} is currently using {} blocks while max_blocks is {}; no blocks will be added"
178             logger.warn(template.format(self.label, self.provisioned_blocks, self.max_blocks))
179             return None
180 
181         # Note: Fix this later to avoid confusing behavior.
182         # We should always allocate blocks in integer counts of node_granularity
183         blocksize = max(self.nodes_per_block, blocksize)
184 
185         job_name = "parsl.{0}.{1}".format(job_name, time.time())
186 
187         script_path = "{0}/{1}.submit".format(self.script_dir, job_name)
188         script_path = os.path.abspath(script_path)
189         userscript_path = "{0}/{1}.script".format(self.script_dir, job_name)
190         userscript_path = os.path.abspath(userscript_path)
191 
192         self.environment["JOBNAME"] = "'{}'".format(job_name)
193 
194         job_config = {}
195         job_config["job_name"] = job_name
196         job_config["submit_script_dir"] = self.channel.script_dir
197         job_config["project"] = self.project
198         job_config["nodes"] = self.nodes_per_block
199         job_config["scheduler_options"] = self.scheduler_options
200         job_config["worker_init"] = self.worker_init
201         job_config["user_script"] = command
202         job_config["tasks_per_node"] = tasks_per_node
203         job_config["requirements"] = self.requirements
204         job_config["environment"] = ' '.join(['{}={}'.format(key, value) for key, value in self.environment.items()])
205 
206         # Move the user script
207         # This is where the command should be wrapped by the launchers.
208         wrapped_command = self.launcher(command,
209                                         tasks_per_node,
210                                         self.nodes_per_block)
211 
212         with open(userscript_path, 'w') as f:
213             f.write(job_config["worker_init"] + '\n' + wrapped_command)
214 
215         user_script_path = self.channel.push_file(userscript_path, self.channel.script_dir)
216         job_config["input_files"] = user_script_path
217         job_config["job_script"] = os.path.basename(user_script_path)
218 
219         # Construct and move the submit script
220         self._write_submit_script(template_string, script_path, job_name, job_config)
221         channel_script_path = self.channel.push_file(script_path, self.channel.script_dir)
222 
223         cmd = "condor_submit {0}".format(channel_script_path)
224         retcode, stdout, stderr = super().execute_wait(cmd, 3)
225         logger.debug("Retcode:%s STDOUT:%s STDERR:%s", retcode, stdout.strip(), stderr.strip())
226 
227         job_id = []
228 
229         if retcode == 0:
230             for line in stdout.split('\n'):
231                 if re.match('^[0-9]', line) is not None:
232                     cluster = line.split(" ")[5]
233                     # We know the first job id ("process" in condor terms) within a
234                     # cluster is 0 and we know the total number of jobs from
235                     # condor_submit, so we use some list comprehensions to expand
236                     # the condor_submit output into job IDs
237                     # e.g., ['118907.0', '118907.1', '118907.2', '118907.3', '118907.4', '118908.0']
238                     processes = [str(x) for x in range(0, int(line[0]))]
239                     job_id += [cluster + process for process in processes]
240 
241             self._add_resource(job_id)
242         return job_id[0]
243 
244     def cancel(self, job_ids):
245         """Cancels the jobs specified by a list of job IDs.
246 
247         Parameters
248         ----------
249         job_ids : list of str
250             The job IDs to cancel.
251 
252         Returns
253         -------
254         list of bool
255             Each entry in the list will be True if the job is cancelled succesfully, otherwise False.
256         """
257 
258         job_id_list = ' '.join(job_ids)
259         cmd = "condor_rm {0}; condor_rm -forcex {0}".format(job_id_list)
260         logger.debug("Attempting removal of jobs : {0}".format(cmd))
261         retcode, stdout, stderr = self.channel.execute_wait(cmd, 3)
262         rets = None
263         if retcode == 0:
264             for jid in job_ids:
265                 self.resources[jid]['status'] = 'CANCELLED'
266             rets = [True for i in job_ids]
267         else:
268             rets = [False for i in job_ids]
269 
270         return rets
271 
272     @property
273     def scaling_enabled(self):
274         return True
275 
276     @property
277     def current_capacity(self):
278         return self
279 
280     def _add_resource(self, job_id):
281         for jid in job_id:
282             self.resources[jid] = {'status': 'PENDING', 'size': 1}
283         return True
284 
285 
286 if __name__ == "__main__":
287 
288     print("None")
289
```
--- 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/condor/condor.py b/parsl/providers/condor/condor.py
--- a/parsl/providers/condor/condor.py
+++ b/parsl/providers/condor/condor.py
@@ -3,6 +3,7 @@
 import re
 import time
 
+from parsl.channels import LocalChannel
 from parsl.utils import RepresentationMixin
 from parsl.launchers import SingleNodeLauncher
 from parsl.providers.condor.template import template_string
@@ -59,7 +60,7 @@
         :class:`~parsl.launchers.SingleNodeLauncher` (the default),
     """
     def __init__(self,
-                 channel=None,
+                 channel=LocalChannel(),
                  nodes_per_block=1,
                  init_blocks=1,
                  min_blocks=0,

{"golden_diff": "diff --git a/parsl/providers/condor/condor.py b/parsl/providers/condor/condor.py\n--- a/parsl/providers/condor/condor.py\n+++ b/parsl/providers/condor/condor.py\n@@ -3,6 +3,7 @@\n import re\n import time\n \n+from parsl.channels import LocalChannel\n from parsl.utils import RepresentationMixin\n from parsl.launchers import SingleNodeLauncher\n from parsl.providers.condor.template import template_string\n@@ -59,7 +60,7 @@\n         :class:`~parsl.launchers.SingleNodeLauncher` (the default),\n     \"\"\"\n     def __init__(self,\n-                 channel=None,\n+                 channel=LocalChannel(),\n                  nodes_per_block=1,\n                  init_blocks=1,\n                  min_blocks=0,\n", "issue": "Condor provider should set default channel\nWhen running the condor provider without explicitly setting the channel:\r\n```\r\n  File \"/afs/hep.wisc.edu/home/awoodard/.local/lib/python3.6/site-packages/parsl-0.7.2-py3.6.egg/parsl/dataflow/dflow.py\", line 762, in add_executors\r\n    if executor.provider.channel.script_dir is None:\r\nAttributeError: 'NoneType' object has no attribute 'script_dir'\r\n```\r\n\r\n\n", "before_files": [{"content": "import logging\nimport os\nimport re\nimport time\n\nfrom parsl.utils import RepresentationMixin\nfrom parsl.launchers import SingleNodeLauncher\nfrom parsl.providers.condor.template import template_string\nfrom parsl.providers.cluster_provider import ClusterProvider\n\nlogger = logging.getLogger(__name__)\n\n# See http://pages.cs.wisc.edu/~adesmet/status.html\ntranslate_table = {\n    '1': 'PENDING',\n    '2': 'RUNNING',\n    '3': 'CANCELLED',\n    '4': 'COMPLETED',\n    '5': 'FAILED',\n    '6': 'FAILED',\n}\n\n\nclass CondorProvider(RepresentationMixin, ClusterProvider):\n    \"\"\"HTCondor Execution Provider.\n\n    Parameters\n    ----------\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    init_blocks : int\n        Number of blocks to provision at time of initialization\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    environment : dict of str\n        A dictionary of environmant variable name and value pairs which will be set before\n        running a task.\n    project : str\n        Project which the job will be charged against\n    scheduler_options : str\n        String to add specific condor attributes to the HTCondor submit script.\n    worker_init : str\n        Command to be run before starting a worker.\n    requirements : str\n        Condor requirements.\n    launcher : Launcher\n        Launcher for this provider. Possible launchers include\n        :class:`~parsl.launchers.SingleNodeLauncher` (the default),\n    \"\"\"\n    def __init__(self,\n                 channel=None,\n                 nodes_per_block=1,\n                 init_blocks=1,\n                 min_blocks=0,\n                 max_blocks=10,\n                 parallelism=1,\n                 environment=None,\n                 project='',\n                 scheduler_options='',\n                 walltime=\"00:10:00\",\n                 worker_init='',\n                 launcher=SingleNodeLauncher(),\n                 requirements=''):\n\n        label = 'condor'\n        super().__init__(label,\n                         channel,\n                         nodes_per_block,\n                         init_blocks,\n                         min_blocks,\n                         max_blocks,\n                         parallelism,\n                         walltime,\n                         launcher)\n\n        self.provisioned_blocks = 0\n\n        self.environment = environment if environment is not None else {}\n        for key, value in self.environment.items():\n            # To escape literal quote marks, double them\n            # See: http://research.cs.wisc.edu/htcondor/manual/v8.6/condor_submit.html\n            try:\n                self.environment[key] = \"'{}'\".format(value.replace(\"'\", '\"').replace('\"', '\"\"'))\n            except AttributeError:\n                pass\n\n        self.project = project\n        self.scheduler_options = scheduler_options\n        self.worker_init = worker_init\n        self.requirements = requirements\n\n    def _status(self):\n        \"\"\"Update the resource dictionary with job statuses.\"\"\"\n\n        job_id_list = ' '.join(self.resources.keys())\n        cmd = \"condor_q {0} -af:jr JobStatus\".format(job_id_list)\n        retcode, stdout, stderr = super().execute_wait(cmd)\n        \"\"\"\n        Example output:\n\n        $ condor_q 34524642.0 34524643.0 -af:jr JobStatus\n        34524642.0 2\n        34524643.0 1\n        \"\"\"\n\n        for line in stdout.strip().split('\\n'):\n            parts = line.split()\n            job_id = parts[0]\n            status = translate_table.get(parts[1], 'UNKNOWN')\n            self.resources[job_id]['status'] = status\n\n    def status(self, job_ids):\n        \"\"\"Get the status of a list of jobs identified by their ids.\n\n        Parameters\n        ----------\n        job_ids : list of int\n            Identifiers of jobs for which the status will be returned.\n\n        Returns\n        -------\n        List of int\n            Status codes for the requested jobs.\n\n        \"\"\"\n        self._status()\n        return [self.resources[jid]['status'] for jid in job_ids]\n\n    def submit(self, command, blocksize, tasks_per_node, job_name=\"parsl.auto\"):\n        \"\"\"Submits the command onto an Local Resource Manager job of blocksize parallel elements.\n\n        example file with the complex case of multiple submits per job:\n            Universe =vanilla\n            output = out.$(Cluster).$(Process)\n            error = err.$(Cluster).$(Process)\n            log = log.$(Cluster)\n            leave_in_queue = true\n            executable = test.sh\n            queue 5\n            executable = foo\n            queue 1\n\n        $ condor_submit test.sub\n        Submitting job(s)......\n        5 job(s) submitted to cluster 118907.\n        1 job(s) submitted to cluster 118908.\n\n        Parameters\n        ----------\n        command : str\n            Command to execute\n        blocksize : int\n            Number of blocks to request.\n        job_name : str\n            Job name prefix.\n        tasks_per_node : int\n            command invocations to be launched per node\n        Returns\n        -------\n        None or str\n            None if at capacity and cannot provision more; otherwise the identifier for the job.\n        \"\"\"\n\n        logger.debug(\"Attempting to launch with blocksize: {}\".format(blocksize))\n        if self.provisioned_blocks >= self.max_blocks:\n            template = \"Provider {} is currently using {} blocks while max_blocks is {}; no blocks will be added\"\n            logger.warn(template.format(self.label, self.provisioned_blocks, self.max_blocks))\n            return None\n\n        # Note: Fix this later to avoid confusing behavior.\n        # We should always allocate blocks in integer counts of node_granularity\n        blocksize = max(self.nodes_per_block, blocksize)\n\n        job_name = \"parsl.{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        userscript_path = \"{0}/{1}.script\".format(self.script_dir, job_name)\n        userscript_path = os.path.abspath(userscript_path)\n\n        self.environment[\"JOBNAME\"] = \"'{}'\".format(job_name)\n\n        job_config = {}\n        job_config[\"job_name\"] = job_name\n        job_config[\"submit_script_dir\"] = self.channel.script_dir\n        job_config[\"project\"] = self.project\n        job_config[\"nodes\"] = self.nodes_per_block\n        job_config[\"scheduler_options\"] = self.scheduler_options\n        job_config[\"worker_init\"] = self.worker_init\n        job_config[\"user_script\"] = command\n        job_config[\"tasks_per_node\"] = tasks_per_node\n        job_config[\"requirements\"] = self.requirements\n        job_config[\"environment\"] = ' '.join(['{}={}'.format(key, value) for key, value in self.environment.items()])\n\n        # Move the user script\n        # This is where the command should be wrapped by the launchers.\n        wrapped_command = self.launcher(command,\n                                        tasks_per_node,\n                                        self.nodes_per_block)\n\n        with open(userscript_path, 'w') as f:\n            f.write(job_config[\"worker_init\"] + '\\n' + wrapped_command)\n\n        user_script_path = self.channel.push_file(userscript_path, self.channel.script_dir)\n        job_config[\"input_files\"] = user_script_path\n        job_config[\"job_script\"] = os.path.basename(user_script_path)\n\n        # Construct and move the submit script\n        self._write_submit_script(template_string, script_path, job_name, job_config)\n        channel_script_path = self.channel.push_file(script_path, self.channel.script_dir)\n\n        cmd = \"condor_submit {0}\".format(channel_script_path)\n        retcode, stdout, stderr = super().execute_wait(cmd, 3)\n        logger.debug(\"Retcode:%s STDOUT:%s STDERR:%s\", retcode, stdout.strip(), stderr.strip())\n\n        job_id = []\n\n        if retcode == 0:\n            for line in stdout.split('\\n'):\n                if re.match('^[0-9]', line) is not None:\n                    cluster = line.split(\" \")[5]\n                    # We know the first job id (\"process\" in condor terms) within a\n                    # cluster is 0 and we know the total number of jobs from\n                    # condor_submit, so we use some list comprehensions to expand\n                    # the condor_submit output into job IDs\n                    # e.g., ['118907.0', '118907.1', '118907.2', '118907.3', '118907.4', '118908.0']\n                    processes = [str(x) for x in range(0, int(line[0]))]\n                    job_id += [cluster + process for process in processes]\n\n            self._add_resource(job_id)\n        return job_id[0]\n\n    def cancel(self, job_ids):\n        \"\"\"Cancels the jobs specified by a list of job IDs.\n\n        Parameters\n        ----------\n        job_ids : list of str\n            The job IDs to cancel.\n\n        Returns\n        -------\n        list of bool\n            Each entry in the list will be True if the job is cancelled succesfully, otherwise False.\n        \"\"\"\n\n        job_id_list = ' '.join(job_ids)\n        cmd = \"condor_rm {0}; condor_rm -forcex {0}\".format(job_id_list)\n        logger.debug(\"Attempting removal of jobs : {0}\".format(cmd))\n        retcode, stdout, stderr = self.channel.execute_wait(cmd, 3)\n        rets = None\n        if retcode == 0:\n            for jid in job_ids:\n                self.resources[jid]['status'] = '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 scaling_enabled(self):\n        return True\n\n    @property\n    def current_capacity(self):\n        return self\n\n    def _add_resource(self, job_id):\n        for jid in job_id:\n            self.resources[jid] = {'status': 'PENDING', 'size': 1}\n        return True\n\n\nif __name__ == \"__main__\":\n\n    print(\"None\")\n", "path": "parsl/providers/condor/condor.py"}], "after_files": [{"content": "import logging\nimport os\nimport re\nimport time\n\nfrom parsl.channels import LocalChannel\nfrom parsl.utils import RepresentationMixin\nfrom parsl.launchers import SingleNodeLauncher\nfrom parsl.providers.condor.template import template_string\nfrom parsl.providers.cluster_provider import ClusterProvider\n\nlogger = logging.getLogger(__name__)\n\n# See http://pages.cs.wisc.edu/~adesmet/status.html\ntranslate_table = {\n    '1': 'PENDING',\n    '2': 'RUNNING',\n    '3': 'CANCELLED',\n    '4': 'COMPLETED',\n    '5': 'FAILED',\n    '6': 'FAILED',\n}\n\n\nclass CondorProvider(RepresentationMixin, ClusterProvider):\n    \"\"\"HTCondor Execution Provider.\n\n    Parameters\n    ----------\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    init_blocks : int\n        Number of blocks to provision at time of initialization\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    environment : dict of str\n        A dictionary of environmant variable name and value pairs which will be set before\n        running a task.\n    project : str\n        Project which the job will be charged against\n    scheduler_options : str\n        String to add specific condor attributes to the HTCondor submit script.\n    worker_init : str\n        Command to be run before starting a worker.\n    requirements : str\n        Condor requirements.\n    launcher : Launcher\n        Launcher for this provider. Possible launchers include\n        :class:`~parsl.launchers.SingleNodeLauncher` (the default),\n    \"\"\"\n    def __init__(self,\n                 channel=LocalChannel(),\n                 nodes_per_block=1,\n                 init_blocks=1,\n                 min_blocks=0,\n                 max_blocks=10,\n                 parallelism=1,\n                 environment=None,\n                 project='',\n                 scheduler_options='',\n                 walltime=\"00:10:00\",\n                 worker_init='',\n                 launcher=SingleNodeLauncher(),\n                 requirements=''):\n\n        label = 'condor'\n        super().__init__(label,\n                         channel,\n                         nodes_per_block,\n                         init_blocks,\n                         min_blocks,\n                         max_blocks,\n                         parallelism,\n                         walltime,\n                         launcher)\n\n        self.provisioned_blocks = 0\n\n        self.environment = environment if environment is not None else {}\n        for key, value in self.environment.items():\n            # To escape literal quote marks, double them\n            # See: http://research.cs.wisc.edu/htcondor/manual/v8.6/condor_submit.html\n            try:\n                self.environment[key] = \"'{}'\".format(value.replace(\"'\", '\"').replace('\"', '\"\"'))\n            except AttributeError:\n                pass\n\n        self.project = project\n        self.scheduler_options = scheduler_options\n        self.worker_init = worker_init\n        self.requirements = requirements\n\n    def _status(self):\n        \"\"\"Update the resource dictionary with job statuses.\"\"\"\n\n        job_id_list = ' '.join(self.resources.keys())\n        cmd = \"condor_q {0} -af:jr JobStatus\".format(job_id_list)\n        retcode, stdout, stderr = super().execute_wait(cmd)\n        \"\"\"\n        Example output:\n\n        $ condor_q 34524642.0 34524643.0 -af:jr JobStatus\n        34524642.0 2\n        34524643.0 1\n        \"\"\"\n\n        for line in stdout.strip().split('\\n'):\n            parts = line.split()\n            job_id = parts[0]\n            status = translate_table.get(parts[1], 'UNKNOWN')\n            self.resources[job_id]['status'] = status\n\n    def status(self, job_ids):\n        \"\"\"Get the status of a list of jobs identified by their ids.\n\n        Parameters\n        ----------\n        job_ids : list of int\n            Identifiers of jobs for which the status will be returned.\n\n        Returns\n        -------\n        List of int\n            Status codes for the requested jobs.\n\n        \"\"\"\n        self._status()\n        return [self.resources[jid]['status'] for jid in job_ids]\n\n    def submit(self, command, blocksize, tasks_per_node, job_name=\"parsl.auto\"):\n        \"\"\"Submits the command onto an Local Resource Manager job of blocksize parallel elements.\n\n        example file with the complex case of multiple submits per job:\n            Universe =vanilla\n            output = out.$(Cluster).$(Process)\n            error = err.$(Cluster).$(Process)\n            log = log.$(Cluster)\n            leave_in_queue = true\n            executable = test.sh\n            queue 5\n            executable = foo\n            queue 1\n\n        $ condor_submit test.sub\n        Submitting job(s)......\n        5 job(s) submitted to cluster 118907.\n        1 job(s) submitted to cluster 118908.\n\n        Parameters\n        ----------\n        command : str\n            Command to execute\n        blocksize : int\n            Number of blocks to request.\n        job_name : str\n            Job name prefix.\n        tasks_per_node : int\n            command invocations to be launched per node\n        Returns\n        -------\n        None or str\n            None if at capacity and cannot provision more; otherwise the identifier for the job.\n        \"\"\"\n\n        logger.debug(\"Attempting to launch with blocksize: {}\".format(blocksize))\n        if self.provisioned_blocks >= self.max_blocks:\n            template = \"Provider {} is currently using {} blocks while max_blocks is {}; no blocks will be added\"\n            logger.warn(template.format(self.label, self.provisioned_blocks, self.max_blocks))\n            return None\n\n        # Note: Fix this later to avoid confusing behavior.\n        # We should always allocate blocks in integer counts of node_granularity\n        blocksize = max(self.nodes_per_block, blocksize)\n\n        job_name = \"parsl.{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        userscript_path = \"{0}/{1}.script\".format(self.script_dir, job_name)\n        userscript_path = os.path.abspath(userscript_path)\n\n        self.environment[\"JOBNAME\"] = \"'{}'\".format(job_name)\n\n        job_config = {}\n        job_config[\"job_name\"] = job_name\n        job_config[\"submit_script_dir\"] = self.channel.script_dir\n        job_config[\"project\"] = self.project\n        job_config[\"nodes\"] = self.nodes_per_block\n        job_config[\"scheduler_options\"] = self.scheduler_options\n        job_config[\"worker_init\"] = self.worker_init\n        job_config[\"user_script\"] = command\n        job_config[\"tasks_per_node\"] = tasks_per_node\n        job_config[\"requirements\"] = self.requirements\n        job_config[\"environment\"] = ' '.join(['{}={}'.format(key, value) for key, value in self.environment.items()])\n\n        # Move the user script\n        # This is where the command should be wrapped by the launchers.\n        wrapped_command = self.launcher(command,\n                                        tasks_per_node,\n                                        self.nodes_per_block)\n\n        with open(userscript_path, 'w') as f:\n            f.write(job_config[\"worker_init\"] + '\\n' + wrapped_command)\n\n        user_script_path = self.channel.push_file(userscript_path, self.channel.script_dir)\n        job_config[\"input_files\"] = user_script_path\n        job_config[\"job_script\"] = os.path.basename(user_script_path)\n\n        # Construct and move the submit script\n        self._write_submit_script(template_string, script_path, job_name, job_config)\n        channel_script_path = self.channel.push_file(script_path, self.channel.script_dir)\n\n        cmd = \"condor_submit {0}\".format(channel_script_path)\n        retcode, stdout, stderr = super().execute_wait(cmd, 3)\n        logger.debug(\"Retcode:%s STDOUT:%s STDERR:%s\", retcode, stdout.strip(), stderr.strip())\n\n        job_id = []\n\n        if retcode == 0:\n            for line in stdout.split('\\n'):\n                if re.match('^[0-9]', line) is not None:\n                    cluster = line.split(\" \")[5]\n                    # We know the first job id (\"process\" in condor terms) within a\n                    # cluster is 0 and we know the total number of jobs from\n                    # condor_submit, so we use some list comprehensions to expand\n                    # the condor_submit output into job IDs\n                    # e.g., ['118907.0', '118907.1', '118907.2', '118907.3', '118907.4', '118908.0']\n                    processes = [str(x) for x in range(0, int(line[0]))]\n                    job_id += [cluster + process for process in processes]\n\n            self._add_resource(job_id)\n        return job_id[0]\n\n    def cancel(self, job_ids):\n        \"\"\"Cancels the jobs specified by a list of job IDs.\n\n        Parameters\n        ----------\n        job_ids : list of str\n            The job IDs to cancel.\n\n        Returns\n        -------\n        list of bool\n            Each entry in the list will be True if the job is cancelled succesfully, otherwise False.\n        \"\"\"\n\n        job_id_list = ' '.join(job_ids)\n        cmd = \"condor_rm {0}; condor_rm -forcex {0}\".format(job_id_list)\n        logger.debug(\"Attempting removal of jobs : {0}\".format(cmd))\n        retcode, stdout, stderr = self.channel.execute_wait(cmd, 3)\n        rets = None\n        if retcode == 0:\n            for jid in job_ids:\n                self.resources[jid]['status'] = '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 scaling_enabled(self):\n        return True\n\n    @property\n    def current_capacity(self):\n        return self\n\n    def _add_resource(self, job_id):\n        for jid in job_id:\n            self.resources[jid] = {'status': 'PENDING', 'size': 1}\n        return True\n\n\nif __name__ == \"__main__\":\n\n    print(\"None\")\n", "path": "parsl/providers/condor/condor.py"}]}