parsl 2024.3.11__py3-none-any.whl → 2025.1.13__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 (369) hide show
  1. parsl/__init__.py +9 -10
  2. parsl/addresses.py +29 -7
  3. parsl/app/app.py +7 -8
  4. parsl/app/bash.py +15 -8
  5. parsl/app/errors.py +10 -13
  6. parsl/app/futures.py +8 -10
  7. parsl/app/python.py +2 -1
  8. parsl/benchmark/perf.py +2 -1
  9. parsl/concurrent/__init__.py +2 -2
  10. parsl/config.py +57 -10
  11. parsl/configs/ASPIRE1.py +6 -5
  12. parsl/configs/Azure.py +9 -8
  13. parsl/configs/bridges.py +6 -4
  14. parsl/configs/cc_in2p3.py +3 -3
  15. parsl/configs/ec2.py +3 -1
  16. parsl/configs/expanse.py +4 -3
  17. parsl/configs/frontera.py +3 -4
  18. parsl/configs/htex_local.py +3 -4
  19. parsl/configs/illinoiscluster.py +3 -1
  20. parsl/configs/improv.py +34 -0
  21. parsl/configs/kubernetes.py +4 -3
  22. parsl/configs/local_threads.py +5 -1
  23. parsl/configs/midway.py +5 -3
  24. parsl/configs/osg.py +4 -2
  25. parsl/configs/polaris.py +4 -2
  26. parsl/configs/stampede2.py +6 -5
  27. parsl/configs/summit.py +3 -3
  28. parsl/configs/toss3_llnl.py +4 -3
  29. parsl/configs/vineex_local.py +6 -4
  30. parsl/configs/wqex_local.py +5 -3
  31. parsl/curvezmq.py +4 -0
  32. parsl/data_provider/data_manager.py +4 -3
  33. parsl/data_provider/file_noop.py +1 -2
  34. parsl/data_provider/files.py +3 -3
  35. parsl/data_provider/ftp.py +1 -3
  36. parsl/data_provider/globus.py +7 -6
  37. parsl/data_provider/http.py +2 -2
  38. parsl/data_provider/rsync.py +1 -1
  39. parsl/data_provider/staging.py +2 -2
  40. parsl/data_provider/zip.py +135 -0
  41. parsl/dataflow/dependency_resolvers.py +115 -0
  42. parsl/dataflow/dflow.py +262 -224
  43. parsl/dataflow/errors.py +3 -5
  44. parsl/dataflow/futures.py +27 -14
  45. parsl/dataflow/memoization.py +5 -5
  46. parsl/dataflow/rundirs.py +5 -6
  47. parsl/dataflow/taskrecord.py +4 -5
  48. parsl/executors/__init__.py +4 -2
  49. parsl/executors/base.py +45 -15
  50. parsl/executors/errors.py +13 -0
  51. parsl/executors/execute_task.py +37 -0
  52. parsl/executors/flux/execute_parsl_task.py +3 -3
  53. parsl/executors/flux/executor.py +18 -19
  54. parsl/executors/flux/flux_instance_manager.py +26 -27
  55. parsl/executors/high_throughput/errors.py +43 -3
  56. parsl/executors/high_throughput/executor.py +316 -282
  57. parsl/executors/high_throughput/interchange.py +158 -167
  58. parsl/executors/high_throughput/manager_record.py +5 -0
  59. parsl/executors/high_throughput/manager_selector.py +55 -0
  60. parsl/executors/high_throughput/monitoring_info.py +2 -1
  61. parsl/executors/high_throughput/mpi_executor.py +113 -0
  62. parsl/executors/high_throughput/mpi_prefix_composer.py +10 -11
  63. parsl/executors/high_throughput/mpi_resource_management.py +6 -17
  64. parsl/executors/high_throughput/probe.py +9 -7
  65. parsl/executors/high_throughput/process_worker_pool.py +115 -77
  66. parsl/executors/high_throughput/zmq_pipes.py +81 -23
  67. parsl/executors/radical/executor.py +130 -79
  68. parsl/executors/radical/rpex_resources.py +17 -15
  69. parsl/executors/radical/rpex_worker.py +4 -3
  70. parsl/executors/status_handling.py +157 -51
  71. parsl/executors/taskvine/__init__.py +1 -1
  72. parsl/executors/taskvine/errors.py +1 -1
  73. parsl/executors/taskvine/exec_parsl_function.py +2 -2
  74. parsl/executors/taskvine/executor.py +41 -57
  75. parsl/executors/taskvine/factory.py +1 -1
  76. parsl/executors/taskvine/factory_config.py +1 -1
  77. parsl/executors/taskvine/manager.py +18 -13
  78. parsl/executors/taskvine/manager_config.py +9 -5
  79. parsl/executors/threads.py +6 -6
  80. parsl/executors/workqueue/errors.py +1 -1
  81. parsl/executors/workqueue/exec_parsl_function.py +6 -5
  82. parsl/executors/workqueue/executor.py +64 -63
  83. parsl/executors/workqueue/parsl_coprocess.py +1 -1
  84. parsl/jobs/error_handlers.py +2 -2
  85. parsl/jobs/job_status_poller.py +30 -113
  86. parsl/jobs/states.py +7 -2
  87. parsl/jobs/strategy.py +43 -31
  88. parsl/launchers/__init__.py +12 -3
  89. parsl/launchers/errors.py +1 -1
  90. parsl/launchers/launchers.py +6 -12
  91. parsl/log_utils.py +9 -6
  92. parsl/monitoring/db_manager.py +59 -95
  93. parsl/monitoring/errors.py +6 -0
  94. parsl/monitoring/monitoring.py +87 -356
  95. parsl/monitoring/queries/pandas.py +1 -2
  96. parsl/monitoring/radios/base.py +13 -0
  97. parsl/monitoring/radios/filesystem.py +52 -0
  98. parsl/monitoring/radios/htex.py +57 -0
  99. parsl/monitoring/radios/multiprocessing.py +17 -0
  100. parsl/monitoring/radios/udp.py +56 -0
  101. parsl/monitoring/radios/zmq.py +17 -0
  102. parsl/monitoring/remote.py +33 -37
  103. parsl/monitoring/router.py +212 -0
  104. parsl/monitoring/types.py +5 -6
  105. parsl/monitoring/visualization/app.py +4 -2
  106. parsl/monitoring/visualization/models.py +0 -1
  107. parsl/monitoring/visualization/plots/default/workflow_plots.py +11 -4
  108. parsl/monitoring/visualization/plots/default/workflow_resource_plots.py +1 -0
  109. parsl/monitoring/visualization/utils.py +0 -1
  110. parsl/monitoring/visualization/views.py +16 -8
  111. parsl/multiprocessing.py +0 -1
  112. parsl/process_loggers.py +1 -2
  113. parsl/providers/__init__.py +8 -17
  114. parsl/providers/aws/aws.py +2 -3
  115. parsl/providers/azure/azure.py +4 -5
  116. parsl/providers/base.py +2 -18
  117. parsl/providers/cluster_provider.py +4 -12
  118. parsl/providers/condor/condor.py +7 -17
  119. parsl/providers/errors.py +2 -2
  120. parsl/providers/googlecloud/googlecloud.py +2 -1
  121. parsl/providers/grid_engine/grid_engine.py +5 -14
  122. parsl/providers/kubernetes/kube.py +80 -40
  123. parsl/providers/local/local.py +13 -26
  124. parsl/providers/lsf/lsf.py +5 -23
  125. parsl/providers/pbspro/pbspro.py +5 -17
  126. parsl/providers/slurm/slurm.py +81 -39
  127. parsl/providers/torque/torque.py +3 -14
  128. parsl/serialize/__init__.py +8 -3
  129. parsl/serialize/base.py +1 -2
  130. parsl/serialize/concretes.py +5 -4
  131. parsl/serialize/facade.py +3 -3
  132. parsl/serialize/proxystore.py +3 -2
  133. parsl/tests/__init__.py +1 -1
  134. parsl/tests/configs/azure_single_node.py +4 -5
  135. parsl/tests/configs/bridges.py +3 -2
  136. parsl/tests/configs/cc_in2p3.py +1 -3
  137. parsl/tests/configs/comet.py +2 -1
  138. parsl/tests/configs/ec2_single_node.py +1 -2
  139. parsl/tests/configs/ec2_spot.py +1 -2
  140. parsl/tests/configs/flux_local.py +11 -0
  141. parsl/tests/configs/frontera.py +2 -3
  142. parsl/tests/configs/htex_local.py +3 -5
  143. parsl/tests/configs/htex_local_alternate.py +11 -15
  144. parsl/tests/configs/htex_local_intask_staging.py +5 -9
  145. parsl/tests/configs/htex_local_rsync_staging.py +4 -8
  146. parsl/tests/configs/local_radical.py +1 -3
  147. parsl/tests/configs/local_radical_mpi.py +2 -2
  148. parsl/tests/configs/local_threads_checkpoint_periodic.py +8 -10
  149. parsl/tests/configs/local_threads_monitoring.py +0 -1
  150. parsl/tests/configs/midway.py +2 -2
  151. parsl/tests/configs/nscc_singapore.py +3 -3
  152. parsl/tests/configs/osg_htex.py +1 -1
  153. parsl/tests/configs/petrelkube.py +3 -2
  154. parsl/tests/configs/slurm_local.py +24 -0
  155. parsl/tests/configs/summit.py +1 -0
  156. parsl/tests/configs/taskvine_ex.py +4 -7
  157. parsl/tests/configs/user_opts.py +2 -8
  158. parsl/tests/configs/workqueue_ex.py +4 -6
  159. parsl/tests/conftest.py +27 -13
  160. parsl/tests/integration/test_stress/test_python_simple.py +3 -4
  161. parsl/tests/integration/test_stress/test_python_threads.py +3 -5
  162. parsl/tests/manual_tests/htex_local.py +4 -6
  163. parsl/tests/manual_tests/test_basic.py +1 -0
  164. parsl/tests/manual_tests/test_log_filter.py +3 -1
  165. parsl/tests/manual_tests/test_memory_limits.py +6 -8
  166. parsl/tests/manual_tests/test_regression_220.py +2 -1
  167. parsl/tests/manual_tests/test_udp_simple.py +4 -4
  168. parsl/tests/manual_tests/test_worker_count.py +3 -2
  169. parsl/tests/scaling_tests/htex_local.py +2 -4
  170. parsl/tests/scaling_tests/test_scale.py +0 -9
  171. parsl/tests/scaling_tests/vineex_condor.py +1 -2
  172. parsl/tests/scaling_tests/vineex_local.py +1 -2
  173. parsl/tests/site_tests/site_config_selector.py +1 -6
  174. parsl/tests/site_tests/test_provider.py +4 -2
  175. parsl/tests/site_tests/test_site.py +2 -0
  176. parsl/tests/sites/test_affinity.py +7 -7
  177. parsl/tests/sites/test_dynamic_executor.py +3 -4
  178. parsl/tests/sites/test_ec2.py +3 -2
  179. parsl/tests/sites/test_worker_info.py +4 -5
  180. parsl/tests/test_aalst_patterns.py +0 -1
  181. parsl/tests/test_bash_apps/test_apptimeout.py +2 -2
  182. parsl/tests/test_bash_apps/test_basic.py +10 -4
  183. parsl/tests/test_bash_apps/test_error_codes.py +5 -7
  184. parsl/tests/test_bash_apps/test_inputs_default.py +25 -0
  185. parsl/tests/test_bash_apps/test_kwarg_storage.py +1 -1
  186. parsl/tests/test_bash_apps/test_memoize.py +2 -8
  187. parsl/tests/test_bash_apps/test_memoize_ignore_args.py +9 -14
  188. parsl/tests/test_bash_apps/test_memoize_ignore_args_regr.py +9 -14
  189. parsl/tests/test_bash_apps/test_multiline.py +1 -1
  190. parsl/tests/test_bash_apps/test_pipeline.py +1 -1
  191. parsl/tests/test_bash_apps/test_std_uri.py +123 -0
  192. parsl/tests/test_bash_apps/test_stdout.py +33 -8
  193. parsl/tests/test_callables.py +2 -2
  194. parsl/tests/test_checkpointing/test_periodic.py +21 -39
  195. parsl/tests/test_checkpointing/test_python_checkpoint_1.py +1 -0
  196. parsl/tests/test_checkpointing/test_python_checkpoint_2.py +2 -2
  197. parsl/tests/test_checkpointing/test_python_checkpoint_3.py +0 -1
  198. parsl/tests/test_checkpointing/test_regression_239.py +1 -1
  199. parsl/tests/test_checkpointing/test_task_exit.py +2 -3
  200. parsl/tests/test_docs/test_from_slides.py +5 -2
  201. parsl/tests/test_docs/test_kwargs.py +4 -1
  202. parsl/tests/test_docs/test_tutorial_1.py +1 -2
  203. parsl/tests/test_docs/test_workflow1.py +2 -2
  204. parsl/tests/test_docs/test_workflow2.py +0 -1
  205. parsl/tests/test_error_handling/test_rand_fail.py +2 -2
  206. parsl/tests/test_error_handling/test_resource_spec.py +10 -12
  207. parsl/tests/test_error_handling/test_retries.py +6 -16
  208. parsl/tests/test_error_handling/test_retry_handler.py +1 -0
  209. parsl/tests/test_error_handling/test_retry_handler_failure.py +2 -1
  210. parsl/tests/test_error_handling/test_serialization_fail.py +1 -1
  211. parsl/tests/test_error_handling/test_wrap_with_logs.py +1 -0
  212. parsl/tests/test_execute_task.py +29 -0
  213. parsl/tests/test_flux.py +1 -1
  214. parsl/tests/test_htex/test_basic.py +2 -3
  215. parsl/tests/test_htex/test_block_manager_selector_unit.py +20 -0
  216. parsl/tests/test_htex/test_command_client_timeout.py +66 -0
  217. parsl/tests/test_htex/test_connected_blocks.py +3 -2
  218. parsl/tests/test_htex/test_cpu_affinity_explicit.py +6 -10
  219. parsl/tests/test_htex/test_disconnected_blocks.py +6 -5
  220. parsl/tests/test_htex/test_disconnected_blocks_failing_provider.py +71 -0
  221. parsl/tests/test_htex/test_drain.py +79 -0
  222. parsl/tests/test_htex/test_htex.py +51 -25
  223. parsl/tests/test_htex/test_manager_failure.py +0 -1
  224. parsl/tests/test_htex/test_manager_selector_by_block.py +51 -0
  225. parsl/tests/test_htex/test_managers_command.py +36 -0
  226. parsl/tests/test_htex/test_missing_worker.py +2 -12
  227. parsl/tests/test_htex/test_multiple_disconnected_blocks.py +9 -9
  228. parsl/tests/test_htex/test_resource_spec_validation.py +45 -0
  229. parsl/tests/test_htex/test_zmq_binding.py +29 -8
  230. parsl/tests/test_monitoring/test_app_names.py +86 -0
  231. parsl/tests/test_monitoring/test_basic.py +73 -25
  232. parsl/tests/test_monitoring/test_db_locks.py +6 -4
  233. parsl/tests/test_monitoring/test_fuzz_zmq.py +19 -8
  234. parsl/tests/test_monitoring/test_htex_init_blocks_vs_monitoring.py +80 -0
  235. parsl/tests/test_monitoring/test_incomplete_futures.py +5 -4
  236. parsl/tests/test_monitoring/test_memoization_representation.py +4 -2
  237. parsl/tests/test_monitoring/test_stdouterr.py +134 -0
  238. parsl/tests/test_monitoring/test_viz_colouring.py +1 -0
  239. parsl/tests/test_mpi_apps/test_bad_mpi_config.py +33 -26
  240. parsl/tests/test_mpi_apps/test_mpi_mode_enabled.py +28 -11
  241. parsl/tests/test_mpi_apps/test_mpi_prefix.py +4 -4
  242. parsl/tests/test_mpi_apps/test_mpi_scheduler.py +7 -2
  243. parsl/tests/test_mpi_apps/test_mpiex.py +64 -0
  244. parsl/tests/test_mpi_apps/test_resource_spec.py +42 -49
  245. parsl/tests/test_providers/test_kubernetes_provider.py +102 -0
  246. parsl/tests/test_providers/test_local_provider.py +3 -132
  247. parsl/tests/test_providers/test_pbspro_template.py +2 -3
  248. parsl/tests/test_providers/test_slurm_template.py +2 -3
  249. parsl/tests/test_providers/test_submiterror_deprecation.py +2 -1
  250. parsl/tests/test_python_apps/test_context_manager.py +128 -0
  251. parsl/tests/test_python_apps/test_dep_standard_futures.py +2 -1
  252. parsl/tests/test_python_apps/test_dependencies_deep.py +59 -0
  253. parsl/tests/test_python_apps/test_fail.py +0 -25
  254. parsl/tests/test_python_apps/test_futures.py +2 -1
  255. parsl/tests/test_python_apps/test_inputs_default.py +22 -0
  256. parsl/tests/test_python_apps/test_join.py +0 -1
  257. parsl/tests/test_python_apps/test_lifted.py +11 -7
  258. parsl/tests/test_python_apps/test_memoize_bad_id_for_memo.py +1 -0
  259. parsl/tests/test_python_apps/test_outputs.py +1 -1
  260. parsl/tests/test_python_apps/test_pluggable_future_resolution.py +161 -0
  261. parsl/tests/test_radical/test_mpi_funcs.py +1 -2
  262. parsl/tests/test_regression/test_1480.py +2 -1
  263. parsl/tests/test_regression/test_1653.py +2 -1
  264. parsl/tests/test_regression/test_226.py +1 -0
  265. parsl/tests/test_regression/test_2652.py +1 -0
  266. parsl/tests/test_regression/test_69a.py +0 -1
  267. parsl/tests/test_regression/test_854.py +4 -2
  268. parsl/tests/test_regression/test_97_parallelism_0.py +1 -2
  269. parsl/tests/test_regression/test_98.py +0 -1
  270. parsl/tests/test_scaling/test_block_error_handler.py +9 -4
  271. parsl/tests/test_scaling/test_regression_1621.py +11 -15
  272. parsl/tests/test_scaling/test_regression_3568_scaledown_vs_MISSING.py +84 -0
  273. parsl/tests/test_scaling/test_regression_3696_oscillation.py +103 -0
  274. parsl/tests/test_scaling/test_scale_down.py +2 -5
  275. parsl/tests/test_scaling/test_scale_down_htex_auto_scale.py +6 -18
  276. parsl/tests/test_scaling/test_scale_down_htex_unregistered.py +71 -0
  277. parsl/tests/test_scaling/test_shutdown_scalein.py +73 -0
  278. parsl/tests/test_scaling/test_worker_interchange_bad_messages_3262.py +90 -0
  279. parsl/tests/test_serialization/test_2555_caching_deserializer.py +1 -1
  280. parsl/tests/test_serialization/test_3495_deserialize_managerlost.py +47 -0
  281. parsl/tests/test_serialization/test_basic.py +2 -1
  282. parsl/tests/test_serialization/test_htex_code_cache.py +3 -4
  283. parsl/tests/test_serialization/test_pack_resource_spec.py +2 -1
  284. parsl/tests/test_serialization/test_proxystore_configured.py +10 -6
  285. parsl/tests/test_serialization/test_proxystore_impl.py +5 -3
  286. parsl/tests/test_shutdown/test_kill_monitoring.py +64 -0
  287. parsl/tests/test_staging/staging_provider.py +2 -2
  288. parsl/tests/test_staging/test_1316.py +3 -4
  289. parsl/tests/test_staging/test_docs_1.py +2 -1
  290. parsl/tests/test_staging/test_docs_2.py +2 -1
  291. parsl/tests/test_staging/test_elaborate_noop_file.py +2 -3
  292. parsl/tests/{test_data → test_staging}/test_file.py +6 -6
  293. parsl/tests/{test_data → test_staging}/test_output_chain_filenames.py +3 -0
  294. parsl/tests/test_staging/test_staging_ftp.py +1 -0
  295. parsl/tests/test_staging/test_staging_https.py +5 -2
  296. parsl/tests/test_staging/test_staging_stdout.py +64 -0
  297. parsl/tests/test_staging/test_zip_in.py +39 -0
  298. parsl/tests/test_staging/test_zip_out.py +110 -0
  299. parsl/tests/test_staging/test_zip_to_zip.py +41 -0
  300. parsl/tests/test_summary.py +2 -2
  301. parsl/tests/test_thread_parallelism.py +0 -1
  302. parsl/tests/test_threads/test_configs.py +1 -2
  303. parsl/tests/test_threads/test_lazy_errors.py +2 -2
  304. parsl/tests/test_utils/test_execute_wait.py +35 -0
  305. parsl/tests/test_utils/test_sanitize_dns.py +76 -0
  306. parsl/tests/unit/test_address.py +20 -0
  307. parsl/tests/unit/test_file.py +99 -0
  308. parsl/tests/unit/test_usage_tracking.py +66 -0
  309. parsl/usage_tracking/api.py +65 -0
  310. parsl/usage_tracking/levels.py +6 -0
  311. parsl/usage_tracking/usage.py +104 -62
  312. parsl/utils.py +139 -6
  313. parsl/version.py +1 -1
  314. {parsl-2024.3.11.data → parsl-2025.1.13.data}/scripts/exec_parsl_function.py +6 -5
  315. parsl-2025.1.13.data/scripts/interchange.py +649 -0
  316. {parsl-2024.3.11.data → parsl-2025.1.13.data}/scripts/process_worker_pool.py +115 -77
  317. parsl-2025.1.13.dist-info/METADATA +96 -0
  318. parsl-2025.1.13.dist-info/RECORD +462 -0
  319. {parsl-2024.3.11.dist-info → parsl-2025.1.13.dist-info}/WHEEL +1 -1
  320. parsl/channels/__init__.py +0 -7
  321. parsl/channels/base.py +0 -141
  322. parsl/channels/errors.py +0 -113
  323. parsl/channels/local/local.py +0 -164
  324. parsl/channels/oauth_ssh/oauth_ssh.py +0 -110
  325. parsl/channels/ssh/ssh.py +0 -276
  326. parsl/channels/ssh_il/__init__.py +0 -0
  327. parsl/channels/ssh_il/ssh_il.py +0 -74
  328. parsl/configs/ad_hoc.py +0 -35
  329. parsl/executors/radical/rpex_master.py +0 -42
  330. parsl/monitoring/radios.py +0 -175
  331. parsl/providers/ad_hoc/__init__.py +0 -0
  332. parsl/providers/ad_hoc/ad_hoc.py +0 -248
  333. parsl/providers/cobalt/__init__.py +0 -0
  334. parsl/providers/cobalt/cobalt.py +0 -236
  335. parsl/providers/cobalt/template.py +0 -17
  336. parsl/tests/configs/ad_hoc_cluster_htex.py +0 -35
  337. parsl/tests/configs/cooley_htex.py +0 -37
  338. parsl/tests/configs/htex_ad_hoc_cluster.py +0 -28
  339. parsl/tests/configs/local_adhoc.py +0 -18
  340. parsl/tests/configs/swan_htex.py +0 -43
  341. parsl/tests/configs/theta.py +0 -37
  342. parsl/tests/integration/test_channels/__init__.py +0 -0
  343. parsl/tests/integration/test_channels/test_channels.py +0 -17
  344. parsl/tests/integration/test_channels/test_local_channel.py +0 -42
  345. parsl/tests/integration/test_channels/test_scp_1.py +0 -45
  346. parsl/tests/integration/test_channels/test_ssh_1.py +0 -40
  347. parsl/tests/integration/test_channels/test_ssh_errors.py +0 -46
  348. parsl/tests/integration/test_channels/test_ssh_file_transport.py +0 -41
  349. parsl/tests/integration/test_channels/test_ssh_interactive.py +0 -24
  350. parsl/tests/manual_tests/test_ad_hoc_htex.py +0 -48
  351. parsl/tests/manual_tests/test_fan_in_out_htex_remote.py +0 -88
  352. parsl/tests/manual_tests/test_oauth_ssh.py +0 -13
  353. parsl/tests/sites/test_local_adhoc.py +0 -61
  354. parsl/tests/test_channels/__init__.py +0 -0
  355. parsl/tests/test_channels/test_large_output.py +0 -22
  356. parsl/tests/test_data/__init__.py +0 -0
  357. parsl/tests/test_mpi_apps/test_mpi_mode_disabled.py +0 -51
  358. parsl/tests/test_providers/test_cobalt_deprecation_warning.py +0 -16
  359. parsl-2024.3.11.dist-info/METADATA +0 -98
  360. parsl-2024.3.11.dist-info/RECORD +0 -447
  361. parsl/{channels/local → monitoring/radios}/__init__.py +0 -0
  362. parsl/{channels/oauth_ssh → tests/test_shutdown}/__init__.py +0 -0
  363. parsl/tests/{test_data → test_staging}/test_file_apps.py +0 -0
  364. parsl/tests/{test_data → test_staging}/test_file_staging.py +0 -0
  365. parsl/{channels/ssh → tests/unit}/__init__.py +0 -0
  366. {parsl-2024.3.11.data → parsl-2025.1.13.data}/scripts/parsl_coprocess.py +1 -1
  367. {parsl-2024.3.11.dist-info → parsl-2025.1.13.dist-info}/LICENSE +0 -0
  368. {parsl-2024.3.11.dist-info → parsl-2025.1.13.dist-info}/entry_points.txt +0 -0
  369. {parsl-2024.3.11.dist-info → parsl-2025.1.13.dist-info}/top_level.txt +0 -0
