parsl 2025.6.30__py3-none-any.whl → 2025.7.14__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/anvil.py ADDED
@@ -0,0 +1,34 @@
1
+ from parsl.config import Config
2
+ from parsl.executors import HighThroughputExecutor
3
+ from parsl.launchers import SrunLauncher
4
+ from parsl.providers import SlurmProvider
5
+ from parsl.usage_tracking.levels import LEVEL_1
6
+
7
+ config = Config(
8
+ executors=[
9
+ HighThroughputExecutor(
10
+ label='Anvil_GPU_Executor',
11
+ working_dir='/anvil/scratch/path/to/your/directory',
12
+ max_workers_per_node=1,
13
+ provider=SlurmProvider(
14
+ partition='gpu',
15
+ account='YOUR_ALLOCATION_ON_ANVIL',
16
+ nodes_per_block=1,
17
+ cores_per_node=4,
18
+ mem_per_node=50,
19
+ init_blocks=1,
20
+ max_blocks=1,
21
+ walltime='00:30:00',
22
+ # string to prepend to #SBATCH blocks in the submit
23
+ # script to the scheduler
24
+ scheduler_options='#SBATCH --gres gpu:1 --gpus-per-node=1',
25
+ # Command to be run before starting a worker, such as:
26
+ # 'module load Anaconda; source activate parsl_env'.
27
+ worker_init='module load modtree/gpu',
28
+ exclusive=False,
29
+ launcher=SrunLauncher(),
30
+ ),
31
+ )
32
+ ],
33
+ usage_tracking=LEVEL_1,
34
+ )
parsl/configs/delta.py ADDED
@@ -0,0 +1,35 @@
1
+ from parsl.config import Config
2
+ from parsl.executors import HighThroughputExecutor
3
+ from parsl.launchers import SrunLauncher
4
+ from parsl.providers import SlurmProvider
5
+ from parsl.usage_tracking.levels import LEVEL_1
6
+
7
+ config = Config(
8
+ executors=[
9
+ HighThroughputExecutor(
10
+ label='Delta_GPU_Executor',
11
+ working_dir='/scratch/path/to/your/directory',
12
+ max_workers_per_node=1,
13
+ provider=SlurmProvider(
14
+ partition='gpuA100x4',
15
+ account='YOUR_ALLOCATION_ON_DELTA',
16
+ constraint="scratch",
17
+ nodes_per_block=1,
18
+ cores_per_node=32,
19
+ mem_per_node=220,
20
+ init_blocks=1,
21
+ max_blocks=1,
22
+ walltime='00:30:00',
23
+ # string to prepend to #SBATCH blocks in the submit
24
+ # script to the scheduler
25
+ scheduler_options='#SBATCH --gpus-per-node=1 --gpu-bind=closest',
26
+ # Command to be run before starting a worker, such as:
27
+ # 'module load Anaconda; source activate parsl_env'.
28
+ worker_init='',
29
+ exclusive=False,
30
+ launcher=SrunLauncher(),
31
+ ),
32
+ )
33
+ ],
34
+ usage_tracking=LEVEL_1,
35
+ )
@@ -369,7 +369,11 @@ class Interchange:
369
369
  'Processing message type %r from manager %r', mtype, manager_id
370
370
  )
371
371
 
372
- if mtype == 'registration':
372
+ if mtype == 'connection_probe':
373
+ self.manager_sock.send_multipart([manager_id, b''])
374
+ return
375
+
376
+ elif mtype == 'registration':
373
377
  ix_minor_py = self.current_platform['python_v'].rsplit('.', 1)[0]
374
378
  ix_parsl_v = self.current_platform['parsl_v']
375
379
  mgr_minor_py = meta['python_v'].rsplit('.', 1)[0]
@@ -1,62 +1,70 @@
1
- import argparse
2
1
  import logging
3
- import time
4
- import uuid
2
+ import pickle
3
+ from contextlib import ExitStack
4
+ from typing import Optional
5
5
 
6
6
  import zmq
