parsl 2024.4.15__py3-none-any.whl → 2024.4.22__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/addresses.py CHANGED
@@ -113,7 +113,7 @@ def get_all_addresses() -> Set[str]:
113
113
  try:
114
114
  s_addresses.add(address_by_interface(interface))
115
115
  except Exception:
116
- logger.info("Ignoring failure to fetch address from interface {}".format(interface))
116
+ logger.debug("Ignoring failure to fetch address from interface {}".format(interface))
117
117
 
118
118
  resolution_functions: List[Callable[[], str]]
119
119
  resolution_functions = [address_by_hostname, address_by_route, address_by_query]
@@ -121,7 +121,7 @@ def get_all_addresses() -> Set[str]:
121
121
  try:
122
122
  s_addresses.add(f())
123
123
  except Exception:
124
- logger.info("Ignoring an address finder exception")
124
+ logger.debug("Ignoring an address finder exception")
125
125
 
126
126
  return s_addresses
127
127
 
parsl/app/bash.py CHANGED
@@ -5,6 +5,7 @@ import logging
5
5
 
6
6
  from parsl.app.errors import wrap_error
7
7
  from parsl.app.app import AppBase
8
+ from parsl.data_provider.files import File
8
9
  from parsl.dataflow.dflow import DataFlowKernelLoader
9
10
 
10
11
  logger = logging.getLogger(__name__)
@@ -54,13 +55,20 @@ def remote_side_bash_executor(func, *args, **kwargs):
54
55
  if stdfspec is None:
55
56
  return None
56
57
 
57
- fname, mode = get_std_fname_mode(fdname, stdfspec)
58
+ if isinstance(stdfspec, File):
59
+ # a File is an os.PathLike and so we can use it directly for
60
+ # the subsequent file operations
61
+ fname = stdfspec
62
+ mode = "w"
63
+ else:
64
+ fname, mode = get_std_fname_mode(fdname, stdfspec)
65
+
58
66
  try:
59
67
  if os.path.dirname(fname):
60
68
  os.makedirs(os.path.dirname(fname), exist_ok=True)
61
69
  fd = open(fname, mode)
62
70
  except Exception as e:
63
- raise pe.BadStdStreamFile(fname, e)
71
+ raise pe.BadStdStreamFile(str(fname)) from e
64
72
  return fd
65
73
 
66
74
  std_out = open_std_fd('stdout')
parsl/app/errors.py CHANGED
@@ -78,16 +78,14 @@ class BadStdStreamFile(ParslError):
78
78
 
79
79
  Contains:
80
80
  reason(string)
81
- exception object
82
81
  """
83
82
 
84
- def __init__(self, reason: str, exception: Exception) -> None:
85
- super().__init__(reason, exception)
83
+ def __init__(self, reason: str) -> None:
84
+ super().__init__(reason)
86
85
  self._reason = reason
87
- self._exception = exception
88
86
 
89
87
  def __repr__(self) -> str:
90
- return "Bad Stream File: {} Exception: {}".format(self._reason, self._exception)
88
+ return "Bad Stream File: {}".format(self._reason)
91
89
 
92
90
  def __str__(self) -> str:
93
91
  return self.__repr__()
parsl/dataflow/dflow.py CHANGED
@@ -219,14 +219,18 @@ class DataFlowKernel:
219
219
  task_log_info = self._create_task_log_info(task_record)
220
220
  self.monitoring.send(MessageType.TASK_INFO, task_log_info)
221
221
 
222
- def _create_task_log_info(self, task_record):
222
+ def _create_task_log_info(self, task_record: TaskRecord) -> Dict[str, Any]:
223
223
  """
224
224
  Create the dictionary that will be included in the log.
