parsl 2023.11.13__py3-none-any.whl → 2023.11.20__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- parsl/__init__.py +1 -0
- parsl/executors/base.py +0 -2
- parsl/executors/high_throughput/executor.py +1 -1
- parsl/executors/radical/__init__.py +4 -0
- parsl/executors/radical/executor.py +550 -0
- parsl/executors/radical/rpex_master.py +42 -0
- parsl/executors/radical/rpex_resources.py +165 -0
- parsl/executors/radical/rpex_worker.py +61 -0
- parsl/executors/status_handling.py +0 -1
- parsl/jobs/job_status_poller.py +2 -3
- parsl/tests/configs/local_radical.py +20 -0
- parsl/tests/configs/local_radical_mpi.py +20 -0
- parsl/tests/test_python_apps/test_garbage_collect.py +1 -1
- parsl/tests/test_radical/__init__.py +0 -0
- parsl/tests/test_radical/test_mpi_funcs.py +27 -0
- parsl/tests/test_regression/test_1606_wait_for_current_tasks.py +1 -1
- parsl/version.py +1 -1
- {parsl-2023.11.13.dist-info → parsl-2023.11.20.dist-info}/METADATA +5 -2
- {parsl-2023.11.13.dist-info → parsl-2023.11.20.dist-info}/RECORD +26 -17
- {parsl-2023.11.13.data → parsl-2023.11.20.data}/scripts/exec_parsl_function.py +0 -0
- {parsl-2023.11.13.data → parsl-2023.11.20.data}/scripts/parsl_coprocess.py +0 -0
- {parsl-2023.11.13.data → parsl-2023.11.20.data}/scripts/process_worker_pool.py +0 -0
- {parsl-2023.11.13.dist-info → parsl-2023.11.20.dist-info}/LICENSE +0 -0
- {parsl-2023.11.13.dist-info → parsl-2023.11.20.dist-info}/WHEEL +0 -0
- {parsl-2023.11.13.dist-info → parsl-2023.11.20.dist-info}/entry_points.txt +0 -0
- {parsl-2023.11.13.dist-info → parsl-2023.11.20.dist-info}/top_level.txt +0 -0
parsl/__init__.py
CHANGED
parsl/executors/base.py
CHANGED
@@ -8,7 +8,7 @@ import datetime
|
|
8
8
|
import pickle
|
9
9
|
import warnings
|
10
10
|
from multiprocessing import Queue
|
11
|
-
from typing import Dict, Sequence
|
11
|
+
from typing import Dict, Sequence
|
12
12
|
from typing import List, Optional, Tuple, Union, Callable
|
13
13
|
import math
|
14
14
|
|
@@ -0,0 +1,550 @@
|
|
1
|
+
"""RadicalPilotExecutor builds on the RADICAL-Pilot/Parsl
|
2
|
+
"""
|
3
|
+
import os
|
4
|
+
import sys
|
5
|
+
import time
|
6
|
+
import parsl
|
7
|
+
import queue
|
8
|
+
import logging
|
9
|
+
import inspect
|
10
|
+
import requests
|
11
|
+
import typeguard
|
12
|
+
import threading as mt
|
13
|
+
|
14
|
+
from functools import partial
|
15
|
+
from typing import Optional, Dict
|
16
|
+
from pathlib import Path, PosixPath
|
17
|
+
from concurrent.futures import Future
|
18
|
+
|
19
|
+
from parsl.app.python import timeout
|
20
|
+
from .rpex_resources import ResourceConfig
|
21
|
+
from parsl.data_provider.files import File
|
22
|
+
from parsl.utils import RepresentationMixin
|
23
|
+
from parsl.app.errors import BashExitFailure
|
24
|
+
from parsl.executors.base import ParslExecutor
|
25
|
+
from parsl.app.errors import RemoteExceptionWrapper
|
26
|
+
from parsl.serialize import pack_apply_message, deserialize
|
27
|
+
from parsl.serialize.errors import SerializationError, DeserializationError
|
28
|
+
|
29
|
+
try:
|
30
|
+
import radical.pilot as rp
|
31
|
+
import radical.utils as ru
|
32
|
+
except ImportError:
|
33
|
+
_rp_enabled = False
|
34
|
+
else:
|
35
|
+
_rp_enabled = True
|
36
|
+
|
37
|
+
|
38
|
+
RPEX = 'RPEX'
|
39
|
+
BASH = 'bash'
|
40
|
+
PYTHON = 'python'
|
41
|
+
|
42
|
+
CWD = os.getcwd()
|
43
|
+
PWD = os.path.abspath(os.path.dirname(__file__))
|
44
|
+
|
45
|
+
PARSL_RP_RESOURCE_MAP = {'cores': 'ranks',
|
46
|
+
'disk': 'lfs_per_rank',
|
47
|
+
'memory': 'mem_per_rank'}
|
48
|
+
|
49
|
+
logger = logging.getLogger(__name__)
|
50
|
+
|
51
|
+
|
52
|
+
class RadicalPilotExecutor(ParslExecutor, RepresentationMixin):
|
53
|
+
"""Executor is designed for executing heterogeneous tasks
|
54
|
+
in terms of type/resource.
|
55
|
+
|
56
|
+
The RadicalPilotExecutor system has the following components:
|
57
|
+
|
58
|
+
1. "start" :creating the RADICAL-executor session and pilot.
|
59
|
+
2. "translate":unwrap/identify/ out of parsl task and construct RP task.
|
60
|
+
3. "submit" :translating and submitting Parsl tasks to Radical Pilot.
|
61
|
+
4. "shut_down":shutting down the RADICAL-executor components.
|
62
|
+
|
63
|
+
Here is a diagram
|
64
|
+
|
65
|
+
.. code:: python
|
66
|
+
|
67
|
+
----------------------------------------------------------------------------
|
68
|
+
Parsl Data Flow Kernel | Task Translator | rp.TaskManager
|
69
|
+
---------------------------------------|-------------------|----------------
|
70
|
+
| |
|
71
|
+
-> Dep. check ------> Parsl_tasks{} <--+--> Parsl Task |
|
72
|
+
Data management +dfk.submit | | |
|
73
|
+
| v |
|
74
|
+
| RP Task(s) -> | submit(task)
|
75
|
+
----------------------------------------------------------------------------
|
76
|
+
|
77
|
+
The RadicalPilotExecutor creates a ``rp.Session``, ``rp.TaskManager``,
|
78
|
+
and ``rp.PilotManager``. The executor receives the parsl apps from the
|
79
|
+
DFK and translates these apps (in-memory) into ``rp.TaskDescription``
|
80
|
+
object to be passed to the ``rp.TaskManager``. This executor has two
|
81
|
+
submission mechanisms:
|
82
|
+
|
83
|
+
1. Default_mode: where the executor submits the tasks directly to
|
84
|
+
RADICAL-Pilot.
|
85
|
+
|
86
|
+
2. Bulk_mode: where the executor accumulates N tasks (functions and
|
87
|
+
executables) and submit them.
|
88
|
+
|
89
|
+
Parameters
|
90
|
+
----------
|
91
|
+
rpex_cfg : :class: `~parsl.executors.rpex_resources.ResourceConfig`
|
92
|
+
a dataclass specifying resource configuration.
|
93
|
+
Default is ResourceConfig instance.
|
94
|
+
|
95
|
+
label : str
|
96
|
+
Label for this executor instance.
|
97
|
+
Default is "RPEX".
|
98
|
+
|
99
|
+
bulk_mode : bool
|
100
|
+
Enable bulk mode submission and execution. Default is False (stream).
|
101
|
+
|
102
|
+
resource : Optional[str]
|
103
|
+
The resource name of the targeted HPC machine or cluster.
|
104
|
+
Default is local.localhost (user local machine).
|
105
|
+
|
106
|
+
runtime : int
|
107
|
+
The maximum runtime for the entire job in minutes.
|
108
|
+
Default is 30.
|
109
|
+
|
110
|
+
working_dir : str
|
111
|
+
The working dir to be used by the executor.
|
112
|
+
|
113
|
+
rpex_pilot_kwargs: Dict of kwargs that are passed directly to the rp.PilotDescription object.
|
114
|
+
|
115
|
+
For more information: https://radicalpilot.readthedocs.io/en/stable/
|
116
|
+
"""
|
117
|
+
|
118
|
+
@typeguard.typechecked
|
119
|
+
def __init__(self,
|
120
|
+
resource: str,
|
121
|
+
label: str = RPEX,
|
122
|
+
bulk_mode: bool = False,
|
123
|
+
working_dir: Optional[str] = None,
|
124
|
+
rpex_cfg: Optional[ResourceConfig] = None, **rpex_pilot_kwargs):
|
125
|
+
|
126
|
+
super().__init__()
|
127
|
+
self.pmgr = None
|
128
|
+
self.tmgr = None
|
129
|
+
self.run_dir = '.'
|
130
|
+
self.label = label
|
131
|
+
self.session = None
|
132
|
+
self.resource = resource
|
133
|
+
self._uid = RPEX.lower()
|
134
|
+
self.bulk_mode = bulk_mode
|
135
|
+
self.working_dir = working_dir
|
136
|
+
self.pilot_kwargs = rpex_pilot_kwargs
|
137
|
+
self.future_tasks: Dict[str, Future] = {}
|
138
|
+
|
139
|
+
if rpex_cfg:
|
140
|
+
self.rpex_cfg = rpex_cfg
|
141
|
+
elif not rpex_cfg and 'local' in resource:
|
142
|
+
self.rpex_cfg = ResourceConfig()
|
143
|
+
else:
|
144
|
+
raise ValueError('Resource config file must be '
|
145
|
+
'specified for a non-local execution')
|
146
|
+
|
147
|
+
def task_state_cb(self, task, state):
|
148
|
+
"""
|
149
|
+
Update the state of Parsl Future tasks
|
150
|
+
Based on RP task state callbacks.
|
151
|
+
"""
|
152
|
+
if not task.uid.startswith('master'):
|
153
|
+
parsl_task = self.future_tasks[task.uid]
|
154
|
+
|
155
|
+
if state == rp.DONE:
|
156
|
+
if task.description['mode'] in [rp.TASK_EXEC,
|
157
|
+
rp.TASK_PROC,
|
158
|
+
rp.TASK_EXECUTABLE]:
|
159
|
+
parsl_task.set_result(int(task.exit_code))
|
160
|
+
else:
|
161
|
+
# we do not support MPI function output
|
162
|
+
# serialization. TODO: To be fixed soon.
|
163
|
+
if not task.description.get('use_mpi'):
|
164
|
+
result = deserialize(eval(task.return_value))
|
165
|
+
parsl_task.set_result(result)
|
166
|
+
else:
|
167
|
+
parsl_task.set_result(task.return_value)
|
168
|
+
|
169
|
+
elif state == rp.CANCELED:
|
170
|
+
parsl_task.cancel()
|
171
|
+
|
172
|
+
elif state == rp.FAILED:
|
173
|
+
if task.description['mode'] in [rp.TASK_EXEC,
|
174
|
+
rp.TASK_EXECUTABLE]:
|
175
|
+
parsl_task.set_exception(BashExitFailure(task.name,
|
176
|
+
task.exit_code))
|
177
|
+
else:
|
178
|
+
if task.exception:
|
179
|
+
# unpack a serialized exception
|
180
|
+
if not task.description.get('use_mpi') or task.description['mode'] == rp.TASK_PROC:
|
181
|
+
self._unpack_and_set_parsl_exception(parsl_task, task.exception)
|
182
|
+
# we do not serialize mpi function exception
|
183
|
+
else:
|
184
|
+
parsl_task.set_exception(eval(task.exception))
|
185
|
+
else:
|
186
|
+
parsl_task.set_exception('Task failed for an unknown reason')
|
187
|
+
|
188
|
+
def start(self):
|
189
|
+
"""Create the Pilot component and pass it.
|
190
|
+
"""
|
191
|
+
logger.info("starting RadicalPilotExecutor")
|
192
|
+
logger.info('Parsl: {0}'.format(parsl.__version__))
|
193
|
+
logger.info('RADICAL pilot: {0}'.format(rp.version))
|
194
|
+
self.session = rp.Session(cfg={'base': self.run_dir},
|
195
|
+
uid=ru.generate_id('rpex.session',
|
196
|
+
mode=ru.ID_PRIVATE))
|
197
|
+
logger.info("RPEX session is created: {0}".format(self.session.path))
|
198
|
+
|
199
|
+
pd_init = {**self.pilot_kwargs,
|
200
|
+
'exit_on_error': True,
|
201
|
+
'resource': self.resource}
|
202
|
+
|
203
|
+
if not self.resource or 'local' in self.resource:
|
204
|
+
# move the agent sandbox to the working dir mainly
|
205
|
+
# for debugging purposes. This will allow parsl
|
206
|
+
# to include the agent sandbox with the ci artifacts.
|
207
|
+
if os.environ.get("LOCAL_SANDBOX"):
|
208
|
+
pd_init['sandbox'] = self.run_dir
|
209
|
+
os.environ["RADICAL_LOG_LVL"] = "DEBUG"
|
210
|
+
|
211
|
+
logger.info("RPEX will be running in the local mode")
|
212
|
+
|
213
|
+
pd = rp.PilotDescription(pd_init)
|
214
|
+
pd.verify()
|
215
|
+
|
216
|
+
self.rpex_cfg = self.rpex_cfg._get_cfg_file(path=self.run_dir)
|
217
|
+
cfg = ru.Config(cfg=ru.read_json(self.rpex_cfg))
|
218
|
+
|
219
|
+
self.master = cfg.master_descr
|
220
|
+
self.n_masters = cfg.n_masters
|
221
|
+
|
222
|
+
tds = list()
|
223
|
+
master_path = '{0}/rpex_master.py'.format(PWD)
|
224
|
+
worker_path = '{0}/rpex_worker.py'.format(PWD)
|
225
|
+
|
226
|
+
for i in range(self.n_masters):
|
227
|
+
td = rp.TaskDescription(self.master)
|
228
|
+
td.mode = rp.RAPTOR_MASTER
|
229
|
+
td.uid = ru.generate_id('master.%(item_counter)06d', ru.ID_CUSTOM,
|
230
|
+
ns=self.session.uid)
|
231
|
+
td.ranks = 1
|
232
|
+
td.cores_per_rank = 1
|
233
|
+
td.arguments = [self.rpex_cfg, i]
|
234
|
+
td.input_staging = self._stage_files([File(master_path),
|
235
|
+
File(worker_path),
|
236
|
+
File(self.rpex_cfg)], mode='in')
|
237
|
+
tds.append(td)
|
238
|
+
|
239
|
+
self.pmgr = rp.PilotManager(session=self.session)
|
240
|
+
self.tmgr = rp.TaskManager(session=self.session)
|
241
|
+
|
242
|
+
# submit pilot(s)
|
243
|
+
pilot = self.pmgr.submit_pilots(pd)
|
244
|
+
if not pilot.description.get('cores'):
|
245
|
+
logger.warning('no "cores" per pilot was set, using default resources {0}'.format(pilot.resources))
|
246
|
+
|
247
|
+
self.tmgr.submit_tasks(tds)
|
248
|
+
|
249
|
+
# prepare or use the current env for the agent/pilot side environment
|
250
|
+
if cfg.pilot_env_mode != 'client':
|
251
|
+
logger.info("creating {0} environment for the executor".format(cfg.pilot_env.name))
|
252
|
+
pilot.prepare_env(env_name=cfg.pilot_env.name,
|
253
|
+
env_spec=cfg.pilot_env.as_dict())
|
254
|
+
else:
|
255
|
+
client_env = sys.prefix
|
256
|
+
logger.info("reusing ({0}) environment for the executor".format(client_env))
|
257
|
+
|
258
|
+
self.tmgr.add_pilots(pilot)
|
259
|
+
self.tmgr.register_callback(self.task_state_cb)
|
260
|
+
|
261
|
+
# create a bulking thread to run the actual task submission
|
262
|
+
# to RP in bulks
|
263
|
+
if self.bulk_mode:
|
264
|
+
self._max_bulk_size = 1024
|
265
|
+
self._max_bulk_time = 3 # seconds
|
266
|
+
self._min_bulk_time = 0.1 # seconds
|
267
|
+
|
268
|
+
self._bulk_queue = queue.Queue()
|
269
|
+
self._bulk_thread = mt.Thread(target=self._bulk_collector)
|
270
|
+
|
271
|
+
self._bulk_thread.daemon = True
|
272
|
+
self._bulk_thread.start()
|
273
|
+
|
274
|
+
return True
|
275
|
+
|
276
|
+
def unwrap(self, func, args):
|
277
|
+
"""
|
278
|
+
Unwrap a parsl app and its args for further processing.
|
279
|
+
|
280
|
+
Parameters
|
281
|
+
----------
|
282
|
+
func : callable
|
283
|
+
The function to be unwrapped.
|
284
|
+
|
285
|
+
args : tuple
|
286
|
+
The arguments associated with the function.
|
287
|
+
|
288
|
+
Returns
|
289
|
+
-------
|
290
|
+
tuple
|
291
|
+
A tuple containing the unwrapped function, adjusted arguments,
|
292
|
+
and task type information.
|
293
|
+
"""
|
294
|
+
|
295
|
+
task_type = ''
|
296
|
+
|
297
|
+
while hasattr(func, '__wrapped__'):
|
298
|
+
func = func.__wrapped__
|
299
|
+
|
300
|
+
try:
|
301
|
+
if isinstance(func, partial):
|
302
|
+
try:
|
303
|
+
task_type = inspect.getsource(func.args[0]).split('\n')[0]
|
304
|
+
if BASH in task_type:
|
305
|
+
task_type = BASH
|
306
|
+
func = func.args[0]
|
307
|
+
else:
|
308
|
+
task_type = PYTHON
|
309
|
+
|
310
|
+
except Exception:
|
311
|
+
logger.exception('unwrap failed')
|
312
|
+
|
313
|
+
return func, args, task_type
|
314
|
+
|
315
|
+
else:
|
316
|
+
task_type = inspect.getsource(func).split('\n')[0]
|
317
|
+
if PYTHON in task_type:
|
318
|
+
task_type = PYTHON
|
319
|
+
else:
|
320
|
+
task_type = ''
|
321
|
+
except Exception as e:
|
322
|
+
raise Exception('failed to obtain task type: {0}'.format(e))
|
323
|
+
|
324
|
+
return func, args, task_type
|
325
|
+
|
326
|
+
def task_translate(self, tid, func, parsl_resource_specification, args, kwargs):
|
327
|
+
"""
|
328
|
+
Convert parsl function to RADICAL-Pilot rp.TaskDescription
|
329
|
+
"""
|
330
|
+
|
331
|
+
task = rp.TaskDescription()
|
332
|
+
task.name = func.__name__
|
333
|
+
|
334
|
+
if parsl_resource_specification and isinstance(parsl_resource_specification, dict):
|
335
|
+
logger.debug('mapping parsl resource specifications >> rp resource specifications')
|
336
|
+
for key, val in parsl_resource_specification.items():
|
337
|
+
if key not in task.as_dict():
|
338
|
+
key = PARSL_RP_RESOURCE_MAP.get(key, None)
|
339
|
+
if not key:
|
340
|
+
logger.warning('ignoring "{0}" key from task resource specification as it is not supported by RP'.format(key))
|
341
|
+
continue
|
342
|
+
setattr(task, key, val)
|
343
|
+
|
344
|
+
func, args, task_type = self.unwrap(func, args)
|
345
|
+
|
346
|
+
if BASH in task_type:
|
347
|
+
if callable(func):
|
348
|
+
# if the user specifies the executable mode then we expect the
|
349
|
+
# a code in a file that need to be executed in an isolated env.
|
350
|
+
if parsl_resource_specification.get('mode') == rp.TASK_EXECUTABLE:
|
351
|
+
# These lines of code are from parsl/app/bash.py
|
352
|
+
try:
|
353
|
+
# Execute the func to get the command
|
354
|
+
bash_app = func(*args, **kwargs)
|
355
|
+
if not isinstance(bash_app, str):
|
356
|
+
raise ValueError("Expected a str for bash_app cmd,"
|
357
|
+
"got: {0}".format(type(bash_app)))
|
358
|
+
except AttributeError as e:
|
359
|
+
raise Exception("failed to obtain bash app cmd") from e
|
360
|
+
|
361
|
+
task.executable = bash_app
|
362
|
+
task.mode = rp.TASK_EXECUTABLE
|
363
|
+
|
364
|
+
# This is the default mode where the bash_app will be executed as
|
365
|
+
# as a single core process by RP. For cores > 1 the user must use
|
366
|
+
# above or use MPI functions if their code is Python.
|
367
|
+
else:
|
368
|
+
task.mode = rp.TASK_PROC
|
369
|
+
task.raptor_id = 'master.%06d' % (tid % self.n_masters)
|
370
|
+
task.executable = self._pack_and_apply_message(func, args, kwargs)
|
371
|
+
|
372
|
+
elif PYTHON in task_type or not task_type:
|
373
|
+
task.mode = rp.TASK_FUNCTION
|
374
|
+
task.raptor_id = 'master.%06d' % (tid % self.n_masters)
|
375
|
+
if kwargs.get('walltime'):
|
376
|
+
func = timeout(func, kwargs['walltime'])
|
377
|
+
|
378
|
+
# we process MPI function differently
|
379
|
+
if 'comm' in kwargs:
|
380
|
+
task.function = rp.PythonTask(func, *args, **kwargs)
|
381
|
+
else:
|
382
|
+
task.function = self._pack_and_apply_message(func, args, kwargs)
|
383
|
+
|
384
|
+
task.input_staging = self._stage_files(kwargs.get("inputs", []),
|
385
|
+
mode='in')
|
386
|
+
task.output_staging = self._stage_files(kwargs.get("outputs", []),
|
387
|
+
mode='out')
|
388
|
+
|
389
|
+
task.input_staging.extend(self._stage_files(list(args), mode='in'))
|
390
|
+
|
391
|
+
self._set_stdout_stderr(task, kwargs)
|
392
|
+
|
393
|
+
try:
|
394
|
+
task.verify()
|
395
|
+
except ru.typeddict.TDKeyError as e:
|
396
|
+
raise Exception(f'{e}. Please check Radical.Pilot TaskDescription documentation')
|
397
|
+
|
398
|
+
return task
|
399
|
+
|
400
|
+
def _pack_and_apply_message(self, func, args, kwargs):
|
401
|
+
try:
|
402
|
+
buffer = pack_apply_message(func, args, kwargs,
|
403
|
+
buffer_threshold=1024 * 1024)
|
404
|
+
task_func = rp.utils.serialize_bson(buffer)
|
405
|
+
except TypeError:
|
406
|
+
raise SerializationError(func.__name__)
|
407
|
+
|
408
|
+
return task_func
|
409
|
+
|
410
|
+
def _unpack_and_set_parsl_exception(self, parsl_task, exception):
|
411
|
+
try:
|
412
|
+
s = rp.utils.deserialize_bson(exception)
|
413
|
+
if isinstance(s, RemoteExceptionWrapper):
|
414
|
+
try:
|
415
|
+
s.reraise()
|
416
|
+
except Exception as e:
|
417
|
+
parsl_task.set_exception(e)
|
418
|
+
elif isinstance(s, Exception):
|
419
|
+
parsl_task.set_exception(s)
|
420
|
+
else:
|
421
|
+
raise ValueError("Unknown exception-like type received: {}".format(type(s)))
|
422
|
+
except Exception as e:
|
423
|
+
parsl_task.set_exception(
|
424
|
+
DeserializationError("Received exception, but handling also threw an exception: {}".format(e)))
|
425
|
+
|
426
|
+
def _set_stdout_stderr(self, task, kwargs):
|
427
|
+
"""
|
428
|
+
set the stdout and stderr of a task
|
429
|
+
"""
|
430
|
+
for k in ['stdout', 'stderr']:
|
431
|
+
k_val = kwargs.get(k, '')
|
432
|
+
if k_val:
|
433
|
+
# check the type of the stderr/out
|
434
|
+
if isinstance(k_val, File):
|
435
|
+
k_val = k_val.filepath
|
436
|
+
elif isinstance(k_val, PosixPath):
|
437
|
+
k_val = k_val.__str__()
|
438
|
+
|
439
|
+
# if the stderr/out has no path
|
440
|
+
# then we consider it local and
|
441
|
+
# we just set the path to the cwd
|
442
|
+
if '/' not in k_val:
|
443
|
+
k_val = CWD + '/' + k_val
|
444
|
+
|
445
|
+
# finally set the stderr/out to
|
446
|
+
# the desired name by the user
|
447
|
+
setattr(task, k, k_val)
|
448
|
+
task.sandbox = Path(k_val).parent.__str__()
|
449
|
+
|
450
|
+
def _stage_files(self, files, mode):
|
451
|
+
"""
|
452
|
+
a function to stage list of input/output
|
453
|
+
files between two locations.
|
454
|
+
"""
|
455
|
+
to_stage = []
|
456
|
+
files = [f for f in files if isinstance(f, File)]
|
457
|
+
for file in files:
|
458
|
+
if mode == 'in':
|
459
|
+
# a workaround RP not supporting
|
460
|
+
# staging https file
|
461
|
+
if file.scheme == 'https':
|
462
|
+
r = requests.get(file.url)
|
463
|
+
p = CWD + '/' + file.filename
|
464
|
+
with open(p, 'wb') as ff:
|
465
|
+
ff.write(r.content)
|
466
|
+
file = File(p)
|
467
|
+
|
468
|
+
f = {'source': file.url,
|
469
|
+
'action': rp.TRANSFER}
|
470
|
+
to_stage.append(f)
|
471
|
+
|
472
|
+
elif mode == 'out':
|
473
|
+
# this indicates that the user
|
474
|
+
# did not provided a specific
|
475
|
+
# output file and RP will stage out
|
476
|
+
# the task.output from pilot://task_folder
|
477
|
+
# to the CWD or file.url
|
478
|
+
if '/' not in file.url:
|
479
|
+
f = {'source': file.filename,
|
480
|
+
'target': file.url,
|
481
|
+
'action': rp.TRANSFER}
|
482
|
+
to_stage.append(f)
|
483
|
+
else:
|
484
|
+
raise ValueError('unknown staging mode')
|
485
|
+
|
486
|
+
return to_stage
|
487
|
+
|
488
|
+
def _bulk_collector(self):
|
489
|
+
|
490
|
+
bulk = list()
|
491
|
+
|
492
|
+
while True:
|
493
|
+
|
494
|
+
now = time.time() # time of last submission
|
495
|
+
|
496
|
+
# collect tasks for min bulk time
|
497
|
+
# NOTE: total collect time could actually be max_time + min_time
|
498
|
+
while time.time() - now < self._max_bulk_time:
|
499
|
+
|
500
|
+
try:
|
501
|
+
task = self._bulk_queue.get(block=True,
|
502
|
+
timeout=self._min_bulk_time)
|
503
|
+
except queue.Empty:
|
504
|
+
task = None
|
505
|
+
|
506
|
+
if task:
|
507
|
+
bulk.append(task)
|
508
|
+
|
509
|
+
if len(bulk) >= self._max_bulk_size:
|
510
|
+
break
|
511
|
+
|
512
|
+
if bulk:
|
513
|
+
logger.debug('submit bulk: %d', len(bulk))
|
514
|
+
self.tmgr.submit_tasks(bulk)
|
515
|
+
bulk = list()
|
516
|
+
|
517
|
+
def submit(self, func, resource_specification, *args, **kwargs):
|
518
|
+
"""
|
519
|
+
Submits tasks in stream mode or bulks (bulk mode)
|
520
|
+
to RADICAL-Pilot rp.TaskManager.
|
521
|
+
"""
|
522
|
+
rp_tid = ru.generate_id('task.%(item_counter)06d', ru.ID_CUSTOM,
|
523
|
+
ns=self.session.uid)
|
524
|
+
parsl_tid = int(rp_tid.split('task.')[1])
|
525
|
+
|
526
|
+
logger.debug("got Task {0} from parsl-dfk".format(parsl_tid))
|
527
|
+
task = self.task_translate(parsl_tid, func, resource_specification, args, kwargs)
|
528
|
+
|
529
|
+
# assign task id for rp task
|
530
|
+
task.uid = rp_tid
|
531
|
+
|
532
|
+
# set the future with corresponding id
|
533
|
+
self.future_tasks[rp_tid] = Future()
|
534
|
+
|
535
|
+
if self.bulk_mode:
|
536
|
+
# push task to rp submit thread
|
537
|
+
self._bulk_queue.put(task)
|
538
|
+
else:
|
539
|
+
# submit the task to rp
|
540
|
+
logger.debug("put {0} to rp-TMGR".format(rp_tid))
|
541
|
+
self.tmgr.submit_tasks(task)
|
542
|
+
|
543
|
+
return self.future_tasks[rp_tid]
|
544
|
+
|
545
|
+
def shutdown(self, hub=True, targets='all', block=False):
|
546
|
+
"""Shutdown the executor, including all RADICAL-Pilot components."""
|
547
|
+
logger.info("RadicalPilotExecutor shutdown")
|
548
|
+
self.session.close(download=True)
|
549
|
+
|
550
|
+
return True
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
import sys
|
4
|
+
|
5
|
+
import radical.utils as ru
|
6
|
+
import radical.pilot as rp
|
7
|
+
|
8
|
+
|
9
|
+
# ------------------------------------------------------------------------------
|
10
|
+
#
|
11
|
+
if __name__ == '__main__':
|
12
|
+
|
13
|
+
# The purpose of this master is to (a) spawn a set or workers
|
14
|
+
# within the same allocation, (b) to distribute work items to
|
15
|
+
# those workers, and (c) to collect the responses again.
|
16
|
+
cfg_fname = str(sys.argv[1])
|
17
|
+
cfg = ru.Config(cfg=ru.read_json(cfg_fname))
|
18
|
+
cfg.rank = int(sys.argv[2])
|
19
|
+
|
20
|
+
worker_descr = cfg.worker_descr
|
21
|
+
n_workers = cfg.n_workers
|
22
|
+
gpus_per_node = cfg.gpus_per_node
|
23
|
+
cores_per_node = cfg.cores_per_node
|
24
|
+
nodes_per_worker = cfg.nodes_per_worker
|
25
|
+
|
26
|
+
# create a master class instance - this will establish communication
|
27
|
+
# to the pilot agent
|
28
|
+
master = rp.raptor.Master(cfg)
|
29
|
+
|
30
|
+
# insert `n` worker into the agent. The agent will schedule (place)
|
31
|
+
# those workers and execute them.
|
32
|
+
worker_descr['ranks'] = nodes_per_worker * cores_per_node
|
33
|
+
worker_descr['gpus_per_rank'] = nodes_per_worker * gpus_per_node
|
34
|
+
worker_ids = master.submit_workers(
|
35
|
+
[rp.TaskDescription(worker_descr) for _ in range(n_workers)])
|
36
|
+
|
37
|
+
# wait for all workers
|
38
|
+
master.wait_workers()
|
39
|
+
master.start()
|
40
|
+
master.join()
|
41
|
+
|
42
|
+
# ------------------------------------------------------------------------------
|
@@ -0,0 +1,165 @@
|
|
1
|
+
import sys
|
2
|
+
import json
|
3
|
+
|
4
|
+
from typing import List
|
5
|
+
|
6
|
+
_setup_paths: List[str]
|
7
|
+
try:
|
8
|
+
import radical.pilot as rp
|
9
|
+
import radical.utils as ru
|
10
|
+
except ImportError:
|
11
|
+
_setup_paths = []
|
12
|
+
else:
|
13
|
+
_setup_paths = [rp.sdist_path,
|
14
|
+
ru.sdist_path]
|
15
|
+
|
16
|
+
|
17
|
+
MPI = "mpi"
|
18
|
+
RP_ENV = "rp"
|
19
|
+
CLIENT = "client"
|
20
|
+
RPEX_ENV = "ve_rpex"
|
21
|
+
MPI_WORKER = "MPIWorker"
|
22
|
+
DEFAULT_WORKER = "DefaultWorker"
|
23
|
+
|
24
|
+
|
25
|
+
class ResourceConfig:
|
26
|
+
"""
|
27
|
+
This ResourceConfig class is an abstraction of the resource
|
28
|
+
configuration of the RAPTOR layer in the RADICAL-Pilot runtime system.
|
29
|
+
|
30
|
+
This class sets up the default configuration values for the executor and
|
31
|
+
allows the user to specify different resource requirements flexibly.
|
32
|
+
|
33
|
+
For more information:
|
34
|
+
https://radicalpilot.readthedocs.io/en/stable/tutorials/raptor.html
|
35
|
+
|
36
|
+
Parameters
|
37
|
+
----------
|
38
|
+
masters : int
|
39
|
+
The number of masters to be deployed by RAPTOR.
|
40
|
+
Default is 1.
|
41
|
+
|
42
|
+
workers : int
|
43
|
+
The number of workers to be deployed by RAPTOR.
|
44
|
+
Default is 1.
|
45
|
+
|
46
|
+
worker_gpus_per_node : int
|
47
|
+
The number of GPUs a worker will operate on per node.
|
48
|
+
Default is 0.
|
49
|
+
|
50
|
+
worker_cores_per_node : int
|
51
|
+
The number of CPU cores a worker will operate on per node.
|
52
|
+
Default is 4.
|
53
|
+
|
54
|
+
cores_per_master : int
|
55
|
+
The number of cores a master will operate on per node.
|
56
|
+
Default is 1.
|
57
|
+
|
58
|
+
nodes_per_worker : int
|
59
|
+
The number of nodes to be occupied by every worker.
|
60
|
+
Default is 1.
|
61
|
+
|
62
|
+
pilot_env_path : str
|
63
|
+
The path to an exisitng pilot environment.
|
64
|
+
Default is an empty string (RADICAL-Pilot will create one).
|
65
|
+
|
66
|
+
pilot_env_name : str
|
67
|
+
The name of the pilot environment.
|
68
|
+
Default is "ve_rpex".
|
69
|
+
|
70
|
+
pilot_env_pre_exec : list
|
71
|
+
List of commands to be executed before starting the pilot environment.
|
72
|
+
Default is an empty list.
|
73
|
+
|
74
|
+
pilot_env_type : str
|
75
|
+
The type of the pilot environment (e.g., 'venv', 'conda').
|
76
|
+
Default is "venv".
|
77
|
+
|
78
|
+
pilot_env_setup : list
|
79
|
+
List of setup commands/packages for the pilot environment.
|
80
|
+
Default setup includes "parsl", rp.sdist_path, and ru.sdist_path.
|
81
|
+
|
82
|
+
python_v : str
|
83
|
+
The Python version to be used in the pilot environment.
|
84
|
+
Default is determined by the system's Python version.
|
85
|
+
|
86
|
+
worker_type : str
|
87
|
+
The type of worker(s) to be deployed by RAPTOR on the compute
|
88
|
+
resources.
|
89
|
+
Default is "DefaultWorker".
|
90
|
+
"""
|
91
|
+
|
92
|
+
masters: int = 1
|
93
|
+
workers: int = 1
|
94
|
+
|
95
|
+
worker_gpus_per_node: int = 0
|
96
|
+
worker_cores_per_node: int = 4
|
97
|
+
|
98
|
+
cores_per_master: int = 1
|
99
|
+
nodes_per_worker: int = 1
|
100
|
+
|
101
|
+
pilot_env_mode: str = CLIENT
|
102
|
+
pilot_env_path: str = ""
|
103
|
+
pilot_env_type: str = "venv"
|
104
|
+
pilot_env_name: str = RP_ENV
|
105
|
+
pilot_env_pre_exec: List[str] = []
|
106
|
+
pilot_env_setup: List[str] = _setup_paths
|
107
|
+
|
108
|
+
python_v: str = f'{sys.version_info[0]}.{sys.version_info[1]}'
|
109
|
+
worker_type: str = DEFAULT_WORKER
|
110
|
+
|
111
|
+
def _get_cfg_file(cls, path=None):
|
112
|
+
|
113
|
+
# Default ENV mode for RP is to reuse
|
114
|
+
# the client side. If this is not the case,
|
115
|
+
# then RP will create a new env named ve_rpex
|
116
|
+
# The user need to make sure that under:
|
117
|
+
# $HOME/.radical/pilot/configs/*_resource.json
|
118
|
+
# that virtenv_mode = local
|
119
|
+
if cls.pilot_env_mode != CLIENT:
|
120
|
+
cls.pilot_env_name = RPEX_ENV
|
121
|
+
|
122
|
+
if MPI in cls.worker_type.lower() and \
|
123
|
+
"mpi4py" not in cls.pilot_env_setup:
|
124
|
+
cls.pilot_env_setup.append("mpi4py")
|
125
|
+
|
126
|
+
cfg = {
|
127
|
+
'n_masters': cls.masters,
|
128
|
+
'n_workers': cls.workers,
|
129
|
+
'gpus_per_node': cls.worker_gpus_per_node,
|
130
|
+
'cores_per_node': cls.worker_cores_per_node,
|
131
|
+
'cores_per_master': cls.cores_per_master,
|
132
|
+
'nodes_per_worker': cls.nodes_per_worker,
|
133
|
+
|
134
|
+
'pilot_env': {
|
135
|
+
"version": cls.python_v,
|
136
|
+
"name": cls.pilot_env_name,
|
137
|
+
"path": cls.pilot_env_path,
|
138
|
+
"type": cls.pilot_env_type,
|
139
|
+
"setup": cls.pilot_env_setup,
|
140
|
+
"pre_exec": cls.pilot_env_pre_exec
|
141
|
+
},
|
142
|
+
|
143
|
+
'pilot_env_mode': cls.pilot_env_mode,
|
144
|
+
|
145
|
+
'master_descr': {
|
146
|
+
"mode": rp.RAPTOR_MASTER,
|
147
|
+
"named_env": cls.pilot_env_name,
|
148
|
+
"executable": "python3 rpex_master.py",
|
149
|
+
},
|
150
|
+
|
151
|
+
'worker_descr': {
|
152
|
+
"mode": rp.RAPTOR_WORKER,
|
153
|
+
"named_env": cls.pilot_env_name,
|
154
|
+
"raptor_file": "./rpex_worker.py",
|
155
|
+
"raptor_class": cls.worker_type if
|
156
|
+
cls.worker_type.lower() != MPI else MPI_WORKER,
|
157
|
+
}}
|
158
|
+
|
159
|
+
# Convert the class instance to a cfg file.
|
160
|
+
config_path = 'rpex.cfg'
|
161
|
+
if path:
|
162
|
+
config_path = path + '/' + config_path
|
163
|
+
with open(config_path, 'w') as f:
|
164
|
+
json.dump(cfg, f, indent=4)
|
165
|
+
return config_path
|
@@ -0,0 +1,61 @@
|
|
1
|
+
import sys
|
2
|
+
import radical.pilot as rp
|
3
|
+
|
4
|
+
import parsl.app.errors as pe
|
5
|
+
from parsl.app.bash import remote_side_bash_executor
|
6
|
+
from parsl.serialize import unpack_apply_message, serialize
|
7
|
+
from parsl.executors.high_throughput.process_worker_pool import execute_task
|
8
|
+
|
9
|
+
|
10
|
+
class ParslWorker:
|
11
|
+
|
12
|
+
def _dispatch_func(self, task):
|
13
|
+
|
14
|
+
try:
|
15
|
+
buffer = rp.utils.deserialize_bson(task['description']['function'])
|
16
|
+
result = execute_task(buffer)
|
17
|
+
val = str(serialize(result, buffer_threshold=1000000))
|
18
|
+
exc = (None, None)
|
19
|
+
ret = 0
|
20
|
+
out = None
|
21
|
+
err = None
|
22
|
+
except Exception:
|
23
|
+
val = None
|
24
|
+
exc = (rp.utils.serialize_bson(pe.RemoteExceptionWrapper(*sys.exc_info())), None)
|
25
|
+
ret = 1
|
26
|
+
out = None
|
27
|
+
err = None
|
28
|
+
|
29
|
+
return out, err, ret, val, exc
|
30
|
+
|
31
|
+
def _dispatch_proc(self, task):
|
32
|
+
|
33
|
+
try:
|
34
|
+
buffer = rp.utils.deserialize_bson(task['description']['executable'])
|
35
|
+
func, args, kwargs = unpack_apply_message(buffer, {}, copy=False)
|
36
|
+
ret = remote_side_bash_executor(func, *args, **kwargs)
|
37
|
+
exc = (None, None)
|
38
|
+
val = None
|
39
|
+
out = None
|
40
|
+
err = None
|
41
|
+
except Exception:
|
42
|
+
val = None
|
43
|
+
exc = (rp.utils.serialize_bson(pe.RemoteExceptionWrapper(*sys.exc_info())), None)
|
44
|
+
ret = 1
|
45
|
+
out = None
|
46
|
+
err = None
|
47
|
+
|
48
|
+
return out, err, ret, val, exc
|
49
|
+
|
50
|
+
|
51
|
+
class MPIWorker(rp.raptor.MPIWorker):
|
52
|
+
def _dispatch_func(self, task):
|
53
|
+
return super()._dispatch_func(task)
|
54
|
+
|
55
|
+
|
56
|
+
class DefaultWorker(rp.raptor.DefaultWorker):
|
57
|
+
def _dispatch_func(self, task):
|
58
|
+
return ParslWorker()._dispatch_func(task)
|
59
|
+
|
60
|
+
def _dispatch_proc(self, task):
|
61
|
+
return ParslWorker()._dispatch_proc(task)
|
@@ -6,7 +6,6 @@ from abc import abstractmethod, abstractproperty
|
|
6
6
|
from concurrent.futures import Future
|
7
7
|
from typing import List, Any, Dict, Optional, Tuple, Union, Callable
|
8
8
|
|
9
|
-
import parsl # noqa F401
|
10
9
|
from parsl.executors.base import ParslExecutor
|
11
10
|
from parsl.executors.errors import BadStateException, ScalingFailed
|
12
11
|
from parsl.jobs.states import JobStatus, JobState
|
parsl/jobs/job_status_poller.py
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
import logging
|
2
|
-
import parsl
|
2
|
+
import parsl
|
3
3
|
import time
|
4
4
|
import zmq
|
5
|
-
from typing import Dict, Sequence
|
6
|
-
from typing import List # noqa F401 (used in type annotation)
|
5
|
+
from typing import Dict, List, Sequence
|
7
6
|
|
8
7
|
from parsl.jobs.states import JobStatus, JobState
|
9
8
|
from parsl.jobs.strategy import Strategy
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
from parsl.config import Config
|
4
|
+
from parsl.executors.radical import RadicalPilotExecutor
|
5
|
+
from parsl.executors.radical import ResourceConfig
|
6
|
+
|
7
|
+
|
8
|
+
rpex_cfg = ResourceConfig()
|
9
|
+
|
10
|
+
|
11
|
+
def fresh_config():
|
12
|
+
|
13
|
+
return Config(
|
14
|
+
executors=[
|
15
|
+
RadicalPilotExecutor(
|
16
|
+
label='RPEXBulk',
|
17
|
+
rpex_cfg=rpex_cfg,
|
18
|
+
bulk_mode=True,
|
19
|
+
resource='local.localhost',
|
20
|
+
runtime=30, cores=4)])
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import os
|
2
|
+
from parsl.config import Config
|
3
|
+
|
4
|
+
|
5
|
+
def fresh_config():
|
6
|
+
from parsl.executors.radical import ResourceConfig
|
7
|
+
from parsl.executors.radical import RadicalPilotExecutor
|
8
|
+
|
9
|
+
rpex_cfg = ResourceConfig()
|
10
|
+
rpex_cfg.worker_type = "MPI"
|
11
|
+
rpex_cfg.worker_cores_per_node = 7
|
12
|
+
|
13
|
+
return Config(
|
14
|
+
executors=[
|
15
|
+
RadicalPilotExecutor(
|
16
|
+
label='RPEXMPI',
|
17
|
+
rpex_cfg=rpex_cfg,
|
18
|
+
bulk_mode=True,
|
19
|
+
resource='local.localhost',
|
20
|
+
runtime=30, cores=8)])
|
File without changes
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import parsl
|
2
|
+
import pytest
|
3
|
+
|
4
|
+
from parsl.tests.configs.local_radical_mpi import fresh_config as local_config
|
5
|
+
|
6
|
+
|
7
|
+
@parsl.python_app
|
8
|
+
def test_mpi_func(msg, sleep, comm=None, parsl_resource_specification={}):
|
9
|
+
import time
|
10
|
+
msg = 'hello %d/%d: %s' % (comm.rank, comm.size, msg)
|
11
|
+
time.sleep(sleep)
|
12
|
+
print(msg)
|
13
|
+
return comm.size
|
14
|
+
|
15
|
+
|
16
|
+
apps = []
|
17
|
+
|
18
|
+
|
19
|
+
@pytest.mark.local
|
20
|
+
def test_radical_mpi(n=7):
|
21
|
+
# rank size should be > 1 for the
|
22
|
+
# radical runtime system to run this function in MPI env
|
23
|
+
for i in range(2, n):
|
24
|
+
spec = {'ranks': i}
|
25
|
+
t = test_mpi_func(msg='mpi.func.%06d' % i, sleep=1, comm=None, parsl_resource_specification=spec)
|
26
|
+
apps.append(t)
|
27
|
+
assert [len(app.result()) for app in apps] == list(range(2, n))
|
parsl/version.py
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: parsl
|
3
|
-
Version: 2023.11.
|
3
|
+
Version: 2023.11.20
|
4
4
|
Summary: Simple data dependent workflows in Python
|
5
5
|
Home-page: https://github.com/Parsl/parsl
|
6
|
-
Download-URL: https://github.com/Parsl/parsl/archive/2023.11.
|
6
|
+
Download-URL: https://github.com/Parsl/parsl/archive/2023.11.20.tar.gz
|
7
7
|
Author: The Parsl Team
|
8
8
|
Author-email: parsl@googlegroups.com
|
9
9
|
License: Apache 2.0
|
@@ -53,6 +53,7 @@ Requires-Dist: pyyaml ; extra == 'all'
|
|
53
53
|
Requires-Dist: cffi ; extra == 'all'
|
54
54
|
Requires-Dist: jsonschema ; extra == 'all'
|
55
55
|
Requires-Dist: proxystore ; extra == 'all'
|
56
|
+
Requires-Dist: radical.pilot ; extra == 'all'
|
56
57
|
Provides-Extra: aws
|
57
58
|
Requires-Dist: boto3 ; extra == 'aws'
|
58
59
|
Provides-Extra: azure
|
@@ -79,6 +80,8 @@ Provides-Extra: oauth_ssh
|
|
79
80
|
Requires-Dist: oauth-ssh >=0.9 ; extra == 'oauth_ssh'
|
80
81
|
Provides-Extra: proxystore
|
81
82
|
Requires-Dist: proxystore ; extra == 'proxystore'
|
83
|
+
Provides-Extra: radical-pilot
|
84
|
+
Requires-Dist: radical.pilot ; extra == 'radical-pilot'
|
82
85
|
Provides-Extra: visualization
|
83
86
|
Requires-Dist: pydot ; extra == 'visualization'
|
84
87
|
Requires-Dist: networkx <2.6,>=2.5 ; extra == 'visualization'
|
@@ -1,4 +1,4 @@
|
|
1
|
-
parsl/__init__.py,sha256=
|
1
|
+
parsl/__init__.py,sha256=hq8rJmP59wzd9-yxaGcmq5gPpshOopH-Y1K0BkUBNY0,1843
|
2
2
|
parsl/addresses.py,sha256=L4RjQ-jGY9RfT-hBpsGw1uCzWaIdrEKxcPWV-TkGJes,4767
|
3
3
|
parsl/config.py,sha256=ysUWBfm9bygayHHdItaJbP4oozkHJJmVQVnWCt5igjE,6808
|
4
4
|
parsl/errors.py,sha256=SzINzQFZDBDbj9l-DPQznD0TbGkNhHIRAPkcBCogf_A,1019
|
@@ -7,7 +7,7 @@ parsl/multiprocessing.py,sha256=uY64wcQmWt2rgylQm4lmr3HE8AxwFGeQQj4l1jKnnrY,1970
|
|
7
7
|
parsl/process_loggers.py,sha256=1G3Rfrh5wuZNo2X03grG4kTYPGOxz7hHCyG6L_A3b0A,1137
|
8
8
|
parsl/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
9
|
parsl/utils.py,sha256=_flbNpTu6IXHbzIyE5JkUbOBIK4poc1R1bjBtwJUVdo,11622
|
10
|
-
parsl/version.py,sha256=
|
10
|
+
parsl/version.py,sha256=BWsiVCPivo19VLVK8LWkn31R3xA4BSgmmc-A9-ilTX4,131
|
11
11
|
parsl/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
12
|
parsl/app/app.py,sha256=wAHchJetgnicT1pn0NJKDeDX0lV3vDFlG8cQd_Ciax4,8522
|
13
13
|
parsl/app/bash.py,sha256=bx9x1XFwkOTpZZD3CPwnVL9SyNRDjbUGtOnuGLvxN_8,5396
|
@@ -70,9 +70,9 @@ parsl/dataflow/rundirs.py,sha256=XKmBZpBEIsGACBhYOkbbs2e5edC0pQegJcSlk4FWeag,115
|
|
70
70
|
parsl/dataflow/states.py,sha256=hV6mfv-y4A6xrujeQglcomnfEs7y3Xm2g6JFwC6dvgQ,2612
|
71
71
|
parsl/dataflow/taskrecord.py,sha256=QlsOUacZZq-sU25P-VmA0xDVUktRFEVRmJoWv3Oyzz0,3105
|
72
72
|
parsl/executors/__init__.py,sha256=J50N97Nm9YRjz6K0oNXDxUYIsDjL43_tp3LVb2w7n-M,381
|
73
|
-
parsl/executors/base.py,sha256=
|
73
|
+
parsl/executors/base.py,sha256=DyKzXZztPagh9xQykTUuJRMR9g3i6qkbZi8YpWrIfXM,4340
|
74
74
|
parsl/executors/errors.py,sha256=xVswxgi7vmJcUMCeYDAPK8sQT2kHFFROVoOr0dnmcWE,2098
|
75
|
-
parsl/executors/status_handling.py,sha256=
|
75
|
+
parsl/executors/status_handling.py,sha256=GNBYrAhOpHSnmJA0NouK9by_uJxXn8vlPgBhcp0pSFo,10940
|
76
76
|
parsl/executors/threads.py,sha256=bMU3JFghm17Lpcua13pr3NgQhkUDDc2mqvF2yJBrVNQ,3353
|
77
77
|
parsl/executors/flux/__init__.py,sha256=P9grTTeRPXfqXurFhlSS7XhmE6tTbnCnyQ1f9b-oYHE,136
|
78
78
|
parsl/executors/flux/execute_parsl_task.py,sha256=yUG_WjZLcX8LrgPl26mpEBWZhQMlVNbRLGu08yIjdf4,1553
|
@@ -80,13 +80,18 @@ parsl/executors/flux/executor.py,sha256=tf9xPmWgEsgEjzs89dJ-sMx-QaqRpM1R1crX3tp0
|
|
80
80
|
parsl/executors/flux/flux_instance_manager.py,sha256=tTEOATClm9SwdgLeBRWPC6D55iNDuh0YxqJOw3c3eQ4,2036
|
81
81
|
parsl/executors/high_throughput/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
82
82
|
parsl/executors/high_throughput/errors.py,sha256=vl69wLuVOplbKxHI9WphEGBExHWkTn5n8T9QhBXuNH0,380
|
83
|
-
parsl/executors/high_throughput/executor.py,sha256=
|
83
|
+
parsl/executors/high_throughput/executor.py,sha256=LXKYFoFqtCQ5H-B3_NI-Syncln8NlwcfWF01sd-eQcc,33806
|
84
84
|
parsl/executors/high_throughput/interchange.py,sha256=tX_EvQf7WkSKMJG-TNmA-WADjhtKZqviYpM406Td4dA,29334
|
85
85
|
parsl/executors/high_throughput/manager_record.py,sha256=T8-JVMfDJU6SJfzJRooD0mO8AHGMXlcn3PBOM0m_vng,366
|
86
86
|
parsl/executors/high_throughput/monitoring_info.py,sha256=3gQpwQjjNDEBz0cQqJZB6hRiwLiWwXs83zkQDmbOwxY,297
|
87
87
|
parsl/executors/high_throughput/probe.py,sha256=lvnuf-vBv57tHvFh-J51F9sDYBES7jCgs6KYgWvmKRs,2749
|
88
88
|
parsl/executors/high_throughput/process_worker_pool.py,sha256=SFDDeDmKmVbP94rKDG9cV1tnO75zoXj4-InakIWCRZk,33032
|
89
89
|
parsl/executors/high_throughput/zmq_pipes.py,sha256=3-UrPu4DlXYb6JufjBcENspLd31Qk5URDaZP6IyC6SM,5720
|
90
|
+
parsl/executors/radical/__init__.py,sha256=CKbtV2numw5QvgIBq1htMUrt9TqDCIC2zifyf2svTNU,186
|
91
|
+
parsl/executors/radical/executor.py,sha256=ZYycq58jXlBlhmIO1355JCK1xIJHkspiy62NN1XiMYQ,20729
|
92
|
+
parsl/executors/radical/rpex_master.py,sha256=nMGxYWw3r-8_vZVnEwfB5eCfdTqXkeQDP5yvU0jXgc8,1368
|
93
|
+
parsl/executors/radical/rpex_resources.py,sha256=d7QlJYBkWE-lPauetroEGIbA8RyVILA699LG0uNviws,4960
|
94
|
+
parsl/executors/radical/rpex_worker.py,sha256=xPkjYdlXc3kjN-95NTMiQ5s-yU3JFDCA3sruPTxF3XU,1834
|
90
95
|
parsl/executors/taskvine/__init__.py,sha256=sWIJdvSLgQKul9dlSjIkNat7yBDgU3SrBF3X2yhT86E,293
|
91
96
|
parsl/executors/taskvine/errors.py,sha256=MNS_NjpvHjwevQXOjqjSEBFroqEWi-LT1ZEVZ2C5Dx0,652
|
92
97
|
parsl/executors/taskvine/exec_parsl_function.py,sha256=oUAKbPWwpbzWwQ47bZQlVDxS8txhnhPsonMf3AOEMGQ,7085
|
@@ -105,7 +110,7 @@ parsl/executors/workqueue/parsl_coprocess_stub.py,sha256=_bJmpPIgL42qM6bVzeEKt1M
|
|
105
110
|
parsl/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
106
111
|
parsl/jobs/error_handlers.py,sha256=WYn69jtWgfEsThCMxkGvJ2qoCfebc4IGd4JEolj2ww8,2279
|
107
112
|
parsl/jobs/errors.py,sha256=cpSQXCrlKtuHsQf7usjF-lX8XsDkFnE5kWpmFjiN6OU,178
|
108
|
-
parsl/jobs/job_status_poller.py,sha256=
|
113
|
+
parsl/jobs/job_status_poller.py,sha256=Q57s6FfWP7LjfUSPAOZ0sT59zpUxnyFNE5BZu-xRNvM,4861
|
109
114
|
parsl/jobs/states.py,sha256=olCTMvAGRSDKLxdpH5ZhtwwSLbqDhJPCUJJyTqPkMhk,3809
|
110
115
|
parsl/jobs/strategy.py,sha256=9V07D8bydpyxvNNRH89JZa0Pt-bjjowrSmCc5mv6awY,12903
|
111
116
|
parsl/launchers/__init__.py,sha256=k8zAB3IBP-brfqXUptKwGkvsIRaXjAJZNBJa2XVtY1A,546
|
@@ -214,6 +219,8 @@ parsl/tests/configs/htex_local_alternate.py,sha256=6FsizxGFamcY_GhvJL4dHEXkchAAe
|
|
214
219
|
parsl/tests/configs/htex_local_intask_staging.py,sha256=RZfHQbSN_GcAKssxZgMG6uD_VE9l2X1VMZ5eyqpU-d4,860
|
215
220
|
parsl/tests/configs/htex_local_rsync_staging.py,sha256=KL76K8gEgbdf5S9RGDStBWrKteEY3B_2HuXVHF2b5xI,914
|
216
221
|
parsl/tests/configs/local_adhoc.py,sha256=y91RPFcKFcH--9oljkRywxwy5KgEo6JwxRkFt97YDqM,443
|
222
|
+
parsl/tests/configs/local_radical.py,sha256=-fk0vRUsYETBirm_PHSxBxu8qMwUwqidV_HarDxUZpQ,489
|
223
|
+
parsl/tests/configs/local_radical_mpi.py,sha256=K6V2HbARujaow5DBAUYSIWt1RaYbt898FCVe7UJ5Ckw,570
|
217
224
|
parsl/tests/configs/local_threads.py,sha256=oEnQSlom_JMLFX9_Ln49JAfOP3nSMbw8gTaDJo_NYfo,202
|
218
225
|
parsl/tests/configs/local_threads_checkpoint.py,sha256=Ex7CI1Eo6wVRsem9uXTtbVJrkKc_vOYlVvCNa2RLpIo,286
|
219
226
|
parsl/tests/configs/local_threads_checkpoint_dfk_exit.py,sha256=ECL1n0uBsXDuW3sLCmjiwe8s3Xd7EFIj5wt446w6bh4,254
|
@@ -351,7 +358,7 @@ parsl/tests/test_python_apps/test_fail.py,sha256=0Gld8LS6NB0Io1bU82vVR73twkuL5nW
|
|
351
358
|
parsl/tests/test_python_apps/test_fibonacci_iterative.py,sha256=ly2s5HuB9R53Z2FM_zy0WWdOk01iVhgcwSpQyK6ErIY,573
|
352
359
|
parsl/tests/test_python_apps/test_fibonacci_recursive.py,sha256=q7LMFcu_pJSNPdz8iY0UiRoIweEWIBGwMjQffHWAuDc,592
|
353
360
|
parsl/tests/test_python_apps/test_futures.py,sha256=ye0i4pQz6uPflOOxkJ8BPM1xSphmLthTxaL3OrlY17o,2354
|
354
|
-
parsl/tests/test_python_apps/test_garbage_collect.py,sha256=
|
361
|
+
parsl/tests/test_python_apps/test_garbage_collect.py,sha256=RPntrLuzPkeNbhS7mmqEnHbyOcuV1YVppgZ8BaX-h84,1076
|
355
362
|
parsl/tests/test_python_apps/test_import_fail.py,sha256=Vd8IMa_UsbHYkr3IGnS-rgGb6zKxB1tOTqMZY5lc_xY,691
|
356
363
|
parsl/tests/test_python_apps/test_join.py,sha256=g4VKJwKdIqDLbuNCkIpjVfME0q4mAci_iQ8wAx3uVUc,2679
|
357
364
|
parsl/tests/test_python_apps/test_lifted.py,sha256=WELQv7VueH6I7d6hhMCWQi6s-0sMH8ek_2v4KH0cTFc,3311
|
@@ -368,9 +375,11 @@ parsl/tests/test_python_apps/test_pipeline.py,sha256=vl5bDAzwW0qoTIarzdkFv7cAuaI
|
|
368
375
|
parsl/tests/test_python_apps/test_simple.py,sha256=LYGjdHvRizTpYzZePPvwKSPwrr2MPuMggYTknHeNhWM,733
|
369
376
|
parsl/tests/test_python_apps/test_timeout.py,sha256=uENfT-1DharQkqkeG7a89E-gU1gjE7ATJrBZGUKvZSA,998
|
370
377
|
parsl/tests/test_python_apps/test_type5.py,sha256=kUyA1NuFu-DDXsJNNvJLZVyewZBt7QAOhcGm2DWFTQw,777
|
378
|
+
parsl/tests/test_radical/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
379
|
+
parsl/tests/test_radical/test_mpi_funcs.py,sha256=vCb5u6fjafEhrPgxwOzUjGXC3O6Yjf3lSYaxUE98pKg,744
|
371
380
|
parsl/tests/test_regression/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
372
381
|
parsl/tests/test_regression/test_1480.py,sha256=HNhuw7OYkBGMhN--XgKIl2JPHUj_hXlgL74oS3FqWk4,545
|
373
|
-
parsl/tests/test_regression/test_1606_wait_for_current_tasks.py,sha256=
|
382
|
+
parsl/tests/test_regression/test_1606_wait_for_current_tasks.py,sha256=frqPtaiVysevj9nCWoQlAeh9K1jQO5zaahr9ev_Mx_0,1134
|
374
383
|
parsl/tests/test_regression/test_1653.py,sha256=ki75gl4Sn5nm26r_6qpJOqxrN5UjTWzViVikU0-Ef24,563
|
375
384
|
parsl/tests/test_regression/test_221.py,sha256=jOS0EVu_2sbh10eg5hnivPvhNt0my_50vQ7jQYS1Bfg,520
|
376
385
|
parsl/tests/test_regression/test_226.py,sha256=9EumcLPJzvbD0IUD6-LmPFbf86gaA1Kj8J8J4P4XdTY,1087
|
@@ -404,12 +413,12 @@ parsl/tests/test_threads/test_configs.py,sha256=QA9YjIMAtZ2jmkfOWqBzEfzQQcFVCDiz
|
|
404
413
|
parsl/tests/test_threads/test_lazy_errors.py,sha256=nGhYfCMHFZYSy6YJ4gnAmiLl9SfYs0WVnuvj8DXQ9bw,560
|
405
414
|
parsl/usage_tracking/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
406
415
|
parsl/usage_tracking/usage.py,sha256=TEuAIm_U_G2ojZxvd0bbVa6gZlU61_mVRa2yJC9mGiI,7555
|
407
|
-
parsl-2023.11.
|
408
|
-
parsl-2023.11.
|
409
|
-
parsl-2023.11.
|
410
|
-
parsl-2023.11.
|
411
|
-
parsl-2023.11.
|
412
|
-
parsl-2023.11.
|
413
|
-
parsl-2023.11.
|
414
|
-
parsl-2023.11.
|
415
|
-
parsl-2023.11.
|
416
|
+
parsl-2023.11.20.data/scripts/exec_parsl_function.py,sha256=NtWNeBvRqksej38eRPw8zPBJ1CeW6vgaitve0tfz_qc,7801
|
417
|
+
parsl-2023.11.20.data/scripts/parsl_coprocess.py,sha256=2pS8OUjBTtOO__VZy-OhUUhr72dhgRpJBNbwqm0fzn4,5439
|
418
|
+
parsl-2023.11.20.data/scripts/process_worker_pool.py,sha256=I-hBCzHA9eiTDjyE5W2uYwLi4A0rnKOBSWpicIcL4g4,33018
|
419
|
+
parsl-2023.11.20.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
|
420
|
+
parsl-2023.11.20.dist-info/METADATA,sha256=NwTE626WKDg4f2EEkgiSNKby3oWEcznmmnpNiojiw9I,3818
|
421
|
+
parsl-2023.11.20.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92
|
422
|
+
parsl-2023.11.20.dist-info/entry_points.txt,sha256=XqnsWDYoEcLbsMcpnYGKLEnSBmaIe1YoM5YsBdJG2tI,176
|
423
|
+
parsl-2023.11.20.dist-info/top_level.txt,sha256=PIheYoUFQtF2icLsgOykgU-Cjuwr2Oi6On2jo5RYgRM,6
|
424
|
+
parsl-2023.11.20.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|