parsl 2024.12.2__py3-none-any.whl → 2024.12.9__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.
Files changed (74) hide show
  1. parsl/configs/cc_in2p3.py +0 -2
  2. parsl/configs/frontera.py +0 -2
  3. parsl/configs/htex_local.py +0 -2
  4. parsl/dataflow/dflow.py +0 -2
  5. parsl/executors/base.py +1 -1
  6. parsl/executors/high_throughput/interchange.py +2 -1
  7. parsl/monitoring/monitoring.py +1 -1
  8. parsl/monitoring/radios/base.py +13 -0
  9. parsl/monitoring/radios/filesystem.py +52 -0
  10. parsl/monitoring/radios/htex.py +57 -0
  11. parsl/monitoring/radios/multiprocessing.py +17 -0
  12. parsl/monitoring/radios/udp.py +56 -0
  13. parsl/monitoring/radios/zmq.py +17 -0
  14. parsl/monitoring/remote.py +4 -6
  15. parsl/monitoring/router.py +1 -1
  16. parsl/providers/cluster_provider.py +2 -5
  17. parsl/providers/condor/condor.py +1 -8
  18. parsl/providers/grid_engine/grid_engine.py +1 -6
  19. parsl/providers/local/local.py +5 -8
  20. parsl/providers/lsf/lsf.py +1 -6
  21. parsl/providers/pbspro/pbspro.py +2 -7
  22. parsl/providers/slurm/slurm.py +3 -9
  23. parsl/providers/torque/torque.py +1 -7
  24. parsl/tests/configs/cc_in2p3.py +0 -2
  25. parsl/tests/configs/frontera.py +0 -2
  26. parsl/tests/configs/htex_local.py +0 -2
  27. parsl/tests/configs/htex_local_alternate.py +0 -2
  28. parsl/tests/configs/htex_local_intask_staging.py +0 -2
  29. parsl/tests/configs/htex_local_rsync_staging.py +0 -2
  30. parsl/tests/configs/slurm_local.py +0 -2
  31. parsl/tests/manual_tests/htex_local.py +0 -2
  32. parsl/tests/manual_tests/test_memory_limits.py +0 -2
  33. parsl/tests/scaling_tests/htex_local.py +0 -2
  34. parsl/tests/sites/test_affinity.py +0 -2
  35. parsl/tests/sites/test_worker_info.py +0 -2
  36. parsl/tests/test_htex/test_drain.py +0 -2
  37. parsl/tests/test_htex/test_manager_selector_by_block.py +0 -2
  38. parsl/tests/test_monitoring/test_htex_init_blocks_vs_monitoring.py +0 -2
  39. parsl/tests/test_providers/test_local_provider.py +1 -2
  40. parsl/tests/test_providers/test_pbspro_template.py +1 -3
  41. parsl/tests/test_providers/test_slurm_template.py +1 -3
  42. parsl/tests/test_scaling/test_regression_1621.py +0 -2
  43. parsl/tests/test_scaling/test_regression_3568_scaledown_vs_MISSING.py +0 -1
  44. parsl/tests/test_scaling/test_scale_down.py +0 -2
  45. parsl/tests/test_scaling/test_scale_down_htex_auto_scale.py +0 -2
  46. parsl/tests/test_scaling/test_scale_down_htex_unregistered.py +0 -2
  47. parsl/tests/test_scaling/test_shutdown_scalein.py +0 -2
  48. parsl/tests/test_scaling/test_worker_interchange_bad_messages_3262.py +0 -2
  49. parsl/tests/test_staging/test_zip_in.py +0 -1
  50. parsl/tests/test_staging/test_zip_out.py +0 -1
  51. parsl/tests/test_staging/test_zip_to_zip.py +0 -1
  52. parsl/tests/test_utils/test_execute_wait.py +35 -0
  53. parsl/utils.py +35 -0
  54. parsl/version.py +1 -1
  55. {parsl-2024.12.2.data → parsl-2024.12.9.data}/scripts/interchange.py +2 -1
  56. {parsl-2024.12.2.dist-info → parsl-2024.12.9.dist-info}/METADATA +2 -2
  57. {parsl-2024.12.2.dist-info → parsl-2024.12.9.dist-info}/RECORD +65 -67
  58. parsl/channels/__init__.py +0 -4
  59. parsl/channels/base.py +0 -54
  60. parsl/channels/errors.py +0 -30
  61. parsl/channels/local/local.py +0 -66
  62. parsl/monitoring/radios.py +0 -191
  63. parsl/tests/integration/test_channels/__init__.py +0 -0
  64. parsl/tests/test_channels/__init__.py +0 -0
  65. parsl/tests/test_channels/test_large_output.py +0 -22
  66. parsl/tests/test_channels/test_local_channel.py +0 -19
  67. /parsl/{channels/local → monitoring/radios}/__init__.py +0 -0
  68. {parsl-2024.12.2.data → parsl-2024.12.9.data}/scripts/exec_parsl_function.py +0 -0
  69. {parsl-2024.12.2.data → parsl-2024.12.9.data}/scripts/parsl_coprocess.py +0 -0
  70. {parsl-2024.12.2.data → parsl-2024.12.9.data}/scripts/process_worker_pool.py +0 -0
  71. {parsl-2024.12.2.dist-info → parsl-2024.12.9.dist-info}/LICENSE +0 -0
  72. {parsl-2024.12.2.dist-info → parsl-2024.12.9.dist-info}/WHEEL +0 -0
  73. {parsl-2024.12.2.dist-info → parsl-2024.12.9.dist-info}/entry_points.txt +0 -0
  74. {parsl-2024.12.2.dist-info → parsl-2024.12.9.dist-info}/top_level.txt +0 -0
