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.
- parsl/__init__.py +9 -10
- parsl/addresses.py +26 -6
- parsl/app/app.py +7 -8
- parsl/app/bash.py +15 -8
- parsl/app/errors.py +10 -13
- parsl/app/futures.py +8 -10
- parsl/app/python.py +2 -1
- parsl/benchmark/perf.py +2 -1
- parsl/concurrent/__init__.py +2 -2
- parsl/config.py +53 -10
- parsl/configs/ASPIRE1.py +6 -5
- parsl/configs/Azure.py +9 -8
- parsl/configs/bridges.py +6 -4
- parsl/configs/cc_in2p3.py +3 -3
- parsl/configs/ec2.py +3 -1
- parsl/configs/expanse.py +4 -3
- parsl/configs/frontera.py +3 -4
- parsl/configs/htex_local.py +3 -4
- parsl/configs/illinoiscluster.py +3 -1
- parsl/configs/improv.py +34 -0
- parsl/configs/kubernetes.py +4 -3
- parsl/configs/local_threads.py +5 -1
- parsl/configs/midway.py +5 -3
- parsl/configs/osg.py +4 -2
- parsl/configs/polaris.py +4 -2
- parsl/configs/stampede2.py +6 -5
- parsl/configs/summit.py +3 -3
- parsl/configs/toss3_llnl.py +4 -3
- parsl/configs/vineex_local.py +6 -4
- parsl/configs/wqex_local.py +5 -3
- parsl/curvezmq.py +4 -0
- parsl/data_provider/data_manager.py +4 -3
- parsl/data_provider/file_noop.py +1 -2
- parsl/data_provider/files.py +3 -3
- parsl/data_provider/ftp.py +1 -3
- parsl/data_provider/globus.py +7 -6
- parsl/data_provider/http.py +2 -2
- parsl/data_provider/rsync.py +1 -1
- parsl/data_provider/staging.py +2 -2
- parsl/data_provider/zip.py +135 -0
- parsl/dataflow/dependency_resolvers.py +115 -0
- parsl/dataflow/dflow.py +259 -223
- parsl/dataflow/errors.py +3 -5
- parsl/dataflow/futures.py +27 -14
- parsl/dataflow/memoization.py +5 -5
- parsl/dataflow/rundirs.py +5 -6
- parsl/dataflow/taskrecord.py +4 -5
- parsl/executors/__init__.py +4 -2
- parsl/executors/base.py +45 -15
- parsl/executors/errors.py +13 -0
- parsl/executors/execute_task.py +37 -0
- parsl/executors/flux/execute_parsl_task.py +3 -3
- parsl/executors/flux/executor.py +18 -19
- parsl/executors/flux/flux_instance_manager.py +26 -27
- parsl/executors/high_throughput/errors.py +43 -3
- parsl/executors/high_throughput/executor.py +307 -285
- parsl/executors/high_throughput/interchange.py +137 -168
- parsl/executors/high_throughput/manager_record.py +4 -0
- parsl/executors/high_throughput/manager_selector.py +55 -0
- parsl/executors/high_throughput/monitoring_info.py +2 -1
- parsl/executors/high_throughput/mpi_executor.py +113 -0
- parsl/executors/high_throughput/mpi_prefix_composer.py +10 -11
- parsl/executors/high_throughput/mpi_resource_management.py +6 -17
- parsl/executors/high_throughput/probe.py +9 -7
- parsl/executors/high_throughput/process_worker_pool.py +77 -75
- parsl/executors/high_throughput/zmq_pipes.py +81 -23
- parsl/executors/radical/executor.py +130 -79
- parsl/executors/radical/rpex_resources.py +17 -15
- parsl/executors/radical/rpex_worker.py +4 -3
- parsl/executors/status_handling.py +157 -51
- parsl/executors/taskvine/__init__.py +1 -1
- parsl/executors/taskvine/errors.py +1 -1
- parsl/executors/taskvine/exec_parsl_function.py +2 -2
- parsl/executors/taskvine/executor.py +38 -55
- parsl/executors/taskvine/factory.py +1 -1
- parsl/executors/taskvine/factory_config.py +1 -1
- parsl/executors/taskvine/manager.py +17 -13
- parsl/executors/taskvine/manager_config.py +7 -2
- parsl/executors/threads.py +6 -6
- parsl/executors/workqueue/errors.py +1 -1
- parsl/executors/workqueue/exec_parsl_function.py +6 -5
- parsl/executors/workqueue/executor.py +64 -63
- parsl/executors/workqueue/parsl_coprocess.py +1 -1
- parsl/jobs/error_handlers.py +2 -2
- parsl/jobs/job_status_poller.py +28 -112
- parsl/jobs/states.py +7 -2
- parsl/jobs/strategy.py +43 -31
- parsl/launchers/__init__.py +12 -3
- parsl/launchers/errors.py +1 -1
- parsl/launchers/launchers.py +0 -6
- parsl/log_utils.py +1 -2
- parsl/monitoring/db_manager.py +55 -93
- parsl/monitoring/errors.py +6 -0
- parsl/monitoring/monitoring.py +85 -311
- parsl/monitoring/queries/pandas.py +1 -2
- parsl/monitoring/radios/base.py +13 -0
- parsl/monitoring/radios/filesystem.py +52 -0
- parsl/monitoring/radios/htex.py +57 -0
- parsl/monitoring/radios/multiprocessing.py +17 -0
- parsl/monitoring/radios/udp.py +56 -0
- parsl/monitoring/radios/zmq.py +17 -0
- parsl/monitoring/remote.py +33 -37
- parsl/monitoring/router.py +212 -0
- parsl/monitoring/types.py +5 -6
- parsl/monitoring/visualization/app.py +4 -2
- parsl/monitoring/visualization/models.py +0 -1
- parsl/monitoring/visualization/plots/default/workflow_plots.py +8 -4
- parsl/monitoring/visualization/plots/default/workflow_resource_plots.py +1 -0
- parsl/monitoring/visualization/utils.py +0 -1
- parsl/monitoring/visualization/views.py +16 -9
- parsl/multiprocessing.py +0 -1
- parsl/process_loggers.py +1 -2
- parsl/providers/__init__.py +8 -17
- parsl/providers/aws/aws.py +2 -3
- parsl/providers/azure/azure.py +4 -5
- parsl/providers/base.py +2 -18
- parsl/providers/cluster_provider.py +3 -9
- parsl/providers/condor/condor.py +7 -17
- parsl/providers/errors.py +2 -2
- parsl/providers/googlecloud/googlecloud.py +2 -1
- parsl/providers/grid_engine/grid_engine.py +5 -14
- parsl/providers/kubernetes/kube.py +80 -40
- parsl/providers/local/local.py +13 -26
- parsl/providers/lsf/lsf.py +5 -23
- parsl/providers/pbspro/pbspro.py +5 -17
- parsl/providers/slurm/slurm.py +81 -39
- parsl/providers/torque/torque.py +3 -14
- parsl/serialize/__init__.py +8 -3
- parsl/serialize/base.py +1 -2
- parsl/serialize/concretes.py +5 -4
- parsl/serialize/facade.py +3 -3
- parsl/serialize/proxystore.py +3 -2
- parsl/tests/__init__.py +1 -1
- parsl/tests/configs/azure_single_node.py +4 -5
- parsl/tests/configs/bridges.py +3 -2
- parsl/tests/configs/cc_in2p3.py +1 -3
- parsl/tests/configs/comet.py +2 -1
- parsl/tests/configs/ec2_single_node.py +1 -2
- parsl/tests/configs/ec2_spot.py +1 -2
- parsl/tests/configs/flux_local.py +11 -0
- parsl/tests/configs/frontera.py +2 -3
- parsl/tests/configs/htex_local.py +3 -5
- parsl/tests/configs/htex_local_alternate.py +11 -15
- parsl/tests/configs/htex_local_intask_staging.py +5 -9
- parsl/tests/configs/htex_local_rsync_staging.py +4 -8
- parsl/tests/configs/local_radical.py +1 -3
- parsl/tests/configs/local_radical_mpi.py +2 -2
- parsl/tests/configs/local_threads_checkpoint_periodic.py +8 -10
- parsl/tests/configs/local_threads_monitoring.py +0 -1
- parsl/tests/configs/midway.py +2 -2
- parsl/tests/configs/nscc_singapore.py +3 -3
- parsl/tests/configs/osg_htex.py +1 -1
- parsl/tests/configs/petrelkube.py +3 -2
- parsl/tests/configs/slurm_local.py +24 -0
- parsl/tests/configs/summit.py +1 -0
- parsl/tests/configs/taskvine_ex.py +4 -7
- parsl/tests/configs/user_opts.py +0 -7
- parsl/tests/configs/workqueue_ex.py +4 -6
- parsl/tests/conftest.py +27 -13
- parsl/tests/integration/test_stress/test_python_simple.py +3 -4
- parsl/tests/integration/test_stress/test_python_threads.py +3 -5
- parsl/tests/manual_tests/htex_local.py +4 -6
- parsl/tests/manual_tests/test_basic.py +1 -0
- parsl/tests/manual_tests/test_log_filter.py +3 -1
- parsl/tests/manual_tests/test_memory_limits.py +6 -8
- parsl/tests/manual_tests/test_regression_220.py +2 -1
- parsl/tests/manual_tests/test_udp_simple.py +4 -4
- parsl/tests/manual_tests/test_worker_count.py +3 -2
- parsl/tests/scaling_tests/htex_local.py +2 -4
- parsl/tests/scaling_tests/test_scale.py +0 -9
- parsl/tests/scaling_tests/vineex_condor.py +1 -2
- parsl/tests/scaling_tests/vineex_local.py +1 -2
- parsl/tests/site_tests/site_config_selector.py +1 -6
- parsl/tests/site_tests/test_provider.py +4 -2
- parsl/tests/site_tests/test_site.py +2 -0
- parsl/tests/sites/test_affinity.py +7 -7
- parsl/tests/sites/test_dynamic_executor.py +3 -4
- parsl/tests/sites/test_ec2.py +3 -2
- parsl/tests/sites/test_worker_info.py +4 -5
- parsl/tests/test_aalst_patterns.py +0 -1
- parsl/tests/test_bash_apps/test_apptimeout.py +2 -2
- parsl/tests/test_bash_apps/test_basic.py +10 -4
- parsl/tests/test_bash_apps/test_error_codes.py +5 -7
- parsl/tests/test_bash_apps/test_inputs_default.py +25 -0
- parsl/tests/test_bash_apps/test_kwarg_storage.py +1 -1
- parsl/tests/test_bash_apps/test_memoize.py +2 -8
- parsl/tests/test_bash_apps/test_memoize_ignore_args.py +9 -14
- parsl/tests/test_bash_apps/test_memoize_ignore_args_regr.py +9 -14
- parsl/tests/test_bash_apps/test_multiline.py +1 -1
- parsl/tests/test_bash_apps/test_pipeline.py +1 -1
- parsl/tests/test_bash_apps/test_std_uri.py +123 -0
- parsl/tests/test_bash_apps/test_stdout.py +33 -8
- parsl/tests/test_callables.py +2 -2
- parsl/tests/test_checkpointing/test_periodic.py +21 -39
- parsl/tests/test_checkpointing/test_python_checkpoint_1.py +1 -0
- parsl/tests/test_checkpointing/test_python_checkpoint_2.py +2 -2
- parsl/tests/test_checkpointing/test_python_checkpoint_3.py +0 -1
- parsl/tests/test_checkpointing/test_regression_239.py +1 -1
- parsl/tests/test_checkpointing/test_task_exit.py +2 -3
- parsl/tests/test_docs/test_from_slides.py +5 -2
- parsl/tests/test_docs/test_kwargs.py +4 -1
- parsl/tests/test_docs/test_tutorial_1.py +1 -2
- parsl/tests/test_docs/test_workflow1.py +2 -2
- parsl/tests/test_docs/test_workflow2.py +0 -1
- parsl/tests/test_error_handling/test_rand_fail.py +2 -2
- parsl/tests/test_error_handling/test_resource_spec.py +10 -12
- parsl/tests/test_error_handling/test_retries.py +6 -16
- parsl/tests/test_error_handling/test_retry_handler.py +1 -0
- parsl/tests/test_error_handling/test_retry_handler_failure.py +2 -1
- parsl/tests/test_error_handling/test_serialization_fail.py +1 -1
- parsl/tests/test_error_handling/test_wrap_with_logs.py +1 -0
- parsl/tests/test_execute_task.py +29 -0
- parsl/tests/test_flux.py +1 -1
- parsl/tests/test_htex/test_basic.py +2 -3
- parsl/tests/test_htex/test_block_manager_selector_unit.py +20 -0
- parsl/tests/test_htex/test_command_client_timeout.py +66 -0
- parsl/tests/test_htex/test_connected_blocks.py +3 -2
- parsl/tests/test_htex/test_cpu_affinity_explicit.py +6 -10
- parsl/tests/test_htex/test_disconnected_blocks.py +6 -5
- parsl/tests/test_htex/test_disconnected_blocks_failing_provider.py +71 -0
- parsl/tests/test_htex/test_drain.py +11 -10
- parsl/tests/test_htex/test_htex.py +51 -25
- parsl/tests/test_htex/test_manager_failure.py +0 -1
- parsl/tests/test_htex/test_manager_selector_by_block.py +51 -0
- parsl/tests/test_htex/test_managers_command.py +36 -0
- parsl/tests/test_htex/test_missing_worker.py +2 -12
- parsl/tests/test_htex/test_multiple_disconnected_blocks.py +9 -9
- parsl/tests/test_htex/test_resource_spec_validation.py +45 -0
- parsl/tests/test_htex/test_zmq_binding.py +29 -8
- parsl/tests/test_monitoring/test_app_names.py +5 -5
- parsl/tests/test_monitoring/test_basic.py +73 -25
- parsl/tests/test_monitoring/test_db_locks.py +6 -4
- parsl/tests/test_monitoring/test_fuzz_zmq.py +19 -8
- parsl/tests/test_monitoring/test_htex_init_blocks_vs_monitoring.py +80 -0
- parsl/tests/test_monitoring/test_incomplete_futures.py +5 -4
- parsl/tests/test_monitoring/test_memoization_representation.py +4 -2
- parsl/tests/test_monitoring/test_stdouterr.py +134 -0
- parsl/tests/test_monitoring/test_viz_colouring.py +1 -0
- parsl/tests/test_mpi_apps/test_bad_mpi_config.py +33 -26
- parsl/tests/test_mpi_apps/test_mpi_mode_enabled.py +28 -11
- parsl/tests/test_mpi_apps/test_mpi_prefix.py +4 -4
- parsl/tests/test_mpi_apps/test_mpi_scheduler.py +7 -2
- parsl/tests/test_mpi_apps/test_mpiex.py +64 -0
- parsl/tests/test_mpi_apps/test_resource_spec.py +42 -49
- parsl/tests/test_providers/test_kubernetes_provider.py +102 -0
- parsl/tests/test_providers/test_local_provider.py +3 -132
- parsl/tests/test_providers/test_pbspro_template.py +2 -3
- parsl/tests/test_providers/test_slurm_template.py +2 -3
- parsl/tests/test_providers/test_submiterror_deprecation.py +2 -1
- parsl/tests/test_python_apps/test_context_manager.py +128 -0
- parsl/tests/test_python_apps/test_dep_standard_futures.py +2 -1
- parsl/tests/test_python_apps/test_dependencies_deep.py +59 -0
- parsl/tests/test_python_apps/test_fail.py +0 -25
- parsl/tests/test_python_apps/test_futures.py +2 -1
- parsl/tests/test_python_apps/test_inputs_default.py +22 -0
- parsl/tests/test_python_apps/test_join.py +0 -1
- parsl/tests/test_python_apps/test_lifted.py +11 -7
- parsl/tests/test_python_apps/test_memoize_bad_id_for_memo.py +1 -0
- parsl/tests/test_python_apps/test_outputs.py +1 -1
- parsl/tests/test_python_apps/test_pluggable_future_resolution.py +161 -0
- parsl/tests/test_radical/test_mpi_funcs.py +1 -2
- parsl/tests/test_regression/test_1480.py +2 -1
- parsl/tests/test_regression/test_1653.py +2 -1
- parsl/tests/test_regression/test_226.py +1 -0
- parsl/tests/test_regression/test_2652.py +1 -0
- parsl/tests/test_regression/test_69a.py +0 -1
- parsl/tests/test_regression/test_854.py +4 -2
- parsl/tests/test_regression/test_97_parallelism_0.py +1 -2
- parsl/tests/test_regression/test_98.py +0 -1
- parsl/tests/test_scaling/test_block_error_handler.py +9 -4
- parsl/tests/test_scaling/test_regression_1621.py +11 -15
- parsl/tests/test_scaling/test_regression_3568_scaledown_vs_MISSING.py +84 -0
- parsl/tests/test_scaling/test_regression_3696_oscillation.py +103 -0
- parsl/tests/test_scaling/test_scale_down.py +2 -5
- parsl/tests/test_scaling/test_scale_down_htex_auto_scale.py +5 -8
- parsl/tests/test_scaling/test_scale_down_htex_unregistered.py +71 -0
- parsl/tests/test_scaling/test_shutdown_scalein.py +73 -0
- parsl/tests/test_scaling/test_worker_interchange_bad_messages_3262.py +90 -0
- parsl/tests/test_serialization/test_2555_caching_deserializer.py +1 -1
- parsl/tests/test_serialization/test_3495_deserialize_managerlost.py +47 -0
- parsl/tests/test_serialization/test_basic.py +2 -1
- parsl/tests/test_serialization/test_htex_code_cache.py +3 -4
- parsl/tests/test_serialization/test_pack_resource_spec.py +2 -1
- parsl/tests/test_serialization/test_proxystore_configured.py +10 -6
- parsl/tests/test_serialization/test_proxystore_impl.py +5 -3
- parsl/tests/test_shutdown/test_kill_monitoring.py +64 -0
- parsl/tests/test_staging/staging_provider.py +2 -2
- parsl/tests/test_staging/test_1316.py +3 -4
- parsl/tests/test_staging/test_docs_1.py +2 -1
- parsl/tests/test_staging/test_docs_2.py +2 -1
- parsl/tests/test_staging/test_elaborate_noop_file.py +2 -3
- parsl/tests/{test_data → test_staging}/test_file.py +6 -6
- parsl/tests/{test_data → test_staging}/test_output_chain_filenames.py +3 -0
- parsl/tests/test_staging/test_staging_ftp.py +1 -0
- parsl/tests/test_staging/test_staging_https.py +5 -2
- parsl/tests/test_staging/test_staging_stdout.py +64 -0
- parsl/tests/test_staging/test_zip_in.py +39 -0
- parsl/tests/test_staging/test_zip_out.py +110 -0
- parsl/tests/test_staging/test_zip_to_zip.py +41 -0
- parsl/tests/test_summary.py +2 -2
- parsl/tests/test_thread_parallelism.py +0 -1
- parsl/tests/test_threads/test_configs.py +1 -2
- parsl/tests/test_threads/test_lazy_errors.py +2 -2
- parsl/tests/test_utils/test_execute_wait.py +35 -0
- parsl/tests/test_utils/test_sanitize_dns.py +76 -0
- parsl/tests/unit/test_address.py +20 -0
- parsl/tests/unit/test_file.py +99 -0
- parsl/tests/unit/test_usage_tracking.py +66 -0
- parsl/usage_tracking/api.py +65 -0
- parsl/usage_tracking/levels.py +6 -0
- parsl/usage_tracking/usage.py +104 -62
- parsl/utils.py +137 -4
- parsl/version.py +1 -1
- {parsl-2024.3.18.data → parsl-2025.1.13.data}/scripts/exec_parsl_function.py +6 -5
- parsl-2025.1.13.data/scripts/interchange.py +649 -0
- {parsl-2024.3.18.data → parsl-2025.1.13.data}/scripts/process_worker_pool.py +77 -75
- parsl-2025.1.13.dist-info/METADATA +96 -0
- parsl-2025.1.13.dist-info/RECORD +462 -0
- {parsl-2024.3.18.dist-info → parsl-2025.1.13.dist-info}/WHEEL +1 -1
- parsl/channels/__init__.py +0 -7
- parsl/channels/base.py +0 -141
- parsl/channels/errors.py +0 -113
- parsl/channels/local/local.py +0 -164
- parsl/channels/oauth_ssh/oauth_ssh.py +0 -110
- parsl/channels/ssh/ssh.py +0 -276
- parsl/channels/ssh_il/__init__.py +0 -0
- parsl/channels/ssh_il/ssh_il.py +0 -74
- parsl/configs/ad_hoc.py +0 -35
- parsl/executors/radical/rpex_master.py +0 -42
- parsl/monitoring/radios.py +0 -175
- parsl/providers/ad_hoc/__init__.py +0 -0
- parsl/providers/ad_hoc/ad_hoc.py +0 -248
- parsl/providers/cobalt/__init__.py +0 -0
- parsl/providers/cobalt/cobalt.py +0 -236
- parsl/providers/cobalt/template.py +0 -17
- parsl/tests/configs/ad_hoc_cluster_htex.py +0 -35
- parsl/tests/configs/cooley_htex.py +0 -37
- parsl/tests/configs/htex_ad_hoc_cluster.py +0 -28
- parsl/tests/configs/local_adhoc.py +0 -18
- parsl/tests/configs/swan_htex.py +0 -43
- parsl/tests/configs/theta.py +0 -37
- parsl/tests/integration/test_channels/__init__.py +0 -0
- parsl/tests/integration/test_channels/test_channels.py +0 -17
- parsl/tests/integration/test_channels/test_local_channel.py +0 -42
- parsl/tests/integration/test_channels/test_scp_1.py +0 -45
- parsl/tests/integration/test_channels/test_ssh_1.py +0 -40
- parsl/tests/integration/test_channels/test_ssh_errors.py +0 -46
- parsl/tests/integration/test_channels/test_ssh_file_transport.py +0 -41
- parsl/tests/integration/test_channels/test_ssh_interactive.py +0 -24
- parsl/tests/manual_tests/test_ad_hoc_htex.py +0 -48
- parsl/tests/manual_tests/test_fan_in_out_htex_remote.py +0 -88
- parsl/tests/manual_tests/test_oauth_ssh.py +0 -13
- parsl/tests/sites/test_local_adhoc.py +0 -61
- parsl/tests/test_channels/__init__.py +0 -0
- parsl/tests/test_channels/test_large_output.py +0 -22
- parsl/tests/test_data/__init__.py +0 -0
- parsl/tests/test_mpi_apps/test_mpi_mode_disabled.py +0 -51
- parsl/tests/test_providers/test_cobalt_deprecation_warning.py +0 -16
- parsl-2024.3.18.dist-info/METADATA +0 -98
- parsl-2024.3.18.dist-info/RECORD +0 -449
- parsl/{channels/local → monitoring/radios}/__init__.py +0 -0
- parsl/{channels/oauth_ssh → tests/test_shutdown}/__init__.py +0 -0
- parsl/tests/{test_data → test_staging}/test_file_apps.py +0 -0
- parsl/tests/{test_data → test_staging}/test_file_staging.py +0 -0
- parsl/{channels/ssh → tests/unit}/__init__.py +0 -0
- {parsl-2024.3.18.data → parsl-2025.1.13.data}/scripts/parsl_coprocess.py +1 -1
- {parsl-2024.3.18.dist-info → parsl-2025.1.13.dist-info}/LICENSE +0 -0
- {parsl-2024.3.18.dist-info → parsl-2025.1.13.dist-info}/entry_points.txt +0 -0
- {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
|
-
|
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,
|
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
|
-
|
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
|
-
|
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,41 +1,48 @@
|
|
1
1
|
import pytest
|
2
2
|
|
3
3
|
from parsl import Config
|
4
|
-
from parsl.executors import
|
5
|
-
from parsl.launchers import
|
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
|
11
|
-
"""
|
10
|
+
def test_bad_launcher():
|
11
|
+
"""TypeError if a launcher other than SimpleLauncher is supplied"""
|
12
12
|
|
13
|
-
for launcher in [SrunLauncher(),
|
14
|
-
with pytest.raises(
|
13
|
+
for launcher in [SrunLauncher(), AprunLauncher()]:
|
14
|
+
with pytest.raises(TypeError):
|
15
15
|
Config(executors=[
|
16
|
-
|
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
|
25
|
-
"""
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
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
|
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
|
-
|
6
|
+
compose_all,
|
7
7
|
compose_aprun_launch_cmd,
|
8
8
|
compose_mpiexec_launch_cmd,
|
9
|
-
|
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
|
-
|
6
|
-
from parsl.executors.high_throughput.mpi_resource_management import
|
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'
|