7
- from zmq.utils.monitor import recv_monitor_message
8
7
 
9
- from parsl.addresses import get_all_addresses, tcp_url
8
+ from parsl import curvezmq
10
9
 
11
10
  logger = logging.getLogger(__name__)
12
11
 
13
12
 
14
- def probe_addresses(addresses, port, timeout=120):
13
+ def probe_addresses(
14
+ zmq_context: curvezmq.ClientContext,
15
+ addresses: str,
16
+ timeout_ms: int = 120_000,
17
+ identity: Optional[bytes] = None,
18
+ ):
15
19
  """
16
- Parameters
17
- ----------
18
-
19
- addresses: [string]
20
- List of addresses as strings
21
- port: int
22
- Port on the interchange
23
- timeout: int
24
- Timeout in seconds
25
-
26
- Returns
27
- -------
28
- None or string address
20
+ Given a single-line CSV list of addresses, return the first proven valid address.
21
+
22
+ This function will connect to each address in ``addresses`` (a comma-separated
23
+ list of URLs) and attempt to send a CONNECTION_PROBE packet. Returns the first
24
+ address that receives a response.
25
+
26
+ If no address receives a response within the ``timeout_ms`` (specified in
27
+ milliseconds), then raise ``ConnectionError``.
28
+
29
+ :param zmq_context: A ZMQ Context; the call-site may provide an encrypted ZMQ
30
+ context for assurance that the returned address is the expected and correct
31
+ endpoint
32
+ :param addresses: a comma-separated string of addresses to attempt. Example:
33
+ ``tcp://127.0.0.1:1234,tcp://[3812::03aa]:5678``
34
+ :param timeout_ms: how long to wait for a response from the probes. The probes
35
+ are initiated and await concurrently, so this timeout will be the total wall
36
+ time in the worst case of "no addresses are valid."
37
+ :param identity: a ZMQ connection identity; used for logging connection probes
38
+ at the interchange
39
+ :raises: ``ConnectionError`` if no addresses are determined valid
40
+ :returns: a single address, the first one that received a response
29
41
  """
30
- context = zmq.Context()
31
- addr_map = {}
32
- for addr in addresses:
33
- socket = context.socket(zmq.DEALER)
34
- socket.setsockopt(zmq.LINGER, 0)
35
- socket.setsockopt(zmq.IPV6, True)
36
- url = tcp_url(addr, port)
37
- logger.debug("Trying to connect back on {}".format(url))
38
- socket.connect(url)
39
- addr_map[addr] = {'sock': socket,
40
- 'mon_sock': socket.get_monitor_socket(events=zmq.EVENT_CONNECTED)}
41
-
42
- start_t = time.time()
43
-
44
- first_connected = None
45
- while time.time() < start_t + timeout and not first_connected:
46
- for addr in addr_map:
47
- try:
48
- recv_monitor_message(addr_map[addr]['mon_sock'], zmq.NOBLOCK)
49
- first_connected = addr
50
- logger.info("Connected to interchange on {}".format(first_connected))
51
- break
52
- except zmq.Again:
53
- pass
54
- time.sleep(0.01)
55
-
56
- for addr in addr_map:
57
- addr_map[addr]['sock'].close()
58
-
59
- return first_connected
42
+ if not addresses:
43
+ raise ValueError("No address to probe!")
44
+
45
+ sock_map = {}
46
+ urls = addresses.split(",")
47
+ with ExitStack() as stk:
48
+ poller = zmq.Poller()
49
+ for url in urls:
50
+ logger.debug("Testing ZMQ connection to url: %s", url)
51
+ s: zmq.Socket = stk.enter_context(zmq_context.socket(zmq.DEALER))
52
+ s.setsockopt(zmq.LINGER, 0)
53
+ s.setsockopt(zmq.IPV6, True)
54
+ if identity:
55
+ s.setsockopt(zmq.IDENTITY, identity)
56
+ stk.enter_context(s.connect(url))
57
+ poller.register(s, zmq.POLLIN)
58
+ sock_map[s] = url
59
+
60
+ s.send(pickle.dumps({'type': 'connection_probe'}))
61
+
62
+ for sock, evt in poller.poll(timeout=timeout_ms):
63
+ sock.recv() # clear the buffer for good netizenry
64
+ return sock_map.get(sock)
65
+
66
+ addys = ", ".join(urls) # just slightly more human friendly
67
+ raise ConnectionError(f"No viable ZMQ url from: {addys}")
60
68
 