@@ -1,41 +1,47 @@
1
1
  #!python
2
2
 
3
3
  import argparse
4
+ import json
4
5
  import logging
6
+ import math
7
+ import multiprocessing
5
8
  import os
6
- import sys
9
+ import pickle
7
10
  import platform
11
+ import queue
12
+ import subprocess
13
+ import sys
8
14
  import threading
9
- import pickle
10
15
  import time
11
- import queue
12
16
  import uuid
13
- from typing import Sequence, Optional, Dict, List
14
-
15
- import zmq
16
- import math
17
- import json
18
- import psutil
19
- import multiprocessing
20
17
  from multiprocessing.managers import DictProxy
21
18
  from multiprocessing.sharedctypes import Synchronized
19
+ from typing import Dict, List, Optional, Sequence
20
+
21
+ import psutil
22
+ import zmq
22
23
 
23
24
  from parsl import curvezmq
24
- from parsl.process_loggers import wrap_with_logs
25
- from parsl.version import VERSION as PARSL_VERSION
25
+ from parsl.addresses import tcp_url
26
26
  from parsl.app.errors import RemoteExceptionWrapper
27
+ from parsl.executors.execute_task import execute_task
27
28
  from parsl.executors.high_throughput.errors import WorkerLost
28
- from parsl.executors.high_throughput.probe import probe_addresses
29
- from parsl.multiprocessing import SpawnContext
30
- from parsl.serialize import unpack_res_spec_apply_message, serialize
29
+ from parsl.executors.high_throughput.mpi_prefix_composer import (
30
+ VALID_LAUNCHERS,
31
+ compose_all,
32
+ )
31
33
  from parsl.executors.high_throughput.mpi_resource_management import (
34
+ MPITaskScheduler,
32
35
  TaskScheduler,
33
- MPITaskScheduler
34
36
  )
