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
parsl/channels/errors.py DELETED
@@ -1,113 +0,0 @@
1
- ''' Exceptions raise by Apps.
2
- '''
3
- from parsl.errors import ParslError
4
- from typing import Optional
5
-
6
-
7
- class ChannelError(ParslError):
8
- """ Base class for all exceptions
9
-
10
- Only to be invoked when only a more specific error is not available.
11
- """
12
- def __init__(self, reason: str, e: Exception, hostname: str) -> None:
13
- self.reason = reason
14
- self.e = e
15
- self.hostname = hostname
16
-
17
- def __str__(self) -> str:
18
- return "Hostname:{0}, Reason:{1}".format(self.hostname, self.reason)
19
-
20
-
21
- class BadHostKeyException(ChannelError):
22
- ''' SSH channel could not be created since server's host keys could not
23
- be verified
24
-
25
- Contains:
26
- reason(string)
27
- e (paramiko exception object)
28
- hostname (string)
29
- '''
30
-
31
- def __init__(self, e: Exception, hostname: str) -> None:
32
- super().__init__("SSH channel could not be created since server's host keys could not be "
33
- "verified", e, hostname)
34
-
35
-
36
- class BadScriptPath(ChannelError):
37
- ''' An error raised during execution of an app.
38
- What this exception contains depends entirely on context
39
- Contains:
40
- reason(string)
41
- e (paramiko exception object)
42
- hostname (string)
43
- '''
44
-
45
- def __init__(self, e: Exception, hostname: str) -> None:
46
- super().__init__("Inaccessible remote script dir. Specify script_dir", e, hostname)
47
-
48
-
49
- class BadPermsScriptPath(ChannelError):
50
- ''' User does not have permissions to access the script_dir on the remote site
51
-
52
- Contains:
53
- reason(string)
54
- e (paramiko exception object)
55
- hostname (string)
56
- '''
57
-
58
- def __init__(self, e: Exception, hostname: str) -> None:
59
- super().__init__("User does not have permissions to access the script_dir", e, hostname)
60
-
61
-
62
- class FileExists(ChannelError):
63
- ''' Push or pull of file over channel fails since a file of the name already
64
- exists on the destination.
65
-
66
- Contains:
67
- reason(string)
68
- e (paramiko exception object)
69
- hostname (string)
70
- '''
71
-
72
- def __init__(self, e: Exception, hostname: str, filename: Optional[str] = None) -> None:
73
- super().__init__("File name collision in channel transport phase: {}".format(filename),
74
- e, hostname)
75
-
76
-
77
- class AuthException(ChannelError):
78
- ''' An error raised during execution of an app.
79
- What this exception contains depends entirely on context
80
- Contains:
81
- reason(string)
82
- e (paramiko exception object)
83
- hostname (string)
84
- '''
85
-
86
- def __init__(self, e: Exception, hostname: str) -> None:
87
- super().__init__("Authentication to remote server failed", e, hostname)
88
-
89
-
90
- class SSHException(ChannelError):
91
- ''' if there was any other error connecting or establishing an SSH session
92
-
93
- Contains:
94
- reason(string)
95
- e (paramiko exception object)
96
- hostname (string)
97
- '''
98
-
99
- def __init__(self, e: Exception, hostname: str) -> None:
100
- super().__init__("Error connecting or establishing an SSH session", e, hostname)
101
-
102
-
103
- class FileCopyException(ChannelError):
104
- ''' File copy operation failed
105
-
106
- Contains:
107
- reason(string)
108
- e (paramiko exception object)
109
- hostname (string)
110
- '''
111
-
112
- def __init__(self, e: Exception, hostname: str) -> None:
113
- super().__init__("File copy failed due to {0}".format(e), e, hostname)
@@ -1,164 +0,0 @@
1
- import copy
2
- import logging
3
- import os
4
- import shutil
5
- import subprocess
6
-
7
- from parsl.channels.base import Channel
8
- from parsl.channels.errors import FileCopyException
9
- from parsl.utils import RepresentationMixin
10
-
11
- logger = logging.getLogger(__name__)
12
-
13
-
14
- class LocalChannel(Channel, RepresentationMixin):
15
- ''' This is not even really a channel, since opening a local shell is not heavy
16
- and done so infrequently that they do not need a persistent channel
17
- '''
18
-
19
- def __init__(self, userhome=".", envs={}, script_dir=None):
20
- ''' Initialize the local channel. script_dir is required by set to a default.
21
-
22
- KwArgs:
23
- - userhome (string): (default='.') This is provided as a way to override and set a specific userhome
24
- - envs (dict) : A dictionary of env variables to be set when launching the shell
25
- - script_dir (string): Directory to place scripts
26
- '''
27
- self.userhome = os.path.abspath(userhome)
28
- self.hostname = "localhost"
29
- self.envs = envs
30
- local_env = os.environ.copy()
31
- self._envs = copy.deepcopy(local_env)
32
- self._envs.update(envs)
33
- self.script_dir = script_dir
34
-
35
- def execute_wait(self, cmd, walltime=None, envs={}):
36
- ''' Synchronously execute a commandline string on the shell.
37
-
38
- Args:
39
- - cmd (string) : Commandline string to execute
40
- - walltime (int) : walltime in seconds, this is not really used now.
41
-
42
- Kwargs:
43
- - envs (dict) : Dictionary of env variables. This will be used
44
- to override the envs set at channel initialization.
45
-
46
- Returns:
47
- - retcode : Return code from the execution, -1 on fail
48
- - stdout : stdout string
49
- - stderr : stderr string
50
-
51
- Raises:
52
- None.
53
- '''
54
- current_env = copy.deepcopy(self._envs)
55
- current_env.update(envs)
56
-
57
- try:
58
- proc = subprocess.Popen(
59
- cmd,
60
- stdout=subprocess.PIPE,
61
- stderr=subprocess.PIPE,
62
- cwd=self.userhome,
63
- env=current_env,
64
- shell=True,
65
- preexec_fn=os.setpgrp
66
- )
67
- (stdout, stderr) = proc.communicate(timeout=walltime)
68
- retcode = proc.returncode
69
-
70
- except Exception as e:
71
- logger.warning("Execution of command '{}' failed due to \n{}".format(cmd, e))
72
- raise
73
-
74
- return (retcode, stdout.decode("utf-8"), stderr.decode("utf-8"))
75
-
76
- def push_file(self, source, dest_dir):
77
- ''' If the source files dirpath is the same as dest_dir, a copy
78
- is not necessary, and nothing is done. Else a copy is made.
79
-
80
- Args:
81
- - source (string) : Path to the source file
82
- - dest_dir (string) : Path to the directory to which the files is to be copied
83
-
84
- Returns:
85
- - destination_path (String) : Absolute path of the destination file
86
-
87
- Raises:
88
- - FileCopyException : If file copy failed.
89
- '''
90
-
91
- local_dest = os.path.join(dest_dir, os.path.basename(source))
92
-
93
- # Only attempt to copy if the target dir and source dir are different
94
- if os.path.dirname(source) != dest_dir:
95
- try:
96
- shutil.copyfile(source, local_dest)
97
- os.chmod(local_dest, 0o700)
98
-
99
- except OSError as e:
100
- raise FileCopyException(e, self.hostname)
101
-
102
- else:
103
- os.chmod(local_dest, 0o700)
104
-
105
- return local_dest
106
-
107
- def pull_file(self, remote_source, local_dir):
108
- return self.push_file(remote_source, local_dir)
109
-
110
- def close(self):
111
- ''' There's nothing to close here, and this really doesn't do anything
112
-
113
- Returns:
114
- - False, because it really did not "close" this channel.
115
- '''
116
- return False
117
-
118
- def isdir(self, path):
119
- """Return true if the path refers to an existing directory.
120
-
121
- Parameters
122
- ----------
123
- path : str
124
- Path of directory to check.
125
- """
126
-
127
- return os.path.isdir(path)
128
-
129
- def makedirs(self, path, mode=0o700, exist_ok=False):
130
- """Create a directory.
131
-
132
- If intermediate directories do not exist, they will be created.
133
-
134
- Parameters
135
- ----------
136
- path : str
137
- Path of directory to create.
138
- mode : int
139
- Permissions (posix-style) for the newly-created directory.
140
- exist_ok : bool
141
- If False, raise an OSError if the target directory already exists.
142
- """
143
-
144
- return os.makedirs(path, mode, exist_ok)
145
-
146
- def abspath(self, path):
147
- """Return the absolute path.
148
-
149
- Parameters
150
- ----------
151
- path : str
152
- Path for which the absolute path will be returned.
153
- """
154
- return os.path.abspath(path)
155
-
156
- @property
157
- def script_dir(self):
158
- return self._script_dir
159
-
160
- @script_dir.setter
161
- def script_dir(self, value):
162
- if value is not None:
163
- value = self.abspath(value)
164
- self._script_dir = value
@@ -1,110 +0,0 @@
1
- import logging
2
- import paramiko
3
- import socket
4
-
5
- from parsl.errors import OptionalModuleMissing
6
- from parsl.channels.ssh.ssh import SSHChannel
7
-
8
- try:
9
- from oauth_ssh.ssh_service import SSHService
10
- from oauth_ssh.oauth_ssh_token import find_access_token
11
- _oauth_ssh_enabled = True
12
- except (ImportError, NameError):
13
- _oauth_ssh_enabled = False
14
-
15
-
16
- logger = logging.getLogger(__name__)
17
-
18
-
19
- class OAuthSSHChannel(SSHChannel):
20
- """SSH persistent channel. This enables remote execution on sites
21
- accessible via ssh. This channel uses Globus based OAuth tokens for authentication.
22
- """
23
-
24
- def __init__(self, hostname, username=None, script_dir=None, envs=None, port=22):
25
- ''' Initialize a persistent connection to the remote system.
26
- We should know at this point whether ssh connectivity is possible
27
-
28
- Args:
29
- - hostname (String) : Hostname
30
-
31
- KWargs:
32
- - username (string) : Username on remote system
33
- - script_dir (string) : Full path to a script dir where
34
- generated scripts could be sent to.
35
- - envs (dict) : A dictionary of env variables to be set when executing commands
36
- - port (int) : Port at which the SSHService is running
37
-
38
- Raises:
39
- '''
40
- if not _oauth_ssh_enabled:
41
- raise OptionalModuleMissing(['oauth_ssh'],
42
- "OauthSSHChannel requires oauth_ssh module and config.")
43
-
44
- self.hostname = hostname
45
- self.username = username
46
- self.script_dir = script_dir
47
- self.port = port
48
- self.envs = {}
49
- if envs is not None:
50
- self.envs = envs
51
-
52
- try:
53
- access_token = find_access_token(hostname)
54
- except Exception:
55
- logger.exception("Failed to find the access token for {}".format(hostname))
56
- raise
57
-
58
- try:
59
- self.service = SSHService(hostname, port)
60
- self.transport = self.service.login(access_token, username)
61
-
62
- except Exception:
63
- logger.exception("Caught an exception in the OAuth authentication step with {}".format(hostname))
64
- raise
65
-
66
- self.sftp_client = paramiko.SFTPClient.from_transport(self.transport)
67
-
68
- def execute_wait(self, cmd, walltime=60, envs={}):
69
- ''' Synchronously execute a commandline string on the shell.
70
-
71
- This command does *NOT* honor walltime currently.
72
-
73
- Args:
74
- - cmd (string) : Commandline string to execute
75
-
76
- Kwargs:
77
- - walltime (int) : walltime in seconds
78
- - envs (dict) : Dictionary of env variables
79
-
80
- Returns:
81
- - retcode : Return code from the execution, -1 on fail
82
- - stdout : stdout string
83
- - stderr : stderr string
84
-
85
- Raises:
86
- None.
87
- '''
88
-
89
- session = self.transport.open_session()
90
- session.setblocking(0)
91
-
92
- nbytes = 1024
93
- session.exec_command(self.prepend_envs(cmd, envs))
94
- session.settimeout(walltime)
95
-
96
- try:
97
- # Wait until command is executed
98
- exit_status = session.recv_exit_status()
99
-
100
- stdout = session.recv(nbytes).decode('utf-8')
101
- stderr = session.recv_stderr(nbytes).decode('utf-8')
102
-
103
- except socket.timeout:
104
- logger.exception("Command failed to execute without timeout limit on {}".format(self))
105
- raise
106
-
107
- return exit_status, stdout, stderr
108
-
109
- def close(self):
110
- return self.transport.close()
parsl/channels/ssh/ssh.py DELETED
@@ -1,276 +0,0 @@
1
- import errno
2
- import logging
3
- import os
4
-
5
- import paramiko
6
- from parsl.channels.base import Channel
7
- from parsl.channels.errors import BadHostKeyException, AuthException, SSHException, BadScriptPath, BadPermsScriptPath, FileCopyException
8
- from parsl.utils import RepresentationMixin
9
-
10
- logger = logging.getLogger(__name__)
11
-
12
-
13
- class NoAuthSSHClient(paramiko.SSHClient):
14
- def _auth(self, username, *args):
15
- self._transport.auth_none(username)
16
- return
17
-
18
-
19
- class SSHChannel(Channel, RepresentationMixin):
20
- ''' SSH persistent channel. This enables remote execution on sites
21
- accessible via ssh. It is assumed that the user has setup host keys
22
- so as to ssh to the remote host. Which goes to say that the following
23
- test on the commandline should work:
24
-
25
- >>> ssh <username>@<hostname>
26
-
27
- '''
28
-
29
- def __init__(self, hostname, username=None, password=None, script_dir=None, envs=None,
30
- gssapi_auth=False, skip_auth=False, port=22, key_filename=None, host_keys_filename=None):
31
- ''' Initialize a persistent connection to the remote system.
32
- We should know at this point whether ssh connectivity is possible
33
-
34
- Args:
35
- - hostname (String) : Hostname
36
-
37
- KWargs:
38
- - username (string) : Username on remote system
39
- - password (string) : Password for remote system
40
- - port : The port designated for the ssh connection. Default is 22.
41
- - script_dir (string) : Full path to a script dir where
42
- generated scripts could be sent to.
43
- - envs (dict) : A dictionary of environment variables to be set when executing commands
44
- - key_filename (string or list): the filename, or list of filenames, of optional private key(s)
45
-
46
- Raises:
47
- '''
48
-
49
- self.hostname = hostname
50
- self.username = username
51
- self.password = password
52
- self.port = port
53
- self.script_dir = script_dir
54
- self.skip_auth = skip_auth
55
- self.gssapi_auth = gssapi_auth
56
- self.key_filename = key_filename
57
- self.host_keys_filename = host_keys_filename
58
-
59
- if self.skip_auth:
60
- self.ssh_client = NoAuthSSHClient()
61
- else:
62
- self.ssh_client = paramiko.SSHClient()
63
- self.ssh_client.load_system_host_keys(filename=host_keys_filename)
64
- self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
65
- self.sftp_client = None
66
-
67
- self.envs = {}
68
- if envs is not None:
69
- self.envs = envs
70
-
71
- def _is_connected(self):
72
- transport = self.ssh_client.get_transport() if self.ssh_client else None
73
- return transport and transport.is_active()
74
-
75
- def _connect(self):
76
- if not self._is_connected():
77
- logger.debug(f"connecting to {self.hostname}:{self.port}")
78
- try:
79
- self.ssh_client.connect(
80
- self.hostname,
81
- username=self.username,
82
- password=self.password,
83
- port=self.port,
84
- allow_agent=True,
85
- gss_auth=self.gssapi_auth,
86
- gss_kex=self.gssapi_auth,
87
- key_filename=self.key_filename
88
- )
89
- transport = self.ssh_client.get_transport()
90
- self.sftp_client = paramiko.SFTPClient.from_transport(transport)
91
-
92
- except paramiko.BadHostKeyException as e:
93
- raise BadHostKeyException(e, self.hostname)
94
-
95
- except paramiko.AuthenticationException as e:
96
- raise AuthException(e, self.hostname)
97
-
98
- except paramiko.SSHException as e:
99
- raise SSHException(e, self.hostname)
100
-
101
- except Exception as e:
102
- raise SSHException(e, self.hostname)
103
-
104
- def _valid_sftp_client(self):
105
- self._connect()
106
- return self.sftp_client
107
-
108
- def _valid_ssh_client(self):
109
- self._connect()
110
- return self.ssh_client
111
-
112
- def prepend_envs(self, cmd, env={}):
113
- env.update(self.envs)
114
-
115
- if len(env.keys()) > 0:
116
- env_vars = ' '.join(['{}={}'.format(key, value) for key, value in env.items()])
117
- return 'env {0} {1}'.format(env_vars, cmd)
118
- return cmd
119
-
120
- def execute_wait(self, cmd, walltime=2, envs={}):
121
- ''' Synchronously execute a commandline string on the shell.
122
-
123
- Args:
124
- - cmd (string) : Commandline string to execute
125
- - walltime (int) : walltime in seconds
126
-
127
- Kwargs:
128
- - envs (dict) : Dictionary of env variables
129
-
130
- Returns:
131
- - retcode : Return code from the execution, -1 on fail
132
- - stdout : stdout string
133
- - stderr : stderr string
134
-
135
- Raises:
136
- None.
137
- '''
138
-
139
- # Execute the command
140
- stdin, stdout, stderr = self._valid_ssh_client().exec_command(
141
- self.prepend_envs(cmd, envs), bufsize=-1, timeout=walltime
142
- )
143
- # Block on exit status from the command
144
- exit_status = stdout.channel.recv_exit_status()
145
- return exit_status, stdout.read().decode("utf-8"), stderr.read().decode("utf-8")
146
-
147
- def push_file(self, local_source, remote_dir):
148
- ''' Transport a local file to a directory on a remote machine
149
-
150
- Args:
151
- - local_source (string): Path
152
- - remote_dir (string): Remote path
153
-
154
- Returns:
155
- - str: Path to copied file on remote machine
156
-
157
- Raises:
158
- - BadScriptPath : if script path on the remote side is bad
159
- - BadPermsScriptPath : You do not have perms to make the channel script dir
160
- - FileCopyException : FileCopy failed.
161
-
162
- '''
163
- remote_dest = os.path.join(remote_dir, os.path.basename(local_source))
164
-
165
- try:
166
- self.makedirs(remote_dir, exist_ok=True)
167
- except IOError as e:
168
- logger.exception("Pushing {0} to {1} failed".format(local_source, remote_dir))
169
- if e.errno == 2:
170
- raise BadScriptPath(e, self.hostname)
171
- elif e.errno == 13:
172
- raise BadPermsScriptPath(e, self.hostname)
173
- else:
174
- logger.exception("File push failed due to SFTP client failure")
175
- raise FileCopyException(e, self.hostname)
176
- try:
177
- self._valid_sftp_client().put(local_source, remote_dest, confirm=True)
178
- # Set perm because some systems require the script to be executable
179
- self._valid_sftp_client().chmod(remote_dest, 0o700)
180
- except Exception as e:
181
- logger.exception("File push from local source {} to remote destination {} failed".format(
182
- local_source, remote_dest))
183
- raise FileCopyException(e, self.hostname)
184
-
185
- return remote_dest
186
-
187
- def pull_file(self, remote_source, local_dir):
188
- ''' Transport file on the remote side to a local directory
189
-
190
- Args:
191
- - remote_source (string): remote_source
192
- - local_dir (string): Local directory to copy to
193
-
194
-
195
- Returns:
196
- - str: Local path to file
197
-
198
- Raises:
199
- - FileExists : Name collision at local directory.
200
- - FileCopyException : FileCopy failed.
201
- '''
202
-
203
- local_dest = os.path.join(local_dir, os.path.basename(remote_source))
204
-
205
- try:
206
- os.makedirs(local_dir)
207
- except OSError as e:
208
- if e.errno != errno.EEXIST:
209
- logger.exception("Failed to create local_dir: {0}".format(local_dir))
210
- raise BadScriptPath(e, self.hostname)
211
-
212
- try:
213
- self._valid_sftp_client().get(remote_source, local_dest)
214
- except Exception as e:
215
- logger.exception("File pull failed")
216
- raise FileCopyException(e, self.hostname)
217
-
218
- return local_dest
219
-
220
- def close(self):
221
- if self._is_connected():
222
- return self.ssh_client.close()
223
-
224
- def isdir(self, path):
225
- """Return true if the path refers to an existing directory.
226
-
227
- Parameters
228
- ----------
229
- path : str
230
- Path of directory on the remote side to check.
231
- """
232
- result = True
233
- try:
234
- self._valid_sftp_client().lstat(path)
235
- except FileNotFoundError:
236
- result = False
237
-
238
- return result
239
-
240
- def makedirs(self, path, mode=0o700, exist_ok=False):
241
- """Create a directory on the remote side.
242
-
243
- If intermediate directories do not exist, they will be created.
244
-
245
- Parameters
246
- ----------
247
- path : str
248
- Path of directory on the remote side to create.
249
- mode : int
250
- Permissions (posix-style) for the newly-created directory.
251
- exist_ok : bool
252
- If False, raise an OSError if the target directory already exists.
253
- """
254
- if exist_ok is False and self.isdir(path):
255
- raise OSError('Target directory {} already exists'.format(path))
256
-
257
- self.execute_wait('mkdir -p {}'.format(path))
258
- self._valid_sftp_client().chmod(path, mode)
259
-
260
- def abspath(self, path):
261
- """Return the absolute path on the remote side.
262
-
263
- Parameters
264
- ----------
265
- path : str
266
- Path for which the absolute path will be returned.
267
- """
268
- return self._valid_sftp_client().normalize(path)
269
-
270
- @property
271
- def script_dir(self):
272
- return self._script_dir
273
-
274
- @script_dir.setter
275
- def script_dir(self, value):
276
- self._script_dir = value
File without changes