61
69
 
62
70
  class TestWorker:
@@ -84,6 +92,10 @@ class TestWorker:
84
92
 
85
93
 
86
94
  if __name__ == "__main__":
95
+ import argparse
96
+ import uuid
97
+
98
+ from parsl.addresses import get_all_addresses, tcp_url
87
99
 
88
100
  parser = argparse.ArgumentParser()
89
101
  parser.add_argument("-p", "--port", required=True,
@@ -154,23 +154,23 @@ class Manager:
154
154
 
155
155
  self._start_time = time.time()
156
156
 
157
- try:
158
- ix_address = probe_addresses(addresses.split(','), port, timeout=address_probe_timeout)
159
- if not ix_address:
160
- raise Exception("No viable address found")
161
- else:
162
- logger.info(f"Connection to Interchange successful on {ix_address}")
163
- ix_url = tcp_url(ix_address, port)
164
- logger.info(f"Interchange url: {ix_url}")
165
- except Exception:
166
- logger.exception("Caught exception while trying to determine viable address to interchange")
167
- print("Failed to find a viable address to connect to interchange. Exiting")
168
- exit(5)
169
-
170
157
  self.cert_dir = cert_dir
171
158
  self.zmq_context = curvezmq.ClientContext(self.cert_dir)
172
159
 
173
- self._ix_url = ix_url
160
+ addresses = ','.join(tcp_url(a, port) for a in addresses.split(','))
161
+ try:
162
+ self._ix_url = probe_addresses(
163
+ self.zmq_context,
164
+ addresses,
165
+ timeout_ms=1_000 * address_probe_timeout,
166
+ identity=uid.encode('utf-8'),
167
+ )
168
+ except ConnectionError:
169
+ addys = ", ".join(addresses.split(","))
170
+ logger.error(f"Unable to connect to interchange; attempted addresses: {addys}")
171
+ raise
172
+
173
+ logger.info(f"Probe discovered interchange url: {self._ix_url}")
174
174
 
175
175
  self.uid = uid
176
176
  self.block_id = block_id
@@ -312,6 +312,14 @@ class Manager:
312
312
  poller.register(results_sock, zmq.POLLIN)
313
313
  poller.register(ix_sock, zmq.POLLIN)
314
314
 
315
+ ix_sock.send(pickle.dumps({"type": "connection_probe"}))
316
+ evts = dict(poller.poll(timeout=self.heartbeat_period))
317
+ if evts.get(ix_sock) is None:
318
+ logger.error(f"Failed to connect to interchange ({self._ix_url}")
319
+
320
+ ix_sock.recv()
321
+ logger.info(f"Successfully connected to interchange via URL: {self._ix_url}")
322
+
315
323
  # Send a registration message
316
324
  msg = self.create_reg_message()
317
325
  logger.debug("Sending registration message: %s", msg)
parsl/version.py CHANGED
@@ -3,4 +3,4 @@
3
3
  Year.Month.Day[alpha/beta/..]
4
4
  Alphas will be numbered like this -> 2024.12.10a0
5
5
  """
6
- VERSION = '2025.06.30'
6
+ VERSION = '2025.07.14'
@@ -369,7 +369,11 @@ class Interchange:
369
369
  'Processing message type %r from manager %r', mtype, manager_id
370
370
  )
371
371
 
372
- if mtype == 'registration':
372
+ if mtype == 'connection_probe':
373
+ self.manager_sock.send_multipart([manager_id, b''])
374
+ return
375
+
376
+ elif mtype == 'registration':
373
377
  ix_minor_py = self.current_platform['python_v'].rsplit('.', 1)[0]
374
378
  ix_parsl_v = self.current_platform['parsl_v']
375
379
  mgr_minor_py = meta['python_v'].rsplit('.', 1)[0]
@@ -154,23 +154,23 @@ class Manager:
154
154
 
155
155
  self._start_time = time.time()
156
156
 
157
- try:
158
- ix_address = probe_addresses(addresses.split(','), port, timeout=address_probe_timeout)
159
- if not ix_address:
160
- raise Exception("No viable address found")
161
- else:
162
- logger.info(f"Connection to Interchange successful on {ix_address}")
163
- ix_url = tcp_url(ix_address, port)
164
- logger.info(f"Interchange url: {ix_url}")
165
- except Exception:
166
- logger.exception("Caught exception while trying to determine viable address to interchange")
167
- print("Failed to find a viable address to connect to interchange. Exiting")
168
- exit(5)
169
-
170
157
  self.cert_dir = cert_dir
171
158
  self.zmq_context = curvezmq.ClientContext(self.cert_dir)
172
159
 
173
- self._ix_url = ix_url
160
+ addresses = ','.join(tcp_url(a, port) for a in addresses.split(','))
161
+ try:
162
+ self._ix_url = probe_addresses(
163
+ self.zmq_context,
164
+ addresses,
165
+ timeout_ms=1_000 * address_probe_timeout,
166
+ identity=uid.encode('utf-8'),
167
+ )
168
+ except ConnectionError:
169
+ addys = ", ".join(addresses.split(","))
170
+ logger.error(f"Unable to connect to interchange; attempted addresses: {addys}")
171
+ raise
172
+
173
+ logger.info(f"Probe discovered interchange url: {self._ix_url}")
174
174
 
175
175
  self.uid = uid
176
176
  self.block_id = block_id
@@ -312,6 +312,14 @@ class Manager:
312
312
  poller.register(results_sock, zmq.POLLIN)
313
313
  poller.register(ix_sock, zmq.POLLIN)
314
314
 
315
+ ix_sock.send(pickle.dumps({"type": "connection_probe"}))
316
+ evts = dict(poller.poll(timeout=self.heartbeat_period))
317
+ if evts.get(ix_sock) is None:
318
+ logger.error(f"Failed to connect to interchange ({self._ix_url}")
319
+
320
+ ix_sock.recv()
321
+ logger.info(f"Successfully connected to interchange via URL: {self._ix_url}")
322
+
315
323
  # Send a registration message
316
324
  msg = self.create_reg_message()
317
325
  logger.debug("Sending registration message: %s", msg)
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: parsl
3
- Version: 2025.6.30
3
+ Version: 2025.7.14
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.06.30.tar.gz
6
+ Download-URL: https://github.com/Parsl/parsl/archive/2025.07.14.tar.gz
7
7
  Author: The Parsl Team
8
8
  Author-email: parsl@googlegroups.com
9
9
  License: Apache 2.0
@@ -8,7 +8,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=HdVM1vluXKc-wEPUuamQMV8xOj_mmygRd2jXw-VuvG4,131
11
+ parsl/version.py,sha256=KbW0mhtCHETE2kbcLlaibu0WKAEYJ0DPYK_4viy4Uyc,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
@@ -21,8 +21,10 @@ parsl/concurrent/__init__.py,sha256=TvIVceJYaJAsxedNBF3Vdo9lEQNHH_j3uxJv0zUjP7w,
21
21
  parsl/configs/ASPIRE1.py,sha256=nQm6BvCPE07YXEsC94wMrHeVAyYcyfvPgWyHIysjAoA,1690
22
22
  parsl/configs/Azure.py,sha256=CJms3xWmdb-S3CksbHrPF2TfMxJC5I0faqUKCOzVg0k,1268
23
23
  parsl/configs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ parsl/configs/anvil.py,sha256=kC0GvIKOPw6Phhghi-Q7IkEaBRKPy9NUsEgQ8RhZ0i4,1259
24
25
  parsl/configs/bridges.py,sha256=NsTvCiHZHbJj-BsOXOpgS4hCblCHW_lnMa_VMb3SIww,1523
25
26
  parsl/configs/cc_in2p3.py,sha256=8KQ18URTKX5o16k66_h-TRCRQeh7Vrsi-s2d_AuOBHs,705
27
+ parsl/configs/delta.py,sha256=5b_arUTDrRiwJyst4A9v2tb43SaGYrf3qx7JNPkGJAc,1282
26
28
  parsl/configs/ec2.py,sha256=5xtlZI4Fc558sYXdM4nQQvQDBNPdzhRRCO14F-8H7Y4,944
27
29
  parsl/configs/expanse.py,sha256=ADUY3GZWSfVKmqFWbgdfC85kRxNPChqOGwly0XdcKSw,1033
28
30
  parsl/configs/frontera.py,sha256=HkZ3sFvFqKrk8kdxMonbUiWjGaZxz3vgvhtgg6_0vpY,1415
@@ -74,15 +76,15 @@ parsl/executors/flux/flux_instance_manager.py,sha256=5T3Rp7ZM-mlT0Pf0Gxgs5_YmnaP
74
76
  parsl/executors/high_throughput/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
77
  parsl/executors/high_throughput/errors.py,sha256=k2XuvvFdUfNs2foHFnxmS-BToRMfdXpYEa4EF3ELKq4,1554
76
78
  parsl/executors/high_throughput/executor.py,sha256=i1XMvB0h40gReWyD4ID14VxdF-Pftl4iGa9vyxC6nKQ,39939
77
- parsl/executors/high_throughput/interchange.py,sha256=pS724a_m66iYDrxngCZy9K-_U0Yl7BaDwOeVShZYWDQ,25838
79
+ parsl/executors/high_throughput/interchange.py,sha256=R6zcY7yh1daMJR8Ke8o9QCj8AtkH7lwzECS0MAWugvA,25964
78
80
  parsl/executors/high_throughput/manager_record.py,sha256=ZMsqFxvreGLRXAw3N-JnODDa9Qfizw2tMmcBhm4lco4,490
79
81
  parsl/executors/high_throughput/manager_selector.py,sha256=UKcUE6v0tO7PDMTThpKSKxVpOpOUilxDL7UbNgpZCxo,2116
80
82
  parsl/executors/high_throughput/monitoring_info.py,sha256=HC0drp6nlXQpAop5PTUKNjdXMgtZVvrBL0JzZJebPP4,298
81
83
  parsl/executors/high_throughput/mpi_executor.py,sha256=P8n81Y9t5cw-YuNFgkrGtc4oG75ntBJDonUIfhkp_5I,5223
82
84
  parsl/executors/high_throughput/mpi_prefix_composer.py,sha256=DmpKugANNa1bdYlqQBLHkrFc15fJpefPPhW9hkAlh1s,4308
83
85
  parsl/executors/high_throughput/mpi_resource_management.py,sha256=73bTW2ZbHRfcrPN318cyjiqDN50AM1cOCQqUGJDIlBg,8199
84
- parsl/executors/high_throughput/probe.py,sha256=SwU6yyntDRFfLGwBeZwuYHJWcpQ0clXvQ-wy39ALDfg,2734
85
- parsl/executors/high_throughput/process_worker_pool.py,sha256=TU3ZaOqgadGEO3qRxWU4K-MDtHAYuZYXVTU1Zel35SE,40286
86
+ parsl/executors/high_throughput/probe.py,sha256=QlBFwSSxMmtH-Aa2JEvCzQLddsbWZluMUxq5ypLR51E,3831
87
+ parsl/executors/high_throughput/process_worker_pool.py,sha256=v-YesFPRU4-Zctyf-N8Tb9YCEqmDNNUaW66YsGsQcxo,40538
86
88
  parsl/executors/high_throughput/zmq_pipes.py,sha256=NUK25IEh0UkxzdqQQyM8tMtuZmjSiTeWu1DzkkAIOhA,8980
87
89
  parsl/executors/radical/__init__.py,sha256=CKbtV2numw5QvgIBq1htMUrt9TqDCIC2zifyf2svTNU,186
88
90
  parsl/executors/radical/executor.py,sha256=e3XS4mvug1uJ6wrt4UH6hBgfbDbc-mQH3xUW2ZmBsMQ,22888
@@ -461,13 +463,13 @@ parsl/usage_tracking/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
461
463
  parsl/usage_tracking/api.py,sha256=iaCY58Dc5J4UM7_dJzEEs871P1p1HdxBMtNGyVdzc9g,1821
462
464
  parsl/usage_tracking/levels.py,sha256=xbfzYEsd55KiZJ-mzNgPebvOH4rRHum04hROzEf41tU,291
463
465
  parsl/usage_tracking/usage.py,sha256=hbMo5BYgIWqMcFWqN-HYP1TbwNrTonpv-usfwnCFJKY,9212
464
- parsl-2025.6.30.data/scripts/exec_parsl_function.py,sha256=YXKVVIa4zXmOtz-0Ca4E_5nQfN_3S2bh2tB75uZZB4w,7774
465
- parsl-2025.6.30.data/scripts/interchange.py,sha256=sbvqyLhW1XrepjCCWL_f9KcAinkTfpHrLg5t28gBr8s,25825
466
- parsl-2025.6.30.data/scripts/parsl_coprocess.py,sha256=zrVjEqQvFOHxsLufPi00xzMONagjVwLZbavPM7bbjK4,5722
467
- parsl-2025.6.30.data/scripts/process_worker_pool.py,sha256=JdCR9-BJK34whhTuAjAadc0qt1tedr-Km7_0bG9BngE,40272
468
- parsl-2025.6.30.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
469
- parsl-2025.6.30.dist-info/METADATA,sha256=D6v24noLAQU8Ka6dYD6bI5wB7-3Kg4B2AXqaR9gpdXQ,4055
470
- parsl-2025.6.30.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
471
- parsl-2025.6.30.dist-info/entry_points.txt,sha256=XqnsWDYoEcLbsMcpnYGKLEnSBmaIe1YoM5YsBdJG2tI,176
472
- parsl-2025.6.30.dist-info/top_level.txt,sha256=PIheYoUFQtF2icLsgOykgU-Cjuwr2Oi6On2jo5RYgRM,6
473
- parsl-2025.6.30.dist-info/RECORD,,
466
+ parsl-2025.7.14.data/scripts/exec_parsl_function.py,sha256=YXKVVIa4zXmOtz-0Ca4E_5nQfN_3S2bh2tB75uZZB4w,7774
467
+ parsl-2025.7.14.data/scripts/interchange.py,sha256=dh9_Q5bLvgHLhSRAXrFIlOd5Yo-ZkudDGFWz0N3hQBg,25951
468
+ parsl-2025.7.14.data/scripts/parsl_coprocess.py,sha256=zrVjEqQvFOHxsLufPi00xzMONagjVwLZbavPM7bbjK4,5722
469
+ parsl-2025.7.14.data/scripts/process_worker_pool.py,sha256=-5VLVjeab6oROulx7OwI9tdNNHd6uap45I1jltm-UDc,40524
470
+ parsl-2025.7.14.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
471
+ parsl-2025.7.14.dist-info/METADATA,sha256=53WZksC8MrkVsgEo77KIKWYY62EknCy5sN6srl3X6cc,4055
472
+ parsl-2025.7.14.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
473
+ parsl-2025.7.14.dist-info/entry_points.txt,sha256=XqnsWDYoEcLbsMcpnYGKLEnSBmaIe1YoM5YsBdJG2tI,176
474
+ parsl-2025.7.14.dist-info/top_level.txt,sha256=PIheYoUFQtF2icLsgOykgU-Cjuwr2Oi6On2jo5RYgRM,6
475
+ parsl-2025.7.14.dist-info/RECORD,,