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
@@ -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')
|
@@ -0,0 +1,64 @@
|
|
1
|
+
import logging
|
2
|
+
import os
|
3
|
+
import zipfile
|
4
|
+
|
5
|
+
import pytest
|
6
|
+
|
7
|
+
import parsl
|
8
|
+
from parsl.app.futures import DataFuture
|
9
|
+
from parsl.data_provider.files import File
|
10
|
+
from parsl.tests.configs.htex_local import fresh_config as local_config
|
11
|
+
|
12
|
+
|
13
|
+
@parsl.bash_app
|
14
|
+
def output_to_stds(*, stdout=parsl.AUTO_LOGNAME, stderr=parsl.AUTO_LOGNAME):
|
15
|
+
return "echo hello ; echo goodbye >&2"
|
16
|
+
|
17
|
+
|
18
|
+
@pytest.mark.staging_required
|
19
|
+
def test_stdout_staging_file(tmpd_cwd, caplog):
|
20
|
+
basename = str(tmpd_cwd) + "/stdout.txt"
|
21
|
+
stdout_file = File("file://" + basename)
|
22
|
+
|
23
|
+
app_future = output_to_stds(stdout=stdout_file)
|
24
|
+
|
25
|
+
assert isinstance(app_future.stdout, DataFuture)
|
26
|
+
app_future.stdout.result()
|
27
|
+
|
28
|
+
assert os.path.exists(basename)
|
29
|
+
|
30
|
+
for record in caplog.records:
|
31
|
+
assert record.levelno < logging.ERROR
|
32
|
+
|
33
|
+
|
34
|
+
@pytest.mark.staging_required
|
35
|
+
def test_stdout_stderr_staging_zip(tmpd_cwd, caplog):
|
36
|
+
zipfile_name = str(tmpd_cwd) + "/staging.zip"
|
37
|
+
stdout_relative_path = "somewhere/test-out.txt"
|
38
|
+
stdout_file = File("zip:" + zipfile_name + "/" + stdout_relative_path)
|
39
|
+
|
40
|
+
stderr_relative_path = "somewhere/test-error.txt"
|
41
|
+
stderr_file = File("zip:" + zipfile_name + "/" + stderr_relative_path)
|
42
|
+
|
43
|
+
app_future = output_to_stds(stdout=stdout_file, stderr=stderr_file)
|
44
|
+
|
45
|
+
assert isinstance(app_future.stdout, DataFuture)
|
46
|
+
app_future.stdout.result()
|
47
|
+
|
48
|
+
# check the file exists as soon as possible
|
49
|
+
assert os.path.exists(zipfile_name)
|
50
|
+
with zipfile.ZipFile(zipfile_name) as z:
|
51
|
+
with z.open(stdout_relative_path) as f:
|
52
|
+
assert f.readlines() == [b'hello\n']
|
53
|
+
|
54
|
+
assert isinstance(app_future.stderr, DataFuture)
|
55
|
+
app_future.stderr.result()
|
56
|
+
with zipfile.ZipFile(zipfile_name) as z:
|
57
|
+
with z.open(stderr_relative_path) as f:
|
58
|
+
# The last line of stderr should be goodbye, but Parsl will write
|
59
|
+
# other Parsl-specific into to stderr before that, so only assert
|
60
|
+
# the behaviour of the final line.
|
61
|
+
assert f.readlines()[-1] == b'goodbye\n'
|
62
|
+
|
63
|
+
for record in caplog.records:
|
64
|
+
assert record.levelno < logging.ERROR
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import random
|
2
|
+
import zipfile
|
3
|
+
|
4
|
+
import pytest
|
5
|
+
|
6
|
+
import parsl
|
7
|
+
from parsl.config import Config
|
8
|
+
from parsl.data_provider.files import File
|
9
|
+
from parsl.data_provider.zip import ZipAuthorityError, ZipFileStaging
|
10
|
+
from parsl.executors import HighThroughputExecutor
|
11
|
+
from parsl.launchers import SimpleLauncher
|
12
|
+
from parsl.providers import LocalProvider
|
13
|
+
from parsl.tests.configs.htex_local import fresh_config as local_config
|
14
|
+
|
15
|
+
|
16
|
+
@parsl.python_app
|
17
|
+
def count_lines(file):
|
18
|
+
with open(file, "r") as f:
|
19
|
+
return len(f.readlines())
|
20
|
+
|
21
|
+
|
22
|
+
@pytest.mark.local
|
23
|
+
def test_zip_in(tmpd_cwd):
|
24
|
+
# basic test of zip file stage-in
|
25
|
+
zip_path = tmpd_cwd / "container.zip"
|
26
|
+
file_base = "data.txt"
|
27
|
+
zip_file = File(f"zip:{zip_path / file_base}")
|
28
|
+
|
29
|
+
# create a zip file containing one file with some abitrary number of lines
|
30
|
+
n_lines = random.randint(0, 1000)
|
31
|
+
|
32
|
+
with zipfile.ZipFile(zip_path, mode='w') as z:
|
33
|
+
with z.open(file_base, mode='w') as f:
|
34
|
+
for _ in range(n_lines):
|
35
|
+
f.write(b'someline\n')
|
36
|
+
|
37
|
+
app_future = count_lines(zip_file)
|
38
|
+
|
39
|
+
assert app_future.result() == n_lines
|
@@ -0,0 +1,110 @@
|
|
1
|
+
import zipfile
|
2
|
+
|
3
|
+
import pytest
|
4
|
+
|
5
|
+
import parsl
|
6
|
+
from parsl.config import Config
|
7
|
+
from parsl.data_provider.data_manager import default_staging
|
8
|
+
from parsl.data_provider.files import File
|
9
|
+
from parsl.data_provider.zip import ZipAuthorityError, ZipFileStaging
|
10
|
+
from parsl.executors import HighThroughputExecutor
|
11
|
+
from parsl.launchers import SimpleLauncher
|
12
|
+
from parsl.providers import LocalProvider
|
13
|
+
from parsl.tests.configs.htex_local import fresh_config as local_config
|
14
|
+
|
15
|
+
|
16
|
+
@pytest.mark.local
|
17
|
+
def test_zip_path_split():
|
18
|
+
from parsl.data_provider.zip import zip_path_split
|
19
|
+
assert zip_path_split("/tmp/foo/this.zip/inside/here.txt") == ("/tmp/foo/this.zip", "inside/here.txt")
|
20
|
+
|
21
|
+
|
22
|
+
@parsl.bash_app
|
23
|
+
def output_something(outputs=()):
|
24
|
+
"""This should output something into every specified output file:
|
25
|
+
the position in the output sequence will be written into the
|
26
|
+
corresponding output file.
|
27
|
+
"""
|
28
|
+
cmds = []
|
29
|
+
for n in range(len(outputs)):
|
30
|
+
cmds.append(f"echo {n} > {outputs[n]}")
|
31
|
+
|
32
|
+
return "; ".join(cmds)
|
33
|
+
|
34
|
+
|
35
|
+
@pytest.mark.local
|
36
|
+
def test_zip_out(tmpd_cwd):
|
37
|
+
# basic test of zip file stage-out
|
38
|
+
zip_path = tmpd_cwd / "container.zip"
|
39
|
+
file_base = "data.txt"
|
40
|
+
of = File(f"zip:{zip_path / file_base}")
|
41
|
+
|
42
|
+
app_future = output_something(outputs=[of])
|
43
|
+
output_file_future = app_future.outputs[0]
|
44
|
+
|
45
|
+
app_future.result()
|
46
|
+
output_file_future.result()
|
47
|
+
|
48
|
+
assert zipfile.is_zipfile(zip_path)
|
49
|
+
|
50
|
+
with zipfile.ZipFile(zip_path) as z:
|
51
|
+
assert file_base in z.namelist()
|
52
|
+
assert len(z.namelist()) == 1
|
53
|
+
with z.open(file_base) as f:
|
54
|
+
assert f.readlines() == [b'0\n']
|
55
|
+
|
56
|
+
|
57
|
+
@pytest.mark.local
|
58
|
+
def test_zip_out_multi(tmpd_cwd):
|
59
|
+
# tests multiple files, multiple zip files and multiple
|
60
|
+
# sub-paths
|
61
|
+
|
62
|
+
zip_path_1 = tmpd_cwd / "container1.zip"
|
63
|
+
zip_path_2 = tmpd_cwd / "container2.zip"
|
64
|
+
|
65
|
+
relative_file_path_1 = "a/b/c/data.txt"
|
66
|
+
relative_file_path_2 = "something.txt"
|
67
|
+
relative_file_path_3 = "a/d/other.txt"
|
68
|
+
of1 = File(f"zip:{zip_path_1 / relative_file_path_1}")
|
69
|
+
of2 = File(f"zip:{zip_path_1 / relative_file_path_2}")
|
70
|
+
of3 = File(f"zip:{zip_path_2 / relative_file_path_3}")
|
71
|
+
|
72
|
+
app_future = output_something(outputs=[of1, of2, of3])
|
73
|
+
|
74
|
+
for f in app_future.outputs:
|
75
|
+
f.result()
|
76
|
+
|
77
|
+
app_future.result()
|
78
|
+
|
79
|
+
assert zipfile.is_zipfile(zip_path_1)
|
80
|
+
|
81
|
+
with zipfile.ZipFile(zip_path_1) as z:
|
82
|
+
assert relative_file_path_1 in z.namelist()
|
83
|
+
assert relative_file_path_2 in z.namelist()
|
84
|
+
assert len(z.namelist()) == 2
|
85
|
+
with z.open(relative_file_path_1) as f:
|
86
|
+
assert f.readlines() == [b'0\n']
|
87
|
+
with z.open(relative_file_path_2) as f:
|
88
|
+
assert f.readlines() == [b'1\n']
|
89
|
+
|
90
|
+
assert zipfile.is_zipfile(zip_path_2)
|
91
|
+
|
92
|
+
with zipfile.ZipFile(zip_path_2) as z:
|
93
|
+
assert relative_file_path_3 in z.namelist()
|
94
|
+
assert len(z.namelist()) == 1
|
95
|
+
with z.open(relative_file_path_3) as f:
|
96
|
+
assert f.readlines() == [b'2\n']
|
97
|
+
|
98
|
+
|
99
|
+
@pytest.mark.local
|
100
|
+
def test_zip_bad_authority(tmpd_cwd):
|
101
|
+
# tests that there's an exception when staging a ZIP url with an authority
|
102
|
+
# section specified, rather than silently ignoring it. This simulates a
|
103
|
+
# user who misunderstands what that piece of what a zip: URL means.
|
104
|
+
|
105
|
+
zip_path = tmpd_cwd / "container.zip"
|
106
|
+
file_base = "data.txt"
|
107
|
+
of = File(f"zip://someauthority/{zip_path / file_base}")
|
108
|
+
|
109
|
+
with pytest.raises(ZipAuthorityError):
|
110
|
+
output_something(outputs=[of])
|
@@ -0,0 +1,41 @@
|
|
1
|
+
import random
|
2
|
+
import zipfile
|
3
|
+
|
4
|
+
import pytest
|
5
|
+
|
6
|
+
import parsl
|
7
|
+
from parsl.config import Config
|
8
|
+
from parsl.data_provider.files import File
|
9
|
+
from parsl.data_provider.zip import ZipAuthorityError, ZipFileStaging
|
10
|
+
from parsl.executors import HighThroughputExecutor
|
11
|
+
from parsl.launchers import SimpleLauncher
|
12
|
+
from parsl.providers import LocalProvider
|
13
|
+
from parsl.tests.configs.htex_local import fresh_config as local_config
|
14
|
+
|
15
|
+
|
16
|
+
@parsl.python_app
|
17
|
+
def generate_lines(n: int, *, outputs):
|
18
|
+
with open(outputs[0], "w") as f:
|
19
|
+
for x in range(n):
|
20
|
+
# write numbered lines
|
21
|
+
f.write(str(x) + "\n")
|
22
|
+
|
23
|
+
|
24
|
+
@parsl.python_app
|
25
|
+
def count_lines(file):
|
26
|
+
with open(file, "r") as f:
|
27
|
+
return len(f.readlines())
|
28
|
+
|
29
|
+
|
30
|
+
@pytest.mark.local
|
31
|
+
def test_zip_pipeline(tmpd_cwd):
|
32
|
+
# basic test of zip file stage-in
|
33
|
+
zip_path = tmpd_cwd / "container.zip"
|
34
|
+
file_base = "data.txt"
|
35
|
+
zip_file = File(f"zip:{zip_path / file_base}")
|
36
|
+
|
37
|
+
n_lines = random.randint(0, 1000)
|
38
|
+
generate_fut = generate_lines(n_lines, outputs=[zip_file])
|
39
|
+
n_lines_out = count_lines(generate_fut.outputs[0]).result()
|
40
|
+
|
41
|
+
assert n_lines == n_lines_out
|
parsl/tests/test_summary.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
import parsl
|
2
1
|
import pytest
|
2
|
+
|
3
|
+
import parsl
|
3
4
|
from parsl.tests.configs.local_threads import fresh_config
|
4
5
|
|
5
6
|
|
@@ -22,7 +23,6 @@ def test_summary(caplog):
|
|
22
23
|
fail().exception()
|
23
24
|
|
24
25
|
parsl.dfk().cleanup()
|
25
|
-
parsl.clear()
|
26
26
|
|
27
27
|
assert "Summary of tasks in DFK:" in caplog.text
|
28
28
|
assert "Tasks in state States.exec_done: 1" in caplog.text
|
@@ -7,9 +7,9 @@ from parsl.tests.configs.local_threads import fresh_config
|
|
7
7
|
|
8
8
|
@python_app
|
9
9
|
def worker_identify(x, sleep_dur=0.2):
|
10
|
-
import time
|
11
10
|
import os
|
12
11
|
import threading
|
12
|
+
import time
|
13
13
|
time.sleep(sleep_dur)
|
14
14
|
return {"pid": os.getpid(),
|
15
15
|
"tid": threading.current_thread()}
|
@@ -30,5 +30,4 @@ def test_parallel_for():
|
|
30
30
|
assert thread_count <= config.executors[0].max_threads, "More threads than allowed"
|
31
31
|
assert process_count == 1, "More processes than allowed"
|
32
32
|
dfk.cleanup()
|
33
|
-
parsl.clear()
|
34
33
|
return d
|
@@ -1,5 +1,6 @@
|
|
1
|
-
import parsl
|
2
1
|
import pytest
|
2
|
+
|
3
|
+
import parsl
|
3
4
|
from parsl import python_app
|
4
5
|
from parsl.tests.configs.local_threads import fresh_config
|
5
6
|
|
@@ -24,5 +25,4 @@ def test_lazy_behavior():
|
|
24
25
|
assert f.done()
|
25
26
|
|
26
27
|
parsl.dfk().cleanup()
|
27
|
-
parsl.clear()
|
28
28
|
return
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import pytest
|
2
|
+
|
3
|
+
from parsl.utils import execute_wait
|
4
|
+
|
5
|
+
|
6
|
+
@pytest.mark.local
|
7
|
+
def test_env():
|
8
|
+
''' Regression testing for issue #27
|
9
|
+
'''
|
10
|
+
|
11
|
+
rc, stdout, stderr = execute_wait("env", 1)
|
12
|
+
|
13
|
+
stdout = stdout.split('\n')
|
14
|
+
x = [s for s in stdout if s.startswith("PATH=")]
|
15
|
+
assert x, "PATH not found"
|
16
|
+
|
17
|
+
x = [s for s in stdout if s.startswith("HOME=")]
|
18
|
+
assert x, "HOME not found"
|
19
|
+
|
20
|
+
|
21
|
+
@pytest.mark.local
|
22
|
+
def test_large_output_2210():
|
23
|
+
"""Regression test for #2210.
|
24
|
+
execute_wait was hanging if the specified command gave too
|
25
|
+
much output, due to a race condition between process exiting and
|
26
|
+
pipes filling up.
|
27
|
+
"""
|
28
|
+
|
29
|
+
# this will output 128kb of stdout
|
30
|
+
execute_wait("yes | dd count=128 bs=1024", walltime=60)
|
31
|
+
|
32
|
+
# if this test fails, execute_wait should raise a timeout
|
33
|
+
# exception.
|
34
|
+
|
35
|
+
# The contents out the output is not verified by this test
|
@@ -0,0 +1,76 @@
|
|
1
|
+
import random
|
2
|
+
import re
|
3
|
+
|
4
|
+
import pytest
|
5
|
+
|
6
|
+
from parsl.utils import sanitize_dns_label_rfc1123, sanitize_dns_subdomain_rfc1123
|
7
|
+
|
8
|
+
# Ref: https://datatracker.ietf.org/doc/html/rfc1123
|
9
|
+
DNS_LABEL_REGEX = r'^[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?$'
|
10
|
+
DNS_SUBDOMAIN_REGEX = r'^[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?(\.[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?)*$'
|
11
|
+
|
12
|
+
test_labels = [
|
13
|
+
"example-label-123", # Valid label
|
14
|
+
"EXAMPLE", # Case sensitivity
|
15
|
+
"!@#example*", # Remove invalid characters
|
16
|
+
"--leading-and-trailing--", # Leading and trailing hyphens
|
17
|
+
"..leading.and.trailing..", # Leading and tailing dots
|
18
|
+
"multiple..dots", # Consecutive dots
|
19
|
+
"valid--label", # Consecutive hyphens
|
20
|
+
"a" * random.randint(64, 70), # Longer than 63 characters
|
21
|
+
f"{'a' * 62}-a", # Trailing hyphen at max length
|
22
|
+
]
|
23
|
+
|
24
|
+
|
25
|
+
def _generate_test_subdomains(num_subdomains: int):
|
26
|
+
subdomains = []
|
27
|
+
for _ in range(num_subdomains):
|
28
|
+
num_labels = random.randint(1, 5)
|
29
|
+
labels = [test_labels[random.randint(0, num_labels - 1)] for _ in range(num_labels)]
|
30
|
+
subdomain = ".".join(labels)
|
31
|
+
subdomains.append(subdomain)
|
32
|
+
return subdomains
|
33
|
+
|
34
|
+
|
35
|
+
@pytest.mark.local
|
36
|
+
@pytest.mark.parametrize("raw_string", test_labels)
|
37
|
+
def test_sanitize_dns_label_rfc1123(raw_string: str):
|
38
|
+
print(sanitize_dns_label_rfc1123(raw_string))
|
39
|
+
assert re.match(DNS_LABEL_REGEX, sanitize_dns_label_rfc1123(raw_string))
|
40
|
+
|
41
|
+
|
42
|
+
@pytest.mark.local
|
43
|
+
@pytest.mark.parametrize("raw_string", ("", "-", "@", "$$$"))
|
44
|
+
def test_sanitize_dns_label_rfc1123_empty(raw_string: str):
|
45
|
+
with pytest.raises(ValueError) as e_info:
|
46
|
+
sanitize_dns_label_rfc1123(raw_string)
|
47
|
+
assert str(e_info.value) == f"Sanitized DNS label is empty for input '{raw_string}'"
|
48
|
+
|
49
|
+
|
50
|
+
@pytest.mark.local
|
51
|
+
@pytest.mark.parametrize("raw_string", _generate_test_subdomains(10))
|
52
|
+
def test_sanitize_dns_subdomain_rfc1123(raw_string: str):
|
53
|
+
assert re.match(DNS_SUBDOMAIN_REGEX, sanitize_dns_subdomain_rfc1123(raw_string))
|
54
|
+
|
55
|
+
|
56
|
+
@pytest.mark.local
|
57
|
+
@pytest.mark.parametrize("char", ("-", "."))
|
58
|
+
def test_sanitize_dns_subdomain_rfc1123_trailing_non_alphanumeric_at_max_length(char: str):
|
59
|
+
raw_string = (f"{'a' * 61}." * 4) + f".aaaa{char}a"
|
60
|
+
assert re.match(DNS_SUBDOMAIN_REGEX, sanitize_dns_subdomain_rfc1123(raw_string))
|
61
|
+
|
62
|
+
|
63
|
+
@pytest.mark.local
|
64
|
+
@pytest.mark.parametrize("raw_string", ("", ".", "..."))
|
65
|
+
def test_sanitize_dns_subdomain_rfc1123_empty(raw_string: str):
|
66
|
+
with pytest.raises(ValueError) as e_info:
|
67
|
+
sanitize_dns_subdomain_rfc1123(raw_string)
|
68
|
+
assert str(e_info.value) == f"Sanitized DNS subdomain is empty for input '{raw_string}'"
|
69
|
+
|
70
|
+
|
71
|
+
@pytest.mark.local
|
72
|
+
@pytest.mark.parametrize(
|
73
|
+
"raw_string", ("a" * 253, "a" * random.randint(254, 300)), ids=("254 chars", ">253 chars")
|
74
|
+
)
|
75
|
+
def test_sanitize_dns_subdomain_rfc1123_max_length(raw_string: str):
|
76
|
+
assert len(sanitize_dns_subdomain_rfc1123(raw_string)) <= 253
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import pytest
|
2
|
+
|
3
|
+
from parsl.addresses import tcp_url
|
4
|
+
|
5
|
+
|
6
|
+
@pytest.mark.local
|
7
|
+
@pytest.mark.parametrize("address, port,expected", [
|
8
|
+
("127.0.0.1", 55001, "tcp://127.0.0.1:55001"),
|
9
|
+
("127.0.0.1", "55001", "tcp://127.0.0.1:55001"),
|
10
|
+
("127.0.0.1", None, "tcp://127.0.0.1"),
|
11
|
+
("::1", "55001", "tcp://[::1]:55001"),
|
12
|
+
("::ffff:127.0.0.1", 55001, "tcp://[::ffff:127.0.0.1]:55001"),
|
13
|
+
("::ffff:127.0.0.1", None, "tcp://::ffff:127.0.0.1"),
|
14
|
+
("::ffff:127.0.0.1", None, "tcp://::ffff:127.0.0.1"),
|
15
|
+
("*", None, "tcp://*"),
|
16
|
+
])
|
17
|
+
def test_tcp_url(address, port, expected):
|
18
|
+
"""Confirm valid address generation"""
|
19
|
+
result = tcp_url(address, port)
|
20
|
+
assert result == expected
|
@@ -0,0 +1,99 @@
|
|
1
|
+
import os
|
2
|
+
from unittest import mock
|
3
|
+
|
4
|
+
import pytest
|
5
|
+
|
6
|
+
from parsl import File
|
7
|
+
|
8
|
+
_MOCK_BASE = "parsl.data_provider.files."
|
9
|
+
|
10
|
+
|
11
|
+
@pytest.mark.local
|
12
|
+
@pytest.mark.parametrize("scheme", ("http", "https", "ftp", "ftps", "asdfasdf"))
|
13
|
+
def test_file_init_scheme(scheme):
|
14
|
+
basename = "some_base_name"
|
15
|
+
path = f"/some/path/1/2/3/{basename}"
|
16
|
+
fqdn = "some.fqdn.example.com"
|
17
|
+
exp_url = f"{scheme}://{fqdn}{path}"
|
18
|
+
f = File(exp_url)
|
19
|
+
assert f.url == exp_url, "Expected given url to be stored"
|
20
|
+
assert f.scheme == scheme
|
21
|
+
assert f.netloc == fqdn
|
22
|
+
assert f.path == path
|
23
|
+
assert f.filename == basename
|
24
|
+
assert f.local_path is None, "Expect only set by API consumer, not constructor"
|
25
|
+
|
26
|
+
|
27
|
+
@pytest.mark.local
|
28
|
+
@pytest.mark.parametrize("url", ("some weird :// url", "", "a"))
|
29
|
+
def test_file_init_file_url_fallback(url):
|
30
|
+
exp_url = "some weird :// url"
|
31
|
+
f = File(exp_url)
|
32
|
+
assert f.url == exp_url
|
33
|
+
assert not f.netloc, "invalid host, should be no netloc"
|
34
|
+
assert f.path == exp_url, "Should fail to fully parse, so path is whole url"
|
35
|
+
assert f.filename == exp_url.rsplit("/", 1)[-1]
|
36
|
+
|
37
|
+
assert f.scheme == "file"
|
38
|
+
|
39
|
+
|
40
|
+
@pytest.mark.local
|
41
|
+
def test_file_proxies_for_filepath(randomstring):
|
42
|
+
# verify (current) expected internal hookup
|
43
|
+
exp_filepath = randomstring()
|
44
|
+
with mock.patch(
|
45
|
+
f"{_MOCK_BASE}File.filepath", new_callable=mock.PropertyMock
|
46
|
+
) as mock_fpath:
|
47
|
+
mock_fpath.return_value = exp_filepath
|
48
|
+
f = File("")
|
49
|
+
assert str(f) == exp_filepath
|
50
|
+
assert os.fspath(f) == exp_filepath
|
51
|
+
|
52
|
+
|
53
|
+
@pytest.mark.local
|
54
|
+
@pytest.mark.parametrize("scheme", ("file://", ""))
|
55
|
+
def test_file_filepath_local_path_is_priority(scheme, randomstring):
|
56
|
+
exp_path = "/some/local/path"
|
57
|
+
url = f"{scheme}{exp_path}"
|
58
|
+
f = File(url)
|
59
|
+
|
60
|
+
f.local_path = randomstring()
|
61
|
+
assert f.filepath == f.local_path
|
62
|
+
|
63
|
+
f.local_path = None
|
64
|
+
assert f.filepath == exp_path
|
65
|
+
|
66
|
+
|
67
|
+
@pytest.mark.local
|
68
|
+
def test_file_filepath_requires_local_accessible_path():
|
69
|
+
with pytest.raises(ValueError) as pyt_exc:
|
70
|
+
_ = File("http://").filepath
|
71
|
+
|
72
|
+
assert "No local_path" in str(pyt_exc.value), "Expected reason in exception"
|
73
|
+
|
74
|
+
|
75
|
+
@pytest.mark.local
|
76
|
+
@pytest.mark.parametrize("scheme", ("https", "ftps", "", "file", "asdfasdf"))
|
77
|
+
def test_file_repr(scheme):
|
78
|
+
netloc = "some.netloc"
|
79
|
+
filename = "some_file_name"
|
80
|
+
path = f"/some/path/{filename}"
|
81
|
+
if scheme:
|
82
|
+
url = f"{scheme}://{netloc}{path}"
|
83
|
+
else:
|
84
|
+
scheme = "file"
|
85
|
+
url = path
|
86
|
+
|
87
|
+
f = File(url)
|
88
|
+
r = repr(f)
|
89
|
+
assert r.startswith("<")
|
90
|
+
assert r.endswith(">")
|
91
|
+
assert f"<{type(f).__name__} " in r
|
92
|
+
assert f" at 0x{id(f):x}" in r
|
93
|
+
assert f" url={url}" in r
|
94
|
+
assert f" scheme={scheme}" in r
|
95
|
+
assert f" path={path}" in r
|
96
|
+
assert f" filename={filename}" in r
|
97
|
+
|
98
|
+
if scheme != "file":
|
99
|
+
assert f" netloc={netloc}" in r
|