35
-
36
- from parsl.executors.high_throughput.mpi_prefix_composer import compose_all, VALID_LAUNCHERS
37
+ from parsl.executors.high_throughput.probe import probe_addresses
38
+ from parsl.multiprocessing import SpawnContext
39
+ from parsl.process_loggers import wrap_with_logs
40
+ from parsl.serialize import serialize
41
+ from parsl.version import VERSION as PARSL_VERSION
37
42
 
38
43
  HEARTBEAT_CODE = (2 ** 32) - 1
44
+ DRAINED_CODE = (2 ** 32) - 2
39
45
 
40
46
 
41
47
  class Manager:
@@ -73,7 +79,8 @@ class Manager:
73
79
  enable_mpi_mode: bool = False,
74
80
  mpi_launcher: str = "mpiexec",
75
81
  available_accelerators: Sequence[str],
76
- cert_dir: Optional[str]):
82
+ cert_dir: Optional[str],
83
+ drain_period: Optional[int]):
77
84
  """
78
85
  Parameters
79
86
  ----------
@@ -138,6 +145,9 @@ class Manager:
138
145
 
139
146
  cert_dir : str | None
140
147
  Path to the certificate directory.
148
+
149
+ drain_period: int | None
150
+ Number of seconds to drain after TODO: could be a nicer timespec involving m,s,h qualifiers for user friendliness?
141
151
  """
