parsl 2024.3.18__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 +26 -6
  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 +53 -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 +259 -223
  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 +307 -285
  57. parsl/executors/high_throughput/interchange.py +137 -168
  58. parsl/executors/high_throughput/manager_record.py +4 -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 +77 -75
  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 +38 -55
  75. parsl/executors/taskvine/factory.py +1 -1
  76. parsl/executors/taskvine/factory_config.py +1 -1
  77. parsl/executors/taskvine/manager.py +17 -13
  78. parsl/executors/taskvine/manager_config.py +7 -2
  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 +28 -112
  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 +0 -6
  91. parsl/log_utils.py +1 -2
  92. parsl/monitoring/db_manager.py +55 -93
  93. parsl/monitoring/errors.py +6 -0
  94. parsl/monitoring/monitoring.py +85 -311
  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 +8 -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 -9
  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 +3 -9
  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 +0 -7
  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 +11 -10
  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 +5 -5
  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 +5 -8
  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 +137 -4
  313. parsl/version.py +1 -1
  314. {parsl-2024.3.18.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.18.data → parsl-2025.1.13.data}/scripts/process_worker_pool.py +77 -75
  317. parsl-2025.1.13.dist-info/METADATA +96 -0
  318. parsl-2025.1.13.dist-info/RECORD +462 -0
  319. {parsl-2024.3.18.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.18.dist-info/METADATA +0 -98
  360. parsl-2024.3.18.dist-info/RECORD +0 -449
  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.18.data → parsl-2025.1.13.data}/scripts/parsl_coprocess.py +1 -1
  367. {parsl-2024.3.18.dist-info → parsl-2025.1.13.dist-info}/LICENSE +0 -0
  368. {parsl-2024.3.18.dist-info → parsl-2025.1.13.dist-info}/entry_points.txt +0 -0
  369. {parsl-2024.3.18.dist-info → parsl-2025.1.13.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,11 @@
1
1
  import logging
2
2
  import os
3
- import parsl
4
- import pytest
5
3
  import time
6
4
 
5
+ import pytest
6
+
7
+ import parsl
8
+
7
9
  logger = logging.getLogger(__name__)
8
10
 
9
11
 
@@ -14,9 +16,10 @@ def this_app():
14
16
 
15
17
  @pytest.mark.local
16
18
  def test_row_counts():
17
- from parsl.tests.configs.htex_local_alternate import fresh_config
18
19
  import sqlalchemy
19
20
  from sqlalchemy import text
21
+
22
+ from parsl.tests.configs.htex_local_alternate import fresh_config
20
23
  if os.path.exists("runinfo/monitoring.db"):
21
24
  logger.info("Monitoring database already exists - deleting")
22
25
  os.remove("runinfo/monitoring.db")
@@ -63,7 +66,6 @@ def test_row_counts():
63
66
 
64
67
  logger.info("cleaning up parsl")
65
68
  parsl.dfk().cleanup()
66
- parsl.clear()
67
69
 
68
70
  # at this point, we should find data consistent with executing one
69
71
  # task in the database.
@@ -1,10 +1,13 @@
1
1
  import logging
2
2
  import os
3
- import parsl
4
- import pytest
5
3
  import socket
6
4
  import time
7
5
 
6
+ import pytest
7
+ import zmq
8
+
9
+ import parsl
10
+
8
11
  logger = logging.getLogger(__name__)
9
12
 
10
13
 
@@ -15,10 +18,11 @@ def this_app():
15
18
 
16
19
  @pytest.mark.local
17
20
  def test_row_counts():
18
- from parsl.tests.configs.htex_local_alternate import fresh_config
19
21
  import sqlalchemy
20
22
  from sqlalchemy import text
21
23
 
24
+ from parsl.tests.configs.htex_local_alternate import fresh_config
25
+
22
26
  if os.path.exists("runinfo/monitoring.db"):
23
27
  logger.info("Monitoring database already exists - deleting")
24
28
  os.remove("runinfo/monitoring.db")
@@ -40,16 +44,24 @@ def test_row_counts():
40
44
  # the latter is what i'm most suspicious of in my present investigation
41
45
 
42
46
  # dig out the interchange port...
43
- hub_address = parsl.dfk().hub_address
44
- hub_interchange_port = parsl.dfk().hub_interchange_port
47
+ hub_address = parsl.dfk().monitoring.hub_address
48
+ hub_zmq_port = parsl.dfk().monitoring.hub_zmq_port
45
49
 
46
50
  # this will send a string to a new socket connection
47
51
  with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
48
- s.connect((hub_address, hub_interchange_port))
52
+ s.connect((hub_address, hub_zmq_port))
49
53
  s.sendall(b'fuzzing\r')
50
54
 
55
+ context = zmq.Context()
56
+ channel_timeout = 10000 # in milliseconds
57
+ hub_channel = context.socket(zmq.DEALER)
58
+ hub_channel.setsockopt(zmq.LINGER, 0)
59
+ hub_channel.set_hwm(0)
60
+ hub_channel.setsockopt(zmq.SNDTIMEO, channel_timeout)
61
+ hub_channel.connect("tcp://{}:{}".format(hub_address, hub_zmq_port))
62
+
51
63
  # this will send a non-object down the DFK's existing ZMQ connection
52
- parsl.dfk().monitoring._dfk_channel.send(b'FuzzyByte\rSTREAM')
64
+ hub_channel.send(b'FuzzyByte\rSTREAM')
53
65
 
54
66
  # This following attack is commented out, because monitoring is not resilient
55
67
  # to this.
@@ -74,7 +86,6 @@ def test_row_counts():
74
86
 
75
87
  logger.info("cleaning up parsl")
76
88
  parsl.dfk().cleanup()
77
- parsl.clear()
78
89
 
79
90
  # at this point, we should find one row in the monitoring database.
80
91
 
@@ -0,0 +1,80 @@
1
+ import logging
2
+ import os
3
+ import time
4
+
5
+ import pytest
6
+
7
+ import parsl
8
+ from parsl.config import Config
9
+ from parsl.executors import HighThroughputExecutor
10
+ from parsl.launchers import SimpleLauncher
11
+ from parsl.monitoring import MonitoringHub
12
+ from parsl.providers import LocalProvider
13
+
14
+
15
+ def fresh_config(run_dir, strategy, db_url):
16
+ return Config(
17
+ run_dir=os.fspath(run_dir),
18
+ executors=[
19
+ HighThroughputExecutor(
20
+ label="htex_local",
21
+ cores_per_worker=1,
22
+ encrypted=True,
23
+ provider=LocalProvider(
24
+ init_blocks=1,
25
+ # min and max are set to 0 to ensure that we don't get
26
+ # a block from ongoing strategy scaling, only from
27
+ # init_blocks
28
+ min_blocks=0,
29
+ max_blocks=0,
30
+ launcher=SimpleLauncher(),
31
+ ),
32
+ )
33
+ ],
34
+ strategy=strategy,
35
+ strategy_period=0.1,
36
+ monitoring=MonitoringHub(
37
+ hub_address="localhost",
38
+ logging_endpoint=db_url
39
+ )
40
+ )
41
+
42
+
43
+ @parsl.python_app
44
+ def this_app():
45
+ pass
46
+
47
+
48
+ @pytest.mark.local
49
+ @pytest.mark.parametrize("strategy", ('none', 'simple', 'htex_auto_scale'))
50
+ def test_row_counts(tmpd_cwd, strategy):
51
+ # this is imported here rather than at module level because
52
+ # it isn't available in a plain parsl install, so this module
53
+ # would otherwise fail to import and break even a basic test
54
+ # run.
55
+ import sqlalchemy
56
+ from sqlalchemy import text
57
+
58
+ db_url = f"sqlite:///{tmpd_cwd}/monitoring.db"
59
+ with parsl.load(fresh_config(tmpd_cwd, strategy, db_url)):
60
+ dfk = parsl.dfk()
61
+ run_id = dfk.run_id
62
+
63
+ this_app().result()
64
+
65
+ engine = sqlalchemy.create_engine(db_url)
66
+ with engine.begin() as connection:
67
+
68
+ binds = {"run_id": run_id}
69
+
70
+ result = connection.execute(text("SELECT COUNT(DISTINCT block_id) FROM block WHERE run_id = :run_id"), binds)
71
+ (c, ) = result.first()
72
+ assert c == 1, "We should see a single block in this database"
73
+
74
+ result = connection.execute(text("SELECT COUNT(*) FROM block WHERE block_id = 0 AND status = 'PENDING' AND run_id = :run_id"), binds)
75
+ (c, ) = result.first()
76
+ assert c == 1, "There should be a single pending status"
77
+
78
+ result = connection.execute(text("SELECT COUNT(*) FROM block WHERE block_id = 0 AND status = 'SCALED_IN' AND run_id = :run_id"), binds)
79
+ (c, ) = result.first()
80
+ assert c == 1, "There should be a single cancelled status"
@@ -1,11 +1,12 @@
1
1
  import logging
2
2
  import os
3
- import parsl
4
- import pytest
5
3
  import random
6
-
7
4
  from concurrent.futures import Future
8
5
 
6
+ import pytest
7
+
8
+ import parsl
9
+
9
10
 
10
11
  @parsl.python_app
11
12
  def this_app(inputs=()):
@@ -16,6 +17,7 @@ def this_app(inputs=()):
16
17
  def test_future_representation(tmpd_cwd):
17
18
  import sqlalchemy
18
19
  from sqlalchemy import text
20
+
19
21
  from parsl.tests.configs.htex_local_alternate import fresh_config
20
22
 
21
23
  monitoring_db = str(tmpd_cwd / "monitoring.db")
@@ -52,7 +54,6 @@ def test_future_representation(tmpd_cwd):
52
54
  # seconds, with the assumption "data will arrive in the DB within
53
55
  # 30 seconds, but probably much sooner".
54
56
  parsl.dfk().cleanup()
55
- parsl.clear()
56
57
 
57
58
  engine = sqlalchemy.create_engine(monitoring_url)
58
59
  with engine.begin() as connection:
@@ -1,9 +1,11 @@
1
1
 
2
2
  import logging
3
3
  import os
4
- import parsl
4
+
5
5
  import pytest
6
6
 
7
+ import parsl
8
+
7
9
  logger = logging.getLogger(__name__)
8
10
 
9
11
 
@@ -16,6 +18,7 @@ def this_app(x):
16
18
  def test_hashsum():
17
19
  import sqlalchemy
18
20
  from sqlalchemy import text
21
+
19
22
  from parsl.tests.configs.htex_local_alternate import fresh_config
20
23
 
21
24
  if os.path.exists("runinfo/monitoring.db"):
@@ -47,7 +50,6 @@ def test_hashsum():
47
50
 
48
51
  logger.info("cleaning up parsl")
49
52
  parsl.dfk().cleanup()
50
- parsl.clear()
51
53
 
52
54
  # at this point, we should find one row in the monitoring database.
53
55
 
@@ -0,0 +1,134 @@
1
+ """Tests monitoring records app name under various decoration patterns.
2
+ """
3
+
4
+ import logging
5
+ import os
6
+ import re
7
+ import time
8
+ from typing import Union
9
+
10
+ import pytest
11
+
12
+ import parsl
13
+ from parsl.config import Config
14
+ from parsl.data_provider.data_manager import default_staging
15
+ from parsl.data_provider.files import File
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
+ )
41
+ )
42
+
43
+
44
+ @parsl.python_app
45
+ def stdapp(stdout=None, stderr=None):
46
+ pass
47
+
48
+
49
+ class ArbitraryPathLike(os.PathLike):
50
+ def __init__(self, path: Union[str, bytes]) -> None:
51
+ self.path = path
52
+
53
+ def __fspath__(self) -> Union[str, bytes]:
54
+ return self.path
55
+
56
+
57
+ class ArbitraryStaging(Staging):
58
+ """This staging provider will not actually do any staging, but will
59
+ accept arbitrary: scheme URLs. That's enough for this monitoring test
60
+ which doesn't need any actual stage out action to happen.
61
+ """
62
+ def can_stage_out(self, file):
63
+ return file.scheme == "arbitrary"
64
+
65
+
66
+ @pytest.mark.local
67
+ @pytest.mark.parametrize('stdx,expected_stdx',
68
+ [('hello.txt', 'hello.txt'),
69
+ (None, ''),
70
+ (('tuple.txt', 'w'), 'tuple.txt'),
71
+ (ArbitraryPathLike('pl.txt'), 'pl.txt'),
72
+ (ArbitraryPathLike(b'pl2.txt'), 'pl2.txt'),
73
+ ((ArbitraryPathLike('pl3.txt'), 'w'), 'pl3.txt'),
74
+ ((ArbitraryPathLike(b'pl4.txt'), 'w'), 'pl4.txt'),
75
+ (parsl.AUTO_LOGNAME,
76
+ lambda p:
77
+ isinstance(p, str) and
78
+ os.path.isabs(p) and
79
+ re.match("^.*/task_0000_stdapp\\.std...$", p)),
80
+ (File("arbitrary:abc123"), "arbitrary:abc123"),
81
+ (File("file:///tmp/pl5"), "file:///tmp/pl5"),
82
+ ])
83
+ @pytest.mark.parametrize('stream', ['stdout', 'stderr'])
84
+ def test_stdstream_to_monitoring(stdx, expected_stdx, stream, tmpd_cwd, caplog):
85
+ """This tests that various forms of stdout/err specification are
86
+ represented in monitoring correctly. The stderr and stdout codepaths
87
+ are generally duplicated, rather than factorised, and so this test
88
+ runs the same tests on both stdout and stderr.
89
+ """
90
+
91
+ # this is imported here rather than at module level because
92
+ # it isn't available in a plain parsl install, so this module
93
+ # would otherwise fail to import and break even a basic test
94
+ # run.
95
+ import sqlalchemy
96
+
97
+ c = fresh_config(tmpd_cwd)
98
+ c.monitoring.logging_endpoint = f"sqlite:///{tmpd_cwd}/monitoring.db"
99
+ c.executors[0].storage_access = default_staging + [ArbitraryStaging()]
100
+
101
+ with parsl.load(c):
102
+ kwargs = {stream: stdx}
103
+ stdapp(**kwargs).result()
104
+
105
+ engine = sqlalchemy.create_engine(c.monitoring.logging_endpoint)
106
+ with engine.begin() as connection:
107
+
108
+ def count_rows(table: str):
109
+ result = connection.execute(sqlalchemy.text(f"SELECT COUNT(*) FROM {table}"))
110
+ (c, ) = result.first()
111
+ return c
112
+
113
+ # one workflow...
114
+ assert count_rows("workflow") == 1
115
+
116
+ # ... with one task ...
117
+ assert count_rows("task") == 1
118
+
119
+ # ... that was tried once ...
120
+ assert count_rows("try") == 1
121
+
122
+ # ... and has the expected name.
123
+ result = connection.execute(sqlalchemy.text(f"SELECT task_{stream} FROM task"))
124
+ (c, ) = result.first()
125
+
126
+ if isinstance(expected_stdx, str):
127
+ assert c == expected_stdx
128
+ elif callable(expected_stdx):
129
+ assert expected_stdx(c)
130
+ else:
131
+ raise RuntimeError("Bad expected_stdx value")
132
+
133
+ for record in caplog.records:
134
+ assert record.levelno < logging.ERROR
@@ -1,4 +1,5 @@
1
1
  import pytest
