parsl 2025.6.16__py3-none-any.whl → 2025.6.30__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/configs/osg.py +1 -1
- parsl/dataflow/dflow.py +14 -4
- parsl/executors/base.py +19 -9
- parsl/executors/flux/executor.py +2 -0
- parsl/executors/globus_compute.py +2 -0
- parsl/executors/high_throughput/executor.py +22 -15
- parsl/executors/high_throughput/interchange.py +173 -191
- parsl/executors/high_throughput/mpi_executor.py +14 -4
- parsl/executors/high_throughput/probe.py +4 -4
- parsl/executors/high_throughput/process_worker_pool.py +88 -94
- parsl/executors/radical/executor.py +3 -0
- parsl/executors/taskvine/executor.py +11 -3
- parsl/executors/taskvine/manager.py +3 -1
- parsl/executors/threads.py +19 -3
- parsl/executors/workqueue/executor.py +11 -3
- parsl/monitoring/errors.py +4 -4
- parsl/monitoring/monitoring.py +26 -88
- parsl/monitoring/radios/base.py +63 -2
- parsl/monitoring/radios/filesystem.py +19 -4
- parsl/monitoring/radios/filesystem_router.py +22 -3
- parsl/monitoring/radios/htex.py +22 -13
- parsl/monitoring/radios/multiprocessing.py +22 -2
- parsl/monitoring/radios/udp.py +57 -19
- parsl/monitoring/radios/udp_router.py +119 -25
- parsl/monitoring/radios/zmq_router.py +9 -10
- parsl/monitoring/remote.py +19 -40
- parsl/providers/local/local.py +12 -13
- parsl/tests/configs/htex_local_alternate.py +0 -1
- parsl/tests/conftest.py +7 -4
- parsl/tests/test_htex/test_interchange_exit_bad_registration.py +5 -7
- parsl/tests/test_htex/test_zmq_binding.py +5 -6
- parsl/tests/test_monitoring/test_basic.py +12 -10
- parsl/tests/test_monitoring/{test_fuzz_zmq.py → test_htex_fuzz_zmq.py} +7 -2
- parsl/tests/test_monitoring/test_htex_init_blocks_vs_monitoring.py +0 -1
- parsl/tests/test_monitoring/test_radio_filesystem.py +48 -0
- parsl/tests/test_monitoring/test_radio_multiprocessing.py +44 -0
- parsl/tests/test_monitoring/test_radio_udp.py +204 -0
- parsl/tests/test_monitoring/test_stdouterr.py +1 -3
- parsl/tests/test_scaling/test_worker_interchange_bad_messages_3262.py +3 -7
- parsl/tests/test_shutdown/test_kill_monitoring.py +1 -1
- parsl/version.py +1 -1
- {parsl-2025.6.16.data → parsl-2025.6.30.data}/scripts/interchange.py +173 -191
- {parsl-2025.6.16.data → parsl-2025.6.30.data}/scripts/process_worker_pool.py +88 -94
- {parsl-2025.6.16.dist-info → parsl-2025.6.30.dist-info}/METADATA +2 -2
- {parsl-2025.6.16.dist-info → parsl-2025.6.30.dist-info}/RECORD +51 -50
- parsl/tests/configs/local_threads_monitoring.py +0 -10
- parsl/tests/manual_tests/test_udp_simple.py +0 -51
- {parsl-2025.6.16.data → parsl-2025.6.30.data}/scripts/exec_parsl_function.py +0 -0
- {parsl-2025.6.16.data → parsl-2025.6.30.data}/scripts/parsl_coprocess.py +0 -0
- {parsl-2025.6.16.dist-info → parsl-2025.6.30.dist-info}/LICENSE +0 -0
- {parsl-2025.6.16.dist-info → parsl-2025.6.30.dist-info}/WHEEL +0 -0
- {parsl-2025.6.16.dist-info → parsl-2025.6.30.dist-info}/entry_points.txt +0 -0
- {parsl-2025.6.16.dist-info → parsl-2025.6.30.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
import pytest
|
2
|
+
|
3
|
+
from parsl.monitoring.message_type import MessageType
|
4
|
+
from parsl.monitoring.radios.filesystem import FilesystemRadio
|
5
|
+
from parsl.multiprocessing import SpawnQueue
|
6
|
+
|
7
|
+
|
8
|
+
@pytest.mark.local
|
9
|
+
def test_filesystem(tmpd_cwd):
|
10
|
+
"""Test filesystem radio/receiver pair.
|
11
|
+
This test checks that the pair can be started up locally, that a message
|
12
|
+
is conveyed from radio to receiver, and that the receiver process goes
|
13
|
+
away at shutdown.
|
14
|
+
"""
|
15
|
+
|
16
|
+
resource_msgs = SpawnQueue()
|
17
|
+
|
18
|
+
radio_config = FilesystemRadio()
|
19
|
+
|
20
|
+
# start receiver
|
21
|
+
receiver = radio_config.create_receiver(run_dir=str(tmpd_cwd),
|
22
|
+
resource_msgs=resource_msgs)
|
23
|
+
|
24
|
+
# make radio
|
25
|
+
|
26
|
+
radio_sender = radio_config.create_sender()
|
27
|
+
|
28
|
+
# send message into radio
|
29
|
+
|
30
|
+
message = (MessageType.RESOURCE_INFO, {})
|
31
|
+
|
32
|
+
radio_sender.send(message)
|
33
|
+
|
34
|
+
# verify it comes out of the receiver
|
35
|
+
|
36
|
+
m = resource_msgs.get()
|
37
|
+
|
38
|
+
assert m == message, "The sent message should appear in the queue"
|
39
|
+
|
40
|
+
# shut down router
|
41
|
+
|
42
|
+
receiver.shutdown()
|
43
|
+
|
44
|
+
# we can't inspect the process if it has been closed properly, but
|
45
|
+
# we can verify that it raises the expected ValueError the closed
|
46
|
+
# processes raise on access.
|
47
|
+
with pytest.raises(ValueError):
|
48
|
+
receiver.process.exitcode
|
@@ -0,0 +1,44 @@
|
|
1
|
+
import pytest
|
2
|
+
|
3
|
+
from parsl.monitoring.message_type import MessageType
|
4
|
+
from parsl.monitoring.radios.multiprocessing import MultiprocessingQueueRadio
|
5
|
+
from parsl.multiprocessing import SpawnQueue
|
6
|
+
|
7
|
+
|
8
|
+
@pytest.mark.local
|
9
|
+
def test_radio(tmpd_cwd):
|
10
|
+
"""Test filesystem radio/receiver pair.
|
11
|
+
This test checks that the pair can be started up locally, that a message
|
12
|
+
is conveyed from radio to receiver, and that the receiver process goes
|
13
|
+
away at shutdown.
|
14
|
+
"""
|
15
|
+
|
16
|
+
resource_msgs = SpawnQueue()
|
17
|
+
|
18
|
+
radio_config = MultiprocessingQueueRadio()
|
19
|
+
|
20
|
+
# start receiver
|
21
|
+
receiver = radio_config.create_receiver(run_dir=str(tmpd_cwd),
|
22
|
+
resource_msgs=resource_msgs)
|
23
|
+
|
24
|
+
# make radio
|
25
|
+
|
26
|
+
radio_sender = radio_config.create_sender()
|
27
|
+
|
28
|
+
# send message into radio
|
29
|
+
|
30
|
+
message = (MessageType.RESOURCE_INFO, {})
|
31
|
+
|
32
|
+
radio_sender.send(message)
|
33
|
+
|
34
|
+
# verify it comes out of the receiver
|
35
|
+
|
36
|
+
m = resource_msgs.get()
|
37
|
+
|
38
|
+
assert m == message, "The sent message should appear in the queue"
|
39
|
+
|
40
|
+
# Shut down router.
|
41
|
+
# In the multiprocessing radio, nothing happens at shutdown, so this
|
42
|
+
# validates that the call executes without raising an exception, but
|
43
|
+
# not much else.
|
44
|
+
receiver.shutdown()
|
@@ -0,0 +1,204 @@
|
|
1
|
+
import socket
|
2
|
+
import time
|
3
|
+
|
4
|
+
import pytest
|
5
|
+
|
6
|
+
from parsl.monitoring.message_type import MessageType
|
7
|
+
from parsl.monitoring.radios.udp import UDPRadio
|
8
|
+
from parsl.multiprocessing import SpawnQueue
|
9
|
+
|
10
|
+
|
11
|
+
@pytest.mark.local
|
12
|
+
def test_udp(tmpd_cwd):
|
13
|
+
"""Test UDP radio/receiver pair.
|
14
|
+
This test checks that the pair can be started up locally, that a message
|
15
|
+
is conveyed from radio to receiver, and that the receiver process goes
|
16
|
+
away at shutdown.
|
17
|
+
"""
|
18
|
+
|
19
|
+
resource_msgs = SpawnQueue()
|
20
|
+
|
21
|
+
radio_config = UDPRadio(address="localhost", atexit_timeout=0)
|
22
|
+
|
23
|
+
# start receiver
|
24
|
+
udp_receiver = radio_config.create_receiver(run_dir=str(tmpd_cwd),
|
25
|
+
resource_msgs=resource_msgs)
|
26
|
+
|
27
|
+
# check hash properties
|
28
|
+
|
29
|
+
assert len(radio_config.hmac_key) == 64, "With default hash, should expect 64 byte key"
|
30
|
+
|
31
|
+
# make radio
|
32
|
+
|
33
|
+
radio_sender = radio_config.create_sender()
|
34
|
+
|
35
|
+
# send message into radio
|
36
|
+
|
37
|
+
message = (MessageType.RESOURCE_INFO, {})
|
38
|
+
|
39
|
+
radio_sender.send(message)
|
40
|
+
|
41
|
+
# verify it comes out of the receiver
|
42
|
+
|
43
|
+
m = resource_msgs.get()
|
44
|
+
|
45
|
+
assert m == message, "The sent message should appear in the queue"
|
46
|
+
|
47
|
+
# shut down router
|
48
|
+
|
49
|
+
udp_receiver.shutdown()
|
50
|
+
|
51
|
+
# we can't inspect the process if it has been closed properly, but
|
52
|
+
# we can verify that it raises the expected ValueError the closed
|
53
|
+
# processes raise on access.
|
54
|
+
with pytest.raises(ValueError):
|
55
|
+
udp_receiver.process.exitcode
|
56
|
+
|
57
|
+
|
58
|
+
@pytest.mark.local
|
59
|
+
def test_bad_hmac(tmpd_cwd, caplog):
|
60
|
+
"""Test when HMAC does not match.
|
61
|
+
"""
|
62
|
+
|
63
|
+
resource_msgs = SpawnQueue()
|
64
|
+
|
65
|
+
radio_config = UDPRadio(address="localhost", atexit_timeout=0)
|
66
|
+
|
67
|
+
# start receiver
|
68
|
+
udp_receiver = radio_config.create_receiver(run_dir=str(tmpd_cwd),
|
69
|
+
resource_msgs=resource_msgs)
|
70
|
+
|
71
|
+
# check the hmac is configured in the right place,
|
72
|
+
# then change it to something different (by prepending a new byte)
|
73
|
+
assert radio_config.hmac_key is not None
|
74
|
+
radio_config.hmac_key += b'x'
|
75
|
+
|
76
|
+
# make radio, after changing the HMAC key
|
77
|
+
|
78
|
+
radio_sender = radio_config.create_sender()
|
79
|
+
|
80
|
+
# send message into radio
|
81
|
+
|
82
|
+
message = (MessageType.RESOURCE_INFO, {})
|
83
|
+
|
84
|
+
radio_sender.send(message)
|
85
|
+
|
86
|
+
# We should expect no message from the UDP side. That's hard to
|
87
|
+
# state in this scenario because UDP doesn't have any delivery
|
88
|
+
# guarantees for the test-failing case.
|
89
|
+
# So sleep a while to allow that test to misdeliver and fail.
|
90
|
+
time.sleep(1)
|
91
|
+
|
92
|
+
assert resource_msgs.empty(), "receiving queue should be empty"
|
93
|
+
assert udp_receiver.process.is_alive(), "UDP router process should still be alive"
|
94
|
+
|
95
|
+
with open(f"{tmpd_cwd}/monitoring_udp_router.log", "r") as logfile:
|
96
|
+
assert "ERROR" in logfile.read(), "Router log file should contain an error"
|
97
|
+
|
98
|
+
# shut down router
|
99
|
+
|
100
|
+
udp_receiver.shutdown()
|
101
|
+
|
102
|
+
# we can't inspect the process if it has been closed properly, but
|
103
|
+
# we can verify that it raises the expected ValueError the closed
|
104
|
+
# processes raise on access.
|
105
|
+
with pytest.raises(ValueError):
|
106
|
+
udp_receiver.process.exitcode
|
107
|
+
|
108
|
+
|
109
|
+
@pytest.mark.local
|
110
|
+
def test_wrong_digest(tmpd_cwd, caplog):
|
111
|
+
"""Test when HMAC does not match.
|
112
|
+
"""
|
113
|
+
|
114
|
+
resource_msgs = SpawnQueue()
|
115
|
+
|
116
|
+
radio_config = UDPRadio(address="localhost", atexit_timeout=0)
|
117
|
+
|
118
|
+
# start receiver
|
119
|
+
udp_receiver = radio_config.create_receiver(run_dir=str(tmpd_cwd),
|
120
|
+
resource_msgs=resource_msgs)
|
121
|
+
|
122
|
+
# check the hmac is configured in the right place,
|
123
|
+
# then change it to a different digest. The choice of different
|
124
|
+
# digest is arbitrary.
|
125
|
+
assert radio_config.hmac_digest is not None
|
126
|
+
radio_config.hmac_digest = "sha3_224"
|
127
|
+
|
128
|
+
# make radio, after changing the HMAC digest
|
129
|
+
|
130
|
+
radio_sender = radio_config.create_sender()
|
131
|
+
|
132
|
+
# send message into radio
|
133
|
+
|
134
|
+
message = (MessageType.RESOURCE_INFO, {})
|
135
|
+
|
136
|
+
radio_sender.send(message)
|
137
|
+
|
138
|
+
# We should expect no message from the UDP side. That's hard to
|
139
|
+
# state in this scenario because UDP doesn't have any delivery
|
140
|
+
# guarantees for the test-failing case.
|
141
|
+
# So sleep a while to allow that test to misdeliver and fail.
|
142
|
+
time.sleep(1)
|
143
|
+
|
144
|
+
assert resource_msgs.empty(), "receiving queue should be empty"
|
145
|
+
assert udp_receiver.process.is_alive(), "UDP router process should still be alive"
|
146
|
+
|
147
|
+
with open(f"{tmpd_cwd}/monitoring_udp_router.log", "r") as logfile:
|
148
|
+
assert "ERROR" in logfile.read(), "Router log file should contain an error"
|
149
|
+
|
150
|
+
# shut down router
|
151
|
+
|
152
|
+
udp_receiver.shutdown()
|
153
|
+
|
154
|
+
# we can't inspect the process if it has been closed properly, but
|
155
|
+
# we can verify that it raises the expected ValueError the closed
|
156
|
+
# processes raise on access.
|
157
|
+
with pytest.raises(ValueError):
|
158
|
+
udp_receiver.process.exitcode
|
159
|
+
|
160
|
+
|
161
|
+
@pytest.mark.local
|
162
|
+
def test_short_message(tmpd_cwd, caplog):
|
163
|
+
"""Test when UDP message is so short it can't even be parsed into
|
164
|
+
HMAC + the rest.
|
165
|
+
"""
|
166
|
+
|
167
|
+
resource_msgs = SpawnQueue()
|
168
|
+
|
169
|
+
radio_config = UDPRadio(address="localhost", atexit_timeout=0)
|
170
|
+
|
171
|
+
# start receiver
|
172
|
+
udp_receiver = radio_config.create_receiver(run_dir=str(tmpd_cwd),
|
173
|
+
resource_msgs=resource_msgs)
|
174
|
+
|
175
|
+
# now send a bad UDP message, rather than using the sender mechanism.
|
176
|
+
|
177
|
+
sock = socket.socket(socket.AF_INET,
|
178
|
+
socket.SOCK_DGRAM,
|
179
|
+
socket.IPPROTO_UDP)
|
180
|
+
|
181
|
+
sock.sendto(b'', (radio_config.address, radio_config.port))
|
182
|
+
sock.close()
|
183
|
+
|
184
|
+
# We should expect no message from the UDP side. That's hard to
|
185
|
+
# state in this scenario because UDP doesn't have any delivery
|
186
|
+
# guarantees for the test-failing case.
|
187
|
+
# So sleep a while to allow that test to misdeliver and fail.
|
188
|
+
time.sleep(1)
|
189
|
+
|
190
|
+
assert resource_msgs.empty(), "receiving queue should be empty"
|
191
|
+
assert udp_receiver.process.is_alive(), "UDP router process should still be alive"
|
192
|
+
|
193
|
+
with open(f"{tmpd_cwd}/monitoring_udp_router.log", "r") as logfile:
|
194
|
+
assert "ERROR" in logfile.read(), "Router log file should contain an error"
|
195
|
+
|
196
|
+
# shut down router
|
197
|
+
|
198
|
+
udp_receiver.shutdown()
|
199
|
+
|
200
|
+
# we can't inspect the process if it has been closed properly, but
|
201
|
+
# we can verify that it raises the expected ValueError the closed
|
202
|
+
# processes raise on access.
|
203
|
+
with pytest.raises(ValueError):
|
204
|
+
udp_receiver.process.exitcode
|
@@ -1,7 +1,3 @@
|
|
1
|
-
import os
|
2
|
-
import signal
|
3
|
-
import time
|
4
|
-
|
5
1
|
import pytest
|
6
2
|
import zmq
|
7
3
|
|
@@ -61,11 +57,11 @@ def test_bad_messages(try_assert, msg):
|
|
61
57
|
|
62
58
|
with parsl.load(c):
|
63
59
|
|
64
|
-
# send a bad message into the interchange on the
|
60
|
+
# send a bad message into the interchange on the worker_sock worker
|
65
61
|
# channel, and then check that the interchange is still alive enough
|
66
62
|
# that we can scale out a block and run a task.
|
67
63
|
|
68
|
-
|
64
|
+
worker_port = htex.command_client.run("WORKER_BINDS")
|
69
65
|
|
70
66
|
context = zmq.Context()
|
71
67
|
channel_timeout = 10000 # in milliseconds
|
@@ -75,7 +71,7 @@ def test_bad_messages(try_assert, msg):
|
|
75
71
|
|
76
72
|
task_channel.set_hwm(0)
|
77
73
|
task_channel.setsockopt(zmq.SNDTIMEO, channel_timeout)
|
78
|
-
task_channel.connect(f"tcp://localhost:{
|
74
|
+
task_channel.connect(f"tcp://localhost:{worker_port}")
|
79
75
|
|
80
76
|
task_channel.send(msg)
|
81
77
|
|
@@ -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", ["dbm_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/version.py
CHANGED