142
152
 
143
153
  logger.info("Manager initializing")
@@ -150,8 +160,8 @@ class Manager:
150
160
  raise Exception("No viable address found")
151
161
  else:
152
162
  logger.info("Connection to Interchange successful on {}".format(ix_address))
153
- task_q_url = "tcp://{}:{}".format(ix_address, task_port)
154
- result_q_url = "tcp://{}:{}".format(ix_address, result_port)
163
+ task_q_url = tcp_url(ix_address, task_port)
164
+ result_q_url = tcp_url(ix_address, result_port)
155
165
  logger.info("Task url : {}".format(task_q_url))
156
166
  logger.info("Result url : {}".format(result_q_url))
157
167
  except Exception:
@@ -176,6 +186,7 @@ class Manager:
176
186
 
177
187
  self.uid = uid
178
188
  self.block_id = block_id
189
+ self.start_time = time.time()
179
190
 
180
191
  self.enable_mpi_mode = enable_mpi_mode
181
192
  self.mpi_launcher = mpi_launcher
@@ -227,6 +238,14 @@ class Manager:
227
238
  self.heartbeat_period = heartbeat_period
228
239
  self.heartbeat_threshold = heartbeat_threshold
229
240
  self.poll_period = poll_period
241
+
242
+ self.drain_time: float
243
+ if drain_period:
244
+ self.drain_time = self._start_time + drain_period
245
+ logger.info(f"Will request drain at {self.drain_time}")
246
+ else:
247
+ self.drain_time = float('inf')
248
+
230
249
  self.cpu_affinity = cpu_affinity