@@ -1,191 +0,0 @@
1
- import logging
2
- import os
3
- import pickle
4
- import socket
5
- import uuid
6
- from abc import ABCMeta, abstractmethod
7
- from multiprocessing.queues import Queue
8
-
9
- import zmq
10
-
11
- logger = logging.getLogger(__name__)
12
-
13
-
14
- class MonitoringRadioSender(metaclass=ABCMeta):
15
- @abstractmethod
16
- def send(self, message: object) -> None:
17
- pass
18
-
19
-
20
- class FilesystemRadioSender(MonitoringRadioSender):
21
- """A MonitoringRadioSender that sends messages over a shared filesystem.
22
-
23
- The messsage directory structure is based on maildir,
24
- https://en.wikipedia.org/wiki/Maildir
25
-
26
- The writer creates a message in tmp/ and then when it is fully
27
- written, moves it atomically into new/
28
-
29
- The reader ignores tmp/ and only reads and deletes messages from
30
- new/
31
-
32
- This avoids a race condition of reading partially written messages.
33
-
34
- This radio is likely to give higher shared filesystem load compared to
35
- the UDP radio, but should be much more reliable.
36
- """
37
-
38
- def __init__(self, *, monitoring_url: str, timeout: int = 10, run_dir: str):
39
- logger.info("filesystem based monitoring channel initializing")
40
- self.base_path = f"{run_dir}/monitor-fs-radio/"
41
- self.tmp_path = f"{self.base_path}/tmp"
42
- self.new_path = f"{self.base_path}/new"
43
-
44
- os.makedirs(self.tmp_path, exist_ok=True)
45
- os.makedirs(self.new_path, exist_ok=True)
46
-
47
- def send(self, message: object) -> None:
48
- logger.info("Sending a monitoring message via filesystem")
49
-
50
- unique_id = str(uuid.uuid4())
51
-
52
- tmp_filename = f"{self.tmp_path}/{unique_id}"
53
- new_filename = f"{self.new_path}/{unique_id}"
54
- buffer = message
55
-
56
- # this will write the message out then atomically
57
- # move it into new/, so that a partially written
58
- # file will never be observed in new/
59
- with open(tmp_filename, "wb") as f:
60
- pickle.dump(buffer, f)
61
- os.rename(tmp_filename, new_filename)
62
-
63
-
64
- class HTEXRadioSender(MonitoringRadioSender):
65
-
66
- def __init__(self, monitoring_url: str, timeout: int = 10):
67
- """
68
- Parameters
69
- ----------
70
-
71
- monitoring_url : str
72
- URL of the form <scheme>://<IP>:<PORT>
73
- timeout : int
74
- timeout, default=10s
75
- """
76
- logger.info("htex-based monitoring channel initialising")
77
-
78
- def send(self, message: object) -> None:
79
- """ Sends a message to the UDP receiver
80
-
81
- Parameter
82
- ---------
83
-
84
- message: object
85
- Arbitrary pickle-able object that is to be sent
86
-
87
- Returns:
88
- None
89
- """
90
-
91
- import parsl.executors.high_throughput.monitoring_info
92
-
93
- result_queue = parsl.executors.high_throughput.monitoring_info.result_queue
94
-
95
- # this message needs to go in the result queue tagged so that it is treated
96
- # i) as a monitoring message by the interchange, and then further more treated
97
- # as a RESOURCE_INFO message when received by monitoring (rather than a NODE_INFO
98
- # which is the implicit default for messages from the interchange)
99
-
100
- # for the interchange, the outer wrapper, this needs to be a dict:
101
-
102
- interchange_msg = {
103
- 'type': 'monitoring',
104
- 'payload': message
105
- }
106
-
107
- if result_queue:
108
- result_queue.put(pickle.dumps(interchange_msg))
109
- else:
110
- logger.error("result_queue is uninitialized - cannot put monitoring message")
111
-
112
- return
113
-
114
-
115
- class UDPRadioSender(MonitoringRadioSender):
116
-
117
- def __init__(self, monitoring_url: str, timeout: int = 10):
118
- """
119
- Parameters
120
- ----------
121
-
122
- monitoring_url : str
123
- URL of the form <scheme>://<IP>:<PORT>
124
- timeout : int
125
- timeout, default=10s
126
- """
127
- self.monitoring_url = monitoring_url
128
- self.sock_timeout = timeout
129
- try:
130
- self.scheme, self.ip, port = (x.strip('/') for x in monitoring_url.split(':'))
131
- self.port = int(port)
132
- except Exception:
133
- raise Exception("Failed to parse monitoring url: {}".format(monitoring_url))
134
-
135
- self.sock = socket.socket(socket.AF_INET,
136
- socket.SOCK_DGRAM,
137
- socket.IPPROTO_UDP) # UDP
138
- self.sock.settimeout(self.sock_timeout)
139
-
140
- def send(self, message: object) -> None:
141
- """ Sends a message to the UDP receiver
142
-
143
- Parameter
144
- ---------
145
-
146
- message: object
147
- Arbitrary pickle-able object that is to be sent
148
-
149
- Returns:
150
- None
151
- """
152
- try:
153
- buffer = pickle.dumps(message)
154
- except Exception:
155
- logging.exception("Exception during pickling", exc_info=True)
156
- return
157
-
158
- try:
159
- self.sock.sendto(buffer, (self.ip, self.port))
160
- except socket.timeout:
161
- logging.error("Could not send message within timeout limit")
162
- return
163
- return
164
-
165
-
166
- class MultiprocessingQueueRadioSender(MonitoringRadioSender):
167
- """A monitoring radio which connects over a multiprocessing Queue.
168
- This radio is intended to be used on the submit side, where components
169
- in the submit process, or processes launched by multiprocessing, will have
170
- access to a Queue shared with the monitoring database code (bypassing the
171
- monitoring router).
172
- """
173
- def __init__(self, queue: Queue) -> None:
174
- self.queue = queue
175
-
176
- def send(self, message: object) -> None:
177
- self.queue.put(message)
178
-
179
-
180
- class ZMQRadioSender(MonitoringRadioSender):
181
- """A monitoring radio which connects over ZMQ. This radio is not
182
- thread-safe, because its use of ZMQ is not thread-safe.
183
- """
184
-
185
- def __init__(self, hub_address: str, hub_zmq_port: int) -> None:
186
- self._hub_channel = zmq.Context().socket(zmq.DEALER)
187
- self._hub_channel.set_hwm(0)
188
- self._hub_channel.connect(f"tcp://{hub_address}:{hub_zmq_port}")
189
-
190
- def send(self, message: object) -> None:
191
- self._hub_channel.send_pyobj(message)
File without changes
File without changes
@@ -1,22 +0,0 @@
1
- import pytest
2
-
3
- from parsl.channels.local.local import LocalChannel
4
-
5
-
6
- @pytest.mark.local
7
- def test_local_large_output_2210():
8
- """Regression test for #2210.
9
- The local channel was hanging if the specified command gave too
10
- much output, due to a race condition between process exiting and
11
- pipes filling up.
12
- """
13
-
14
- c = LocalChannel()
15
-
16
- # this will output 128kb of stdout
17
- c.execute_wait("yes | dd count=128 bs=1024", walltime=60)
18
-
19
- # if this test fails, execute_wait should raise a timeout
20
- # exception.
21
-
22
- # The contents out the output is not verified by this test
@@ -1,19 +0,0 @@
1
- import pytest
2
-
3
- from parsl.channels.local.local import LocalChannel
4
-
5
-
6
- @pytest.mark.local
7
- def test_env():
8
- ''' Regression testing for issue #27
9
- '''
10
-
11
- lc = LocalChannel()
12
- rc, stdout, stderr = lc.execute_wait("env", 1)
13
-
14
- stdout = stdout.split('\n')
15
- x = [s for s in stdout if s.startswith("PATH=")]
16
- assert x, "PATH not found"
17
-
18
- x = [s for s in stdout if s.startswith("HOME=")]
19
- assert x, "HOME not found"
File without changes