parsl 2024.3.11__py3-none-any.whl → 2025.1.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- parsl/__init__.py +9 -10
- parsl/addresses.py +29 -7
- 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 +57 -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 +262 -224
- 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 +316 -282
- parsl/executors/high_throughput/interchange.py +158 -167
- parsl/executors/high_throughput/manager_record.py +5 -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 +115 -77
- 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 +41 -57
- parsl/executors/taskvine/factory.py +1 -1
- parsl/executors/taskvine/factory_config.py +1 -1
- parsl/executors/taskvine/manager.py +18 -13
- parsl/executors/taskvine/manager_config.py +9 -5
- 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 +30 -113
- 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 +6 -12
- parsl/log_utils.py +9 -6
- parsl/monitoring/db_manager.py +59 -95
- parsl/monitoring/errors.py +6 -0
- parsl/monitoring/monitoring.py +87 -356
- 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 +11 -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 -8
- 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 +4 -12
- 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 +2 -8
- 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 +79 -0
- 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 +86 -0
- 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 +6 -18
- 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 +139 -6
- parsl/version.py +1 -1
- {parsl-2024.3.11.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.11.data → parsl-2025.1.13.data}/scripts/process_worker_pool.py +115 -77
- parsl-2025.1.13.dist-info/METADATA +96 -0
- parsl-2025.1.13.dist-info/RECORD +462 -0
- {parsl-2024.3.11.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.11.dist-info/METADATA +0 -98
- parsl-2024.3.11.dist-info/RECORD +0 -447
- 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.11.data → parsl-2025.1.13.data}/scripts/parsl_coprocess.py +1 -1
- {parsl-2024.3.11.dist-info → parsl-2025.1.13.dist-info}/LICENSE +0 -0
- {parsl-2024.3.11.dist-info → parsl-2025.1.13.dist-info}/entry_points.txt +0 -0
- {parsl-2024.3.11.dist-info → parsl-2025.1.13.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
import time
|
2
|
+
|
3
|
+
import pytest
|
4
|
+
|
5
|
+
import parsl
|
6
|
+
from parsl.config import Config
|
7
|
+
from parsl.executors import HighThroughputExecutor
|
8
|
+
from parsl.launchers import SimpleLauncher
|
9
|
+
from parsl.providers import LocalProvider
|
10
|
+
|
11
|
+
# this constant is used to scale some durations that happen
|
12
|
+
# based around the expected drain period: the drain period
|
13
|
+
# is TIME_CONST seconds, and the single executed task will
|
14
|
+
# last twice that many number of seconds.
|
15
|
+
TIME_CONST = 4
|
16
|
+
|
17
|
+
CONNECTED_MANAGERS_POLL_MS = 100
|
18
|
+
|
19
|
+
|
20
|
+
def local_config():
|
21
|
+
return Config(
|
22
|
+
executors=[
|
23
|
+
HighThroughputExecutor(
|
24
|
+
label="htex_local",
|
25
|
+
drain_period=TIME_CONST,
|
26
|
+
worker_debug=True,
|
27
|
+
cores_per_worker=1,
|
28
|
+
encrypted=True,
|
29
|
+
provider=LocalProvider(
|
30
|
+
init_blocks=1,
|
31
|
+
min_blocks=0,
|
32
|
+
max_blocks=0,
|
33
|
+
launcher=SimpleLauncher(),
|
34
|
+
),
|
35
|
+
)
|
36
|
+
],
|
37
|
+
strategy='none',
|
38
|
+
strategy_period=0.1
|
39
|
+
)
|
40
|
+
|
41
|
+
|
42
|
+
@parsl.python_app
|
43
|
+
def f(n):
|
44
|
+
import time
|
45
|
+
time.sleep(n)
|
46
|
+
|
47
|
+
|
48
|
+
@pytest.mark.local
|
49
|
+
def test_drain(try_assert):
|
50
|
+
|
51
|
+
htex = parsl.dfk().executors['htex_local']
|
52
|
+
|
53
|
+
# wait till we have a block running...
|
54
|
+
|
55
|
+
try_assert(lambda: len(htex.connected_managers()) == 1, check_period_ms=CONNECTED_MANAGERS_POLL_MS)
|
56
|
+
|
57
|
+
managers = htex.connected_managers()
|
58
|
+
assert managers[0]['active'], "The manager should be active"
|
59
|
+
assert not managers[0]['draining'], "The manager should not be draining"
|
60
|
+
|
61
|
+
fut = f(TIME_CONST * 2)
|
62
|
+
|
63
|
+
time.sleep(TIME_CONST)
|
64
|
+
|
65
|
+
# this assert should happen *very fast* after the above delay...
|
66
|
+
try_assert(lambda: htex.connected_managers()[0]['draining'], timeout_ms=500, check_period_ms=CONNECTED_MANAGERS_POLL_MS)
|
67
|
+
|
68
|
+
# and the test task should still be running...
|
69
|
+
assert not fut.done(), "The test task should still be running"
|
70
|
+
|
71
|
+
fut.result()
|
72
|
+
|
73
|
+
# and now we should see the manager disappear...
|
74
|
+
# ... with strategy='none', this should be coming from draining but
|
75
|
+
# that information isn't immediately obvious from the absence in
|
76
|
+
# connected managers.
|
77
|
+
# As with the above draining assert, this should happen very fast after
|
78
|
+
# the task ends.
|
79
|
+
try_assert(lambda: len(htex.connected_managers()) == 0, timeout_ms=500, check_period_ms=CONNECTED_MANAGERS_POLL_MS)
|
@@ -1,12 +1,12 @@
|
|
1
|
+
import logging
|
1
2
|
import pathlib
|
2
|
-
import
|
3
|
+
from subprocess import Popen, TimeoutExpired
|
4
|
+
from typing import Optional, Sequence
|
3
5
|
from unittest import mock
|
4
6
|
|
5
7
|
import pytest
|
6
8
|
|
7
|
-
from parsl import curvezmq
|
8
|
-
from parsl import HighThroughputExecutor
|
9
|
-
from parsl.multiprocessing import ForkProcess
|
9
|
+
from parsl import HighThroughputExecutor, curvezmq
|
10
10
|
|
11
11
|
_MOCK_BASE = "parsl.executors.high_throughput.executor"
|
12
12
|
|
@@ -72,51 +72,77 @@ def test_htex_start_encrypted(
|
|
72
72
|
@pytest.mark.local
|
73
73
|
@pytest.mark.parametrize("started", (True, False))
|
74
74
|
@pytest.mark.parametrize("timeout_expires", (True, False))
|
75
|
-
@mock.patch(f"{_MOCK_BASE}.logger")
|
76
75
|
def test_htex_shutdown(
|
77
|
-
mock_logger: mock.MagicMock,
|
78
76
|
started: bool,
|
79
77
|
timeout_expires: bool,
|
80
78
|
htex: HighThroughputExecutor,
|
79
|
+
caplog
|
81
80
|
):
|
82
|
-
mock_ix_proc = mock.Mock(spec=
|
81
|
+
mock_ix_proc = mock.Mock(spec=Popen)
|
83
82
|
|
84
83
|
if started:
|
85
84
|
htex.interchange_proc = mock_ix_proc
|
86
|
-
|
85
|
+
|
86
|
+
# This will, in the absence of any exit trigger, block forever if
|
87
|
+
# no timeout is given and if the interchange does not terminate.
|
88
|
+
# Raise an exception to report that, rather than actually block,
|
89
|
+
# and hope that nothing is catching that exception.
|
90
|
+
|
91
|
+
# this function implements the behaviour if the interchange has
|
92
|
+
# not received a termination call
|
93
|
+
def proc_wait_alive(timeout):
|
94
|
+
if timeout:
|
95
|
+
raise TimeoutExpired(cmd="mock-interchange", timeout=timeout)
|
96
|
+
else:
|
97
|
+
raise RuntimeError("This wait call would hang forever")
|
98
|
+
|
99
|
+
def proc_wait_terminated(timeout):
|
100
|
+
return 0
|
101
|
+
|
102
|
+
mock_ix_proc.wait.side_effect = proc_wait_alive
|
87
103
|
|
88
104
|
if not timeout_expires:
|
89
105
|
# Simulate termination of the Interchange process
|
90
106
|
def kill_interchange(*args, **kwargs):
|
91
|
-
mock_ix_proc.
|
107
|
+
mock_ix_proc.wait.side_effect = proc_wait_terminated
|
92
108
|
|
93
109
|
mock_ix_proc.terminate.side_effect = kill_interchange
|
94
110
|
|
95
|
-
|
111
|
+
with caplog.at_level(logging.INFO):
|
112
|
+
htex.shutdown()
|
96
113
|
|
97
|
-
mock_logs = mock_logger.info.call_args_list
|
98
114
|
if started:
|
99
115
|
assert mock_ix_proc.terminate.called
|
100
|
-
assert mock_ix_proc.
|
101
|
-
assert {"timeout": 10} == mock_ix_proc.
|
116
|
+
assert mock_ix_proc.wait.called
|
117
|
+
assert {"timeout": 10} == mock_ix_proc.wait.call_args[1]
|
102
118
|
if timeout_expires:
|
103
|
-
assert "Unable to terminate Interchange" in
|
119
|
+
assert "Unable to terminate Interchange" in caplog.text
|
104
120
|
assert mock_ix_proc.kill.called
|
105
|
-
assert "Attempting" in
|
106
|
-
assert "Finished" in
|
121
|
+
assert "Attempting HighThroughputExecutor shutdown" in caplog.text
|
122
|
+
assert "Finished HighThroughputExecutor shutdown" in caplog.text
|
107
123
|
else:
|
108
124
|
assert not mock_ix_proc.terminate.called
|
109
|
-
assert not mock_ix_proc.
|
110
|
-
assert "has not started" in
|
125
|
+
assert not mock_ix_proc.wait.called
|
126
|
+
assert "HighThroughputExecutor has not started" in caplog.text
|
111
127
|
|
112
128
|
|
113
129
|
@pytest.mark.local
|
114
|
-
|
115
|
-
|
116
|
-
|
130
|
+
@pytest.mark.parametrize("cmd", (None, "custom-launch-cmd"))
|
131
|
+
def test_htex_worker_pool_launch_cmd(cmd: Optional[str]):
|
132
|
+
if cmd:
|
133
|
+
htex = HighThroughputExecutor(launch_cmd=cmd)
|
134
|
+
assert htex.launch_cmd == cmd
|
135
|
+
else:
|
136
|
+
htex = HighThroughputExecutor()
|
137
|
+
assert htex.launch_cmd.startswith("process_worker_pool.py")
|
117
138
|
|
118
|
-
warning_msg = "max_workers is deprecated"
|
119
|
-
assert any(warning_msg in str(warning.message) for warning in record)
|
120
139
|
|
121
|
-
|
122
|
-
|
140
|
+
@pytest.mark.local
|
141
|
+
@pytest.mark.parametrize("cmd", (None, ["custom", "launch", "cmd"]))
|
142
|
+
def test_htex_interchange_launch_cmd(cmd: Optional[Sequence[str]]):
|
143
|
+
if cmd:
|
144
|
+
htex = HighThroughputExecutor(interchange_launch_cmd=cmd)
|
145
|
+
assert htex.interchange_launch_cmd == cmd
|
146
|
+
else:
|
147
|
+
htex = HighThroughputExecutor()
|
148
|
+
assert htex.interchange_launch_cmd == ["interchange.py"]
|
@@ -0,0 +1,51 @@
|
|
1
|
+
import time
|
2
|
+
|
3
|
+
import pytest
|
4
|
+
|
5
|
+
import parsl
|
6
|
+
from parsl.app.app import bash_app, python_app
|
7
|
+
from parsl.config import Config
|
8
|
+
from parsl.executors import HighThroughputExecutor
|
9
|
+
from parsl.executors.high_throughput.manager_selector import (
|
10
|
+
BlockIdManagerSelector,
|
11
|
+
ManagerSelector,
|
12
|
+
)
|
13
|
+
from parsl.launchers import WrappedLauncher
|
14
|
+
from parsl.providers import LocalProvider
|
15
|
+
from parsl.usage_tracking.levels import LEVEL_1
|
16
|
+
|
17
|
+
BLOCK_COUNT = 2
|
18
|
+
|
19
|
+
|
20
|
+
@parsl.python_app
|
21
|
+
def get_worker_pid():
|
22
|
+
import os
|
23
|
+
return os.environ.get('PARSL_WORKER_BLOCK_ID')
|
24
|
+
|
25
|
+
|
26
|
+
@pytest.mark.local
|
27
|
+
def test_block_id_selection(try_assert):
|
28
|
+
htex = HighThroughputExecutor(
|
29
|
+
label="htex_local",
|
30
|
+
max_workers_per_node=1,
|
31
|
+
manager_selector=BlockIdManagerSelector(),
|
32
|
+
provider=LocalProvider(
|
33
|
+
init_blocks=BLOCK_COUNT,
|
34
|
+
max_blocks=BLOCK_COUNT,
|
35
|
+
min_blocks=BLOCK_COUNT,
|
36
|
+
),
|
37
|
+
)
|
38
|
+
|
39
|
+
config = Config(
|
40
|
+
executors=[htex],
|
41
|
+
usage_tracking=LEVEL_1,
|
42
|
+
)
|
43
|
+
|
44
|
+
with parsl.load(config):
|
45
|
+
blockids = []
|
46
|
+
try_assert(lambda: len(htex.connected_managers()) == BLOCK_COUNT, timeout_ms=20000)
|
47
|
+
for i in range(10):
|
48
|
+
future = get_worker_pid()
|
49
|
+
blockids.append(future.result())
|
50
|
+
|
51
|
+
assert all(blockid == "1" for blockid in blockids)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import logging
|
2
|
+
import sys
|
3
|
+
|
4
|
+
import pytest
|
5
|
+
|
6
|
+
import parsl
|
7
|
+
from parsl.app.app import python_app
|
8
|
+
from parsl.tests.configs.htex_local import fresh_config
|
9
|
+
|
10
|
+
|
11
|
+
def local_config():
|
12
|
+
config = fresh_config()
|
13
|
+
config.executors[0].poll_period = 1
|
14
|
+
config.executors[0].max_workers_per_node = 1
|
15
|
+
return config
|
16
|
+
|
17
|
+
|
18
|
+
@python_app
|
19
|
+
def dummy():
|
20
|
+
pass
|
21
|
+
|
22
|
+
|
23
|
+
@pytest.mark.local
|
24
|
+
def test_connected_managers():
|
25
|
+
|
26
|
+
# Run dummy function to ensure a manager is online
|
27
|
+
x = dummy()
|
28
|
+
assert x.result() is None
|
29
|
+
executor = parsl.dfk().executors['htex_local']
|
30
|
+
manager_info_list = executor.connected_managers()
|
31
|
+
assert len(manager_info_list) == 1
|
32
|
+
manager_info = manager_info_list[0]
|
33
|
+
assert 'python_version' in manager_info
|
34
|
+
assert 'parsl_version' in manager_info
|
35
|
+
assert manager_info['parsl_version'] == parsl.__version__
|
36
|
+
assert manager_info['python_version'] == f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
|
@@ -5,18 +5,12 @@ from parsl.app.app import python_app
|
|
5
5
|
from parsl.tests.configs.htex_local import fresh_config
|
6
6
|
|
7
7
|
|
8
|
-
def
|
8
|
+
def local_config():
|
9
9
|
config = fresh_config()
|
10
10
|
config.executors[0].poll_period = 1
|
11
11
|
config.executors[0].max_workers_per_node = 1
|
12
12
|
config.executors[0].launch_cmd = "executable_that_hopefully_does_not_exist_1030509.py"
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
def local_teardown():
|
17
|
-
|
18
|
-
parsl.dfk().cleanup()
|
19
|
-
parsl.clear()
|
13
|
+
return config
|
20
14
|
|
21
15
|
|
22
16
|
@python_app
|
@@ -37,7 +31,3 @@ def test_that_it_fails():
|
|
37
31
|
raise Exception("The app somehow ran without a valid worker")
|
38
32
|
|
39
33
|
assert parsl.dfk().config.executors[0]._executor_bad_state.is_set()
|
40
|
-
|
41
|
-
# htex needs shutting down explicitly because dfk.cleanup() will not
|
42
|
-
# do that, as it is in bad state
|
43
|
-
parsl.dfk().config.executors[0].shutdown()
|
@@ -1,11 +1,13 @@
|
|
1
1
|
import logging
|
2
|
-
|
2
|
+
|
3
3
|
import pytest
|
4
|
-
|
4
|
+
|
5
|
+
import parsl
|
5
6
|
from parsl import Config
|
6
|
-
from parsl.
|
7
|
+
from parsl.executors import HighThroughputExecutor
|
7
8
|
from parsl.executors.errors import BadStateException
|
8
|
-
from parsl.jobs.states import
|
9
|
+
from parsl.jobs.states import JobState, JobStatus
|
10
|
+
from parsl.providers import LocalProvider
|
9
11
|
|
10
12
|
|
11
13
|
def local_config():
|
@@ -19,16 +21,14 @@ def local_config():
|
|
19
21
|
poll_period=100,
|
20
22
|
max_workers_per_node=1,
|
21
23
|
provider=LocalProvider(
|
22
|
-
worker_init="
|
23
|
-
init_blocks=2
|
24
|
-
max_blocks=4,
|
25
|
-
min_blocks=0,
|
24
|
+
worker_init="exit 0",
|
25
|
+
init_blocks=2
|
26
26
|
),
|
27
27
|
)
|
28
28
|
],
|
29
29
|
run_dir="/tmp/test_htex",
|
30
30
|
max_idletime=0.5,
|
31
|
-
strategy='
|
31
|
+
strategy='none',
|
32
32
|
)
|
33
33
|
|
34
34
|
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import queue
|
2
|
+
from unittest import mock
|
3
|
+
|
4
|
+
import pytest
|
5
|
+
|
6
|
+
from parsl.executors import HighThroughputExecutor
|
7
|
+
from parsl.executors.errors import InvalidResourceSpecification
|
8
|
+
|
9
|
+
|
10
|
+
def double(x):
|
11
|
+
return x * 2
|
12
|
+
|
13
|
+
|
14
|
+
@pytest.mark.local
|
15
|
+
def test_submit_calls_validate():
|
16
|
+
|
17
|
+
htex = HighThroughputExecutor()
|
18
|
+
htex.outgoing_q = mock.Mock(spec=queue.Queue)
|
19
|
+
htex.validate_resource_spec = mock.Mock(spec=htex.validate_resource_spec)
|
20
|
+
|
21
|
+
res_spec = {}
|
22
|
+
htex.submit(double, res_spec, (5,), {})
|
23
|
+
htex.validate_resource_spec.assert_called()
|
24
|
+
|
25
|
+
|
26
|
+
@pytest.mark.local
|
27
|
+
def test_resource_spec_validation():
|
28
|
+
htex = HighThroughputExecutor()
|
29
|
+
ret_val = htex.validate_resource_spec({})
|
30
|
+
assert ret_val is None
|
31
|
+
|
32
|
+
|
33
|
+
@pytest.mark.local
|
34
|
+
def test_resource_spec_validation_one_key():
|
35
|
+
htex = HighThroughputExecutor()
|
36
|
+
ret_val = htex.validate_resource_spec({"priority": 2})
|
37
|
+
assert ret_val is None
|
38
|
+
|
39
|
+
|
40
|
+
@pytest.mark.local
|
41
|
+
def test_resource_spec_validation_bad_keys():
|
42
|
+
htex = HighThroughputExecutor()
|
43
|
+
|
44
|
+
with pytest.raises(InvalidResourceSpecification):
|
45
|
+
htex.validate_resource_spec({"num_nodes": 2})
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import logging
|
1
2
|
import pathlib
|
2
3
|
from typing import Optional
|
3
4
|
from unittest import mock
|
@@ -8,6 +9,24 @@ import zmq
|
|
8
9
|
|
9
10
|
from parsl import curvezmq
|
10
11
|
from parsl.executors.high_throughput.interchange import Interchange
|
12
|
+
from parsl.executors.high_throughput.manager_selector import RandomManagerSelector
|
13
|
+
|
14
|
+
|
15
|
+
def make_interchange(*, interchange_address: Optional[str], cert_dir: Optional[str]) -> Interchange:
|
16
|
+
return Interchange(interchange_address=interchange_address,
|
17
|
+
cert_dir=cert_dir,
|
18
|
+
client_address="127.0.0.1",
|
19
|
+
client_ports=(50055, 50056, 50057),
|
20
|
+
worker_ports=None,
|
21
|
+
worker_port_range=(54000, 55000),
|
22
|
+
hub_address=None,
|
23
|
+
hub_zmq_port=None,
|
24
|
+
heartbeat_threshold=60,
|
25
|
+
logdir=".",
|
26
|
+
logging_level=logging.INFO,
|
27
|
+
manager_selector=RandomManagerSelector(),
|
28
|
+
poll_period=10,
|
29
|
+
run_id="test_run_id")
|
11
30
|
|
12
31
|
|
13
32
|
@pytest.fixture
|
@@ -31,7 +50,7 @@ def test_interchange_curvezmq_sockets(
|
|
31
50
|
mock_socket: mock.MagicMock, cert_dir: Optional[str], encrypted: bool
|
32
51
|
):
|
33
52
|
address = "127.0.0.1"
|
34
|
-
ix =
|
53
|
+
ix = make_interchange(interchange_address=address, cert_dir=cert_dir)
|
35
54
|
assert isinstance(ix.zmq_context, curvezmq.ServerContext)
|
36
55
|
assert ix.zmq_context.encrypted is encrypted
|
37
56
|
assert mock_socket.call_count == 5
|
@@ -40,7 +59,7 @@ def test_interchange_curvezmq_sockets(
|
|
40
59
|
@pytest.mark.local
|
41
60
|
@pytest.mark.parametrize("encrypted", (True, False), indirect=True)
|
42
61
|
def test_interchange_binding_no_address(cert_dir: Optional[str]):
|
43
|
-
ix =
|
62
|
+
ix = make_interchange(interchange_address=None, cert_dir=cert_dir)
|
44
63
|
assert ix.interchange_address == "*"
|
45
64
|
|
46
65
|
|
@@ -49,17 +68,18 @@ def test_interchange_binding_no_address(cert_dir: Optional[str]):
|
|
49
68
|
def test_interchange_binding_with_address(cert_dir: Optional[str]):
|
50
69
|
# Using loopback address
|
51
70
|
address = "127.0.0.1"
|
52
|
-
ix =
|
71
|
+
ix = make_interchange(interchange_address=address, cert_dir=cert_dir)
|
53
72
|
assert ix.interchange_address == address
|
54
73
|
|
55
74
|
|
75
|
+
@pytest.mark.skip("This behaviour is possibly unexpected. See issue #3037")
|
56
76
|
@pytest.mark.local
|
57
77
|
@pytest.mark.parametrize("encrypted", (True, False), indirect=True)
|
58
78
|
def test_interchange_binding_with_non_ipv4_address(cert_dir: Optional[str]):
|
59
79
|
# Confirm that a ipv4 address is required
|
60
80
|
address = "localhost"
|
61
81
|
with pytest.raises(zmq.error.ZMQError):
|
62
|
-
|
82
|
+
make_interchange(interchange_address=address, cert_dir=cert_dir)
|
63
83
|
|
64
84
|
|
65
85
|
@pytest.mark.local
|
@@ -67,8 +87,8 @@ def test_interchange_binding_with_non_ipv4_address(cert_dir: Optional[str]):
|
|
67
87
|
def test_interchange_binding_bad_address(cert_dir: Optional[str]):
|
68
88
|
"""Confirm that we raise a ZMQError when a bad address is supplied"""
|
69
89
|
address = "550.0.0.0"
|
70
|
-
with pytest.raises(
|
71
|
-
|
90
|
+
with pytest.raises(ValueError):
|
91
|
+
make_interchange(interchange_address=address, cert_dir=cert_dir)
|
72
92
|
|
73
93
|
|
74
94
|
@pytest.mark.local
|
@@ -76,11 +96,12 @@ def test_interchange_binding_bad_address(cert_dir: Optional[str]):
|
|
76
96
|
def test_limited_interface_binding(cert_dir: Optional[str]):
|
77
97
|
"""When address is specified the worker_port would be bound to it rather than to 0.0.0.0"""
|
78
98
|
address = "127.0.0.1"
|
79
|
-
ix =
|
99
|
+
ix = make_interchange(interchange_address=address, cert_dir=cert_dir)
|
80
100
|
ix.worker_result_port
|
81
101
|
proc = psutil.Process()
|
82
102
|
conns = proc.connections(kind="tcp")
|
83
103
|
|
84
104
|
matched_conns = [conn for conn in conns if conn.laddr.port == ix.worker_result_port]
|
85
105
|
assert len(matched_conns) == 1
|
86
|
-
|
106
|
+
# laddr.ip can return ::ffff:127.0.0.1 when using IPv6
|
107
|
+
assert address in matched_conns[0].laddr.ip
|
@@ -0,0 +1,86 @@
|
|
1
|
+
"""Tests monitoring records app name under various decoration patterns.
|
2
|
+
"""
|
3
|
+
|
4
|
+
import os
|
5
|
+
import time
|
6
|
+
|
7
|
+
import pytest
|
8
|
+
|
9
|
+
import parsl
|
10
|
+
from parsl.tests.configs.htex_local_alternate import fresh_config
|
11
|
+
|
12
|
+
|
13
|
+
@parsl.python_app
|
14
|
+
def regular_decorated_app():
|
15
|
+
return 5
|
16
|
+
|
17
|
+
|
18
|
+
@pytest.mark.local
|
19
|
+
def get_regular_decorated_app():
|
20
|
+
return regular_decorated_app
|
21
|
+
|
22
|
+
|
23
|
+
def for_decoration_later():
|
24
|
+
return 77
|
25
|
+
|
26
|
+
|
27
|
+
def get_for_decoration_later():
|
28
|
+
return parsl.python_app(for_decoration_later)
|
29
|
+
|
30
|
+
|
31
|
+
def get_decorated_closure():
|
32
|
+
|
33
|
+
r = 53
|
34
|
+
|
35
|
+
@parsl.python_app
|
36
|
+
def decorated_closure():
|
37
|
+
return r
|
38
|
+
|
39
|
+
return decorated_closure
|
40
|
+
|
41
|
+
|
42
|
+
@pytest.mark.local
|
43
|
+
@pytest.mark.parametrize("get_app,expected_name,expected_result",
|
44
|
+
[(get_regular_decorated_app, "regular_decorated_app", 5),
|
45
|
+
(get_for_decoration_later, "for_decoration_later", 77),
|
46
|
+
(get_decorated_closure, "decorated_closure", 53)
|
47
|
+
])
|
48
|
+
def test_app_name(get_app, expected_name, expected_result, tmpd_cwd):
|
49
|
+
|
50
|
+
# this is imported here rather than at module level because
|
51
|
+
# it isn't available in a plain parsl install, so this module
|
52
|
+
# would otherwise fail to import and break even a basic test
|
53
|
+
# run.
|
54
|
+
import sqlalchemy
|
55
|
+
|
56
|
+
c = fresh_config()
|
57
|
+
c.run_dir = tmpd_cwd
|
58
|
+
c.monitoring.logging_endpoint = f"sqlite:///{tmpd_cwd}/monitoring.db"
|
59
|
+
parsl.load(c)
|
60
|
+
|
61
|
+
app = get_app()
|
62
|
+
assert app().result() == expected_result
|
63
|
+
|
64
|
+
parsl.dfk().cleanup()
|
65
|
+
|
66
|
+
engine = sqlalchemy.create_engine(c.monitoring.logging_endpoint)
|
67
|
+
with engine.begin() as connection:
|
68
|
+
|
69
|
+
def count_rows(table: str):
|
70
|
+
result = connection.execute(sqlalchemy.text(f"SELECT COUNT(*) FROM {table}"))
|
71
|
+
(c, ) = result.first()
|
72
|
+
return c
|
73
|
+
|
74
|
+
# one workflow...
|
75
|
+
assert count_rows("workflow") == 1
|
76
|
+
|
77
|
+
# ... with one task ...
|
78
|
+
assert count_rows("task") == 1
|
79
|
+
|
80
|
+
# ... that was tried once ...
|
81
|
+
assert count_rows("try") == 1
|
82
|
+
|
83
|
+
# ... and has the expected name.
|
84
|
+
result = connection.execute(sqlalchemy.text("SELECT task_func_name FROM task"))
|
85
|
+
(c, ) = result.first()
|
86
|
+
assert c == expected_name
|