231
250
 
232
251
  # Define accelerator available, adjust worker count accordingly
@@ -247,6 +266,7 @@ class Manager:
247
266
  'worker_count': self.worker_count,
248
267
  'uid': self.uid,
249
268
  'block_id': self.block_id,
269
+ 'start_time': self.start_time,
250
270
  'prefetch_capacity': self.prefetch_capacity,
251
271
  'max_capacity': self.worker_count + self.prefetch_capacity,
252
272
  'os': platform.system(),
@@ -262,10 +282,19 @@ class Manager:
262
282
  """ Send heartbeat to the incoming task queue
263
283
  """
264
284
  msg = {'type': 'heartbeat'}
285
+ # don't need to dumps and encode this every time - could do as a global on import?
265
286
  b_msg = json.dumps(msg).encode('utf-8')
266
287
  self.task_incoming.send(b_msg)
267
288
  logger.debug("Sent heartbeat")
268
289
 
290
+ def drain_to_incoming(self):
291
+ """ Send heartbeat to the incoming task queue
292
+ """
293
+ msg = {'type': 'drain'}
294
+ b_msg = json.dumps(msg).encode('utf-8')
295
+ self.task_incoming.send(b_msg)
296
+ logger.debug("Sent drain")
297
+
269
298
  @wrap_with_logs