2
+
2
3
  from parsl.dataflow.states import States
3
4
 
4
5
 
@@ -1,41 +1,48 @@
1
1
  import pytest
2
2
 
3
3
  from parsl import Config
4
- from parsl.executors import HighThroughputExecutor
5
- from parsl.launchers import SrunLauncher, SingleNodeLauncher, SimpleLauncher, AprunLauncher
4
+ from parsl.executors import MPIExecutor
5
+ from parsl.launchers import AprunLauncher, SimpleLauncher, SrunLauncher
6
6
  from parsl.providers import SlurmProvider
7
7
 
8
8
 
9
9
  @pytest.mark.local
10
- def test_bad_launcher_with_mpi_mode():
11
- """AssertionError if a launcher other than SingleNodeLauncher is supplied"""
10
+ def test_bad_launcher():
11
+ """TypeError if a launcher other than SimpleLauncher is supplied"""
12
12
 
13
- for launcher in [SrunLauncher(), SimpleLauncher(), AprunLauncher()]:
14
- with pytest.raises(AssertionError):
13
+ for launcher in [SrunLauncher(), AprunLauncher()]:
14
+ with pytest.raises(TypeError):
15
15
  Config(executors=[
16
- HighThroughputExecutor(
17
- enable_mpi_mode=True,
16
+ MPIExecutor(
18
17
  provider=SlurmProvider(launcher=launcher),
19
18
  )
20
19
  ])
21
20
 
22
21
 
23
22
  @pytest.mark.local
24
- def test_correct_launcher_with_mpi_mode():
25
- """Confirm that SingleNodeLauncer works with mpi_mode"""
26
-
27
- config = Config(executors=[
28
- HighThroughputExecutor(
29
- enable_mpi_mode=True,
30
- provider=SlurmProvider(launcher=SingleNodeLauncher()),
31
- )
32
- ])
33
- assert isinstance(config.executors[0].provider.launcher, SingleNodeLauncher)
34
-
35
- config = Config(executors=[
36
- HighThroughputExecutor(
37
- enable_mpi_mode=True,
38
- provider=SlurmProvider(),
39
- )
40
- ])
41
- assert isinstance(config.executors[0].provider.launcher, SingleNodeLauncher)
23
+ def test_bad_mpi_launcher():
24
+ """ValueError if an unsupported mpi_launcher is specified"""
25
+
26
+ with pytest.raises(ValueError):
27
+ Config(executors=[
28
+ MPIExecutor(
29
+ mpi_launcher="bad_launcher",
30
+ provider=SlurmProvider(launcher=SimpleLauncher()),
31
+ )
32
+ ])
33
+
34
+
35
+ @pytest.mark.local
36
+ @pytest.mark.parametrize(
37
+ "mpi_launcher",
38
+ ["srun", "aprun", "mpiexec"]
39
+ )
40
+ def test_correct_launcher_with_mpi_mode(mpi_launcher: str):
41
+ """Confirm that SimpleLauncher works with mpi_mode"""
42
+
43
+ executor = MPIExecutor(
44
+ mpi_launcher=mpi_launcher,
45
+ provider=SlurmProvider(launcher=SimpleLauncher()),
46
+ )
47
+
48
+ assert isinstance(executor.provider.launcher, SimpleLauncher)
@@ -1,34 +1,43 @@
1
1
  import logging
2
+ import os
2
3
  import random
3
4
  from typing import Dict
5
+
4
6
  import pytest
5
- import parsl
6
- from parsl import python_app, bash_app
7
- from parsl.tests.configs.htex_local import fresh_config
8
7
 
9
- import os
8
+ import parsl
9
+ from parsl import Config, bash_app, python_app
10
+ from parsl.executors import MPIExecutor
11
+ from parsl.executors.errors import InvalidResourceSpecification
12
+ from parsl.launchers import SimpleLauncher
13
+ from parsl.providers import LocalProvider
10
14
 
11
15
  EXECUTOR_LABEL = "MPI_TEST"
12
16
 
13
17
 
14
18
  def local_setup():
15
- config = fresh_config()
16
- config.executors[0].label = EXECUTOR_LABEL
17
- config.executors[0].max_workers_per_node = 2
18
- config.executors[0].enable_mpi_mode = True
19
- config.executors[0].mpi_launcher = "mpiexec"
20
19
 
21
20
  cwd = os.path.abspath(os.path.dirname(__file__))
22
21
  pbs_nodefile = os.path.join(cwd, "mocks", "pbs_nodefile")
23
22
 
24
- config.executors[0].provider.worker_init = f"export PBS_NODEFILE={pbs_nodefile}"
23
+ config = Config(
24
+ executors=[
25
+ MPIExecutor(
26
+ label=EXECUTOR_LABEL,
27
+ max_workers_per_block=2,
28
+ mpi_launcher="mpiexec",
29
+ provider=LocalProvider(
30
+ worker_init=f"export PBS_NODEFILE={pbs_nodefile}",
31
+ launcher=SimpleLauncher()
32
+ )
33
+ )
34
+ ])
25
35
 
26
36
  parsl.load(config)
27
37
 
28
38
 
29
39
  def local_teardown():
30
40
  parsl.dfk().cleanup()
31
- parsl.clear()
32
41
 
33
42
 
34
43
  @python_app
@@ -169,3 +178,11 @@ def test_simulated_load(rounds: int = 100):
169
178
  total_ranks, nodes = future.result(timeout=10)
170
179
  assert len(nodes) == futures[future]["num_nodes"]
171
180
  assert total_ranks == futures[future]["num_nodes"] * futures[future]["ranks_per_node"]
181
+
182
+
183
+ @pytest.mark.local
184
+ def test_missing_resource_spec():
185
+
186
+ with pytest.raises(InvalidResourceSpecification):
187
+ future = mock_app(sleep_dur=0.4)
188
+ future.result(timeout=10)
@@ -1,14 +1,14 @@
1
1
  import logging
2
+
2
3
  import pytest
3
4
 
4
- from parsl.executors.high_throughput.mpi_resource_management import Scheduler
5
5
  from parsl.executors.high_throughput.mpi_prefix_composer import (
6
- compose_srun_launch_cmd,
6
+ compose_all,
7
7
  compose_aprun_launch_cmd,
8
8
  compose_mpiexec_launch_cmd,
9
- compose_all,
9
+ compose_srun_launch_cmd,
10
10
  )
11
-
11
+ from parsl.executors.high_throughput.mpi_resource_management import Scheduler
12
12
 
13
13
  resource_spec = {"num_nodes": 2,
14
14
  "num_ranks": 8,
@@ -1,9 +1,14 @@
1
1
  import logging
2
2
  import os
3
+ import pickle
3
4
  from unittest import mock
5
+
4
6
  import pytest
5
- import pickle
6
- from parsl.executors.high_throughput.mpi_resource_management import TaskScheduler, MPITaskScheduler
7
+
8
+ from parsl.executors.high_throughput.mpi_resource_management import (
9
+ MPITaskScheduler,
10
+ TaskScheduler,
11
+ )
7
12
  from parsl.multiprocessing import SpawnContext
8
13
  from parsl.serialize import pack_res_spec_apply_message, unpack_res_spec_apply_message
9
14
 
@@ -0,0 +1,64 @@
1
+ """Tests for the wrapper class"""
2
+ from inspect import signature
3
+ from pathlib import Path
4
+
5
+ import pytest
6
+
7
+ from parsl import Config, HighThroughputExecutor
8
+ from parsl.executors.high_throughput.mpi_executor import MPIExecutor
9
+ from parsl.launchers import SimpleLauncher
10
+ from parsl.providers import LocalProvider
11
+
12
+ from .test_mpi_mode_enabled import get_env_vars
13
+
14
+ cwd = Path(__file__).parent.absolute()
15
+ pbs_nodefile = cwd.joinpath("mocks", "pbs_nodefile")
16
+
17
+
18
+ def local_config():
19
+ return Config(
20
+ executors=[
21
+ MPIExecutor(
22
+ max_workers_per_block=1,
23
+ provider=LocalProvider(
24
+ worker_init=f"export PBS_NODEFILE={pbs_nodefile}",
25
+ launcher=SimpleLauncher()
26
+ )
27
+ )
28
+ ]
29
+ )
30
+
31
+
32
+ @pytest.mark.local
33
+ def test_docstring():
34
+ """Ensure the old kwargs are copied over into the new class"""
35
+ assert 'label' in MPIExecutor.__doc__
36
+ assert 'max_workers_per_block' in MPIExecutor.__doc__
37
+ assert 'available_accelerators' not in MPIExecutor.__doc__
38
+
39
+
40
+ @pytest.mark.local
41
+ def test_init():
42
+ """Ensure all relevant kwargs are copied over from HTEx"""
43
+
44
+ new_kwargs = {'max_workers_per_block', 'mpi_launcher'}
45
+ excluded_kwargs = {'available_accelerators', 'cores_per_worker', 'max_workers_per_node',
46
+ 'mem_per_worker', 'cpu_affinity', 'manager_selector'}
47
+
48
+ # Get the kwargs from both HTEx and MPIEx
49
+ htex_kwargs = set(signature(HighThroughputExecutor.__init__).parameters)
50
+ mpix_kwargs = set(signature(MPIExecutor.__init__).parameters)
51
+
52
+ assert mpix_kwargs.difference(htex_kwargs) == new_kwargs
53
+ assert len(mpix_kwargs.intersection(excluded_kwargs)) == 0
54
+ assert mpix_kwargs.union(excluded_kwargs).difference(new_kwargs) == htex_kwargs
55
+
56
+
57
+ @pytest.mark.local
58
+ def test_get_env():
59
+ future = get_env_vars(parsl_resource_specification={
60
+ "num_nodes": 3,
61
+ "ranks_per_node": 5,
62
+ })
63
+ env_vars = future.result()
64
+ assert env_vars['PARSL_NUM_RANKS'] == '15'