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
@@ -0,0 +1,71 @@
|
|
1
|
+
import logging
|
2
|
+
import time
|
3
|
+
|
4
|
+
import pytest
|
5
|
+
|
6
|
+
import parsl
|
7
|
+
from parsl import File, python_app
|
8
|
+
from parsl.config import Config
|
9
|
+
from parsl.executors import HighThroughputExecutor
|
10
|
+
from parsl.jobs.states import TERMINAL_STATES, JobState
|
11
|
+
from parsl.launchers import SingleNodeLauncher
|
12
|
+
from parsl.providers import LocalProvider
|
13
|
+
|
14
|
+
logger = logging.getLogger(__name__)
|
15
|
+
|
16
|
+
_max_blocks = 1
|
17
|
+
_min_blocks = 0
|
18
|
+
|
19
|
+
|
20
|
+
def local_config():
|
21
|
+
return Config(
|
22
|
+
executors=[
|
23
|
+
HighThroughputExecutor(
|
24
|
+
heartbeat_period=1,
|
25
|
+
heartbeat_threshold=2,
|
26
|
+
poll_period=100,
|
27
|
+
label="htex_local",
|
28
|
+
address="127.0.0.1",
|
29
|
+
max_workers_per_node=1,
|
30
|
+
encrypted=True,
|
31
|
+
launch_cmd="sleep inf",
|
32
|
+
provider=LocalProvider(
|
33
|
+
init_blocks=1,
|
34
|
+
max_blocks=_max_blocks,
|
35
|
+
min_blocks=_min_blocks,
|
36
|
+
launcher=SingleNodeLauncher(),
|
37
|
+
),
|
38
|
+
)
|
39
|
+
],
|
40
|
+
max_idletime=0.5,
|
41
|
+
strategy='htex_auto_scale',
|
42
|
+
strategy_period=0.1
|
43
|
+
)
|
44
|
+
|
45
|
+
|
46
|
+
# see issue #1885 for details of failures of this test.
|
47
|
+
# at the time of issue #1885 this test was failing frequently
|
48
|
+
# in CI.
|
49
|
+
@pytest.mark.local
|
50
|
+
def test_scaledown_with_register(try_assert):
|
51
|
+
dfk = parsl.dfk()
|
52
|
+
htex = dfk.executors['htex_local']
|
53
|
+
|
54
|
+
num_managers = len(htex.connected_managers())
|
55
|
+
assert num_managers == 0, "Expected 0 managers at start"
|
56
|
+
|
57
|
+
try_assert(lambda: len(htex.status()),
|
58
|
+
fail_msg="Expected 1 block at start")
|
59
|
+
|
60
|
+
s = htex.status()
|
61
|
+
assert s['0'].state == JobState.RUNNING, "Expected block to be in RUNNING"
|
62
|
+
|
63
|
+
def check_zero_blocks():
|
64
|
+
s = htex.status()
|
65
|
+
return len(s) == 1 and s['0'].state in TERMINAL_STATES
|
66
|
+
|
67
|
+
try_assert(
|
68
|
+
check_zero_blocks,
|
69
|
+
fail_msg="Expected 0 blocks after idle scaledown",
|
70
|
+
timeout_ms=15000,
|
71
|
+
)
|
@@ -0,0 +1,73 @@
|
|
1
|
+
import random
|
2
|
+
import threading
|
3
|
+
|
4
|
+
import pytest
|
5
|
+
|
6
|
+
import parsl
|
7
|
+
from parsl.config import Config
|
8
|
+
from parsl.executors import HighThroughputExecutor
|
9
|
+
from parsl.launchers import SimpleLauncher
|
10
|
+
from parsl.providers import LocalProvider
|
11
|
+
|
12
|
+
# we need some blocks, but it doesn't matter too much how many, as long
|
13
|
+
# as they can all start up and get registered within the try_assert
|
14
|
+
# timeout later on.
|
15
|
+
BLOCK_COUNT = 3
|
16
|
+
|
17
|
+
|
18
|
+
class AccumulatingLocalProvider(LocalProvider):
|
19
|
+
def __init__(self, *args, **kwargs):
|
20
|
+
# Use a list for submitted job IDs because if there are multiple
|
21
|
+
# submissions returning the same job ID, this test should count
|
22
|
+
# those...
|
23
|
+
self.submit_job_ids = []
|
24
|
+
|
25
|
+
# ... but there's no requirement, I think, that cancel must be called
|
26
|
+
# only once per job id. What matters here is that each job ID is
|
27
|
+
# cancelled at least once.
|
28
|
+
self.cancel_job_ids = set()
|
29
|
+
|
30
|
+
super().__init__(*args, **kwargs)
|
31
|
+
|
32
|
+
def submit(self, *args, **kwargs):
|
33
|
+
job_id = super().submit(*args, **kwargs)
|
34
|
+
self.submit_job_ids.append(job_id)
|
35
|
+
return job_id
|
36
|
+
|
37
|
+
def cancel(self, job_ids):
|
38
|
+
self.cancel_job_ids.update(job_ids)
|
39
|
+
return super().cancel(job_ids)
|
40
|
+
|
41
|
+
|
42
|
+
@pytest.mark.local
|
43
|
+
def test_shutdown_scalein_blocks(tmpd_cwd, try_assert):
|
44
|
+
"""
|
45
|
+
This test scales up several blocks, and then checks that they are all
|
46
|
+
scaled in at DFK shutdown.
|
47
|
+
"""
|
48
|
+
accumulating_provider = AccumulatingLocalProvider(
|
49
|
+
init_blocks=BLOCK_COUNT,
|
50
|
+
min_blocks=0,
|
51
|
+
max_blocks=0,
|
52
|
+
launcher=SimpleLauncher(),
|
53
|
+
)
|
54
|
+
|
55
|
+
htex = HighThroughputExecutor(
|
56
|
+
label="htex_local",
|
57
|
+
cores_per_worker=1,
|
58
|
+
provider=accumulating_provider
|
59
|
+
)
|
60
|
+
|
61
|
+
config = Config(
|
62
|
+
executors=[htex],
|
63
|
+
strategy='none',
|
64
|
+
strategy_period=0.1,
|
65
|
+
run_dir=str(tmpd_cwd)
|
66
|
+
)
|
67
|
+
|
68
|
+
with parsl.load(config):
|
69
|
+
# this will wait for everything to be scaled out fully
|
70
|
+
try_assert(lambda: len(htex.connected_managers()) == BLOCK_COUNT)
|
71
|
+
|
72
|
+
assert len(accumulating_provider.submit_job_ids) == BLOCK_COUNT, f"Exactly {BLOCK_COUNT} blocks should have been launched"
|
73
|
+
assert len(accumulating_provider.cancel_job_ids) == BLOCK_COUNT, f"Exactly {BLOCK_COUNT} blocks should have been scaled in"
|
@@ -0,0 +1,90 @@
|
|
1
|
+
import os
|
2
|
+
import signal
|
3
|
+
import time
|
4
|
+
|
5
|
+
import pytest
|
6
|
+
import zmq
|
7
|
+
|
8
|
+
import parsl
|
9
|
+
from parsl.config import Config
|
10
|
+
from parsl.executors import HighThroughputExecutor
|
11
|
+
from parsl.launchers import SimpleLauncher
|
12
|
+
from parsl.providers import LocalProvider
|
13
|
+
|
14
|
+
T_s = 1
|
15
|
+
|
16
|
+
|
17
|
+
def fresh_config():
|
18
|
+
htex = HighThroughputExecutor(
|
19
|
+
heartbeat_period=1 * T_s,
|
20
|
+
heartbeat_threshold=3 * T_s,
|
21
|
+
label="htex_local",
|
22
|
+
worker_debug=True,
|
23
|
+
cores_per_worker=1,
|
24
|
+
encrypted=False,
|
25
|
+
provider=LocalProvider(
|
26
|
+
init_blocks=0,
|
27
|
+
min_blocks=0,
|
28
|
+
max_blocks=0,
|
29
|
+
launcher=SimpleLauncher(),
|
30
|
+
),
|
31
|
+
)
|
32
|
+
c = Config(
|
33
|
+
executors=[htex],
|
34
|
+
strategy='none',
|
35
|
+
strategy_period=0.5,
|
36
|
+
)
|
37
|
+
return c, htex
|
38
|
+
|
39
|
+
|
40
|
+
@parsl.python_app
|
41
|
+
def app():
|
42
|
+
return 7
|
43
|
+
|
44
|
+
|
45
|
+
@pytest.mark.local
|
46
|
+
@pytest.mark.parametrize("msg",
|
47
|
+
(b'FuzzyByte\rSTREAM', # not JSON
|
48
|
+
b'{}', # missing fields
|
49
|
+
b'{"type":"heartbeat"}', # regression test #3262
|
50
|
+
)
|
51
|
+
)
|
52
|
+
def test_bad_messages(try_assert, msg):
|
53
|
+
"""This tests that the interchange is resilient to a few different bad
|
54
|
+
messages: malformed messages caused by implementation errors, and
|
55
|
+
heartbeat messages from managers that are not registered.
|
56
|
+
|
57
|
+
The heartbeat test is a regression test for issues #3262, #3632
|
58
|
+
"""
|
59
|
+
|
60
|
+
c, htex = fresh_config()
|
61
|
+
|
62
|
+
with parsl.load(c):
|
63
|
+
|
64
|
+
# send a bad message into the interchange on the task_outgoing worker
|
65
|
+
# channel, and then check that the interchange is still alive enough
|
66
|
+
# that we can scale out a block and run a task.
|
67
|
+
|
68
|
+
(task_port, result_port) = htex.command_client.run("WORKER_PORTS")
|
69
|
+
|
70
|
+
context = zmq.Context()
|
71
|
+
channel_timeout = 10000 # in milliseconds
|
72
|
+
task_channel = context.socket(zmq.DEALER)
|
73
|
+
task_channel.setsockopt(zmq.LINGER, 0)
|
74
|
+
task_channel.setsockopt(zmq.IDENTITY, b'testid')
|
75
|
+
|
76
|
+
task_channel.set_hwm(0)
|
77
|
+
task_channel.setsockopt(zmq.SNDTIMEO, channel_timeout)
|
78
|
+
task_channel.connect(f"tcp://localhost:{task_port}")
|
79
|
+
|
80
|
+
task_channel.send(msg)
|
81
|
+
|
82
|
+
# If the interchange exits, it's likely that this test will hang rather
|
83
|
+
# than raise an error, because the interchange interaction code
|
84
|
+
# assumes the interchange is always there.
|
85
|
+
# In the case of issue #3262, an exception message goes to stderr, and
|
86
|
+
# no error goes to the interchange log file.
|
87
|
+
htex.scale_out_facade(1)
|
88
|
+
try_assert(lambda: len(htex.connected_managers()) == 1, timeout_ms=10000)
|
89
|
+
|
90
|
+
assert app().result() == 7
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import os
|
2
|
+
import signal
|
3
|
+
|
4
|
+
import pytest
|
5
|
+
|
6
|
+
import parsl
|
7
|
+
from parsl import Config, HighThroughputExecutor
|
8
|
+
from parsl.executors.high_throughput.errors import ManagerLost
|
9
|
+
|
10
|
+
|
11
|
+
@parsl.python_app
|
12
|
+
def get_manager_pgid():
|
13
|
+
import os
|
14
|
+
return os.getpgid(os.getpid())
|
15
|
+
|
16
|
+
|
17
|
+
@parsl.python_app
|
18
|
+
def lose_manager():
|
19
|
+
import os
|
20
|
+
import signal
|
21
|
+
|
22
|
+
manager_pid = os.getppid()
|
23
|
+
os.kill(manager_pid, signal.SIGSTOP)
|
24
|
+
|
25
|
+
|
26
|
+
@pytest.mark.local
|
27
|
+
def test_manager_lost_system_failure(tmpd_cwd):
|
28
|
+
hte = HighThroughputExecutor(
|
29
|
+
label="htex_local",
|
30
|
+
address="127.0.0.1",
|
31
|
+
max_workers_per_node=2,
|
32
|
+
cores_per_worker=1,
|
33
|
+
worker_logdir_root=str(tmpd_cwd),
|
34
|
+
heartbeat_period=1,
|
35
|
+
heartbeat_threshold=3,
|
36
|
+
)
|
37
|
+
c = Config(executors=[hte], strategy='simple', strategy_period=0.1)
|
38
|
+
|
39
|
+
with parsl.load(c):
|
40
|
+
manager_pgid = get_manager_pgid().result()
|
41
|
+
try:
|
42
|
+
with pytest.raises(ManagerLost):
|
43
|
+
lose_manager().result()
|
44
|
+
finally:
|
45
|
+
# Allow process to clean itself up
|
46
|
+
os.killpg(manager_pgid, signal.SIGCONT)
|
47
|
+
os.killpg(manager_pgid, signal.SIGTERM)
|
@@ -1,10 +1,9 @@
|
|
1
|
-
import parsl
|
2
|
-
import pytest
|
3
|
-
|
4
1
|
from typing import Any
|
5
2
|
|
6
|
-
|
3
|
+
import pytest
|
7
4
|
|
5
|
+
import parsl
|
6
|
+
from parsl.serialize.facade import methods_for_code
|
8
7
|
from parsl.tests.configs.htex_local import fresh_config as local_config
|
9
8
|
|
10
9
|
|
@@ -1,19 +1,24 @@
|
|
1
1
|
import logging
|
2
|
-
import pytest
|
3
2
|
import uuid
|
4
3
|
|
4
|
+
import pytest
|
5
|
+
|
5
6
|
import parsl
|
6
|
-
from parsl.serialize.facade import
|
7
|
+
from parsl.serialize.facade import (
|
8
|
+
additional_methods_for_deserialization,
|
9
|
+
methods_for_data,
|
10
|
+
register_method_for_data,
|
11
|
+
)
|
7
12
|
from parsl.tests.configs.htex_local import fresh_config
|
8
13
|
|
9
|
-
|
10
14
|
logger = logging.getLogger(__name__)
|
11
15
|
|
12
16
|
|
13
17
|
def local_setup():
|
14
|
-
from parsl.serialize.proxystore import ProxyStoreSerializer
|
15
|
-
from proxystore.store import Store, register_store
|
16
18
|
from proxystore.connectors.file import FileConnector
|
19
|
+
from proxystore.store import Store, register_store
|
20
|
+
|
21
|
+
from parsl.serialize.proxystore import ProxyStoreSerializer
|
17
22
|
|
18
23
|
parsl.load(fresh_config())
|
19
24
|
|
@@ -38,7 +43,6 @@ def local_setup():
|
|
38
43
|
|
39
44
|
def local_teardown():
|
40
45
|
parsl.dfk().cleanup()
|
41
|
-
parsl.clear()
|
42
46
|
|
43
47
|
methods_for_data.clear()
|
44
48
|
methods_for_data.update(previous_methods)
|
@@ -1,6 +1,7 @@
|
|
1
|
-
import pytest
|
2
1
|
import uuid
|
3
2
|
|
3
|
+
import pytest
|
4
|
+
|
4
5
|
|
5
6
|
def policy_example(o):
|
6
7
|
"""Example policy will proxy only lists."""
|
@@ -13,10 +14,11 @@ def test_proxystore_nonglobal():
|
|
13
14
|
"""
|
14
15
|
# import in function, because proxystore is not importable in base parsl
|
15
16
|
# installation.
|
16
|
-
from
|
17
|
+
from proxystore.connectors.file import FileConnector
|
17
18
|
from proxystore.proxy import Proxy
|
18
19
|
from proxystore.store import Store, register_store
|
19
|
-
|
20
|
+
|
21
|
+
from parsl.serialize.proxystore import ProxyStoreSerializer
|
20
22
|
|
21
23
|
store = Store(name='parsl_store_' + str(uuid.uuid4()), connector=FileConnector(store_dir="/tmp"))
|
22
24
|
register_store(store)
|
@@ -0,0 +1,64 @@
|
|
1
|
+
import os
|
2
|
+
import signal
|
3
|
+
import time
|
4
|
+
|
5
|
+
import pytest
|
6
|
+
|
7
|
+
import parsl
|
8
|
+
from parsl.tests.configs.htex_local_alternate import fresh_config
|
9
|
+
|
10
|
+
# This is a very generous upper bound on expected shutdown time of target
|
11
|
+
# process after receiving a signal, measured in seconds.
|
12
|
+
PERMITTED_SHUTDOWN_TIME_S = 60
|
13
|
+
|
14
|
+
|
15
|
+
@parsl.python_app
|
16
|
+
def simple_app():
|
17
|
+
return True
|
18
|
+
|
19
|
+
|
20
|
+
@pytest.mark.local
|
21
|
+
def test_no_kills():
|
22
|
+
"""This tests that we can create a monitoring-enabled DFK and shut it down."""
|
23
|
+
|
24
|
+
parsl.load(fresh_config())
|
25
|
+
|
26
|
+
assert parsl.dfk().monitoring is not None, "This test requires monitoring"
|
27
|
+
|
28
|
+
parsl.dfk().cleanup()
|
29
|
+
|
30
|
+
|
31
|
+
@pytest.mark.local
|
32
|
+
@pytest.mark.parametrize("sig", [signal.SIGINT, signal.SIGTERM, signal.SIGKILL, signal.SIGQUIT])
|
33
|
+
@pytest.mark.parametrize("process_attr", ["router_proc", "dbm_proc"])
|
34
|
+
def test_kill_monitoring_helper_process(sig, process_attr, try_assert):
|
35
|
+
"""This tests that we can kill a monitoring process and still have successful shutdown.
|
36
|
+
SIGINT emulates some racy behaviour when ctrl-C is pressed: that
|
37
|
+
monitoring processes receive a ctrl-C too, and so the other processes
|
38
|
+
need to be tolerant to monitoring processes arbitrarily exiting.
|
39
|
+
"""
|
40
|
+
|
41
|
+
parsl.load(fresh_config())
|
42
|
+
|
43
|
+
dfk = parsl.dfk()
|
44
|
+
|
45
|
+
assert dfk.monitoring is not None, "Monitoring required"
|
46
|
+
|
47
|
+
target_proc = getattr(dfk.monitoring, process_attr)
|
48
|
+
|
49
|
+
assert target_proc is not None, "prereq: target process must exist"
|
50
|
+
assert target_proc.is_alive(), "prereq: target process must be alive"
|
51
|
+
|
52
|
+
target_pid = target_proc.pid
|
53
|
+
assert target_pid is not None, "prereq: target process must have a pid"
|
54
|
+
|
55
|
+
os.kill(target_pid, sig)
|
56
|
+
|
57
|
+
try_assert(lambda: not target_proc.is_alive(), timeout_ms=PERMITTED_SHUTDOWN_TIME_S * 1000)
|
58
|
+
|
59
|
+
# now we have broken one piece of the monitoring system, do some app
|
60
|
+
# execution and then shut down.
|
61
|
+
|
62
|
+
simple_app().result()
|
63
|
+
|
64
|
+
parsl.dfk().cleanup()
|
@@ -62,8 +62,8 @@ def make_stage_out_app(executor, dfk):
|
|
62
62
|
|
63
63
|
|
64
64
|
def stage_out_noop(app_fu, inputs=[], _parsl_staging_inhibit=True):
|
65
|
-
import time
|
66
65
|
import logging
|
66
|
+
import time
|
67
67
|
logger = logging.getLogger(__name__)
|
68
68
|
logger.info("stage_out_noop")
|
69
69
|
time.sleep(1)
|
@@ -75,8 +75,8 @@ def make_stage_in_app(executor, dfk):
|
|
75
75
|
|
76
76
|
|
77
77
|
def stage_in_noop(parent_fut=None, outputs=[], _parsl_staging_inhibit=True):
|
78
|
-
import time
|
79
78
|
import logging
|
79
|
+
import time
|
80
80
|
logger = logging.getLogger(__name__)
|
81
81
|
logger.info("stage_in_noop")
|
82
82
|
time.sleep(1)
|
@@ -1,8 +1,9 @@
|
|
1
|
+
import time
|
2
|
+
|
1
3
|
import pytest
|
2
4
|
|
3
5
|
import parsl
|
4
|
-
import
|
5
|
-
from parsl import python_app, ThreadPoolExecutor
|
6
|
+
from parsl import ThreadPoolExecutor, python_app
|
6
7
|
from parsl.config import Config
|
7
8
|
from parsl.data_provider.files import File
|
8
9
|
from parsl.data_provider.staging import Staging
|
@@ -60,7 +61,6 @@ def test_1316_local_path_on_execution_side_sp2():
|
|
60
61
|
assert not file.local_path, "The local_path on the submit side should not be set"
|
61
62
|
|
62
63
|
parsl.dfk().cleanup()
|
63
|
-
parsl.clear()
|
64
64
|
|
65
65
|
|
66
66
|
@pytest.mark.local
|
@@ -83,4 +83,3 @@ def test_1316_local_path_setting_preserves_dependency_sp2():
|
|
83
83
|
assert not file.local_path, "The local_path on the submit side should not be set"
|
84
84
|
|
85
85
|
parsl.dfk().cleanup()
|
86
|
-
parsl.clear()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import pytest
|
2
2
|
|
3
|
-
from parsl import
|
3
|
+
from parsl import File, python_app
|
4
4
|
|
5
5
|
|
6
6
|
@python_app
|
@@ -12,6 +12,7 @@ def convert(inputs=[], outputs=[]):
|
|
12
12
|
|
13
13
|
|
14
14
|
@pytest.mark.cleannet
|
15
|
+
@pytest.mark.staging_required
|
15
16
|
def test():
|
16
17
|
# create an remote Parsl file
|
17
18
|
inp = File('ftp://ftp.iana.org/pub/mirror/rirstats/arin/ARIN-STATS-FORMAT-CHANGE.txt')
|
@@ -6,15 +6,15 @@
|
|
6
6
|
# of the globus staging provider
|
7
7
|
|
8
8
|
import logging
|
9
|
+
|
9
10
|
import pytest
|
10
11
|
|
11
12
|
import parsl
|
12
|
-
|
13
13
|
from parsl import bash_app, python_app
|
14
14
|
from parsl.config import Config
|
15
15
|
from parsl.data_provider.files import File
|
16
16
|
from parsl.executors.threads import ThreadPoolExecutor
|
17
|
-
from parsl.tests.test_staging.staging_provider import
|
17
|
+
from parsl.tests.test_staging.staging_provider import NoOpError, NoOpTestingFileStaging
|
18
18
|
|
19
19
|
logger = logging.getLogger(__name__)
|
20
20
|
|
@@ -44,7 +44,6 @@ def storage_access_parsl():
|
|
44
44
|
yield _setup_config
|
45
45
|
|
46
46
|
parsl.dfk().cleanup()
|
47
|
-
parsl.clear()
|
48
47
|
|
49
48
|
|
50
49
|
@pytest.mark.local
|
@@ -22,11 +22,11 @@ def test_files():
|
|
22
22
|
|
23
23
|
|
24
24
|
@pytest.mark.local
|
25
|
-
def test_open():
|
26
|
-
|
27
|
-
|
25
|
+
def test_open(tmpd_cwd):
|
26
|
+
fpath = tmpd_cwd / 'test-open.txt'
|
27
|
+
fpath.write_text('Hello')
|
28
28
|
|
29
|
-
pfile = File(
|
29
|
+
pfile = File(fpath)
|
30
30
|
|
31
|
-
with open(
|
32
|
-
assert (opfile.
|
31
|
+
with open(pfile) as opfile:
|
32
|
+
assert (opfile.read() == 'Hello')
|
@@ -1,5 +1,7 @@
|
|
1
1
|
from concurrent.futures import Future
|
2
2
|
|
3
|
+
import pytest
|
4
|
+
|
3
5
|
from parsl import File
|
4
6
|
from parsl.app.app import bash_app
|
5
7
|
|
@@ -14,6 +16,7 @@ def app2(inputs=(), outputs=(), stdout=None, stderr=None, mock=False):
|
|
14
16
|
return f"echo '{inputs[0]}' > {outputs[0]}"
|
15
17
|
|
16
18
|
|
19
|
+
@pytest.mark.shared_fs
|
17
20
|
def test_behavior(tmpd_cwd):
|
18
21
|
expected_path = str(tmpd_cwd / "simple-out.txt")
|
19
22
|
app1_future = app1(
|
@@ -1,9 +1,9 @@
|
|
1
|
+
import pytest
|
2
|
+
|
1
3
|
import parsl
|
2
4
|
from parsl.app.app import python_app
|
3
5
|
from parsl.data_provider.files import File
|
4
6
|
|
5
|
-
import pytest
|
6
|
-
|
7
7
|
# This config is for the local test which will adding an executor.
|
8
8
|
# Most tests in this file should be non-local and use the configuration
|
9
9
|
# specificed with --config, not this one.
|
@@ -48,6 +48,7 @@ def sort_strings_additional_executor(inputs=(), outputs=()):
|
|
48
48
|
|
49
49
|
|
50
50
|
@pytest.mark.cleannet
|
51
|
+
@pytest.mark.staging_required
|
51
52
|
def test_staging_https_cleannet(tmpd_cwd):
|
52
53
|
unsorted_file = File(_unsorted_url)
|
53
54
|
sorted_file = File(tmpd_cwd / 'sorted.txt')
|
@@ -68,6 +69,7 @@ def test_staging_https_local(tmpd_cwd):
|
|
68
69
|
|
69
70
|
|
70
71
|
@pytest.mark.cleannet
|
72
|
+
@pytest.mark.staging_required
|
71
73
|
def test_staging_https_kwargs(tmpd_cwd):
|
72
74
|
unsorted_file = File(_unsorted_url)
|
73
75
|
sorted_file = File(tmpd_cwd / 'sorted.txt')
|
@@ -78,6 +80,7 @@ def test_staging_https_kwargs(tmpd_cwd):
|
|
78
80
|
|
79
81
|
|
80
82
|
@pytest.mark.cleannet
|
83
|
+
@pytest.mark.staging_required
|
81
84
|
def test_staging_https_args(tmpd_cwd):
|
82
85
|
unsorted_file = File(_unsorted_url)
|
83
86
|
sorted_file = File(tmpd_cwd / 'sorted.txt')
|