270
299
  def pull_tasks(self, kill_event):
271
300
  """ Pull tasks from the incoming tasks zmq pipe onto the internal
@@ -298,6 +327,7 @@ class Manager:
298
327
  # time here are correctly copy-pasted from the relevant if
299
328
  # statements.
300
329
  next_interesting_event_time = min(last_beat + self.heartbeat_period,
330
+ self.drain_time,
301
331
  last_interchange_contact + self.heartbeat_threshold)
302
332
  try:
303
333
  pending_task_count = self.pending_task_queue.qsize()
@@ -312,6 +342,17 @@ class Manager:
312
342
  self.heartbeat_to_incoming()
313
343
  last_beat = time.time()
314
344
 
345
+ if time.time() > self.drain_time:
346
+ logger.info("Requesting drain")
347
+ self.drain_to_incoming()
348
+ # This will start the pool draining...
349
+ # Drained exit behaviour does not happen here. It will be
350
+ # driven by the interchange sending a DRAINED_CODE message.
351
+
352
+ # now set drain time to the far future so we don't send a drain
353
+ # message every iteration.
354
+ self.drain_time = float('inf')
355
+
315
356
  poll_duration_s = max(0, next_interesting_event_time - time.time())
316
357
  socks = dict(poller.poll(timeout=poll_duration_s * 1000))
317
358
 
@@ -322,10 +363,14 @@ class Manager:
322
363
 
323
364
  if tasks == HEARTBEAT_CODE:
324
365
  logger.debug("Got heartbeat from interchange")
325
-
366
+ elif tasks == DRAINED_CODE:
367
+ logger.info("Got fully drained message from interchange - setting kill flag")
368
+ kill_event.set()
326
369
  else:
327
370
  task_recv_counter += len(tasks)
328
- logger.debug("Got executor tasks: {}, cumulative count of tasks: {}".format([t['task_id'] for t in tasks], task_recv_counter))
371
+ logger.debug("Got executor tasks: {}, cumulative count of tasks: {}".format(
372
+ [t['task_id'] for t in tasks], task_recv_counter
373
+ ))
329
374
 
330
375
  for task in tasks:
331
376
  self.task_scheduler.put_task(task)
@@ -490,9 +535,8 @@ class Manager:
490
535
  self._worker_watchdog_thread.start()
491
536
  self._monitoring_handler_thread.start()
492
537
 
493
- logger.info("Loop start")
538
+ logger.info("Manager threads started")
494
539
 
495
- # TODO : Add mechanism in this loop to stop the worker pool
496
540
  # This might need a multiprocessing event to signal back.
497
541
  self._kill_event.wait()
498
542
  logger.critical("Received kill event, terminating worker processes")
@@ -548,45 +592,13 @@ def update_resource_spec_env_vars(mpi_launcher: str, resource_spec: Dict, node_i
548
592
  os.environ[key] = prefix_table[key]
549
593
 
550
594
 
551
- def execute_task(bufs, mpi_launcher: Optional[str] = None):
552
- """Deserialize the buffer and execute the task.
553
-
554
- Returns the result or throws exception.
555
- """
556
- user_ns = locals()
557
- user_ns.update({'__builtins__': __builtins__})
558
-
559
- f, args, kwargs, resource_spec = unpack_res_spec_apply_message(bufs, user_ns, copy=False)
560
-
561
- for varname in resource_spec:
562
- envname = "PARSL_" + str(varname).upper()
563
- os.environ[envname] = str(resource_spec[varname])
564
-
565
- if resource_spec.get("MPI_NODELIST"):
566
- worker_id = os.environ['PARSL_WORKER_RANK']
567
- nodes_for_task = resource_spec["MPI_NODELIST"].split(',')
568
- logger.info(f"Launching task on provisioned nodes: {nodes_for_task}")
569
- assert mpi_launcher
570
- update_resource_spec_env_vars(mpi_launcher,
571
- resource_spec=resource_spec,
572
- node_info=nodes_for_task)
573
- # We might need to look into callability of the function from itself
574
- # since we change it's name in the new namespace
575
- prefix = "parsl_"
576
- fname = prefix + "f"
577
- argname = prefix + "args"
578
- kwargname = prefix + "kwargs"
579
- resultname = prefix + "result"
580
-
581
- user_ns.update({fname: f,
582
- argname: args,
583
- kwargname: kwargs,
584
- resultname: resultname})
585
-
586
- code = "{0} = {1}(*{2}, **{3})".format(resultname, fname,
587
- argname, kwargname)
588
- exec(code, user_ns, user_ns)
589
- return user_ns.get(resultname)
595
+ def _init_mpi_env(mpi_launcher: str, resource_spec: Dict):
596
+ node_list = resource_spec.get("MPI_NODELIST")
597
+ if node_list is None:
598
+ return
599
+ nodes_for_task = node_list.split(',')
600
+ logger.info(f"Launching task on provisioned nodes: {nodes_for_task}")
601
+ update_resource_spec_env_vars(mpi_launcher=mpi_launcher, resource_spec=resource_spec, node_info=nodes_for_task)
590
602
 
591
603
 
592
604
  @wrap_with_logs(target="worker_log")
@@ -608,14 +620,6 @@ def worker(
608
620
  debug: bool,
609
621
  mpi_launcher: str,
610
622
  ):
611
- """
612
-
613
- Put request token into queue
614
- Get task from task_queue
615
- Pop request from queue
616
- Put result into result_queue
617
- """
618
-
619
623
  # override the global logger inherited from the __main__ process (which
620
624
  # usually logs to manager.log) with one specific to this worker.
621
625
  global logger
@@ -640,7 +644,8 @@ def worker(
640
644
  # If desired, set process affinity
641
645
  if cpu_affinity != "none":
642
646
  # Count the number of cores per worker
643
- avail_cores = sorted(os.sched_getaffinity(0)) # Get the available threads
647
+ # OSX does not implement os.sched_getaffinity
648
+ avail_cores = sorted(os.sched_getaffinity(0)) # type: ignore[attr-defined, unused-ignore]
644
649
  cores_per_worker = len(avail_cores) // pool_size
645
650
  assert cores_per_worker > 0, "Affinity does not work if there are more workers than cores"
646
651
 
@@ -680,12 +685,39 @@ def worker(
680
685
  os.environ["KMP_AFFINITY"] = f"explicit,proclist=[{proc_list}]" # For Intel OpenMP
681
686
 
682
687
  # Set the affinity for this worker
683
- os.sched_setaffinity(0, my_cores)
688
+ # OSX does not implement os.sched_setaffinity so type checking
689
+ # is ignored here in two ways:
690
+ # On a platform without sched_setaffinity, that attribute will not
691
+ # be defined, so ignore[attr-defined] will tell mypy to ignore this
692
+ # incorrect-for-OS X attribute access.
693
+ # On a platform with sched_setaffinity, that type: ignore message
694
+ # will be redundant, and ignore[unused-ignore] tells mypy to ignore
695
+ # that this ignore is unneeded.
696
+ os.sched_setaffinity(0, my_cores) # type: ignore[attr-defined, unused-ignore]
684
697
  logger.info("Set worker CPU affinity to {}".format(my_cores))
685
698
 
686
699
  # If desired, pin to accelerator
687
700
  if accelerator is not None:
688
- os.environ["CUDA_VISIBLE_DEVICES"] = accelerator
701
+
702
+ # If CUDA devices, find total number of devices to allow for MPS
703
+ # See: https://developer.nvidia.com/system-management-interface
704
+ nvidia_smi_cmd = "nvidia-smi -L > /dev/null && nvidia-smi -L | wc -l"
705
+ nvidia_smi_ret = subprocess.run(nvidia_smi_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
706
+ if nvidia_smi_ret.returncode == 0:
707
+ num_cuda_devices = int(nvidia_smi_ret.stdout.split()[0])
708
+ else:
709
+ num_cuda_devices = None
710
+
711
+ try:
712
+ if num_cuda_devices is not None:
713
+ procs_per_cuda_device = pool_size // num_cuda_devices
714
+ partitioned_accelerator = str(int(accelerator) // procs_per_cuda_device) # multiple workers will share a GPU
715
+ os.environ["CUDA_VISIBLE_DEVICES"] = partitioned_accelerator
716
+ logger.info(f'Pinned worker to partitioned cuda device: {partitioned_accelerator}')
717
+ else:
718
+ os.environ["CUDA_VISIBLE_DEVICES"] = accelerator
719
+ except (TypeError, ValueError, ZeroDivisionError):
720
+ os.environ["CUDA_VISIBLE_DEVICES"] = accelerator
689
721
  os.environ["ROCR_VISIBLE_DEVICES"] = accelerator
690
722
  os.environ["ZE_AFFINITY_MASK"] = accelerator
691
723
  os.environ["ZE_ENABLE_PCI_ID_DEVICE_ORDER"] = '1'
@@ -724,8 +756,10 @@ def worker(
724
756
  ready_worker_count.value -= 1
725
757
  worker_enqueued = False
726
758
 
759
+ _init_mpi_env(mpi_launcher=mpi_launcher, resource_spec=req["resource_spec"])
760
+
727
761
  try:
728
- result = execute_task(req['buffer'], mpi_launcher=mpi_launcher)
762
+ result = execute_task(req['buffer'])
729
763
  serialized_result = serialize(result, buffer_threshold=1000000)
730
764
  except Exception as e:
731
765
  logger.info('Caught an exception: {}'.format(e))
@@ -804,6 +838,8 @@ if __name__ == "__main__":
804
838
  help="Heartbeat period in seconds. Uses manager default unless set")
805
839
  parser.add_argument("--hb_threshold", default=120,
806
840
  help="Heartbeat threshold in seconds. Uses manager default unless set")
841
+ parser.add_argument("--drain_period", default=None,
842
+ help="Drain this pool after specified number of seconds. By default, does not drain.")
807
843
  parser.add_argument("--address_probe_timeout", default=30,
808
844
  help="Timeout to probe for viable address to interchange. Default: 30s")
809
845
  parser.add_argument("--poll", default=10,
@@ -824,7 +860,7 @@ if __name__ == "__main__":
824
860
  required=True,
825
861
  help="Whether/how workers should control CPU affinity.")
826
862
  parser.add_argument("--available-accelerators", type=str, nargs="*",
827
- help="Names of available accelerators")
863
+ help="Names of available accelerators, if not given assumed to be zero accelerators available", default=[])
828
864
  parser.add_argument("--enable_mpi_mode", action='store_true',
829
865
  help="Enable MPI mode")
830
866
  parser.add_argument("--mpi-launcher", type=str, choices=VALID_LAUNCHERS,
@@ -856,6 +892,7 @@ if __name__ == "__main__":
856
892
  logger.info("Prefetch capacity: {}".format(args.prefetch_capacity))
857
893
  logger.info("Heartbeat threshold: {}".format(args.hb_threshold))
858
894
  logger.info("Heartbeat period: {}".format(args.hb_period))
895
+ logger.info("Drain period: {}".format(args.drain_period))
859
896
  logger.info("CPU affinity: {}".format(args.cpu_affinity))
860
897
  logger.info("Accelerators: {}".format(" ".join(args.available_accelerators)))
861
898
  logger.info("enable_mpi_mode: {}".format(args.enable_mpi_mode))
@@ -876,6 +913,7 @@ if __name__ == "__main__":
876
913
  prefetch_capacity=int(args.prefetch_capacity),
877
914
  heartbeat_threshold=int(args.hb_threshold),
878
915
  heartbeat_period=int(args.hb_period),
916
+ drain_period=None if args.drain_period == "None" else int(args.drain_period),
879
917
  poll_period=int(args.poll),
880
918
  cpu_affinity=args.cpu_affinity,
881
919
  enable_mpi_mode=args.enable_mpi_mode,
@@ -0,0 +1,96 @@
1
+ Metadata-Version: 2.1
2
+ Name: parsl
3
+ Version: 2025.1.13
4
+ Summary: Simple data dependent workflows in Python
5
+ Home-page: https://github.com/Parsl/parsl
6
+ Download-URL: https://github.com/Parsl/parsl/archive/2025.01.13.tar.gz
7
+ Author: The Parsl Team
8
+ Author-email: parsl@googlegroups.com
9
+ License: Apache 2.0
10
+ Keywords: Workflows,Scientific computing
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.9.0
19
+ License-File: LICENSE
20
+ Requires-Dist: pyzmq>=17.1.2
21
+ Requires-Dist: typeguard!=3.*,<5,>=2.10
22
+ Requires-Dist: typing-extensions<5,>=4.6
23
+ Requires-Dist: globus-sdk
24
+ Requires-Dist: dill
25
+ Requires-Dist: tblib
26
+ Requires-Dist: requests
27
+ Requires-Dist: psutil>=5.5.1
28
+ Requires-Dist: setproctitle
29
+ Requires-Dist: filelock<4,>=3.13
30
+ Provides-Extra: all
31
+ Requires-Dist: sqlalchemy<2.1,>=2; extra == "all"
32
+ Requires-Dist: pydot>=1.4.2; extra == "all"
33
+ Requires-Dist: networkx<3.3,>=3.2; extra == "all"
34
+ Requires-Dist: Flask>=1.0.2; extra == "all"
35
+ Requires-Dist: flask-sqlalchemy; extra == "all"
36
+ Requires-Dist: pandas<3,>=2.2; extra == "all"
37
+ Requires-Dist: plotly; extra == "all"
38
+ Requires-Dist: python-daemon; extra == "all"
39
+ Requires-Dist: boto3; extra == "all"
40
+ Requires-Dist: kubernetes; extra == "all"
41
+ Requires-Dist: ipython<=8.6.0; extra == "all"
42
+ Requires-Dist: nbsphinx; extra == "all"
43
+ Requires-Dist: sphinx<7.2,>=7.1; extra == "all"
44
+ Requires-Dist: sphinx-rtd-theme; extra == "all"
45
+ Requires-Dist: google-auth; extra == "all"
46
+ Requires-Dist: google-api-python-client; extra == "all"
47
+ Requires-Dist: python-gssapi; extra == "all"
48
+ Requires-Dist: azure<=4; extra == "all"
49
+ Requires-Dist: msrestazure; extra == "all"
50
+ Requires-Dist: work-queue; extra == "all"
51
+ Requires-Dist: pyyaml; extra == "all"
52
+ Requires-Dist: cffi; extra == "all"
53
+ Requires-Dist: jsonschema; extra == "all"
54
+ Requires-Dist: proxystore; extra == "all"
55
+ Requires-Dist: radical.pilot==1.90; extra == "all"
56
+ Requires-Dist: radical.utils==1.90; extra == "all"
57
+ Provides-Extra: aws
58
+ Requires-Dist: boto3; extra == "aws"
59
+ Provides-Extra: azure
60
+ Requires-Dist: azure<=4; extra == "azure"
61
+ Requires-Dist: msrestazure; extra == "azure"
62
+ Provides-Extra: docs
63
+ Requires-Dist: ipython<=8.6.0; extra == "docs"
64
+ Requires-Dist: nbsphinx; extra == "docs"
65
+ Requires-Dist: sphinx<7.2,>=7.1; extra == "docs"
66
+ Requires-Dist: sphinx-rtd-theme; extra == "docs"
67
+ Provides-Extra: flux
68
+ Requires-Dist: pyyaml; extra == "flux"
69
+ Requires-Dist: cffi; extra == "flux"
70
+ Requires-Dist: jsonschema; extra == "flux"
71
+ Provides-Extra: google_cloud
72
+ Requires-Dist: google-auth; extra == "google-cloud"
73
+ Requires-Dist: google-api-python-client; extra == "google-cloud"
74
+ Provides-Extra: gssapi
75
+ Requires-Dist: python-gssapi; extra == "gssapi"
76
+ Provides-Extra: kubernetes
77
+ Requires-Dist: kubernetes; extra == "kubernetes"
78
+ Provides-Extra: monitoring
79
+ Requires-Dist: sqlalchemy<2.1,>=2; extra == "monitoring"
80
+ Provides-Extra: proxystore
81
+ Requires-Dist: proxystore; extra == "proxystore"
82
+ Provides-Extra: radical-pilot
83
+ Requires-Dist: radical.pilot==1.90; extra == "radical-pilot"
84
+ Requires-Dist: radical.utils==1.90; extra == "radical-pilot"
85
+ Provides-Extra: visualization
86
+ Requires-Dist: pydot>=1.4.2; extra == "visualization"
87
+ Requires-Dist: networkx<3.3,>=3.2; extra == "visualization"
88
+ Requires-Dist: Flask>=1.0.2; extra == "visualization"
89
+ Requires-Dist: flask-sqlalchemy; extra == "visualization"
90
+ Requires-Dist: pandas<3,>=2.2; extra == "visualization"
91
+ Requires-Dist: plotly; extra == "visualization"
92
+ Requires-Dist: python-daemon; extra == "visualization"
93
+ Provides-Extra: workqueue
94
+ Requires-Dist: work-queue; extra == "workqueue"
95
+
96
+ Simple parallel workflows system for Python