225
225
  """
226
226
  info_to_monitor = ['func_name', 'memoize', 'hashsum', 'fail_count', 'fail_cost', 'status',
227
227
  'id', 'time_invoked', 'try_time_launched', 'time_returned', 'try_time_returned', 'executor']
228
228
 
229
- task_log_info = {"task_" + k: task_record[k] for k in info_to_monitor}
229
+ # mypy cannot verify that these task_record[k] references are valid:
230
+ # They are valid if all entries in info_to_monitor are declared in the definition of TaskRecord
231
+ # This type: ignore[literal-required] asserts that fact.
232
+ task_log_info = {"task_" + k: task_record[k] for k in info_to_monitor} # type: ignore[literal-required]
233
+
230
234
  task_log_info['run_id'] = self.run_id
231
235
  task_log_info['try_id'] = task_record['try_id']
232
236
  task_log_info['timestamp'] = datetime.datetime.now()
@@ -238,33 +242,28 @@ class DataFlowKernel:
238
242
  task_log_info['task_inputs'] = str(task_record['kwargs'].get('inputs', None))
239
243
  task_log_info['task_outputs'] = str(task_record['kwargs'].get('outputs', None))
240
244
  task_log_info['task_stdin'] = task_record['kwargs'].get('stdin', None)
241
- stdout_spec = task_record['kwargs'].get('stdout', None)
242
- stderr_spec = task_record['kwargs'].get('stderr', None)
243
245
 
244
- # stdout and stderr strings are set to the filename if we can
245
- # interpret the specification; otherwise, set to the empty string
246
- # (on exception, or when not specified)
246
+ def std_spec_to_name(name, spec):
247
+ if spec is None:
248
+ name = ""
249
+ elif isinstance(spec, File):
250
+ name = spec.url
251
+ else:
252
+ # fallthrough case is various str, os.PathLike, tuple modes that
253
+ # can be interpreted by get_std_fname_mode.
254
+ try:
255
+ name, _ = get_std_fname_mode(name, spec)
256
+ except Exception:
257
+ logger.exception(f"Could not parse {name} specification {spec} for task {task_record['id']}")
258
+ name = ""
259
+ return name
247
260
 
248
- if stdout_spec is not None:
249
- try:
250
- stdout_name, _ = get_std_fname_mode('stdout', stdout_spec)
251
- except Exception:
252
- logger.exception("Could not parse stdout specification {} for task {}".format(stdout_spec, task_record['id']))
253
- stdout_name = ""
254
- else:
255
- stdout_name = ""
261
+ stdout_spec = task_record['kwargs'].get('stdout')
262
+ task_log_info['task_stdout'] = std_spec_to_name('stdout', stdout_spec)
256
263
 
257
- if stderr_spec is not None:
258
- try:
259
- stderr_name, _ = get_std_fname_mode('stderr', stderr_spec)
260
- except Exception:
261
- logger.exception("Could not parse stderr specification {} for task {}".format(stderr_spec, task_record['id']))
262
- stderr_name = ""
263
- else:
264
- stderr_name = ""
264
+ stderr_spec = task_record['kwargs'].get('stderr')
265
+ task_log_info['task_stderr'] = std_spec_to_name('stderr', stderr_spec)
265
266
 
266
- task_log_info['task_stdout'] = stdout_name
267
- task_log_info['task_stderr'] = stderr_name
268
267
  task_log_info['task_fail_history'] = ",".join(task_record['fail_history'])
269
268
  task_log_info['task_depends'] = None
270
269
  if task_record['depends'] is not None:
@@ -774,6 +773,10 @@ class DataFlowKernel:
774
773
  (inputs[idx], func) = self.data_manager.optionally_stage_in(f, func, executor)
775
774
 
776
775
  for kwarg, f in kwargs.items():
776
+ # stdout and stderr files should not be staging in (they will be staged *out*
777
+ # in _add_output_deps)
778
+ if kwarg in ['stdout', 'stderr']:
779
+ continue
777
780
  (kwargs[kwarg], func) = self.data_manager.optionally_stage_in(f, func, executor)
778
781
 
779
782
  newargs = list(args)
@@ -786,33 +789,56 @@ class DataFlowKernel:
786
789
  logger.debug("Adding output dependencies")
787
790
  outputs = kwargs.get('outputs', [])
788
791
  app_fut._outputs = []
789
- for idx, f in enumerate(outputs):
790
- if isinstance(f, File) and not self.check_staging_inhibited(kwargs):
792
+
793
+ # Pass over all possible outputs: the outputs kwarg, stdout and stderr
794
+ # and for each of those, perform possible stage-out. This can result in:
795
+ # a DataFuture to be exposed in app_fut to represent the completion of
796
+ # that stageout (sometimes backed by a new sub-workflow for separate-task
797
+ # stageout), a replacement for the function to be executed (intended to
798
+ # be the original function wrapped with an in-task stageout wrapper), a
799
+ # rewritten File object to be passed to task to be executed
800
+
801
+ @typechecked
802
+ def stageout_one_file(file: File, rewritable_func: Callable):
803
+ if not self.check_staging_inhibited(kwargs):
791
804
  # replace a File with a DataFuture - either completing when the stageout
792
805
  # future completes, or if no stage out future is returned, then when the
793
806
  # app itself completes.
794
807
 
795
808
  # The staging code will get a clean copy which it is allowed to mutate,
796
809
  # while the DataFuture-contained original will not be modified by any staging.
797
- f_copy = f.cleancopy()
798
- outputs[idx] = f_copy
810
+ f_copy = file.cleancopy()
799
811
 
800
- logger.debug("Submitting stage out for output file {}".format(repr(f)))
812
+ logger.debug("Submitting stage out for output file {}".format(repr(file)))
801
813
  stageout_fut = self.data_manager.stage_out(f_copy, executor, app_fut)
802
814
  if stageout_fut:
803
- logger.debug("Adding a dependency on stageout future for {}".format(repr(f)))
804
- app_fut._outputs.append(DataFuture(stageout_fut, f, tid=app_fut.tid))
815
+ logger.debug("Adding a dependency on stageout future for {}".format(repr(file)))
816
+ df = DataFuture(stageout_fut, file, tid=app_fut.tid)
805
817
  else:
806
- logger.debug("No stageout dependency for {}".format(repr(f)))
807
- app_fut._outputs.append(DataFuture(app_fut, f, tid=app_fut.tid))
818
+ logger.debug("No stageout dependency for {}".format(repr(file)))
819
+ df = DataFuture(app_fut, file, tid=app_fut.tid)
808
820
 
809
821
  # this is a hook for post-task stageout
810
822
  # note that nothing depends on the output - which is maybe a bug
811
823
  # in the not-very-tested stageout system?
812
- func = self.data_manager.replace_task_stage_out(f_copy, func, executor)
824
+ rewritable_func = self.data_manager.replace_task_stage_out(f_copy, rewritable_func, executor)
825
+ return rewritable_func, f_copy, df
813
826
  else:
814
- logger.debug("Not performing output staging for: {}".format(repr(f)))
815
- app_fut._outputs.append(DataFuture(app_fut, f, tid=app_fut.tid))
827
+ logger.debug("Not performing output staging for: {}".format(repr(file)))
828
+ return rewritable_func, file, DataFuture(app_fut, file, tid=app_fut.tid)
829
+
830
+ for idx, file in enumerate(outputs):
831
+ func, outputs[idx], o = stageout_one_file(file, func)
832
+ app_fut._outputs.append(o)
833
+
834
+ file = kwargs.get('stdout')
835
+ if isinstance(file, File):
836
+ func, kwargs['stdout'], app_fut._stdout_future = stageout_one_file(file, func)
837
+
838
+ file = kwargs.get('stderr')
839
+ if isinstance(file, File):
840
+ func, kwargs['stderr'], app_fut._stderr_future = stageout_one_file(file, func)
841
+
816
842
  return func
817
843
 
818
844
  def _gather_all_deps(self, args: Sequence[Any], kwargs: Dict[str, Any]) -> List[Future]:
@@ -1140,8 +1166,6 @@ class DataFlowKernel:
1140
1166
  executor.hub_port = self.hub_zmq_port
1141
1167
  if self.monitoring:
1142
1168
  executor.monitoring_radio = self.monitoring.radio
1143
- else:
1144
- executor.monitoring_radio = None
1145
1169
  if hasattr(executor, 'provider'):
1146
1170
  if hasattr(executor.provider, 'script_dir'):
1147
1171
  executor.provider.script_dir = os.path.join(self.run_dir, 'submit_scripts')
@@ -1402,8 +1426,14 @@ class DataFlowKernel:
1402
1426
  logger.info(f"{name} for task {tid} will not be redirected.")
1403
1427
  elif isinstance(target, str):
1404
1428
  logger.info(f"{name} for task {tid} will be redirected to {target}")
1405
- elif isinstance(target, tuple) and len(target) == 2:
1429
+ elif isinstance(target, os.PathLike):
1430
+ logger.info(f"{name} for task {tid} will be redirected to {os.fspath(target)}")
1431
+ elif isinstance(target, tuple) and len(target) == 2 and isinstance(target[0], str):
1406
1432
  logger.info(f"{name} for task {tid} will be redirected to {target[0]} with mode {target[1]}")
1433
+ elif isinstance(target, tuple) and len(target) == 2 and isinstance(target[0], os.PathLike):
1434
+ logger.info(f"{name} for task {tid} will be redirected to {os.fspath(target[0])} with mode {target[1]}")
1435
+ elif isinstance(target, DataFuture):
1436
+ logger.info(f"{name} for task {tid} will staged to {target.file_obj.url}")
1407
1437
  else:
1408
1438
  logger.error(f"{name} for task {tid} has unknown specification: {target!r}")
1409
1439
 
parsl/dataflow/futures.py CHANGED
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from concurrent.futures import Future
4
4
  import logging
5
5
  import threading
6
- from typing import Any, Optional, Sequence
6
+ from typing import Any, Optional, Sequence, Union
7
7
 
8
8
  import parsl.app.app as app
9
9
 
@@ -70,13 +70,34 @@ class AppFuture(Future):
70
70
  self._outputs = []
71
71
  self.task_record = task_record
72
72
 
73
+ self._stdout_future: Optional[DataFuture] = None
74
+ self._stderr_future: Optional[DataFuture] = None
75
+
73
76
  @property
74
- def stdout(self) -> Optional[str]:
75
- return self.task_record['kwargs'].get('stdout')
77
+ def stdout(self) -> Union[None, str, DataFuture]:
78
+ """Return app stdout. If stdout was specified as a string, then this
79
+ property will return that string. If stdout was specified as a File,
80
+ then this property will return a DataFuture representing that file
81
+ stageout.
82
+ TODO: this can be a tuple too I think?"""
83
+ if self._stdout_future:
84
+ return self._stdout_future
85
+ else:
86
+ # this covers the str and None cases
87
+ return self.task_record['kwargs'].get('stdout')
76
88
 
77
89
  @property
78
- def stderr(self) -> Optional[str]:
79
- return self.task_record['kwargs'].get('stderr')
90
+ def stderr(self) -> Union[None, str, DataFuture]:
91
+ """Return app stderr. If stdout was specified as a string, then this
92
+ property will return that string. If stdout was specified as a File,
93
+ then this property will return a DataFuture representing that file
94
+ stageout.
95
+ TODO: this can be a tuple too I think?"""
96
+ if self._stderr_future:
97
+ return self._stderr_future
98
+ else:
99
+ # this covers the str and None cases
100
+ return self.task_record['kwargs'].get('stderr')
80
101
 
81
102
  @property
82
103
  def tid(self) -> int:
parsl/executors/base.py CHANGED
@@ -1,3 +1,4 @@
1
+ import os
1
2
  from abc import ABCMeta, abstractmethod
2
3
  from concurrent.futures import Future
3
4
  from typing import Any, Callable, Dict, Optional
@@ -45,6 +46,21 @@ class ParslExecutor(metaclass=ABCMeta):
45
46
  label: str = "undefined"
46
47
  radio_mode: str = "udp"
47
48
 
49
+ def __init__(
50
+ self,
51
+ *,
52
+ hub_address: Optional[str] = None,
53
+ hub_port: Optional[int] = None,
54
+ monitoring_radio: Optional[MonitoringRadio] = None,
55
+ run_dir: str = ".",
56
+ run_id: Optional[str] = None,
57
+ ):
58
+ self.hub_address = hub_address
59
+ self.hub_port = hub_port
60
+ self.monitoring_radio = monitoring_radio
61
+ self.run_dir = os.path.abspath(run_dir)
62
+ self.run_id = run_id
63
+
48
64
  def __enter__(self) -> Self:
49
65
  return self
50
66
 
@@ -644,6 +644,12 @@ class TaskVineExecutor(BlockProviderExecutor, putils.RepresentationMixin):
644
644
  logger.debug("Joining on factory process")
645
645
  self._factory_process.join()
646
646
 
647
+ # Shutdown multiprocessing queues
648
+ self._ready_task_queue.close()
649
+ self._ready_task_queue.join_thread()
650
+ self._finished_task_queue.close()
651
+ self._finished_task_queue.join_thread()
652
+
647
653
  self._is_shutdown = True
648
654
  logger.debug("TaskVine shutdown completed")
649
655
 
@@ -735,6 +735,12 @@ class WorkQueueExecutor(BlockProviderExecutor, putils.RepresentationMixin):
735
735
  logger.debug("Joining on collector thread")
736
736
  self.collector_thread.join()
737
737
 
738
+ logger.debug("Closing multiprocessing queues")
739
+ self.task_queue.close()
740
+ self.task_queue.join_thread()
741
+ self.collector_queue.close()
742
+ self.collector_queue.join_thread()
743
+
738
744
  self.is_shutdown = True
739
745
  logger.debug("Work Queue shutdown completed")
740
746
 
@@ -195,6 +195,8 @@ class MonitoringHub(RepresentationMixin):
195
195
 
196
196
  try:
197
197
  comm_q_result = comm_q.get(block=True, timeout=120)
198
+ comm_q.close()
199
+ comm_q.join_thread()
198
200
  except queue.Empty:
199
201
  logger.error("Hub has not completed initialization in 120s. Aborting")
200
202
  raise Exception("Hub failed to start")
@@ -258,6 +260,19 @@ class MonitoringHub(RepresentationMixin):
258
260
  self.filesystem_proc.terminate()
259
261
  self.filesystem_proc.join()
260
262
 
263
+ logger.info("Closing monitoring multiprocessing queues")
264
+ self.exception_q.close()
265
+ self.exception_q.join_thread()
266
+ self.priority_msgs.close()
267
+ self.priority_msgs.join_thread()
268
+ self.resource_msgs.close()
269
+ self.resource_msgs.join_thread()
270
+ self.node_msgs.close()
271
+ self.node_msgs.join_thread()
272
+ self.block_msgs.close()
273
+ self.block_msgs.join_thread()
274
+ logger.info("Closed monitoring multiprocessing queues")
275
+
261
276
 
262
277
  @wrap_with_logs
263
278
  def filesystem_receiver(logdir: str, q: "queue.Queue[AddressedMonitoringMessage]", run_dir: str) -> None:
@@ -50,6 +50,8 @@ def test_auto_log_filename_format(caplog):
50
50
  foo_future.result())
51
51
 
52
52
  log_fpath = foo_future.stdout
53
+ assert isinstance(log_fpath, str)
54
+
53
55
  log_pattern = fr".*/task_\d+_foo_{app_label}"
54
56
  assert re.match(log_pattern, log_fpath), 'Output file "{0}" does not match pattern "{1}"'.format(
55
57
  log_fpath, log_pattern)
@@ -15,7 +15,7 @@ def local_setup():
15
15
 
16
16
 
17
17
  def local_teardown():
18
- parsl.dfk().cleanup
18
+ parsl.dfk().cleanup()
19
19
  parsl.clear()
20
20
 
21
21
 
@@ -53,6 +53,7 @@ def test_interchange_binding_with_address(cert_dir: Optional[str]):
53
53
  assert ix.interchange_address == address
54
54
 
55
55
 
56
+ @pytest.mark.skip("This behaviour is possibly unexpected. See issue #3037")
56
57
  @pytest.mark.local
57
58
  @pytest.mark.parametrize("encrypted", (True, False), indirect=True)
58
59
  def test_interchange_binding_with_non_ipv4_address(cert_dir: Optional[str]):
@@ -0,0 +1,137 @@
1
+ """Tests monitoring records app name under various decoration patterns.
2
+ """
3
+
4
+ import logging
5
+ import os
6
+ import parsl
7
+ import pytest
8
+ import re
9
+ import time
10
+
11
+ from typing import Union
12
+
13
+ from parsl.config import Config
14
+ from parsl.data_provider.files import File
15
+ from parsl.data_provider.data_manager import default_staging
16
+ from parsl.data_provider.staging import Staging
17
+ from parsl.executors import HighThroughputExecutor
18
+ from parsl.monitoring import MonitoringHub
19
+ from parsl.providers import LocalProvider
20
+
21
+
22
+ def fresh_config(run_dir):
23
+ return Config(
24
+ run_dir=str(run_dir),
25
+ executors=[
26
+ HighThroughputExecutor(
27
+ address="127.0.0.1",
28
+ label="htex_Local",
29
+ provider=LocalProvider(
30
+ init_blocks=1,
31
+ min_blocks=1,
32
+ max_blocks=1,
33
+ )
34
+ )
35
+ ],
36
+ strategy='simple',
37
+ strategy_period=0.1,
38
+ monitoring=MonitoringHub(
39
+ hub_address="localhost",
40
+ hub_port=55055,
41
+ )
42
+ )
43
+
44
+
45
+ @parsl.python_app
46
+ def stdapp(stdout=None, stderr=None):
47
+ pass
48
+
49
+
50
+ class ArbitraryPathLike(os.PathLike):
51
+ def __init__(self, path: Union[str, bytes]) -> None:
52
+ self.path = path
53
+
54
+ def __fspath__(self) -> Union[str, bytes]:
55
+ return self.path
56
+
57
+
58
+ class ArbitraryStaging(Staging):
59
+ """This staging provider will not actually do any staging, but will
60
+ accept arbitrary: scheme URLs. That's enough for this monitoring test
61
+ which doesn't need any actual stage out action to happen.
62
+ """
63
+ def can_stage_out(self, file):
64
+ return file.scheme == "arbitrary"
65
+
66
+
67
+ @pytest.mark.local
68
+ @pytest.mark.parametrize('stdx,expected_stdx',
69
+ [('hello.txt', 'hello.txt'),
70
+ (None, ''),
71
+ (('tuple.txt', 'w'), 'tuple.txt'),
72
+ (ArbitraryPathLike('pl.txt'), 'pl.txt'),
73
+ (ArbitraryPathLike(b'pl2.txt'), 'pl2.txt'),
74
+ ((ArbitraryPathLike('pl3.txt'), 'w'), 'pl3.txt'),
75
+ ((ArbitraryPathLike(b'pl4.txt'), 'w'), 'pl4.txt'),
76
+ (parsl.AUTO_LOGNAME,
77
+ lambda p:
78
+ isinstance(p, str) and
79
+ os.path.isabs(p) and
80
+ re.match("^.*/task_0000_stdapp\\.std...$", p)),
81
+ (File("arbitrary:abc123"), "arbitrary:abc123"),
82
+ (File("file:///tmp/pl5"), "file:///tmp/pl5"),
83
+ ])
84
+ @pytest.mark.parametrize('stream', ['stdout', 'stderr'])
85
+ def test_stdstream_to_monitoring(stdx, expected_stdx, stream, tmpd_cwd, caplog):
86
+ """This tests that various forms of stdout/err specification are
87
+ represented in monitoring correctly. The stderr and stdout codepaths
88
+ are generally duplicated, rather than factorised, and so this test
89
+ runs the same tests on both stdout and stderr.
90
+ """
91
+
92
+ # this is imported here rather than at module level because
93
+ # it isn't available in a plain parsl install, so this module
94
+ # would otherwise fail to import and break even a basic test
95
+ # run.
96
+ import sqlalchemy
97
+
98
+ c = fresh_config(tmpd_cwd)
99
+ c.monitoring.logging_endpoint = f"sqlite:///{tmpd_cwd}/monitoring.db"
100
+ c.executors[0].storage_access = default_staging + [ArbitraryStaging()]
101
+
102
+ with parsl.load(c):
103
+ kwargs = {stream: stdx}
104
+ stdapp(**kwargs).result()
105
+
106
+ parsl.clear()
107
+
108
+ engine = sqlalchemy.create_engine(c.monitoring.logging_endpoint)
109
+ with engine.begin() as connection:
110
+
111
+ def count_rows(table: str):
112
+ result = connection.execute(f"SELECT COUNT(*) FROM {table}")
113
+ (c, ) = result.first()
114
+ return c
115
+
116
+ # one workflow...
117
+ assert count_rows("workflow") == 1
118
+
119
+ # ... with one task ...
120
+ assert count_rows("task") == 1
121
+
122
+ # ... that was tried once ...
123
+ assert count_rows("try") == 1
124
+
125
+ # ... and has the expected name.
126
+ result = connection.execute(f"SELECT task_{stream} FROM task")
127
+ (c, ) = result.first()
128
+
129
+ if isinstance(expected_stdx, str):
130
+ assert c == expected_stdx
131
+ elif callable(expected_stdx):
132
+ assert expected_stdx(c)
133
+ else:
134
+ raise RuntimeError("Bad expected_stdx value")
135
+
136
+ for record in caplog.records:
137
+ assert record.levelno < logging.ERROR
@@ -24,15 +24,15 @@ def local_teardown():
24
24
 
25
25
 
26
26
  @pytest.mark.local
27
- def test_within_context_manger():
27
+ def test_within_context_manger(tmpd_cwd):
28
28
  config = fresh_config()
29
29
  with parsl.load(config=config) as dfk:
30
30
  assert isinstance(dfk, DataFlowKernel)
31
31
 
32
- bash_future = foo(1)
32
+ bash_future = foo(1, stdout=tmpd_cwd / 'foo.stdout')
33
33
  assert bash_future.result() == 0
34
34
 
35
- with open('foo.stdout', 'r') as f:
35
+ with open(tmpd_cwd / 'foo.stdout', 'r') as f:
36
36
  assert f.read() == "2\n"
37
37
 
38
38
  with pytest.raises(NoDataFlowKernelError) as excinfo:
@@ -9,6 +9,14 @@ from parsl.executors import HighThroughputExecutor
9
9
  from parsl.launchers import SimpleLauncher
10
10
  from parsl.providers import LocalProvider
11
11
 
12
+ # Timing notes:
13
+ # The configured strategy_period must be much smaller than the delay in
14
+ # app() so that multiple iterations of the strategy have had a chance
15
+ # to (mis)behave.
16
+ # The status polling interval in OneShotLocalProvider must be much bigger
17
+ # than the above times, so that the job status cached from the provider
18
+ # will not be updated while the single invocation of app() runs.
19
+
12
20
 
13
21
  @parsl.python_app
14
22
  def app():
@@ -55,20 +63,12 @@ def test_one_block(tmpd_cwd):
55
63
  )
56
64
  ],
57
65
  strategy='simple',
66
+ strategy_period=0.1
58
67
  )
59
68
 
60
- parsl.load(config)
61
- dfk = parsl.dfk()
62
-
63
- def poller():
64
- import time
65
- while True:
66
- dfk.job_status_poller.poll()
67
- time.sleep(0.1)
69
+ with parsl.load(config):
70
+ app().result()
68
71
 
69
- threading.Thread(target=poller, daemon=True).start()
70
- app().result()
71
- parsl.dfk().cleanup()
72
72
  parsl.clear()
73
73
 
74
74
  assert oneshot_provider.recorded_submits == 1
@@ -0,0 +1,61 @@
1
+ import logging
2
+ import os
3
+ import parsl
4
+ import pytest
5
+ import zipfile
6
+
7
+ from parsl.app.futures import DataFuture
8
+ from parsl.tests.configs.htex_local import fresh_config as local_config
9
+ from parsl.data_provider.files import File
10
+
11
+
12
+ @parsl.bash_app
13
+ def output_to_stds(*, stdout=parsl.AUTO_LOGNAME, stderr=parsl.AUTO_LOGNAME):
14
+ return "echo hello ; echo goodbye >&2"
15
+
16
+
17
+ def test_stdout_staging_file(tmpd_cwd, caplog):
18
+ basename = str(tmpd_cwd) + "/stdout.txt"
19
+ stdout_file = File("file://" + basename)
20
+
21
+ app_future = output_to_stds(stdout=stdout_file)
22
+
23
+ assert isinstance(app_future.stdout, DataFuture)
24
+ app_future.stdout.result()
25
+
26
+ assert os.path.exists(basename)
27
+
28
+ for record in caplog.records:
29
+ assert record.levelno < logging.ERROR
30
+
31
+
32
+ def test_stdout_stderr_staging_zip(tmpd_cwd, caplog):
33
+ zipfile_name = str(tmpd_cwd) + "/staging.zip"
34
+ stdout_relative_path = "somewhere/test-out.txt"
35
+ stdout_file = File("zip:" + zipfile_name + "/" + stdout_relative_path)
36
+
37
+ stderr_relative_path = "somewhere/test-error.txt"
38
+ stderr_file = File("zip:" + zipfile_name + "/" + stderr_relative_path)
39
+
40
+ app_future = output_to_stds(stdout=stdout_file, stderr=stderr_file)
41
+
42
+ assert isinstance(app_future.stdout, DataFuture)
43
+ app_future.stdout.result()
44
+
45
+ # check the file exists as soon as possible
46
+ assert os.path.exists(zipfile_name)
47
+ with zipfile.ZipFile(zipfile_name) as z:
48
+ with z.open(stdout_relative_path) as f:
49
+ assert f.readlines() == [b'hello\n']
50
+
51
+ assert isinstance(app_future.stderr, DataFuture)
52
+ app_future.stderr.result()
53
+ with zipfile.ZipFile(zipfile_name) as z:
54
+ with z.open(stderr_relative_path) as f:
55
+ # The last line of stderr should be goodbye, but Parsl will write
56
+ # other Parsl-specific into to stderr before that, so only assert
57
+ # the behaviour of the final line.
58
+ assert f.readlines()[-1] == b'goodbye\n'
59
+
60
+ for record in caplog.records:
61
+ assert record.levelno < logging.ERROR
parsl/utils.py CHANGED
@@ -13,6 +13,7 @@ import typeguard
13
13
  from typing_extensions import Type
14
14
 
15
15
  import parsl
16
+ from parsl.app.errors import BadStdStreamFile
16
17
  from parsl.version import VERSION
17
18
 
18
19
 
@@ -121,9 +122,17 @@ def get_std_fname_mode(
121
122
  if len(stdfspec) != 2:
122
123
  msg = (f"std descriptor {fdname} has incorrect tuple length "
123
124
  f"{len(stdfspec)}")
124
- raise pe.BadStdStreamFile(msg, TypeError('Bad Tuple Length'))
125
+ raise pe.BadStdStreamFile(msg)
125
126
  fname, mode = stdfspec
126
- return str(fname), mode
127
+
128
+ path = os.fspath(fname)
129
+
130
+ if isinstance(path, str):
131
+ return path, mode
132
+ elif isinstance(path, bytes):
133
+ return path.decode(), mode
134
+ else:
135
+ raise BadStdStreamFile(f"fname has invalid type {type(path)}")
127
136
 
128
137
 
129
138
  @contextmanager
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 = '2024.04.15'
6
+ VERSION = '2024.04.22'
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: parsl
3
- Version: 2024.4.15
3
+ Version: 2024.4.22
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/2024.04.15.tar.gz
6
+ Download-URL: https://github.com/Parsl/parsl/archive/2024.04.22.tar.gz
7
7
  Author: The Parsl Team
8
8
  Author-email: parsl@googlegroups.com
9
9
  License: Apache 2.0
@@ -55,7 +55,7 @@ Requires-Dist: pyyaml ; extra == 'all'
55
55
  Requires-Dist: cffi ; extra == 'all'
56
56
  Requires-Dist: jsonschema ; extra == 'all'
57
57
  Requires-Dist: proxystore ; extra == 'all'
58
- Requires-Dist: radical.pilot ==1.47 ; extra == 'all'
58
+ Requires-Dist: radical.pilot ==1.52.1 ; extra == 'all'
59
59
  Provides-Extra: aws
60
60
  Requires-Dist: boto3 ; extra == 'aws'
61
61
  Provides-Extra: azure
@@ -84,7 +84,7 @@ Requires-Dist: oauth-ssh >=0.9 ; extra == 'oauth_ssh'
84
84
  Provides-Extra: proxystore
85
85
  Requires-Dist: proxystore ; extra == 'proxystore'
86
86
  Provides-Extra: radical-pilot
87
- Requires-Dist: radical.pilot ==1.47 ; extra == 'radical-pilot'
87
+ Requires-Dist: radical.pilot ==1.52.1 ; extra == 'radical-pilot'
88
88
  Provides-Extra: visualization
89
89
  Requires-Dist: pydot ; extra == 'visualization'
90
90
  Requires-Dist: networkx <2.6,>=2.5 ; extra == 'visualization'
@@ -1,5 +1,5 @@
1
1
  parsl/__init__.py,sha256=hq8rJmP59wzd9-yxaGcmq5gPpshOopH-Y1K0BkUBNY0,1843
2
- parsl/addresses.py,sha256=mO4u1kVxAnBHHIIUyqdzsOxT9aUsGeBP5PfAPkz3sug,4819
2
+ parsl/addresses.py,sha256=0wPo-4HjW0l4ndqCKLmSdbbSWE_3WK7pxRvqBEp-3Lk,4821
3
3
  parsl/config.py,sha256=E90pKPeagHpIdk9XYifHqSpTAaKdDQN59NPDi8PrTAc,7038
4
4
  parsl/curvezmq.py,sha256=FtZEYP1IWDry39cH-tOKUm9TnaR1U7krOmvVYpATcOk,6939
5
5
  parsl/errors.py,sha256=SzINzQFZDBDbj9l-DPQznD0TbGkNhHIRAPkcBCogf_A,1019
@@ -7,12 +7,12 @@ parsl/log_utils.py,sha256=Ckeb7YiIoK0FA8dA5CsWJDe28i9Sf4sxhFwp__VsD3o,3274
7
7
  parsl/multiprocessing.py,sha256=hakfdg-sgxEjwloZeDrt6EhzwdzecvjJhkPHHxh8lII,1938
8
8
  parsl/process_loggers.py,sha256=1G3Rfrh5wuZNo2X03grG4kTYPGOxz7hHCyG6L_A3b0A,1137
9
9
  parsl/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- parsl/utils.py,sha256=A3WDMGaNB4ajVx_jCuc-74W6PFy4zswJy-pLE7u8Dz0,10979
11
- parsl/version.py,sha256=nAZAqY4iXKGB--oYnOxrq5rAQFGqE2qgS9MxaDS4zA8,131
10
+ parsl/utils.py,sha256=DUPrl9ZdzwJzz2rmlRws77OMs43iQo_CT-Kr3uJs-fo,11202
11
+ parsl/version.py,sha256=gYdglqtXYC99qyF-3eJE7_3lJq1gYemHXeDA1r1F6Cc,131
12
12
  parsl/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  parsl/app/app.py,sha256=wAHchJetgnicT1pn0NJKDeDX0lV3vDFlG8cQd_Ciax4,8522
14
- parsl/app/bash.py,sha256=bx9x1XFwkOTpZZD3CPwnVL9SyNRDjbUGtOnuGLvxN_8,5396
15
- parsl/app/errors.py,sha256=M5QdCEohov14A99cLHP41tn6T5JczxmX-WJGYfwPDWE,4039
14
+ parsl/app/bash.py,sha256=VufxGROrlJB3dP03syNutU0x8rLzfI-gooWwBZ4FFQ8,5676
15
+ parsl/app/errors.py,sha256=H0n-5kNMwl71cPJ7bkeHwBegCg639Z6be6ROvY0USg0,3915
16
16
  parsl/app/futures.py,sha256=42UucIjKLJyRkg59BH-Pg_Q9Iue2Y-LSDi6g8q_cKzo,2910
17
17
  parsl/app/python.py,sha256=qzVq7aXu3ZQGsMdQGDDYov_AP7ChvUT00peqIM93XXA,2330
18
18
  parsl/benchmark/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -61,15 +61,15 @@ parsl/data_provider/rsync.py,sha256=2-ZxqrT-hBj39x082NusJaBqsGW4Jd2qCW6JkVPpEl0,
61
61
  parsl/data_provider/staging.py,sha256=l-mAXFburs3BWPjkSmiQKuAgJpsxCG62yATPDbrafYI,4523
62
62
  parsl/data_provider/zip.py,sha256=qzsSpHYp3EUqS3LOaPiZPitr_9zu6oGybVaFOApVbuY,3348
63
63
  parsl/dataflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
- parsl/dataflow/dflow.py,sha256=yFj64cC2xazB_D-7nv3WncWElZnNXhUNQMHg7O1rsLc,63830
64
+ parsl/dataflow/dflow.py,sha256=2l8UCQ1m30cK3vxerUG5Ahzy_nthO_wU0280hUw3igw,65633
65
65
  parsl/dataflow/errors.py,sha256=w2vOt_ymzG2dOqJUO4IDcmTlrCIHlMZL8nBVyVq0O_8,2176
66
- parsl/dataflow/futures.py,sha256=YIo1-ab7fr35czWy8_PPvkSF_Hh-3N_6x6nWziQGPbQ,5059
66
+ parsl/dataflow/futures.py,sha256=XGgaoaT3N2U3vvZ7DEoLkGBrZscq_VzffZ9goLB87ko,6081
67
67
  parsl/dataflow/memoization.py,sha256=AsJO6c6cRp2ac6H8uGn2USlEi78_nX3QWvpxYt4XdYE,9583
68
68
  parsl/dataflow/rundirs.py,sha256=XKmBZpBEIsGACBhYOkbbs2e5edC0pQegJcSlk4FWeag,1154
69
69
  parsl/dataflow/states.py,sha256=hV6mfv-y4A6xrujeQglcomnfEs7y3Xm2g6JFwC6dvgQ,2612
70
70
  parsl/dataflow/taskrecord.py,sha256=bzIBmlDTsRrELtB9PUQwxTWcwrCd8aMsUAzvijle1eo,3114
71
71
  parsl/executors/__init__.py,sha256=J50N97Nm9YRjz6K0oNXDxUYIsDjL43_tp3LVb2w7n-M,381
72
- parsl/executors/base.py,sha256=yQ-o04nlM0aU2jOanzzAEVsAo-RnNdCwpRWCnAFwdOE,4619
72
+ parsl/executors/base.py,sha256=Kvui8yDPzz7yQpm9qSkMdhQS4eu0tQTkVZ99j6HGgA4,5087
73
73
  parsl/executors/errors.py,sha256=xVswxgi7vmJcUMCeYDAPK8sQT2kHFFROVoOr0dnmcWE,2098
74
74
  parsl/executors/status_handling.py,sha256=3kJAbgXSZbbj8uN72Gu08PSUnxMrT5np1I-ihLyM6E8,13631
75
75
  parsl/executors/threads.py,sha256=bMU3JFghm17Lpcua13pr3NgQhkUDDc2mqvF2yJBrVNQ,3353
@@ -96,7 +96,7 @@ parsl/executors/radical/rpex_worker.py,sha256=1M1df-hzFdmZMWbRZlUzIX7uAWMKJ_SkxL
96
96
  parsl/executors/taskvine/__init__.py,sha256=sWIJdvSLgQKul9dlSjIkNat7yBDgU3SrBF3X2yhT86E,293
97
97
  parsl/executors/taskvine/errors.py,sha256=MNS_NjpvHjwevQXOjqjSEBFroqEWi-LT1ZEVZ2C5Dx0,652
98
98
  parsl/executors/taskvine/exec_parsl_function.py,sha256=oUAKbPWwpbzWwQ47bZQlVDxS8txhnhPsonMf3AOEMGQ,7085
99
- parsl/executors/taskvine/executor.py,sha256=wXqnGIK4c6TrpWst45CHSaBBI6wkeBgbhfia5v8X8uo,32807
99
+ parsl/executors/taskvine/executor.py,sha256=M-2Uf34lYwa5lzoMIwqR__QXcE1anvGgUJWggEzT2pQ,33024
100
100
  parsl/executors/taskvine/factory.py,sha256=sHhfGv7xRFrWkQclzRXuFEAHuSXhsZu2lR5LJ81aucA,2638
101
101
  parsl/executors/taskvine/factory_config.py,sha256=AbE2fN2snrF5ITYrrS4DnGn2XkJHUFr_17DYHDHIwq0,3693
102
102
  parsl/executors/taskvine/manager.py,sha256=VxVN2L5zFVPNfSAJrGgq87MRJKpcxf-BHdO5QWxB4TU,25822
@@ -105,7 +105,7 @@ parsl/executors/taskvine/utils.py,sha256=iSrIogeiauL3UNy_9tiZp1cBSNn6fIJkMYQRVi1
105
105
  parsl/executors/workqueue/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
106
  parsl/executors/workqueue/errors.py,sha256=ghB93Ptb_QbOAvgLe7siV_snRRkU_T-cFHv3AR6Ziwo,541
107
107
  parsl/executors/workqueue/exec_parsl_function.py,sha256=NtWNeBvRqksej38eRPw8zPBJ1CeW6vgaitve0tfz_qc,7801
108
- parsl/executors/workqueue/executor.py,sha256=3PZ5Emx2-fnHlCBz88XPH0-SotqkCuQ5OkMN7ZcuIX8,50317
108
+ parsl/executors/workqueue/executor.py,sha256=rSjQ4VWQyX9wvuwQ70ALvRYhz_d9La731eZGqgPR3uU,50523
109
109
  parsl/executors/workqueue/parsl_coprocess.py,sha256=kEFGC-A97c_gweUPvrc9EEGume7vUpkJLJlyAb87xtQ,5737
110
110
  parsl/executors/workqueue/parsl_coprocess_stub.py,sha256=_bJmpPIgL42qM6bVzeEKt1Mn1trSP41rtJguXxPGfHI,735
111
111
  parsl/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -121,7 +121,7 @@ parsl/launchers/launchers.py,sha256=VB--fiVv_IQne3DydTMSdGUY0o0g69puAs-Hd3mJ2vo,
121
121
  parsl/monitoring/__init__.py,sha256=0ywNz6i0lM1xo_7_BIxhETDGeVd2C_0wwD7qgeaMR4c,83
122
122
  parsl/monitoring/db_manager.py,sha256=hdmmXSTXp8Wwhr7vLpQalD_ahRl3SNxKYVsplnThRk8,37021
123
123
  parsl/monitoring/message_type.py,sha256=Khn88afNxcOIciKiCK4GLnn90I5BlRTiOL3zK-P07yQ,401
124
- parsl/monitoring/monitoring.py,sha256=ce4_5F1cbNv8jvfVxgwGLs4r3cPJrqQgMEwINpRRdFg,12862
124
+ parsl/monitoring/monitoring.py,sha256=DWPhuxw698pHePUbT_UJdjICoAAST-Y6t9rT8H01dqA,13462
125
125
  parsl/monitoring/radios.py,sha256=F1IML-IvFJxL93rvWBqwTisRprTs1zW1lFVWMog-LRE,5858
126
126
  parsl/monitoring/remote.py,sha256=0wqANMcuvq3dpja3agdbOzD72n5oUYp7PcNKyLCC35E,13923
127
127
  parsl/monitoring/router.py,sha256=92krSS8xIWDQuxJMxQ3D_gbLcqgKymxr3HVJwAImdrw,9557
@@ -293,7 +293,7 @@ parsl/tests/sites/test_worker_info.py,sha256=oqCJLiZth1yo4KXS83vn7Va0TI0D78GSDlC
293
293
  parsl/tests/sites/test_mpi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
294
294
  parsl/tests/test_bash_apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
295
295
  parsl/tests/test_bash_apps/test_apptimeout.py,sha256=fZP7LMSuRtiP1dp1viAWbH5cTfA5otPLsW-KksBAQaU,570
296
- parsl/tests/test_bash_apps/test_basic.py,sha256=S4W0IVISMLdwEO3DFZswav1PcAxCvq3TgNkey3Cl8wA,2421
296
+ parsl/tests/test_bash_apps/test_basic.py,sha256=HGzJKtETnUxHQwPaTDuZTPMtIX3lSqtidqLxPn2IV8U,2460
297
297
  parsl/tests/test_bash_apps/test_error_codes.py,sha256=mPvLWADdVb5hbKgpKUy06sKMWG3LYu4FVhbmrgo7Lps,3964
298
298
  parsl/tests/test_bash_apps/test_keyword_overlaps.py,sha256=8bfN2qw4uXJsYquppR1lZQrYW834AZc3zjYIIHTfDoE,209
299
299
  parsl/tests/test_bash_apps/test_kwarg_storage.py,sha256=OMMD3sKSngBSjVCHK9wju0hHzszOqbYuWtscyMuh5_8,720
@@ -313,7 +313,7 @@ parsl/tests/test_checkpointing/test_python_checkpoint_3.py,sha256=8Np2OpDeQ8sE1H
313
313
  parsl/tests/test_checkpointing/test_regression_232.py,sha256=AsI6AJ0DcFaefAbEY9qWa41ER0VX-4yLuIdlgvBw360,2637
314
314
  parsl/tests/test_checkpointing/test_regression_233.py,sha256=jii7BKuygK6KMIGtg4IeBjix7Z28cYhv57rE9ixoXMU,1774
315
315
  parsl/tests/test_checkpointing/test_regression_239.py,sha256=P5kmf1LOo_qHtArkBLMhdvNbSPtURDU5u2tI8SXZTb0,2441
316
- parsl/tests/test_checkpointing/test_task_exit.py,sha256=3-ldQhX7YVEAowWK2TiZ6nrQQ7ktfWr-qaCShtjJZK8,1721
316
+ parsl/tests/test_checkpointing/test_task_exit.py,sha256=-caWS118ArPzOBfq_QumIjKcWsttXHnlSQg3Un50aR4,1723
317
317
  parsl/tests/test_docs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
318
318
  parsl/tests/test_docs/test_from_slides.py,sha256=0qJHAsSN3eqn4LAFTyCAq1rIUOotBzyQg7d_rJfBoes,653
319
319
  parsl/tests/test_docs/test_kwargs.py,sha256=-rMtAtarg2FOdxMuDLsZY5Crn_jmSwtelMwRNEtTlVk,925
@@ -343,7 +343,7 @@ parsl/tests/test_htex/test_manager_failure.py,sha256=gemQopZoDEoZLOvep5JZkY6tQlZ
343
343
  parsl/tests/test_htex/test_missing_worker.py,sha256=oiDN3ylsf-72jmX-Y5OWA2kQWpbVbvmoSLnu2vnyZeY,976
344
344
  parsl/tests/test_htex/test_multiple_disconnected_blocks.py,sha256=L4vw_Mo-upp2p9-TyPDfluNJJQ2BxHHNXgS3xhhuE28,1993
345
345
  parsl/tests/test_htex/test_worker_failure.py,sha256=Uz-RHI-LK78FMjXUvrUFmo4iYfmpDVBUcBxxRb3UG9M,603
346
- parsl/tests/test_htex/test_zmq_binding.py,sha256=MQWAL7XxaI1P2k_VUgm5DKD4GVEn5c2Y8MEyOSABRA4,2937
346
+ parsl/tests/test_htex/test_zmq_binding.py,sha256=2-y8HZPzNLrumVqyqG9yZl-lqefSIpez2jr5Ghrtd80,3013
347
347
  parsl/tests/test_monitoring/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
348
348
  parsl/tests/test_monitoring/test_app_names.py,sha256=4Ziggxv0JLP0UGAd5jjXdivUdZQLlMvVVMfiTStjxRk,2191
349
349
  parsl/tests/test_monitoring/test_basic.py,sha256=8LHlS5VM1isVhOZuWCXHMUlV10AGMFNq-8n5ZX9HdLA,3768
@@ -352,6 +352,7 @@ parsl/tests/test_monitoring/test_fuzz_zmq.py,sha256=20Y2TCpOOQpWPLEppi-9InCHPb8H
352
352
  parsl/tests/test_monitoring/test_htex_init_blocks_vs_monitoring.py,sha256=Lfa6ENZWrExRsZcISMdF_G4VjswzSb0wlRSQFoZXkyQ,2765
353
353
  parsl/tests/test_monitoring/test_incomplete_futures.py,sha256=9lJhkWlVB8gCCTkFjObzoh1uCL1pRmU6gFgEzLCztnY,2021
354
354
  parsl/tests/test_monitoring/test_memoization_representation.py,sha256=tErT7zseSMaQ5eNmK3hH90J6OZKuAaFQG50OXK6Jy9s,2660
355
+ parsl/tests/test_monitoring/test_stdouterr.py,sha256=bx2BKZ_iIuS8FZlxdt7n1QsUwTnWaXnORdrlJCTw5aU,4517
355
356
  parsl/tests/test_monitoring/test_viz_colouring.py,sha256=k8SiELxPtnGYZ4r02VQt46RC61fGDVC4nmY768snX1U,591
356
357
  parsl/tests/test_mpi_apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
357
358
  parsl/tests/test_mpi_apps/test_bad_mpi_config.py,sha256=mB-ASx0S-wh1iP6MYZ-CdOwMye3xgteQK-jqufzWNO8,1317
@@ -370,7 +371,7 @@ parsl/tests/test_providers/test_submiterror_deprecation.py,sha256=ZutVj_0VJ7M-5U
370
371
  parsl/tests/test_python_apps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
371
372
  parsl/tests/test_python_apps/test_arg_input_types.py,sha256=JXpfHiu8lr9BN6u1OzqFvGwBhxzsGTPMewHx6Wdo-HI,670
372
373
  parsl/tests/test_python_apps/test_basic.py,sha256=lFqh4ugePbp_FRiHGUXxzV34iS7l8C5UkxTHuLcpnYs,855
373
- parsl/tests/test_python_apps/test_context_manager.py,sha256=iMBjOFmqEO2rDGPJwDySa1kWz-dRnZZNEWDPipS_Xi4,877
374
+ parsl/tests/test_python_apps/test_context_manager.py,sha256=LzJ3WtR1LYiQF_lbmrd5h4mlC7_h_GvzoeUPQZ5vPsw,928
374
375
  parsl/tests/test_python_apps/test_dep_standard_futures.py,sha256=BloeaYBci0jS5al2d8Eqe3OfZ1tvolA5ZflOBQPR9Wo,859
375
376
  parsl/tests/test_python_apps/test_dependencies.py,sha256=IRiTI_lPoWBSFSFnaBlE6Bv08PKEaf-qj5dfqO2RjT0,272
376
377
  parsl/tests/test_python_apps/test_depfail_propagation.py,sha256=3q3HlVWrOixFtXWBvR_ypKtbdAHAJcKndXQ5drwrBQU,1488
@@ -410,7 +411,7 @@ parsl/tests/test_regression/test_97_parallelism_0.py,sha256=PwHxwQirqLJUeBhsNSzU
410
411
  parsl/tests/test_regression/test_98.py,sha256=ZNTA-USpmH85Mt0nu3KFQ1qqmXsyHtYMZWZY0grzuYA,453
411
412
  parsl/tests/test_scaling/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
412
413
  parsl/tests/test_scaling/test_block_error_handler.py,sha256=VFKs_jq7yd7bpdfYva3Sa_TBS8VcjGUS6YJ9Y34RbyI,6050
413
- parsl/tests/test_scaling/test_regression_1621.py,sha256=iRu3GFsg2l9J61AVZKWLc6zJcvI2JYD0WvtTYDSv22I,1770
414
+ parsl/tests/test_scaling/test_regression_1621.py,sha256=Z7nLhn2dxTVApo2XA55vzLziW6_QBQcZHoMsRQ066b0,1963
414
415
  parsl/tests/test_scaling/test_scale_down.py,sha256=T8NVmoIebdpSjrNJCdgDHumpz9eKLkJrpeW7Kwi8cBg,2821
415
416
  parsl/tests/test_scaling/test_scale_down_htex_auto_scale.py,sha256=1vP2a8qygnxuUji7B3kJOUgwjmmIC1fDPhDdqzs5YFA,4597
416
417
  parsl/tests/test_scaling/test_scale_down_htex_unregistered.py,sha256=lGl7m9PRPs4MxAEK7QpSXAxGgCSbabxxYN-ExZUYAUs,2030
@@ -438,6 +439,7 @@ parsl/tests/test_staging/test_staging_ftp.py,sha256=EkRoTcQ00FZGh8lDVYBdKb-pQ-yb
438
439
  parsl/tests/test_staging/test_staging_ftp_in_task.py,sha256=kR2XrGvbvVFDpHg53NnjO04kqEksTJjQAMQwYqBdb2M,884
439
440
  parsl/tests/test_staging/test_staging_globus.py,sha256=ds8nDH5dNbI10FV_GxMHyVaY6GPnuPPzkX9IiqROLF0,2339
440
441
  parsl/tests/test_staging/test_staging_https.py,sha256=ESNuvdc_P5JoPaMjBM3ofi1mNJM0U6vahi9JgbCsrPw,3307
442
+ parsl/tests/test_staging/test_staging_stdout.py,sha256=i4ksb9ehu-bKPgALxm6ADB12EQVTM_xusyFGmSwByLs,2025
441
443
  parsl/tests/test_staging/test_zip_out.py,sha256=qR_iK8wqKOxisMBD5MqKr2RoyoTUmRejAj_O3jgJA2A,3512
442
444
  parsl/tests/test_threads/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
443
445
  parsl/tests/test_threads/test_configs.py,sha256=QA9YjIMAtZ2jmkfOWqBzEfzQQcFVCDizH7Qwiy2HIMQ,909
@@ -446,12 +448,12 @@ parsl/tests/test_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
446
448
  parsl/tests/test_utils/test_representation_mixin.py,sha256=kUZeIDwA2rlbJ3-beGzLLwf3dOplTMCrWJN87etHcyY,1633
447
449
  parsl/usage_tracking/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
448
450
  parsl/usage_tracking/usage.py,sha256=pSADeogWqvkYI_n2pojv4IWDEFAQ3KwXNx6LDTohMHQ,6684
449
- parsl-2024.4.15.data/scripts/exec_parsl_function.py,sha256=NtWNeBvRqksej38eRPw8zPBJ1CeW6vgaitve0tfz_qc,7801
450
- parsl-2024.4.15.data/scripts/parsl_coprocess.py,sha256=Y7Tc-h9WGui-YDe3w_h91w2Sm1JNL1gJ9kAV4PE_gw8,5722
451
- parsl-2024.4.15.data/scripts/process_worker_pool.py,sha256=31tyTtU7hrrsatGReuCbLM-3GWkaYK1bvlFE1MhKYQg,41253
452
- parsl-2024.4.15.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
453
- parsl-2024.4.15.dist-info/METADATA,sha256=TiwDQJFqwAV662D9zzs8kMLQUWiRCKLh8Xpn1J8fneI,4008
454
- parsl-2024.4.15.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
455
- parsl-2024.4.15.dist-info/entry_points.txt,sha256=XqnsWDYoEcLbsMcpnYGKLEnSBmaIe1YoM5YsBdJG2tI,176
456
- parsl-2024.4.15.dist-info/top_level.txt,sha256=PIheYoUFQtF2icLsgOykgU-Cjuwr2Oi6On2jo5RYgRM,6
457
- parsl-2024.4.15.dist-info/RECORD,,
451
+ parsl-2024.4.22.data/scripts/exec_parsl_function.py,sha256=NtWNeBvRqksej38eRPw8zPBJ1CeW6vgaitve0tfz_qc,7801
452
+ parsl-2024.4.22.data/scripts/parsl_coprocess.py,sha256=Y7Tc-h9WGui-YDe3w_h91w2Sm1JNL1gJ9kAV4PE_gw8,5722
453
+ parsl-2024.4.22.data/scripts/process_worker_pool.py,sha256=31tyTtU7hrrsatGReuCbLM-3GWkaYK1bvlFE1MhKYQg,41253
454
+ parsl-2024.4.22.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
455
+ parsl-2024.4.22.dist-info/METADATA,sha256=qMMaRpBRciAOHn3sQoQd3udOTdP8-uWhfhIH8hUX2HU,4012
456
+ parsl-2024.4.22.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
457
+ parsl-2024.4.22.dist-info/entry_points.txt,sha256=XqnsWDYoEcLbsMcpnYGKLEnSBmaIe1YoM5YsBdJG2tI,176
458
+ parsl-2024.4.22.dist-info/top_level.txt,sha256=PIheYoUFQtF2icLsgOykgU-Cjuwr2Oi6On2jo5RYgRM,6
459
+ parsl-2024.4.22.dist-info/RECORD,,