parsl 2025.3.17__py3-none-any.whl → 2025.3.24__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/dataflow/dflow.py +1 -3
- parsl/executors/base.py +13 -37
- parsl/executors/flux/executor.py +1 -0
- parsl/executors/globus_compute.py +1 -1
- parsl/executors/high_throughput/executor.py +18 -0
- parsl/executors/radical/executor.py +1 -0
- parsl/executors/status_handling.py +8 -0
- parsl/executors/taskvine/executor.py +1 -0
- parsl/executors/workqueue/executor.py +1 -0
- parsl/monitoring/errors.py +5 -0
- parsl/monitoring/monitoring.py +55 -115
- parsl/monitoring/radios/zmq_router.py +80 -18
- parsl/multiprocessing.py +42 -2
- parsl/tests/test_monitoring/test_exit_helper.py +6 -7
- parsl/tests/test_monitoring/test_fuzz_zmq.py +1 -1
- parsl/tests/test_monitoring/test_radio_zmq.py +27 -0
- parsl/tests/test_monitoring/test_stdouterr.py +3 -0
- parsl/tests/test_shutdown/test_kill_monitoring.py +1 -1
- parsl/usage_tracking/usage.py +2 -2
- parsl/version.py +1 -1
- {parsl-2025.3.17.dist-info → parsl-2025.3.24.dist-info}/METADATA +2 -2
- {parsl-2025.3.17.dist-info → parsl-2025.3.24.dist-info}/RECORD +30 -29
- {parsl-2025.3.17.data → parsl-2025.3.24.data}/scripts/exec_parsl_function.py +0 -0
- {parsl-2025.3.17.data → parsl-2025.3.24.data}/scripts/interchange.py +0 -0
- {parsl-2025.3.17.data → parsl-2025.3.24.data}/scripts/parsl_coprocess.py +0 -0
- {parsl-2025.3.17.data → parsl-2025.3.24.data}/scripts/process_worker_pool.py +0 -0
- {parsl-2025.3.17.dist-info → parsl-2025.3.24.dist-info}/LICENSE +0 -0
- {parsl-2025.3.17.dist-info → parsl-2025.3.24.dist-info}/WHEEL +0 -0
- {parsl-2025.3.17.dist-info → parsl-2025.3.24.dist-info}/entry_points.txt +0 -0
- {parsl-2025.3.17.dist-info → parsl-2025.3.24.dist-info}/top_level.txt +0 -0
parsl/dataflow/dflow.py
CHANGED
@@ -1128,9 +1128,7 @@ class DataFlowKernel:
|
|
1128
1128
|
executor.run_id = self.run_id
|
1129
1129
|
executor.run_dir = self.run_dir
|
1130
1130
|
if self.monitoring:
|
1131
|
-
executor.
|
1132
|
-
executor.hub_zmq_port = self.monitoring.hub_zmq_port
|
1133
|
-
executor.submit_monitoring_radio = self.monitoring.radio
|
1131
|
+
executor.monitoring_messages = self.monitoring.resource_msgs
|
1134
1132
|
if hasattr(executor, 'provider'):
|
1135
1133
|
if hasattr(executor.provider, 'script_dir'):
|
1136
1134
|
executor.provider.script_dir = os.path.join(self.run_dir, 'submit_scripts')
|
parsl/executors/base.py
CHANGED
@@ -1,11 +1,14 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import os
|
2
4
|
from abc import ABCMeta, abstractmethod
|
3
5
|
from concurrent.futures import Future
|
6
|
+
from multiprocessing.queues import Queue
|
4
7
|
from typing import Any, Callable, Dict, Optional
|
5
8
|
|
6
9
|
from typing_extensions import Literal, Self
|
7
10
|
|
8
|
-
from parsl.monitoring.
|
11
|
+
from parsl.monitoring.types import TaggedMonitoringMessage
|
9
12
|
|
10
13
|
|
11
14
|
class ParslExecutor(metaclass=ABCMeta):
|
@@ -42,6 +45,13 @@ class ParslExecutor(metaclass=ABCMeta):
|
|
42
45
|
invariant, not co-variant, and it looks like @typeguard cannot be
|
43
46
|
persuaded otherwise. So if you're implementing an executor and want to
|
44
47
|
@typeguard the constructor, you'll have to use List[Any] here.
|
48
|
+
|
49
|
+
The DataFlowKernel will set this attribute before calling .start(),
|
50
|
+
if monitoring is enabled:
|
51
|
+
|
52
|
+
monitoring_messages: Optional[Queue[TaggedMonitoringMessage]] - an executor
|
53
|
+
can send messages to the monitoring hub by putting them into
|
54
|
+
this queue.
|
45
55
|
"""
|
46
56
|
|
47
57
|
label: str = "undefined"
|
@@ -50,15 +60,11 @@ class ParslExecutor(metaclass=ABCMeta):
|
|
50
60
|
def __init__(
|
51
61
|
self,
|
52
62
|
*,
|
53
|
-
|
54
|
-
hub_zmq_port: Optional[int] = None,
|
55
|
-
submit_monitoring_radio: Optional[MonitoringRadioSender] = None,
|
63
|
+
monitoring_messages: Optional[Queue[TaggedMonitoringMessage]] = None,
|
56
64
|
run_dir: str = ".",
|
57
65
|
run_id: Optional[str] = None,
|
58
66
|
):
|
59
|
-
self.
|
60
|
-
self.hub_zmq_port = hub_zmq_port
|
61
|
-
self.submit_monitoring_radio = submit_monitoring_radio
|
67
|
+
self.monitoring_messages = monitoring_messages
|
62
68
|
self.run_dir = os.path.abspath(run_dir)
|
63
69
|
self.run_id = run_id
|
64
70
|
|
@@ -125,33 +131,3 @@ class ParslExecutor(metaclass=ABCMeta):
|
|
125
131
|
@run_id.setter
|
126
132
|
def run_id(self, value: Optional[str]) -> None:
|
127
133
|
self._run_id = value
|
128
|
-
|
129
|
-
@property
|
130
|
-
def hub_address(self) -> Optional[str]:
|
131
|
-
"""Address to the Hub for monitoring.
|
132
|
-
"""
|
133
|
-
return self._hub_address
|
134
|
-
|
135
|
-
@hub_address.setter
|
136
|
-
def hub_address(self, value: Optional[str]) -> None:
|
137
|
-
self._hub_address = value
|
138
|
-
|
139
|
-
@property
|
140
|
-
def hub_zmq_port(self) -> Optional[int]:
|
141
|
-
"""Port to the Hub for monitoring.
|
142
|
-
"""
|
143
|
-
return self._hub_zmq_port
|
144
|
-
|
145
|
-
@hub_zmq_port.setter
|
146
|
-
def hub_zmq_port(self, value: Optional[int]) -> None:
|
147
|
-
self._hub_zmq_port = value
|
148
|
-
|
149
|
-
@property
|
150
|
-
def submit_monitoring_radio(self) -> Optional[MonitoringRadioSender]:
|
151
|
-
"""Local radio for sending monitoring messages
|
152
|
-
"""
|
153
|
-
return self._submit_monitoring_radio
|
154
|
-
|
155
|
-
@submit_monitoring_radio.setter
|
156
|
-
def submit_monitoring_radio(self, value: Optional[MonitoringRadioSender]) -> None:
|
157
|
-
self._submit_monitoring_radio = value
|
parsl/executors/flux/executor.py
CHANGED
@@ -231,6 +231,7 @@ class FluxExecutor(ParslExecutor, RepresentationMixin):
|
|
231
231
|
|
232
232
|
def start(self):
|
233
233
|
"""Called when DFK starts the executor when the config is loaded."""
|
234
|
+
super().start()
|
234
235
|
os.makedirs(self.working_dir, exist_ok=True)
|
235
236
|
self._submission_thread.start()
|
236
237
|
|
@@ -67,7 +67,7 @@ class GlobusComputeExecutor(ParslExecutor, RepresentationMixin):
|
|
67
67
|
|
68
68
|
def start(self) -> None:
|
69
69
|
""" Start the Globus Compute Executor """
|
70
|
-
|
70
|
+
super().start()
|
71
71
|
|
72
72
|
def submit(self, func: Callable, resource_specification: Dict[str, Any], *args: Any, **kwargs: Any) -> Future:
|
73
73
|
""" Submit func to globus-compute
|
@@ -29,6 +29,7 @@ from parsl.executors.high_throughput.manager_selector import (
|
|
29
29
|
)
|
30
30
|
from parsl.executors.status_handling import BlockProviderExecutor
|
31
31
|
from parsl.jobs.states import TERMINAL_STATES, JobState, JobStatus
|
32
|
+
from parsl.monitoring.radios.zmq_router import ZMQRadioReceiver, start_zmq_receiver
|
32
33
|
from parsl.process_loggers import wrap_with_logs
|
33
34
|
from parsl.providers import LocalProvider
|
34
35
|
from parsl.providers.base import ExecutionProvider
|
@@ -334,6 +335,10 @@ class HighThroughputExecutor(BlockProviderExecutor, RepresentationMixin, UsageIn
|
|
334
335
|
self._result_queue_thread_exit = threading.Event()
|
335
336
|
self._result_queue_thread: Optional[threading.Thread] = None
|
336
337
|
|
338
|
+
self.zmq_monitoring: Optional[ZMQRadioReceiver]
|
339
|
+
self.zmq_monitoring = None
|
340
|
+
self.hub_zmq_port = None
|
341
|
+
|
337
342
|
radio_mode = "htex"
|
338
343
|
enable_mpi_mode: bool = False
|
339
344
|
mpi_launcher: str = "mpiexec"
|
@@ -407,6 +412,7 @@ class HighThroughputExecutor(BlockProviderExecutor, RepresentationMixin, UsageIn
|
|
407
412
|
def start(self):
|
408
413
|
"""Create the Interchange process and connect to it.
|
409
414
|
"""
|
415
|
+
super().start()
|
410
416
|
if self.encrypted and self.cert_dir is None:
|
411
417
|
logger.debug("Creating CurveZMQ certificates")
|
412
418
|
self.cert_dir = curvezmq.create_certificates(self.logdir)
|
@@ -427,6 +433,15 @@ class HighThroughputExecutor(BlockProviderExecutor, RepresentationMixin, UsageIn
|
|
427
433
|
self.loopback_address, self.interchange_port_range, self.cert_dir
|
428
434
|
)
|
429
435
|
|
436
|
+
if self.monitoring_messages is not None:
|
437
|
+
self.zmq_monitoring = start_zmq_receiver(monitoring_messages=self.monitoring_messages,
|
438
|
+
loopback_address=self.loopback_address,
|
439
|
+
port_range=self.interchange_port_range,
|
440
|
+
logdir=self.logdir,
|
441
|
+
worker_debug=self.worker_debug,
|
442
|
+
)
|
443
|
+
self.hub_zmq_port = self.zmq_monitoring.port
|
444
|
+
|
430
445
|
self._result_queue_thread = None
|
431
446
|
self._start_result_queue_thread()
|
432
447
|
self._start_local_interchange_process()
|
@@ -861,6 +876,9 @@ class HighThroughputExecutor(BlockProviderExecutor, RepresentationMixin, UsageIn
|
|
861
876
|
if self._result_queue_thread:
|
862
877
|
self._result_queue_thread.join()
|
863
878
|
|
879
|
+
if self.zmq_monitoring:
|
880
|
+
self.zmq_monitoring.close()
|
881
|
+
|
864
882
|
logger.info("Finished HighThroughputExecutor shutdown attempt")
|
865
883
|
|
866
884
|
def get_usage_information(self):
|
@@ -215,6 +215,7 @@ class RadicalPilotExecutor(ParslExecutor, RepresentationMixin):
|
|
215
215
|
"""Create the Pilot component and pass it.
|
216
216
|
"""
|
217
217
|
logger.info("starting RadicalPilotExecutor")
|
218
|
+
super().start()
|
218
219
|
logger.info('Parsl: {0}'.format(parsl.__version__))
|
219
220
|
logger.info('RADICAL pilot: {0}'.format(rp.version))
|
220
221
|
self.session = rp.Session(cfg={'base': self.run_dir},
|
@@ -14,6 +14,7 @@ from parsl.executors.errors import BadStateException, ScalingFailed
|
|
14
14
|
from parsl.jobs.error_handlers import noop_error_handler, simple_error_handler
|
15
15
|
from parsl.jobs.states import TERMINAL_STATES, JobState, JobStatus
|
16
16
|
from parsl.monitoring.message_type import MessageType
|
17
|
+
from parsl.monitoring.radios.multiprocessing import MultiprocessingQueueRadioSender
|
17
18
|
from parsl.providers.base import ExecutionProvider
|
18
19
|
from parsl.utils import AtomicIDCounter
|
19
20
|
|
@@ -83,6 +84,13 @@ class BlockProviderExecutor(ParslExecutor):
|
|
83
84
|
# of pending, active and recently terminated blocks
|
84
85
|
self._status = {} # type: Dict[str, JobStatus]
|
85
86
|
|
87
|
+
self.submit_monitoring_radio: Optional[MultiprocessingQueueRadioSender] = None
|
88
|
+
|
89
|
+
def start(self):
|
90
|
+
super().start()
|
91
|
+
if self.monitoring_messages:
|
92
|
+
self.submit_monitoring_radio = MultiprocessingQueueRadioSender(self.monitoring_messages)
|
93
|
+
|
86
94
|
def _make_status_dict(self, block_ids: List[str], status_list: List[JobStatus]) -> Dict[str, JobStatus]:
|
87
95
|
"""Given a list of block ids and a list of corresponding status strings,
|
88
96
|
returns a dictionary mapping each block id to the corresponding status
|
@@ -239,6 +239,7 @@ class TaskVineExecutor(BlockProviderExecutor, putils.RepresentationMixin):
|
|
239
239
|
retrieve Parsl tasks within the TaskVine system.
|
240
240
|
"""
|
241
241
|
|
242
|
+
super().start()
|
242
243
|
# Synchronize connection and communication settings between the manager and factory
|
243
244
|
self.__synchronize_manager_factory_comm_settings()
|
244
245
|
|
@@ -314,6 +314,7 @@ class WorkQueueExecutor(BlockProviderExecutor, putils.RepresentationMixin):
|
|
314
314
|
"""Create submit process and collector thread to create, send, and
|
315
315
|
retrieve Parsl tasks within the Work Queue system.
|
316
316
|
"""
|
317
|
+
super().start()
|
317
318
|
self.tasks_lock = threading.Lock()
|
318
319
|
|
319
320
|
# Create directories for data and results
|
parsl/monitoring/errors.py
CHANGED
parsl/monitoring/monitoring.py
CHANGED
@@ -4,10 +4,9 @@ import logging
|
|
4
4
|
import multiprocessing.synchronize as ms
|
5
5
|
import os
|
6
6
|
import queue
|
7
|
-
|
8
|
-
from multiprocessing.context import ForkProcess as ForkProcessType
|
7
|
+
import warnings
|
9
8
|
from multiprocessing.queues import Queue
|
10
|
-
from typing import TYPE_CHECKING,
|
9
|
+
from typing import TYPE_CHECKING, Any, Optional, Union
|
11
10
|
|
12
11
|
import typeguard
|
13
12
|
|
@@ -15,9 +14,13 @@ from parsl.monitoring.errors import MonitoringHubStartError
|
|
15
14
|
from parsl.monitoring.radios.filesystem_router import filesystem_router_starter
|
16
15
|
from parsl.monitoring.radios.multiprocessing import MultiprocessingQueueRadioSender
|
17
16
|
from parsl.monitoring.radios.udp_router import udp_router_starter
|
18
|
-
from parsl.monitoring.radios.zmq_router import zmq_router_starter
|
19
17
|
from parsl.monitoring.types import TaggedMonitoringMessage
|
20
|
-
from parsl.multiprocessing import
|
18
|
+
from parsl.multiprocessing import (
|
19
|
+
SizedQueue,
|
20
|
+
SpawnEvent,
|
21
|
+
SpawnProcess,
|
22
|
+
join_terminate_close_proc,
|
23
|
+
)
|
21
24
|
from parsl.utils import RepresentationMixin
|
22
25
|
|
23
26
|
_db_manager_excepts: Optional[Exception]
|
@@ -38,7 +41,7 @@ class MonitoringHub(RepresentationMixin):
|
|
38
41
|
def __init__(self,
|
39
42
|
hub_address: str,
|
40
43
|
hub_port: Optional[int] = None,
|
41
|
-
hub_port_range:
|
44
|
+
hub_port_range: Any = None,
|
42
45
|
|
43
46
|
workflow_name: Optional[str] = None,
|
44
47
|
workflow_version: Optional[str] = None,
|
@@ -57,12 +60,11 @@ class MonitoringHub(RepresentationMixin):
|
|
57
60
|
Note that despite the similar name, this is not related to
|
58
61
|
hub_port_range.
|
59
62
|
Default: None
|
60
|
-
hub_port_range :
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
Default: (55050, 56000)
|
63
|
+
hub_port_range : unused
|
64
|
+
Unused, but retained until 2025-09-14 to avoid configuration errors.
|
65
|
+
This value previously configured one ZMQ channel inside the
|
66
|
+
HighThroughputExecutor. That ZMQ channel is now configured by the
|
67
|
+
interchange_port_range parameter of HighThroughputExecutor.
|
66
68
|
workflow_name : str
|
67
69
|
The name for the workflow. Default to the name of the parsl script
|
68
70
|
workflow_version : str
|
@@ -89,6 +91,13 @@ class MonitoringHub(RepresentationMixin):
|
|
89
91
|
|
90
92
|
self.hub_address = hub_address
|
91
93
|
self.hub_port = hub_port
|
94
|
+
|
95
|
+
if hub_port_range is not None:
|
96
|
+
message = "Instead of MonitoringHub.hub_port_range, Use HighThroughputExecutor.interchange_port_range"
|
97
|
+
warnings.warn(message, DeprecationWarning)
|
98
|
+
logger.warning(message)
|
99
|
+
# This is used by RepresentationMixin so needs to exist as an attribute
|
100
|
+
# even though now it is otherwise unused.
|
92
101
|
self.hub_port_range = hub_port_range
|
93
102
|
|
94
103
|
self.logging_endpoint = logging_endpoint
|
@@ -120,90 +129,59 @@ class MonitoringHub(RepresentationMixin):
|
|
120
129
|
# in the future, Queue will allow runtime subscripts.
|
121
130
|
|
122
131
|
if TYPE_CHECKING:
|
123
|
-
zmq_comm_q: Queue[Union[int, str]]
|
124
132
|
udp_comm_q: Queue[Union[int, str]]
|
125
133
|
else:
|
126
|
-
zmq_comm_q: Queue
|
127
134
|
udp_comm_q: Queue
|
128
135
|
|
129
|
-
zmq_comm_q = SizedQueue(maxsize=10)
|
130
136
|
udp_comm_q = SizedQueue(maxsize=10)
|
131
137
|
|
132
138
|
self.resource_msgs: Queue[TaggedMonitoringMessage]
|
133
139
|
self.resource_msgs = SizedQueue()
|
134
140
|
|
135
141
|
self.router_exit_event: ms.Event
|
136
|
-
self.router_exit_event =
|
137
|
-
|
138
|
-
self.
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
self.zmq_router_proc.start()
|
151
|
-
|
152
|
-
self.udp_router_proc = ForkProcess(target=udp_router_starter,
|
153
|
-
kwargs={"comm_q": udp_comm_q,
|
154
|
-
"resource_msgs": self.resource_msgs,
|
155
|
-
"exit_event": self.router_exit_event,
|
156
|
-
"hub_address": self.hub_address,
|
157
|
-
"udp_port": self.hub_port,
|
158
|
-
"run_dir": dfk_run_dir,
|
159
|
-
"logging_level": logging.DEBUG if self.monitoring_debug else logging.INFO,
|
160
|
-
},
|
161
|
-
name="Monitoring-UDP-Router-Process",
|
162
|
-
daemon=True,
|
163
|
-
)
|
142
|
+
self.router_exit_event = SpawnEvent()
|
143
|
+
|
144
|
+
self.udp_router_proc = SpawnProcess(target=udp_router_starter,
|
145
|
+
kwargs={"comm_q": udp_comm_q,
|
146
|
+
"resource_msgs": self.resource_msgs,
|
147
|
+
"exit_event": self.router_exit_event,
|
148
|
+
"hub_address": self.hub_address,
|
149
|
+
"udp_port": self.hub_port,
|
150
|
+
"run_dir": dfk_run_dir,
|
151
|
+
"logging_level": logging.DEBUG if self.monitoring_debug else logging.INFO,
|
152
|
+
},
|
153
|
+
name="Monitoring-UDP-Router-Process",
|
154
|
+
daemon=True,
|
155
|
+
)
|
164
156
|
self.udp_router_proc.start()
|
165
157
|
|
166
158
|
self.dbm_exit_event: ms.Event
|
167
|
-
self.dbm_exit_event =
|
168
|
-
|
169
|
-
self.dbm_proc =
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
159
|
+
self.dbm_exit_event = SpawnEvent()
|
160
|
+
|
161
|
+
self.dbm_proc = SpawnProcess(target=dbm_starter,
|
162
|
+
args=(self.resource_msgs,),
|
163
|
+
kwargs={"run_dir": dfk_run_dir,
|
164
|
+
"logging_level": logging.DEBUG if self.monitoring_debug else logging.INFO,
|
165
|
+
"db_url": self.logging_endpoint,
|
166
|
+
"exit_event": self.dbm_exit_event,
|
167
|
+
},
|
168
|
+
name="Monitoring-DBM-Process",
|
169
|
+
daemon=True,
|
170
|
+
)
|
179
171
|
self.dbm_proc.start()
|
180
|
-
logger.info("Started
|
181
|
-
self.
|
182
|
-
|
183
|
-
self.filesystem_proc =
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
172
|
+
logger.info("Started UDP router process %s and DBM process %s",
|
173
|
+
self.udp_router_proc.pid, self.dbm_proc.pid)
|
174
|
+
|
175
|
+
self.filesystem_proc = SpawnProcess(target=filesystem_router_starter,
|
176
|
+
args=(self.resource_msgs, dfk_run_dir, self.router_exit_event),
|
177
|
+
name="Monitoring-Filesystem-Process",
|
178
|
+
daemon=True
|
179
|
+
)
|
188
180
|
self.filesystem_proc.start()
|
189
181
|
logger.info("Started filesystem radio receiver process %s", self.filesystem_proc.pid)
|
190
182
|
|
191
183
|
self.radio = MultiprocessingQueueRadioSender(self.resource_msgs)
|
192
184
|
|
193
|
-
try:
|
194
|
-
zmq_comm_q_result = zmq_comm_q.get(block=True, timeout=120)
|
195
|
-
zmq_comm_q.close()
|
196
|
-
zmq_comm_q.join_thread()
|
197
|
-
except queue.Empty:
|
198
|
-
logger.error("Monitoring ZMQ Router has not reported port in 120s. Aborting")
|
199
|
-
raise MonitoringHubStartError()
|
200
|
-
|
201
|
-
if isinstance(zmq_comm_q_result, str):
|
202
|
-
logger.error("MonitoringRouter sent an error message: %s", zmq_comm_q_result)
|
203
|
-
raise RuntimeError(f"MonitoringRouter failed to start: {zmq_comm_q_result}")
|
204
|
-
|
205
|
-
self.hub_zmq_port = zmq_comm_q_result
|
206
|
-
|
207
185
|
try:
|
208
186
|
udp_comm_q_result = udp_comm_q.get(block=True, timeout=120)
|
209
187
|
udp_comm_q.close()
|
@@ -232,9 +210,6 @@ class MonitoringHub(RepresentationMixin):
|
|
232
210
|
logger.info("Setting router termination event")
|
233
211
|
self.router_exit_event.set()
|
234
212
|
|
235
|
-
logger.info("Waiting for ZMQ router to terminate")
|
236
|
-
join_terminate_close_proc(self.zmq_router_proc)
|
237
|
-
|
238
213
|
logger.info("Waiting for UDP router to terminate")
|
239
214
|
join_terminate_close_proc(self.udp_router_proc)
|
240
215
|
|
@@ -251,38 +226,3 @@ class MonitoringHub(RepresentationMixin):
|
|
251
226
|
self.resource_msgs.close()
|
252
227
|
self.resource_msgs.join_thread()
|
253
228
|
logger.info("Closed monitoring multiprocessing queues")
|
254
|
-
|
255
|
-
|
256
|
-
def join_terminate_close_proc(process: ForkProcessType, *, timeout: int = 30) -> None:
|
257
|
-
"""Increasingly aggressively terminate a process.
|
258
|
-
|
259
|
-
This function assumes that the process is likely to exit before
|
260
|
-
the join timeout, driven by some other means, such as the
|
261
|
-
MonitoringHub router_exit_event. If the process does not exit, then
|
262
|
-
first terminate() and then kill() will be used to end the process.
|
263
|
-
|
264
|
-
In the case of a very mis-behaving process, this function might take
|
265
|
-
up to 3*timeout to exhaust all termination methods and return.
|
266
|
-
"""
|
267
|
-
logger.debug("Joining process")
|
268
|
-
process.join(timeout)
|
269
|
-
|
270
|
-
# run a sequence of increasingly aggressive steps to shut down the process.
|
271
|
-
if process.is_alive():
|
272
|
-
logger.error("Process did not join. Terminating.")
|
273
|
-
process.terminate()
|
274
|
-
process.join(timeout)
|
275
|
-
if process.is_alive():
|
276
|
-
logger.error("Process did not join after terminate. Killing.")
|
277
|
-
process.kill()
|
278
|
-
process.join(timeout)
|
279
|
-
# This kill should not be caught by any signal handlers so it is
|
280
|
-
# unlikely that this join will timeout. If it does, there isn't
|
281
|
-
# anything further to do except log an error in the next if-block.
|
282
|
-
|
283
|
-
if process.is_alive():
|
284
|
-
logger.error("Process failed to end")
|
285
|
-
# don't call close if the process hasn't ended:
|
286
|
-
# process.close() doesn't work on a running process.
|
287
|
-
else:
|
288
|
-
process.close()
|
@@ -3,16 +3,27 @@ from __future__ import annotations
|
|
3
3
|
import logging
|
4
4
|
import multiprocessing.queues as mpq
|
5
5
|
import os
|
6
|
+
import queue
|
6
7
|
import time
|
7
|
-
from multiprocessing.
|
8
|
+
from multiprocessing.context import SpawnProcess as SpawnProcessType
|
9
|
+
from multiprocessing.queues import Queue as QueueType
|
10
|
+
from multiprocessing.synchronize import Event as EventType
|
8
11
|
from typing import Tuple
|
9
12
|
|
10
13
|
import typeguard
|
11
14
|
import zmq
|
12
15
|
|
16
|
+
from parsl.addresses import tcp_url
|
13
17
|
from parsl.log_utils import set_file_logger
|
18
|
+
from parsl.monitoring.errors import MonitoringRouterStartError
|
14
19
|
from parsl.monitoring.radios.multiprocessing import MultiprocessingQueueRadioSender
|
15
20
|
from parsl.monitoring.types import TaggedMonitoringMessage
|
21
|
+
from parsl.multiprocessing import (
|
22
|
+
SizedQueue,
|
23
|
+
SpawnEvent,
|
24
|
+
SpawnProcess,
|
25
|
+
join_terminate_close_proc,
|
26
|
+
)
|
16
27
|
from parsl.process_loggers import wrap_with_logs
|
17
28
|
from parsl.utils import setproctitle
|
18
29
|
|
@@ -23,21 +34,21 @@ class MonitoringRouter:
|
|
23
34
|
|
24
35
|
def __init__(self,
|
25
36
|
*,
|
26
|
-
|
27
|
-
|
37
|
+
address: str,
|
38
|
+
port_range: Tuple[int, int] = (55050, 56000),
|
28
39
|
|
29
40
|
run_dir: str = ".",
|
30
41
|
logging_level: int = logging.INFO,
|
31
42
|
resource_msgs: mpq.Queue,
|
32
|
-
exit_event:
|
43
|
+
exit_event: EventType,
|
33
44
|
):
|
34
45
|
""" Initializes a monitoring configuration class.
|
35
46
|
|
36
47
|
Parameters
|
37
48
|
----------
|
38
|
-
|
49
|
+
address : str
|
39
50
|
The ip address at which the workers will be able to reach the Hub.
|
40
|
-
|
51
|
+
port_range : tuple(int, int)
|
41
52
|
The MonitoringHub picks ports at random from the range which will be used by Hub.
|
42
53
|
Default: (55050, 56000)
|
43
54
|
run_dir : str
|
@@ -51,11 +62,11 @@ class MonitoringRouter:
|
|
51
62
|
"""
|
52
63
|
os.makedirs(run_dir, exist_ok=True)
|
53
64
|
self.logger = set_file_logger(f"{run_dir}/monitoring_zmq_router.log",
|
54
|
-
name="
|
65
|
+
name="zmq_monitoring_router",
|
55
66
|
level=logging_level)
|
56
67
|
self.logger.debug("Monitoring router starting")
|
57
68
|
|
58
|
-
self.
|
69
|
+
self.address = address
|
59
70
|
|
60
71
|
self.loop_freq = 10.0 # milliseconds
|
61
72
|
|
@@ -64,15 +75,15 @@ class MonitoringRouter:
|
|
64
75
|
self.zmq_receiver_channel.setsockopt(zmq.LINGER, 0)
|
65
76
|
self.zmq_receiver_channel.set_hwm(0)
|
66
77
|
self.zmq_receiver_channel.RCVTIMEO = int(self.loop_freq) # in milliseconds
|
67
|
-
self.logger.debug("
|
68
|
-
self.zmq_receiver_port = self.zmq_receiver_channel.bind_to_random_port(
|
69
|
-
min_port=
|
70
|
-
max_port=
|
78
|
+
self.logger.debug("address: {}. port_range {}".format(address, port_range))
|
79
|
+
self.zmq_receiver_port = self.zmq_receiver_channel.bind_to_random_port(tcp_url(address),
|
80
|
+
min_port=port_range[0],
|
81
|
+
max_port=port_range[1])
|
71
82
|
|
72
83
|
self.target_radio = MultiprocessingQueueRadioSender(resource_msgs)
|
73
84
|
self.exit_event = exit_event
|
74
85
|
|
75
|
-
@wrap_with_logs(target="
|
86
|
+
@wrap_with_logs(target="zmq_monitoring_router")
|
76
87
|
def start(self) -> None:
|
77
88
|
self.logger.info("Starting ZMQ listener")
|
78
89
|
try:
|
@@ -108,17 +119,17 @@ class MonitoringRouter:
|
|
108
119
|
def zmq_router_starter(*,
|
109
120
|
comm_q: mpq.Queue,
|
110
121
|
resource_msgs: mpq.Queue,
|
111
|
-
exit_event:
|
122
|
+
exit_event: EventType,
|
112
123
|
|
113
|
-
|
114
|
-
|
124
|
+
address: str,
|
125
|
+
port_range: Tuple[int, int],
|
115
126
|
|
116
127
|
run_dir: str,
|
117
128
|
logging_level: int) -> None:
|
118
129
|
setproctitle("parsl: monitoring zmq router")
|
119
130
|
try:
|
120
|
-
router = MonitoringRouter(
|
121
|
-
|
131
|
+
router = MonitoringRouter(address=address,
|
132
|
+
port_range=port_range,
|
122
133
|
run_dir=run_dir,
|
123
134
|
logging_level=logging_level,
|
124
135
|
resource_msgs=resource_msgs,
|
@@ -129,3 +140,54 @@ def zmq_router_starter(*,
|
|
129
140
|
else:
|
130
141
|
comm_q.put(router.zmq_receiver_port)
|
131
142
|
router.start()
|
143
|
+
|
144
|
+
|
145
|
+
class ZMQRadioReceiver():
|
146
|
+
def __init__(self, *, process: SpawnProcessType, exit_event: EventType, port: int) -> None:
|
147
|
+
self.process = process
|
148
|
+
self.exit_event = exit_event
|
149
|
+
self.port = port
|
150
|
+
|
151
|
+
def close(self) -> None:
|
152
|
+
self.exit_event.set()
|
153
|
+
join_terminate_close_proc(self.process)
|
154
|
+
|
155
|
+
|
156
|
+
def start_zmq_receiver(*,
|
157
|
+
monitoring_messages: QueueType,
|
158
|
+
loopback_address: str,
|
159
|
+
port_range: Tuple[int, int],
|
160
|
+
logdir: str,
|
161
|
+
worker_debug: bool) -> ZMQRadioReceiver:
|
162
|
+
comm_q = SizedQueue(maxsize=10)
|
163
|
+
|
164
|
+
router_exit_event = SpawnEvent()
|
165
|
+
|
166
|
+
router_proc = SpawnProcess(target=zmq_router_starter,
|
167
|
+
kwargs={"comm_q": comm_q,
|
168
|
+
"resource_msgs": monitoring_messages,
|
169
|
+
"exit_event": router_exit_event,
|
170
|
+
"address": loopback_address,
|
171
|
+
"port_range": port_range,
|
172
|
+
"run_dir": logdir,
|
173
|
+
"logging_level": logging.DEBUG if worker_debug else logging.INFO,
|
174
|
+
},
|
175
|
+
name="Monitoring-ZMQ-Router-Process",
|
176
|
+
daemon=True,
|
177
|
+
)
|
178
|
+
router_proc.start()
|
179
|
+
|
180
|
+
try:
|
181
|
+
logger.debug("Waiting for router process to report port")
|
182
|
+
comm_q_result = comm_q.get(block=True, timeout=120)
|
183
|
+
comm_q.close()
|
184
|
+
comm_q.join_thread()
|
185
|
+
except queue.Empty:
|
186
|
+
logger.error("Monitoring ZMQ Router has not reported port in 120s")
|
187
|
+
raise MonitoringRouterStartError()
|
188
|
+
|
189
|
+
if isinstance(comm_q_result, str):
|
190
|
+
logger.error("MonitoringRouter sent an error message: %s", comm_q_result)
|
191
|
+
raise RuntimeError(f"MonitoringRouter failed to start: {comm_q_result}")
|
192
|
+
|
193
|
+
return ZMQRadioReceiver(process=router_proc, exit_event=router_exit_event, port=comm_q_result)
|
parsl/multiprocessing.py
CHANGED
@@ -6,6 +6,7 @@ import multiprocessing
|
|
6
6
|
import multiprocessing.queues
|
7
7
|
import platform
|
8
8
|
from multiprocessing.context import ForkProcess as ForkProcessType
|
9
|
+
from multiprocessing.context import SpawnProcess as SpawnProcessType
|
9
10
|
from typing import Callable
|
10
11
|
|
11
12
|
logger = logging.getLogger(__name__)
|
@@ -14,6 +15,10 @@ ForkContext = multiprocessing.get_context("fork")
|
|
14
15
|
SpawnContext = multiprocessing.get_context("spawn")
|
15
16
|
|
16
17
|
ForkProcess: Callable[..., ForkProcessType] = ForkContext.Process
|
18
|
+
SpawnProcess: Callable[..., SpawnProcessType] = SpawnContext.Process
|
19
|
+
|
20
|
+
SpawnEvent = SpawnContext.Event
|
21
|
+
SpawnQueue = SpawnContext.Queue
|
17
22
|
|
18
23
|
|
19
24
|
class MacSafeQueue(multiprocessing.queues.Queue):
|
@@ -26,7 +31,7 @@ class MacSafeQueue(multiprocessing.queues.Queue):
|
|
26
31
|
|
27
32
|
def __init__(self, *args, **kwargs):
|
28
33
|
if 'ctx' not in kwargs:
|
29
|
-
kwargs['ctx'] = multiprocessing.get_context()
|
34
|
+
kwargs['ctx'] = multiprocessing.get_context('spawn')
|
30
35
|
super().__init__(*args, **kwargs)
|
31
36
|
self._counter = multiprocessing.Value('i', 0)
|
32
37
|
|
@@ -59,6 +64,41 @@ SizedQueue: Callable[..., multiprocessing.Queue]
|
|
59
64
|
|
60
65
|
if platform.system() != 'Darwin':
|
61
66
|
import multiprocessing
|
62
|
-
SizedQueue =
|
67
|
+
SizedQueue = SpawnQueue
|
63
68
|
else:
|
64
69
|
SizedQueue = MacSafeQueue
|
70
|
+
|
71
|
+
|
72
|
+
def join_terminate_close_proc(process: SpawnProcessType, *, timeout: int = 30) -> None:
|
73
|
+
"""Increasingly aggressively terminate a process.
|
74
|
+
|
75
|
+
This function assumes that the process is likely to exit before
|
76
|
+
the join timeout, driven by some other means, such as the
|
77
|
+
MonitoringHub router_exit_event. If the process does not exit, then
|
78
|
+
first terminate() and then kill() will be used to end the process.
|
79
|
+
|
80
|
+
In the case of a very mis-behaving process, this function might take
|
81
|
+
up to 3*timeout to exhaust all termination methods and return.
|
82
|
+
"""
|
83
|
+
logger.debug("Joining process")
|
84
|
+
process.join(timeout)
|
85
|
+
|
86
|
+
# run a sequence of increasingly aggressive steps to shut down the process.
|
87
|
+
if process.is_alive():
|
88
|
+
logger.error("Process did not join. Terminating.")
|
89
|
+
process.terminate()
|
90
|
+
process.join(timeout)
|
91
|
+
if process.is_alive():
|
92
|
+
logger.error("Process did not join after terminate. Killing.")
|
93
|
+
process.kill()
|
94
|
+
process.join(timeout)
|
95
|
+
# This kill should not be caught by any signal handlers so it is
|
96
|
+
# unlikely that this join will timeout. If it does, there isn't
|
97
|
+
# anything further to do except log an error in the next if-block.
|
98
|
+
|
99
|
+
if process.is_alive():
|
100
|
+
logger.error("Process failed to end")
|
101
|
+
# don't call close if the process hasn't ended:
|
102
|
+
# process.close() doesn't work on a running process.
|
103
|
+
else:
|
104
|
+
process.close()
|
@@ -4,8 +4,7 @@ import signal
|
|
4
4
|
import psutil
|
5
5
|
import pytest
|
6
6
|
|
7
|
-
from parsl.
|
8
|
-
from parsl.multiprocessing import ForkProcess
|
7
|
+
from parsl.multiprocessing import SpawnEvent, SpawnProcess, join_terminate_close_proc
|
9
8
|
|
10
9
|
|
11
10
|
def noop():
|
@@ -14,7 +13,7 @@ def noop():
|
|
14
13
|
|
15
14
|
@pytest.mark.local
|
16
15
|
def test_end_process_already_exited():
|
17
|
-
p =
|
16
|
+
p = SpawnProcess(target=noop)
|
18
17
|
p.start()
|
19
18
|
p.join()
|
20
19
|
join_terminate_close_proc(p)
|
@@ -28,7 +27,7 @@ def hang():
|
|
28
27
|
@pytest.mark.local
|
29
28
|
def test_end_hung_process():
|
30
29
|
"""Test calling against a process that will not exit itself."""
|
31
|
-
p =
|
30
|
+
p = SpawnProcess(target=hang)
|
32
31
|
p.start()
|
33
32
|
pid = p.pid
|
34
33
|
join_terminate_close_proc(p, timeout=1)
|
@@ -46,10 +45,10 @@ def hang_no_sigint(e):
|
|
46
45
|
@pytest.mark.local
|
47
46
|
def test_end_hung_process_no_sigint():
|
48
47
|
"""Test calling against a process that will not exit itself."""
|
49
|
-
e =
|
50
|
-
p =
|
48
|
+
e = SpawnEvent()
|
49
|
+
p = SpawnProcess(target=hang_no_sigint, args=(e,))
|
51
50
|
p.start()
|
52
51
|
pid = p.pid
|
53
|
-
join_terminate_close_proc(p, timeout=
|
52
|
+
join_terminate_close_proc(p, timeout=2)
|
54
53
|
assert not psutil.pid_exists(pid), "process should not exist any more"
|
55
54
|
assert e.is_set(), "hung process should have set event on signal"
|
@@ -45,7 +45,7 @@ def test_row_counts():
|
|
45
45
|
|
46
46
|
# dig out the interchange port...
|
47
47
|
hub_address = parsl.dfk().monitoring.hub_address
|
48
|
-
hub_zmq_port = parsl.dfk().
|
48
|
+
hub_zmq_port = parsl.dfk().executors["htex_Local"].hub_zmq_port
|
49
49
|
|
50
50
|
# this will send a string to a new socket connection
|
51
51
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import pytest
|
2
|
+
|
3
|
+
from parsl.monitoring.radios.zmq import ZMQRadioSender
|
4
|
+
from parsl.monitoring.radios.zmq_router import start_zmq_receiver
|
5
|
+
from parsl.multiprocessing import SpawnQueue
|
6
|
+
|
7
|
+
|
8
|
+
@pytest.mark.local
|
9
|
+
def test_send_recv_message(tmpd_cwd, try_assert):
|
10
|
+
q = SpawnQueue()
|
11
|
+
loopback = "127.0.0.1"
|
12
|
+
r = start_zmq_receiver(monitoring_messages=q,
|
13
|
+
loopback_address=loopback,
|
14
|
+
port_range=(49152, 65535),
|
15
|
+
logdir=str(tmpd_cwd),
|
16
|
+
worker_debug=False)
|
17
|
+
|
18
|
+
s = ZMQRadioSender(loopback, r.port)
|
19
|
+
|
20
|
+
test_msg = ("test", {})
|
21
|
+
s.send(test_msg)
|
22
|
+
|
23
|
+
assert q.get() == test_msg
|
24
|
+
|
25
|
+
assert r.process.is_alive()
|
26
|
+
r.exit_event.set()
|
27
|
+
try_assert(lambda: not r.process.is_alive())
|
@@ -102,6 +102,9 @@ def test_stdstream_to_monitoring(stdx, expected_stdx, stream, tmpd_cwd, caplog):
|
|
102
102
|
kwargs = {stream: stdx}
|
103
103
|
stdapp(**kwargs).result()
|
104
104
|
|
105
|
+
for record in caplog.records:
|
106
|
+
assert record.levelno < logging.ERROR
|
107
|
+
|
105
108
|
engine = sqlalchemy.create_engine(c.monitoring.logging_endpoint)
|
106
109
|
with engine.begin() as connection:
|
107
110
|
|
@@ -30,7 +30,7 @@ def test_no_kills():
|
|
30
30
|
|
31
31
|
@pytest.mark.local
|
32
32
|
@pytest.mark.parametrize("sig", [signal.SIGINT, signal.SIGTERM, signal.SIGKILL, signal.SIGQUIT])
|
33
|
-
@pytest.mark.parametrize("process_attr", ["
|
33
|
+
@pytest.mark.parametrize("process_attr", ["udp_router_proc", "dbm_proc", "filesystem_proc"])
|
34
34
|
def test_kill_monitoring_helper_process(sig, process_attr, try_assert):
|
35
35
|
"""This tests that we can kill a monitoring process and still have successful shutdown.
|
36
36
|
SIGINT emulates some racy behaviour when ctrl-C is pressed: that
|
parsl/usage_tracking/usage.py
CHANGED
@@ -8,7 +8,7 @@ import uuid
|
|
8
8
|
|
9
9
|
from parsl.dataflow.states import States
|
10
10
|
from parsl.errors import ConfigurationError
|
11
|
-
from parsl.multiprocessing import
|
11
|
+
from parsl.multiprocessing import SpawnProcess
|
12
12
|
from parsl.usage_tracking.api import get_parsl_usage
|
13
13
|
from parsl.usage_tracking.levels import DISABLED as USAGE_TRACKING_DISABLED
|
14
14
|
from parsl.usage_tracking.levels import LEVEL_3 as USAGE_TRACKING_LEVEL_3
|
@@ -35,7 +35,7 @@ def async_process(fn: Callable[P, None]) -> Callable[P, None]:
|
|
35
35
|
""" Decorator function to launch a function as a separate process """
|
36
36
|
|
37
37
|
def run(*args, **kwargs):
|
38
|
-
proc =
|
38
|
+
proc = SpawnProcess(target=fn, args=args, kwargs=kwargs, name="Usage-Tracking")
|
39
39
|
proc.start()
|
40
40
|
return proc
|
41
41
|
|
parsl/version.py
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: parsl
|
3
|
-
Version: 2025.3.
|
3
|
+
Version: 2025.3.24
|
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/2025.03.
|
6
|
+
Download-URL: https://github.com/Parsl/parsl/archive/2025.03.24.tar.gz
|
7
7
|
Author: The Parsl Team
|
8
8
|
Author-email: parsl@googlegroups.com
|
9
9
|
License: Apache 2.0
|
@@ -4,11 +4,11 @@ parsl/config.py,sha256=p5HQoxLj5aMagUAYfngcXG2kw0s6SJoc6u7vH2sVhPU,9635
|
|
4
4
|
parsl/curvezmq.py,sha256=6Zi7RqTP_eKWi3DFgapfK2t-Jw8vJS-ZtN1bsrByPeo,7073
|
5
5
|
parsl/errors.py,sha256=SzINzQFZDBDbj9l-DPQznD0TbGkNhHIRAPkcBCogf_A,1019
|
6
6
|
parsl/log_utils.py,sha256=u14Fkl5eDfS4HMpl0JjseNNPdbvPaugWPRQj1_af_Zo,3273
|
7
|
-
parsl/multiprocessing.py,sha256=
|
7
|
+
parsl/multiprocessing.py,sha256=JNAfgdZvQSsxVyUp229OOUqWwf_ZUhpmw8X9CdF3i6k,3614
|
8
8
|
parsl/process_loggers.py,sha256=uQ7Gd0W72Jz7rrcYlOMfLsAEhkRltxXJL2MgdduJjEw,1136
|
9
9
|
parsl/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
10
|
parsl/utils.py,sha256=codTX6_KLhgeTwNkRzc1lo4bgc1M93eJ-lkqOO98fvk,14331
|
11
|
-
parsl/version.py,sha256=
|
11
|
+
parsl/version.py,sha256=RGffnfvs_gOc_lEWgnZpd1jy_cuz-F39xp5lsIo6OyU,131
|
12
12
|
parsl/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
13
|
parsl/app/app.py,sha256=0gbM4AH2OtFOLsv07I5nglpElcwMSOi-FzdZZfrk7So,8532
|
14
14
|
parsl/app/bash.py,sha256=jm2AvePlCT9DZR7H_4ANDWxatp5dN_22FUlT_gWhZ-g,5528
|
@@ -53,7 +53,7 @@ parsl/data_provider/staging.py,sha256=ZDZuuFg38pjUStegKPcvPsfGp3iMeReMzfU6DSwtJj
|
|
53
53
|
parsl/data_provider/zip.py,sha256=S4kVuH9lxAegRURYbvIUR7EYYBOccyslaqyCrVWUBhw,4497
|
54
54
|
parsl/dataflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
55
55
|
parsl/dataflow/dependency_resolvers.py,sha256=Om8Dgh7a0ZwgXAc6TlhxLSzvxXHDlNNV1aBNiD3JTNY,3325
|
56
|
-
parsl/dataflow/dflow.py,sha256=
|
56
|
+
parsl/dataflow/dflow.py,sha256=kAEziQPm9rpGKzqhOuHSnnIXQ3KgrRaeRbYReww7Amw,61620
|
57
57
|
parsl/dataflow/errors.py,sha256=daVfr2BWs1zRsGD6JtosEMttWHvK1df1Npiu_MUvFKg,3998
|
58
58
|
parsl/dataflow/futures.py,sha256=08LuP-HFiHBIZmeKCjlsazw_WpQ5fwevrU2_WbidkYw,6080
|
59
59
|
parsl/dataflow/memoization.py,sha256=QUkTduZ_gdr8i08VWNWrqhfEvoMGsPDZegWUE2_7sGQ,12579
|
@@ -61,19 +61,19 @@ parsl/dataflow/rundirs.py,sha256=JZdzybVGubY35jL2YiKcDo65ZmRl1WyOApc8ajYxztc,108
|
|
61
61
|
parsl/dataflow/states.py,sha256=hV6mfv-y4A6xrujeQglcomnfEs7y3Xm2g6JFwC6dvgQ,2612
|
62
62
|
parsl/dataflow/taskrecord.py,sha256=qIW7T6hn9dYTuNPdUura3HQwwUpUJACwPP5REm5COf4,3042
|
63
63
|
parsl/executors/__init__.py,sha256=PEuXYrnVqwlaz_nt82s9D_YNaVsX7ET29DeIZRUR8hw,577
|
64
|
-
parsl/executors/base.py,sha256=
|
64
|
+
parsl/executors/base.py,sha256=_X-huuXKCoQatT_TYx9ApEuXiVVvUYI0S7uKlVMHP-U,4488
|
65
65
|
parsl/executors/errors.py,sha256=ZxL3nK5samPos8Xixo_jpRtPIiRJfZ5D397_qaXj2g0,2515
|
66
66
|
parsl/executors/execute_task.py,sha256=PtqHxk778UQaNah1AN-TJV5emZbOcU5TGtWDxFn3_F4,1079
|
67
|
-
parsl/executors/globus_compute.py,sha256=
|
68
|
-
parsl/executors/status_handling.py,sha256=
|
67
|
+
parsl/executors/globus_compute.py,sha256=818XKRobNRCs5-h30x2NP2XSLkoWlWoNeBZtv9hF-ec,4851
|
68
|
+
parsl/executors/status_handling.py,sha256=oiy6SQUQWwVciZd9MxF0sna9miqkMfaKv6ZTrj3h-Pc,15772
|
69
69
|
parsl/executors/threads.py,sha256=_LA5NA3GSvtjDend-1HVpjoDoNHHW13rAD0CET99fjQ,3463
|
70
70
|
parsl/executors/flux/__init__.py,sha256=P9grTTeRPXfqXurFhlSS7XhmE6tTbnCnyQ1f9b-oYHE,136
|
71
71
|
parsl/executors/flux/execute_parsl_task.py,sha256=zHP5M7ILGiwnoalZ8WsfVVdZM7uP4iQo2ThVh4crxpM,1530
|
72
|
-
parsl/executors/flux/executor.py,sha256=
|
72
|
+
parsl/executors/flux/executor.py,sha256=ii1i5V7uQnhf1BDq5nnMscmmpXJkCWtrFCuBbDaPyWI,17041
|
73
73
|
parsl/executors/flux/flux_instance_manager.py,sha256=5T3Rp7ZM-mlT0Pf0Gxgs5_YmnaPrSF9ec7zvRfLfYJw,2129
|
74
74
|
parsl/executors/high_throughput/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
75
75
|
parsl/executors/high_throughput/errors.py,sha256=k2XuvvFdUfNs2foHFnxmS-BToRMfdXpYEa4EF3ELKq4,1554
|
76
|
-
parsl/executors/high_throughput/executor.py,sha256=
|
76
|
+
parsl/executors/high_throughput/executor.py,sha256=VVKe3gveCqa3rERAqhVNsWifSrLqyMcUwpdyHCsYdz8,39663
|
77
77
|
parsl/executors/high_throughput/interchange.py,sha256=7sKIvxP3a7HSzqEq25ZCpABx-1Q2f585pFDGzUvo7_4,29459
|
78
78
|
parsl/executors/high_throughput/manager_record.py,sha256=ZMsqFxvreGLRXAw3N-JnODDa9Qfizw2tMmcBhm4lco4,490
|
79
79
|
parsl/executors/high_throughput/manager_selector.py,sha256=UKcUE6v0tO7PDMTThpKSKxVpOpOUilxDL7UbNgpZCxo,2116
|
@@ -85,13 +85,13 @@ parsl/executors/high_throughput/probe.py,sha256=QOEaliO3x5cB6ltMOZMsZQ-ath9AAuFq
|
|
85
85
|
parsl/executors/high_throughput/process_worker_pool.py,sha256=Q7FN0MdXIAOouxDarim6etYVHEgbXFiaMhBahC2ZtIQ,41137
|
86
86
|
parsl/executors/high_throughput/zmq_pipes.py,sha256=NUK25IEh0UkxzdqQQyM8tMtuZmjSiTeWu1DzkkAIOhA,8980
|
87
87
|
parsl/executors/radical/__init__.py,sha256=CKbtV2numw5QvgIBq1htMUrt9TqDCIC2zifyf2svTNU,186
|
88
|
-
parsl/executors/radical/executor.py,sha256=
|
88
|
+
parsl/executors/radical/executor.py,sha256=cRxEAPLvdYozCHT_zSj79KAOltfZySFYUbMU4rTbGng,22859
|
89
89
|
parsl/executors/radical/rpex_resources.py,sha256=Q7-0u3K447LBCe2y7mVcdw6jqWI7SdPXxCKhkr6FoRQ,5139
|
90
90
|
parsl/executors/radical/rpex_worker.py,sha256=z6r82ZujKb6sdKIdHsQ_5EBMDIQieeGcrlt6kGLFo4M,1830
|
91
91
|
parsl/executors/taskvine/__init__.py,sha256=9rwp3M8B0YyEhZMLO0RHaNw7u1nc01WHbXLqnBTanu0,293
|
92
92
|
parsl/executors/taskvine/errors.py,sha256=euIYkSslrNSI85kyi2s0xzOaO9ik4c1fYHstMIeiBJk,652
|
93
93
|
parsl/executors/taskvine/exec_parsl_function.py,sha256=ftGdJU78lKPPkphSHlEi4rj164mhuMHJjghVqfgeXKk,7085
|
94
|
-
parsl/executors/taskvine/executor.py,sha256=
|
94
|
+
parsl/executors/taskvine/executor.py,sha256=PpsMPYx99mgKq_xOqnRdXo20NZOr7JFtOIKETIEK-z0,30930
|
95
95
|
parsl/executors/taskvine/factory.py,sha256=GU5JryEAKJuYKwrSc162BN-lhcKhapvBZHT820pxwic,2772
|
96
96
|
parsl/executors/taskvine/factory_config.py,sha256=ZQC5vyDe8cM0nuv7fbBCV2xnWGAZ87iLlT2UqmFFI1U,3695
|
97
97
|
parsl/executors/taskvine/manager.py,sha256=SUi5mqqMm_rnkBLrZtTQe7RiHqWDn1oOejQscYzfwAU,25797
|
@@ -100,7 +100,7 @@ parsl/executors/taskvine/utils.py,sha256=iSrIogeiauL3UNy_9tiZp1cBSNn6fIJkMYQRVi1
|
|
100
100
|
parsl/executors/workqueue/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
101
101
|
parsl/executors/workqueue/errors.py,sha256=XO2naYhAsHHyiOBH6hpObg3mPNDmvMoFqErsj0-v7jc,541
|
102
102
|
parsl/executors/workqueue/exec_parsl_function.py,sha256=YXKVVIa4zXmOtz-0Ca4E_5nQfN_3S2bh2tB75uZZB4w,7774
|
103
|
-
parsl/executors/workqueue/executor.py,sha256=
|
103
|
+
parsl/executors/workqueue/executor.py,sha256=aRvEt_BGO2AGNaoF8P109z81XhlFqjyjJnka3yIzy-4,49717
|
104
104
|
parsl/executors/workqueue/parsl_coprocess.py,sha256=cF1UmTgVLoey6QzBcbYgEiEsRidSaFfuO54f1HFw_EM,5737
|
105
105
|
parsl/executors/workqueue/parsl_coprocess_stub.py,sha256=_bJmpPIgL42qM6bVzeEKt1Mn1trSP41rtJguXxPGfHI,735
|
106
106
|
parsl/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -115,9 +115,9 @@ parsl/launchers/errors.py,sha256=8YMV_CHpBNVa4eXkGE4x5DaFQlZkDCRCHmBktYcY6TA,467
|
|
115
115
|
parsl/launchers/launchers.py,sha256=cQsNsHuCOL_nQTjPXf0--YsgsDoMoJ77bO1Wt4ncLjs,15134
|
116
116
|
parsl/monitoring/__init__.py,sha256=0ywNz6i0lM1xo_7_BIxhETDGeVd2C_0wwD7qgeaMR4c,83
|
117
117
|
parsl/monitoring/db_manager.py,sha256=L0c5S9ockq0UIchT2bjmkSAWXS-t0G-Q_neOIBfLbm0,33444
|
118
|
-
parsl/monitoring/errors.py,sha256=
|
118
|
+
parsl/monitoring/errors.py,sha256=GParOWoCTp2w1Hmif0PaF5J6p5dWVOwyhO18bcvr_uo,277
|
119
119
|
parsl/monitoring/message_type.py,sha256=Khn88afNxcOIciKiCK4GLnn90I5BlRTiOL3zK-P07yQ,401
|
120
|
-
parsl/monitoring/monitoring.py,sha256=
|
120
|
+
parsl/monitoring/monitoring.py,sha256=ZA-36DYtPRApJDc6cd8R8xoRGfCG4OMa9mzW4OPGCCs,10065
|
121
121
|
parsl/monitoring/remote.py,sha256=t0qCTUMCzeJ_JOARFpjqlTNrAWdEb20BxhmZh9X7kEM,13728
|
122
122
|
parsl/monitoring/types.py,sha256=oOCrzv-ab-_rv4pb8o58Sdb8G_RGp1aZriRbdf9zBEk,339
|
123
123
|
parsl/monitoring/queries/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -131,7 +131,7 @@ parsl/monitoring/radios/multiprocessing.py,sha256=fsfaaoMDp6VJv1DSAl-P0R2ofO6jp1
|
|
131
131
|
parsl/monitoring/radios/udp.py,sha256=bTpt7JYp-5hyBBLzgiLj1_BlSTn28UVp39OYgVGLXCw,1613
|
132
132
|
parsl/monitoring/radios/udp_router.py,sha256=LEiHZVhw3lVFhqUK1FAFFtpvNOWbB6RNRBK8FaMvtDw,5771
|
133
133
|
parsl/monitoring/radios/zmq.py,sha256=fhoHp9ylhf-D3eTJb2aSHRsuic8-FJ_oRNGnniGkCAI,592
|
134
|
-
parsl/monitoring/radios/zmq_router.py,sha256=
|
134
|
+
parsl/monitoring/radios/zmq_router.py,sha256=ksaWb9bceyFhGGMDC8Nc16JhQ3qpmg8uXQypbrvvtcg,7984
|
135
135
|
parsl/monitoring/visualization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
136
136
|
parsl/monitoring/visualization/app.py,sha256=xMeRlAnzl5lHddAOdSBcqY3D5lmOYw3Z3Z2_YyoVwnw,1425
|
137
137
|
parsl/monitoring/visualization/models.py,sha256=C7CcF6w6PhtrdvDX9VgDH-aSrpLfvYU1fJ4-HDUeFVQ,5138
|
@@ -339,12 +339,13 @@ parsl/tests/test_monitoring/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
|
|
339
339
|
parsl/tests/test_monitoring/test_app_names.py,sha256=A-mOMCVhZDnUyJp32fsTUkHdcyval8o7WPEWacDkbD4,2208
|
340
340
|
parsl/tests/test_monitoring/test_basic.py,sha256=VdF6JHfqsEOIMg-ysIAREgygZIjHWNDVLNVQ7jhWxmQ,4592
|
341
341
|
parsl/tests/test_monitoring/test_db_locks.py,sha256=3s3c1xhKo230ZZIJ3f1Ca4U7LcEdXnanOGVXQyNlk2U,2895
|
342
|
-
parsl/tests/test_monitoring/test_exit_helper.py,sha256=
|
343
|
-
parsl/tests/test_monitoring/test_fuzz_zmq.py,sha256
|
342
|
+
parsl/tests/test_monitoring/test_exit_helper.py,sha256=ob8Qd1hlkq_mowygfPetTnYN9LfuqeXHRpPilSfDSog,1232
|
343
|
+
parsl/tests/test_monitoring/test_fuzz_zmq.py,sha256=SQNNHhXxHB_LwW4Ujqkgut3lbG0XVW-hliPagQQpiTc,3449
|
344
344
|
parsl/tests/test_monitoring/test_htex_init_blocks_vs_monitoring.py,sha256=_QV8zjBKVF_qBbBnhT0C3X9AmfS7IKLcOnEw_cU6HeM,2622
|
345
345
|
parsl/tests/test_monitoring/test_incomplete_futures.py,sha256=ZnO1sFSwlWUBHX64C_zwfTVRVC_UFNlU4h0POgx6NEo,2005
|
346
346
|
parsl/tests/test_monitoring/test_memoization_representation.py,sha256=dknv2nO7pNZ1jGxWGsC_AW3rs90gjMIeC5d7pIJ75Xc,2645
|
347
|
-
parsl/tests/test_monitoring/
|
347
|
+
parsl/tests/test_monitoring/test_radio_zmq.py,sha256=7ARjDbde9kSuP4NCx_z-UtmMf2X5opbkkKM7mZcayuA,783
|
348
|
+
parsl/tests/test_monitoring/test_stdouterr.py,sha256=SEMKBk4v5Saoq3QiraFpzUpVe5vS_132GQPSf1Qu0qM,4573
|
348
349
|
parsl/tests/test_monitoring/test_viz_colouring.py,sha256=83Qdmn3gM0j7IL6kPDcuIsp_nl4zj-liPijyIN632SY,592
|
349
350
|
parsl/tests/test_mpi_apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
350
351
|
parsl/tests/test_mpi_apps/test_bad_mpi_config.py,sha256=QKvEUSrHIBrvqu2fRj1MAqxsYxDfcrdQ7dzWdOZejuU,1320
|
@@ -423,7 +424,7 @@ parsl/tests/test_serialization/test_pack_resource_spec.py,sha256=-Vtyh8KyezZw8e7
|
|
423
424
|
parsl/tests/test_serialization/test_proxystore_configured.py,sha256=lGWOSEWul16enDWhW-s7CK0d3eMDzm1324Fmj0cZMVU,2293
|
424
425
|
parsl/tests/test_serialization/test_proxystore_impl.py,sha256=uGd45sfPm9rJhzqKV0rI3lqdSOAUddQf-diEpcJAlcY,1228
|
425
426
|
parsl/tests/test_shutdown/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
426
|
-
parsl/tests/test_shutdown/test_kill_monitoring.py,sha256=
|
427
|
+
parsl/tests/test_shutdown/test_kill_monitoring.py,sha256=UNU_VeorxRq8mRGhjrDmqF_axZMCQjsPfAK0wh6ZN04,1929
|
427
428
|
parsl/tests/test_staging/__init__.py,sha256=WZl9EHSkfYiSoE3Gbulcq2ifmn7IFGUkasJIobL5T5A,208
|
428
429
|
parsl/tests/test_staging/staging_provider.py,sha256=6FDpImkWOLgysqM68NbCAoXZciZokI8dmBWRAxnggBk,3242
|
429
430
|
parsl/tests/test_staging/test_1316.py,sha256=eS0e2BDM2vmPNF60aDr35wcuGgDPfXjTjRV6kyBZOQc,2652
|
@@ -457,14 +458,14 @@ parsl/tests/unit/test_usage_tracking.py,sha256=xEfUlbBRpsFdUdOrCsk1Kz5AfmMxJT7f0
|
|
457
458
|
parsl/usage_tracking/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
458
459
|
parsl/usage_tracking/api.py,sha256=iaCY58Dc5J4UM7_dJzEEs871P1p1HdxBMtNGyVdzc9g,1821
|
459
460
|
parsl/usage_tracking/levels.py,sha256=xbfzYEsd55KiZJ-mzNgPebvOH4rRHum04hROzEf41tU,291
|
460
|
-
parsl/usage_tracking/usage.py,sha256=
|
461
|
-
parsl-2025.3.
|
462
|
-
parsl-2025.3.
|
463
|
-
parsl-2025.3.
|
464
|
-
parsl-2025.3.
|
465
|
-
parsl-2025.3.
|
466
|
-
parsl-2025.3.
|
467
|
-
parsl-2025.3.
|
468
|
-
parsl-2025.3.
|
469
|
-
parsl-2025.3.
|
470
|
-
parsl-2025.3.
|
461
|
+
parsl/usage_tracking/usage.py,sha256=8hq1UPdFlVcC0V3aj0ve-MvCyvwK8Xr3CVuSto3dTW4,9165
|
462
|
+
parsl-2025.3.24.data/scripts/exec_parsl_function.py,sha256=YXKVVIa4zXmOtz-0Ca4E_5nQfN_3S2bh2tB75uZZB4w,7774
|
463
|
+
parsl-2025.3.24.data/scripts/interchange.py,sha256=17MrOc7-FXxKBWTwkzIbUoa8fvvDfPelfjByd3ZD2Wk,29446
|
464
|
+
parsl-2025.3.24.data/scripts/parsl_coprocess.py,sha256=zrVjEqQvFOHxsLufPi00xzMONagjVwLZbavPM7bbjK4,5722
|
465
|
+
parsl-2025.3.24.data/scripts/process_worker_pool.py,sha256=__gFeFQJpV5moRofj3WKQCnKp6gmzieXjzkmzVuTmX4,41123
|
466
|
+
parsl-2025.3.24.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
|
467
|
+
parsl-2025.3.24.dist-info/METADATA,sha256=_Wl6Xlf9aCRKSbnQxEquJuYSA3_uNmlLKPgs-2B9nf8,4023
|
468
|
+
parsl-2025.3.24.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
469
|
+
parsl-2025.3.24.dist-info/entry_points.txt,sha256=XqnsWDYoEcLbsMcpnYGKLEnSBmaIe1YoM5YsBdJG2tI,176
|
470
|
+
parsl-2025.3.24.dist-info/top_level.txt,sha256=PIheYoUFQtF2icLsgOykgU-Cjuwr2Oi6On2jo5RYgRM,6
|
471
|
+
parsl-2025.3.24.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|