hpcflow-new2 0.2.0a218__tar.gz → 0.2.0a220__tar.gz
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.
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/PKG-INFO +1 -1
- hpcflow_new2-0.2.0a220/hpcflow/_version.py +1 -0
- hpcflow_new2-0.2.0a220/hpcflow/data/scripts/import_future_script.py +7 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/app.py +2 -1
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/errors.py +6 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/parameters.py +21 -23
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/utils.py +18 -3
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/workflow.py +15 -5
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/persistence/zarr.py +1 -1
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/submission/jobscript.py +15 -2
- hpcflow_new2-0.2.0a220/hpcflow/sdk/utils/strings.py +61 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/scripts/test_main_scripts.py +27 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_element_set.py +25 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_input_source.py +71 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_utils.py +40 -1
- hpcflow_new2-0.2.0a220/hpcflow/tests/unit/utils/test_strings.py +97 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/pyproject.toml +2 -2
- hpcflow_new2-0.2.0a218/hpcflow/_version.py +0 -1
- hpcflow_new2-0.2.0a218/hpcflow/sdk/utils/strings.py +0 -33
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/LICENSE +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/README.md +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/__init__.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/__pyinstaller/__init__.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/__pyinstaller/hook-hpcflow.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/app.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/cli.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/demo_data_manifest/__init__.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/demo_data_manifest/demo_data_manifest.json +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/__init__.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/bad_script.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/demo_task_1_generate_t1_infile_1.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/demo_task_1_generate_t1_infile_2.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/demo_task_1_parse_p3.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/do_nothing.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/env_specifier_test/input_file_generator_pass_env_spec.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/env_specifier_test/main_script_test_pass_env_spec.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/env_specifier_test/output_file_parser_pass_env_spec.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/env_specifier_test/v1/input_file_generator_basic.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/env_specifier_test/v1/main_script_test_direct_in_direct_out.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/env_specifier_test/v1/output_file_parser_basic.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/env_specifier_test/v2/main_script_test_direct_in_direct_out.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/generate_t1_file_01.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/input_file_generator_basic.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/input_file_generator_basic_FAIL.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/input_file_generator_test_stdout_stderr.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_direct_in.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_direct_in_direct_out.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_direct_in_direct_out_2.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed_group.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_direct_in_direct_out_3.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_direct_in_direct_out_all_iters_test.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_direct_in_direct_out_env_spec.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_direct_in_direct_out_labels.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_direct_in_group_direct_out_3.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_direct_in_group_one_fail_direct_out_3.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_direct_sub_param_in_direct_out.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_hdf5_in_obj.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_hdf5_in_obj_2.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_hdf5_in_obj_group.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_hdf5_out_obj.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_json_and_direct_in_json_out.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_json_in_json_and_direct_out.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_json_in_json_out.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_json_in_json_out_labels.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_json_in_obj.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_json_out_FAIL.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_json_out_obj.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_json_sub_param_in_json_out_labels.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_shell_env_vars.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/main_script_test_std_out_std_err.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/output_file_parser_basic.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/output_file_parser_basic_FAIL.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/output_file_parser_test_stdout_stderr.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/parse_t1_file_01.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/scripts/script_exit_test.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/template_components/__init__.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/template_components/command_files.yaml +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/template_components/environments.yaml +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/template_components/parameters.yaml +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/template_components/task_schemas.yaml +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/workflows/__init__.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/data/workflows/workflow_1.yaml +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/examples.ipynb +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/__init__.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/cli.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/cli_common.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/config/__init__.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/config/callbacks.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/config/cli.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/config/config.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/config/config_file.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/config/errors.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/config/types.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/__init__.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/actions.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/app_aware.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/cache.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/command_files.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/commands.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/element.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/enums.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/environment.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/execute.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/json_like.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/loop.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/loop_cache.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/object_list.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/rule.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/run_dir_files.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/skip_reason.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/task.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/task_schema.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/test_utils.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/types.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/validation.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/core/zarr_io.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/data/__init__.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/data/config_file_schema.yaml +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/data/config_schema.yaml +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/data/environments_spec_schema.yaml +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/data/files_spec_schema.yaml +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/data/parameters_spec_schema.yaml +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/data/task_schema_spec_schema.yaml +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/data/workflow_spec_schema.yaml +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/demo/__init__.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/demo/cli.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/helper/__init__.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/helper/cli.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/helper/helper.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/helper/watcher.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/log.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/persistence/__init__.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/persistence/base.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/persistence/defaults.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/persistence/discovery.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/persistence/json.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/persistence/pending.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/persistence/store_resource.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/persistence/types.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/persistence/utils.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/runtime.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/submission/__init__.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/submission/enums.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/submission/schedulers/__init__.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/submission/schedulers/direct.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/submission/schedulers/sge.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/submission/schedulers/slurm.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/submission/schedulers/utils.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/submission/shells/__init__.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/submission/shells/base.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/submission/shells/bash.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/submission/shells/os_version.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/submission/shells/powershell.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/submission/submission.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/submission/types.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/typing.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/utils/arrays.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/utils/deferred_file.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/utils/hashing.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/sdk/utils/patches.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/api/test_api.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/conftest.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/data/__init__.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/data/benchmark_N_elements.yaml +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/data/benchmark_script_runner.yaml +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/data/multi_path_sequences.yaml +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/data/workflow_1.json +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/data/workflow_1.yaml +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/data/workflow_1_slurm.yaml +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/data/workflow_1_wsl.yaml +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/data/workflow_test_run_abort.yaml +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/schedulers/direct_linux/test_direct_linux_submission.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/schedulers/sge/test_sge_submission.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/schedulers/slurm/test_slurm_submission.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/scripts/test_input_file_generators.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/scripts/test_non_snippet_script.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/scripts/test_ouput_file_parsers.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/shells/wsl/test_wsl_submission.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_action.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_action_rule.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_app.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_cache.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_cli.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_command.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_config.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_config_file.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_element.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_element_iteration.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_group.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_input_value.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_jobscript_unit.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_json_like.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_loop.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_meta_task.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_multi_path_sequences.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_object_list.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_parameter.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_persistence.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_resources.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_run.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_run_directories.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_runtime.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_schema_input.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_shell.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_slurm.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_submission.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_task.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_task_schema.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_value_sequence.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_workflow.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/test_workflow_template.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/utils/test_arrays.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/utils/test_deferred_file_writer.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/utils/test_hashing.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/utils/test_patches.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/unit/utils/test_redirect_std.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/workflows/__init__.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/workflows/test_directory_structure.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/workflows/test_jobscript.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/workflows/test_run_status.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/workflows/test_skip_downstream.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/workflows/test_submission.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/workflows/test_workflows.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/workflows/test_zip.py +0 -0
- {hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/viz_demo.ipynb +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
__version__ = "0.2.0a220"
|
@@ -1794,7 +1794,8 @@ class BaseApp(metaclass=Singleton):
|
|
1794
1794
|
for comp_type in TEMPLATE_COMP_TYPES:
|
1795
1795
|
with open_text_resource(package, f"{comp_type}.yaml") as fh:
|
1796
1796
|
SDK_logger.info(f"Parsing file as YAML: {fh.name!r}")
|
1797
|
-
|
1797
|
+
source = f"from {Path(fh.name)!r}"
|
1798
|
+
components[comp_type] = read_YAML_str(fh.read(), source=source)
|
1798
1799
|
|
1799
1800
|
return components
|
1800
1801
|
|
@@ -1020,3 +1020,9 @@ class MissingElementGroup(ValueError):
|
|
1020
1020
|
f"Adding elements to task {task_name!r}: "
|
1021
1021
|
f"no element group named {group_name!r} found for input {input_path!r}."
|
1022
1022
|
)
|
1023
|
+
|
1024
|
+
|
1025
|
+
class YAMLError(ValueError):
|
1026
|
+
"""
|
1027
|
+
A problem with parsing a YAML file.
|
1028
|
+
"""
|
@@ -2788,31 +2788,31 @@ class InputSource(JSONLike):
|
|
2788
2788
|
|
2789
2789
|
Examples
|
2790
2790
|
--------
|
2791
|
-
|
2792
|
-
|
2793
|
-
|
2794
|
-
|
2795
|
-
|
2791
|
+
For a local task input source, use:
|
2792
|
+
|
2793
|
+
>>> InputSource.from_string("local")
|
2794
|
+
|
2795
|
+
For a schema input default source, use:
|
2796
|
+
|
2797
|
+
>>> InputSource.from_string("default")
|
2798
|
+
|
2799
|
+
For task input sources, specify either the task insert ID (typically this is just
|
2800
|
+
the task index within the workflow), or the task's unique name, which is usually
|
2801
|
+
just the associated task schema's objective, but if multiple tasks use the same
|
2802
|
+
schema, it will be suffixed by an index, starting from one.
|
2803
|
+
|
2804
|
+
>>> InputSource.from_string("task.0.input")
|
2805
|
+
>>> InputSource.from_string("task.my_task.input")
|
2796
2806
|
"""
|
2797
2807
|
return cls(**cls._parse_from_string(str_defn))
|
2798
2808
|
|
2799
2809
|
@staticmethod
|
2800
2810
|
def _parse_from_string(str_defn: str) -> dict[str, Any]:
|
2801
|
-
"""Parse a dot-delimited string definition of an InputSource.
|
2802
|
-
|
2803
|
-
Examples
|
2804
|
-
--------
|
2805
|
-
task.[task_ref].input
|
2806
|
-
task.[task_ref].output
|
2807
|
-
local
|
2808
|
-
default
|
2809
|
-
import.[import_ref]
|
2810
|
-
"""
|
2811
|
+
"""Parse a dot-delimited string definition of an InputSource."""
|
2811
2812
|
parts = str_defn.split(".")
|
2812
2813
|
source_type = get_enum_by_name_or_val(InputSourceType, parts[0])
|
2813
|
-
task_ref: int | None = None
|
2814
|
+
task_ref: int | str | None = None
|
2814
2815
|
task_source_type: TaskSourceType | None = None
|
2815
|
-
import_ref: int | None = None
|
2816
2816
|
if (
|
2817
2817
|
(
|
2818
2818
|
source_type in (InputSourceType.LOCAL, InputSourceType.DEFAULT)
|
@@ -2826,22 +2826,20 @@ class InputSource(JSONLike):
|
|
2826
2826
|
if source_type is InputSourceType.TASK:
|
2827
2827
|
# TODO: does this include element_iters?
|
2828
2828
|
try:
|
2829
|
+
# assume specified by task insert ID
|
2829
2830
|
task_ref = int(parts[1])
|
2830
2831
|
except ValueError:
|
2831
|
-
|
2832
|
+
# assume specified by task unique name
|
2833
|
+
task_ref = parts[1]
|
2832
2834
|
try:
|
2833
2835
|
task_source_type = get_enum_by_name_or_val(TaskSourceType, parts[2])
|
2834
2836
|
except IndexError:
|
2835
2837
|
task_source_type = TaskSourceType.OUTPUT
|
2836
2838
|
elif source_type is InputSourceType.IMPORT:
|
2837
|
-
|
2838
|
-
import_ref = int(parts[1])
|
2839
|
-
except ValueError:
|
2840
|
-
pass
|
2839
|
+
raise NotImplementedError("Import input sources are not yet supported.")
|
2841
2840
|
|
2842
2841
|
return {
|
2843
2842
|
"source_type": source_type,
|
2844
|
-
"import_ref": import_ref,
|
2845
2843
|
"task_ref": task_ref,
|
2846
2844
|
"task_source_type": task_source_type,
|
2847
2845
|
}
|
@@ -30,12 +30,14 @@ import fsspec # type: ignore
|
|
30
30
|
import numpy as np
|
31
31
|
|
32
32
|
from ruamel.yaml import YAML
|
33
|
+
from ruamel.yaml.error import MarkedYAMLError
|
33
34
|
from watchdog.utils.dirsnapshot import DirectorySnapshot
|
34
35
|
|
35
36
|
from hpcflow.sdk.core.errors import (
|
36
37
|
ContainerKeyError,
|
37
38
|
InvalidIdentifier,
|
38
39
|
MissingVariableSubstitutionError,
|
40
|
+
YAMLError,
|
39
41
|
)
|
40
42
|
from hpcflow.sdk.log import TimeIt
|
41
43
|
from hpcflow.sdk.utils.deferred_file import DeferredFileWriter
|
@@ -412,7 +414,10 @@ def substitute_string_vars(string: str, variables: dict[str, str]):
|
|
412
414
|
|
413
415
|
@TimeIt.decorator
|
414
416
|
def read_YAML_str(
|
415
|
-
yaml_str: str,
|
417
|
+
yaml_str: str,
|
418
|
+
typ="safe",
|
419
|
+
variables: dict[str, str] | Literal[False] | None = None,
|
420
|
+
source: str | None = None,
|
416
421
|
) -> Any:
|
417
422
|
"""Load a YAML string. This will produce basic objects.
|
418
423
|
|
@@ -426,11 +431,21 @@ def read_YAML_str(
|
|
426
431
|
String variables to substitute in `yaml_str`. Substitutions will be attempted if
|
427
432
|
the file looks to contain variable references (like "<<var:name>>"). If set to
|
428
433
|
`False`, no substitutions will occur.
|
434
|
+
source:
|
435
|
+
Used to document the source of the YAML string if raising a parsing error.
|
436
|
+
Typically, this should be a string that starts with "from ...", e.g.
|
437
|
+
"from the file path '/path/to/bad/file'".
|
429
438
|
"""
|
430
439
|
if variables is not False and "<<var:" in yaml_str:
|
431
440
|
yaml_str = substitute_string_vars(yaml_str, variables=variables or {})
|
432
441
|
yaml = YAML(typ=typ)
|
433
|
-
|
442
|
+
try:
|
443
|
+
return yaml.load(yaml_str)
|
444
|
+
except MarkedYAMLError as err: # includes `ScannerError` and `ParserError`
|
445
|
+
source_str = f"{source} " if source else ""
|
446
|
+
raise YAMLError(
|
447
|
+
f"The YAML string {source_str}is not formatted correctly."
|
448
|
+
) from err
|
434
449
|
|
435
450
|
|
436
451
|
@TimeIt.decorator
|
@@ -452,7 +467,7 @@ def read_YAML_file(
|
|
452
467
|
"""
|
453
468
|
with fsspec.open(path, "rt") as f:
|
454
469
|
yaml_str: str = f.read()
|
455
|
-
return read_YAML_str(yaml_str, typ=typ, variables=variables)
|
470
|
+
return read_YAML_str(yaml_str, typ=typ, variables=variables, source=f"from {path!r}")
|
456
471
|
|
457
472
|
|
458
473
|
def write_YAML_file(obj, path: str | Path, typ: str = "safe") -> None:
|
@@ -578,7 +578,13 @@ class WorkflowTemplate(JSONLike):
|
|
578
578
|
set to `False`, no substitutions will occur, which may result in an invalid
|
579
579
|
workflow template!
|
580
580
|
"""
|
581
|
-
return cls._from_data(
|
581
|
+
return cls._from_data(
|
582
|
+
read_YAML_str(
|
583
|
+
string,
|
584
|
+
variables=variables,
|
585
|
+
source="(from the inline workflow template definition)",
|
586
|
+
)
|
587
|
+
)
|
582
588
|
|
583
589
|
@classmethod
|
584
590
|
def _check_name(cls, data: dict[str, Any], path: PathLike) -> None:
|
@@ -962,9 +968,12 @@ class Workflow(AppAware):
|
|
962
968
|
f"({task.name!r})..."
|
963
969
|
)
|
964
970
|
wk._add_task(task)
|
965
|
-
if status:
|
966
|
-
status.update(f"Preparing to add {len(template.loops)} loops...")
|
967
971
|
if template.loops:
|
972
|
+
if status:
|
973
|
+
status.update(
|
974
|
+
f"Preparing to add {len(template.loops)} loops; building "
|
975
|
+
f"cache..."
|
976
|
+
)
|
968
977
|
# TODO: if loop with non-initialisable actions, will fail
|
969
978
|
cache = LoopCache.build(workflow=wk, loops=template.loops)
|
970
979
|
for idx, loop in enumerate(template.loops):
|
@@ -979,6 +988,8 @@ class Workflow(AppAware):
|
|
979
988
|
f"Added {len(template.loops)} loops. "
|
980
989
|
f"Committing to store..."
|
981
990
|
)
|
991
|
+
elif status:
|
992
|
+
status.update("Committing to store...")
|
982
993
|
except (Exception, NotImplementedError):
|
983
994
|
if status:
|
984
995
|
status.stop()
|
@@ -4235,8 +4246,7 @@ class Workflow(AppAware):
|
|
4235
4246
|
input_source.task_ref = uniq_names_cur[input_source.task_ref]
|
4236
4247
|
except KeyError:
|
4237
4248
|
raise InvalidInputSourceTaskReference(
|
4238
|
-
|
4239
|
-
f"or inaccessible task: {input_source.task_ref!r}."
|
4249
|
+
input_source, task_ref=input_source.task_ref
|
4240
4250
|
)
|
4241
4251
|
|
4242
4252
|
@TimeIt.decorator
|
@@ -124,7 +124,7 @@ def _encode_numpy_array(
|
|
124
124
|
new_idx = (
|
125
125
|
max((int(i.removeprefix("arr_")) for i in param_arr_group.keys()), default=-1) + 1
|
126
126
|
)
|
127
|
-
param_arr_group.create_dataset(name=f"arr_{new_idx}", data=obj)
|
127
|
+
param_arr_group.create_dataset(name=f"arr_{new_idx}", data=obj, chunks=obj.shape)
|
128
128
|
type_lookup["arrays"].append([path, new_idx])
|
129
129
|
|
130
130
|
return len(type_lookup["arrays"]) - 1
|
@@ -24,6 +24,7 @@ from hpcflow.sdk.core.errors import (
|
|
24
24
|
from hpcflow.sdk.typing import hydrate
|
25
25
|
from hpcflow.sdk.core.json_like import ChildObjectSpec, JSONLike
|
26
26
|
from hpcflow.sdk.core.utils import nth_value, parse_timestamp, current_timestamp
|
27
|
+
from hpcflow.sdk.utils.strings import extract_py_from_future_imports
|
27
28
|
from hpcflow.sdk.log import TimeIt
|
28
29
|
from hpcflow.sdk.submission.schedulers import QueuedScheduler
|
29
30
|
from hpcflow.sdk.submission.schedulers.direct import DirectScheduler
|
@@ -1942,10 +1943,13 @@ class Jobscript(JSONLike):
|
|
1942
1943
|
tab_indent = " "
|
1943
1944
|
|
1944
1945
|
script_funcs_lst: list[str] = []
|
1946
|
+
future_imports: set[str] = set()
|
1945
1947
|
for act_name, (_, snip_path) in script_data.items():
|
1946
1948
|
main_func_name = snip_path.stem
|
1947
1949
|
with snip_path.open("rt") as fp:
|
1948
1950
|
script_str = fp.read()
|
1951
|
+
script_str, future_imports_i = extract_py_from_future_imports(script_str)
|
1952
|
+
future_imports.update(future_imports_i)
|
1949
1953
|
script_funcs_lst.append(
|
1950
1954
|
dedent(
|
1951
1955
|
"""\
|
@@ -2325,13 +2329,22 @@ class Jobscript(JSONLike):
|
|
2325
2329
|
func_invoc_lines=indent(func_invoc_lines, tab_indent * 4),
|
2326
2330
|
)
|
2327
2331
|
|
2332
|
+
future_imports_str = (
|
2333
|
+
f"from __future__ import {', '.join(future_imports)}\n\n"
|
2334
|
+
if future_imports
|
2335
|
+
else ""
|
2336
|
+
)
|
2328
2337
|
script = dedent(
|
2329
2338
|
"""\
|
2330
|
-
{script_funcs}
|
2339
|
+
{future_imports_str}{script_funcs}
|
2331
2340
|
if __name__ == "__main__":
|
2332
2341
|
{main}
|
2333
2342
|
"""
|
2334
|
-
).format(
|
2343
|
+
).format(
|
2344
|
+
future_imports_str=future_imports_str,
|
2345
|
+
script_funcs=script_funcs,
|
2346
|
+
main=indent(main, tab_indent),
|
2347
|
+
)
|
2335
2348
|
|
2336
2349
|
num_elems = [i.num_elements for i in self.blocks]
|
2337
2350
|
num_acts = [len(i) for i in action_scripts]
|
@@ -0,0 +1,61 @@
|
|
1
|
+
from typing import Iterable
|
2
|
+
import re
|
3
|
+
|
4
|
+
|
5
|
+
def shorten_list_str(
|
6
|
+
lst: Iterable, items: int = 10, end_num: int = 1, placeholder: str = "..."
|
7
|
+
) -> str:
|
8
|
+
"""Format a list as a string, including only some maximum number of items.
|
9
|
+
|
10
|
+
Parameters
|
11
|
+
----------
|
12
|
+
lst:
|
13
|
+
The list to format in a shortened form.
|
14
|
+
items:
|
15
|
+
The total number of items to include in the formatted list.
|
16
|
+
end_num:
|
17
|
+
The number of items to include at the end of the formatted list.
|
18
|
+
placeholder
|
19
|
+
The placeholder to use to replace excess items in the formatted list.
|
20
|
+
|
21
|
+
Examples
|
22
|
+
--------
|
23
|
+
>>> shorten_list_str(list(range(20)), items=5)
|
24
|
+
'[0, 1, 2, 3, ..., 19]'
|
25
|
+
|
26
|
+
"""
|
27
|
+
lst = list(lst)
|
28
|
+
if len(lst) <= items + 1: # (don't replace only one item)
|
29
|
+
lst_short = lst
|
30
|
+
else:
|
31
|
+
start_num = items - end_num
|
32
|
+
lst_short = lst[:start_num] + ["..."] + lst[-end_num:]
|
33
|
+
|
34
|
+
return "[" + ", ".join(f"{i}" for i in lst_short) + "]"
|
35
|
+
|
36
|
+
|
37
|
+
def extract_py_from_future_imports(py_str: str) -> tuple[str, set[str]]:
|
38
|
+
"""
|
39
|
+
Remove any `from __future__ import <feature>` lines from a string of Python code, and
|
40
|
+
return the modified string, and a list of `<feature>`s that were imported.
|
41
|
+
|
42
|
+
Notes
|
43
|
+
-----
|
44
|
+
This is required when generated a combined-scripts jobscript that concatenates
|
45
|
+
multiple Python scripts into one script. If `__future__` statements are included in
|
46
|
+
these individual scripts, they must be moved to the top of the file [1].
|
47
|
+
|
48
|
+
References
|
49
|
+
----------
|
50
|
+
[1] https://docs.python.org/3/reference/simple_stmts.html#future-statements
|
51
|
+
|
52
|
+
"""
|
53
|
+
|
54
|
+
pattern = r"^from __future__ import (.*)\n"
|
55
|
+
if future_imports := (set(re.findall(pattern, py_str, flags=re.MULTILINE) or ())):
|
56
|
+
future_imports = {
|
57
|
+
j.strip() for i in future_imports for j in i.split(",") if j.strip()
|
58
|
+
}
|
59
|
+
py_str = re.sub(pattern, "", py_str, flags=re.MULTILINE)
|
60
|
+
|
61
|
+
return (py_str, future_imports)
|
{hpcflow_new2-0.2.0a218 → hpcflow_new2-0.2.0a220}/hpcflow/tests/scripts/test_main_scripts.py
RENAMED
@@ -6,6 +6,7 @@ import time
|
|
6
6
|
import pytest
|
7
7
|
|
8
8
|
from hpcflow.app import app as hf
|
9
|
+
from hpcflow.sdk.core.enums import EARStatus
|
9
10
|
from hpcflow.sdk.core.test_utils import P1_parameter_cls as P1
|
10
11
|
|
11
12
|
# note: when testing the frozen app, we might not have MatFlow installed in the built in
|
@@ -1332,3 +1333,29 @@ def test_combine_scripts_script_data_multiple_input_file_formats(
|
|
1332
1333
|
assert isinstance(t1_p3, hf.ElementParameter)
|
1333
1334
|
assert t0_p2.value == p1_val + 100
|
1334
1335
|
assert t1_p3.value == p1_val + 100
|
1336
|
+
|
1337
|
+
|
1338
|
+
@pytest.mark.integration
|
1339
|
+
@pytest.mark.skipif("hf.run_time_info.is_frozen")
|
1340
|
+
def test_combine_scripts_from_future_import(null_config, tmp_path: Path):
|
1341
|
+
s1 = hf.TaskSchema(
|
1342
|
+
objective="t1",
|
1343
|
+
actions=[
|
1344
|
+
hf.Action(
|
1345
|
+
script="<<script:import_future_script.py>>",
|
1346
|
+
script_exe="python_script",
|
1347
|
+
environments=[hf.ActionEnvironment(environment="python_env")],
|
1348
|
+
),
|
1349
|
+
],
|
1350
|
+
)
|
1351
|
+
|
1352
|
+
wk = hf.Workflow.from_template_data(
|
1353
|
+
template_name="test_future_import",
|
1354
|
+
tasks=[hf.Task(schema=s1)],
|
1355
|
+
resources={"any": {"combine_scripts": True}},
|
1356
|
+
path=tmp_path,
|
1357
|
+
)
|
1358
|
+
wk.submit(status=False, add_to_known=False, wait=True)
|
1359
|
+
|
1360
|
+
run = wk.get_EARs_from_IDs([0])[0]
|
1361
|
+
assert run.status is EARStatus.success
|
@@ -121,3 +121,28 @@ def test_nesting_order_paths_raise(null_config) -> None:
|
|
121
121
|
|
122
122
|
def test_nesting_order_paths_no_raise(null_config) -> None:
|
123
123
|
hf.ElementSet(nesting_order={"inputs.p1": 1, "resources.any": 2, "repeats": 3})
|
124
|
+
|
125
|
+
|
126
|
+
def test_input_source_str_dict_list_str_list_dict_equivalence(null_config) -> None:
|
127
|
+
inp_source_dict: dict[str, str | int] = {
|
128
|
+
"source_type": "task",
|
129
|
+
"task_source_type": "output",
|
130
|
+
"task_ref": 0,
|
131
|
+
}
|
132
|
+
inp_source_str = "task.0.output"
|
133
|
+
inp_source_list_dict = [inp_source_dict]
|
134
|
+
inp_source_list_str = [inp_source_str]
|
135
|
+
assert (
|
136
|
+
hf.ElementSet.from_json_like(
|
137
|
+
{"input_sources": {"p1": inp_source_dict}}
|
138
|
+
).input_sources
|
139
|
+
== hf.ElementSet.from_json_like(
|
140
|
+
{"input_sources": {"p1": inp_source_list_dict}}
|
141
|
+
).input_sources
|
142
|
+
== hf.ElementSet.from_json_like(
|
143
|
+
{"input_sources": {"p1": inp_source_str}}
|
144
|
+
).input_sources
|
145
|
+
== hf.ElementSet.from_json_like(
|
146
|
+
{"input_sources": {"p1": inp_source_list_str}}
|
147
|
+
).input_sources
|
148
|
+
)
|
@@ -1,4 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
|
+
from textwrap import dedent
|
2
3
|
from typing import TYPE_CHECKING
|
3
4
|
import numpy as np
|
4
5
|
import pytest
|
@@ -183,6 +184,7 @@ def test_input_source_from_string_task_same_default_task_source() -> None:
|
|
183
184
|
)
|
184
185
|
|
185
186
|
|
187
|
+
@pytest.mark.skip(reason="Import not yet implemented.")
|
186
188
|
def test_input_source_from_string_import() -> None:
|
187
189
|
import_ref = 0
|
188
190
|
assert hf.InputSource.from_string(f"import.{import_ref}") == hf.InputSource(
|
@@ -1283,3 +1285,72 @@ def test_input_source_inputs_from_multiple_element_sets_with_sub_parameter_seque
|
|
1283
1285
|
{"a": 5, "b": 22},
|
1284
1286
|
{"a": 5, "b": 22},
|
1285
1287
|
]
|
1288
|
+
|
1289
|
+
|
1290
|
+
def test_input_source_task_ref_equivalence(null_config, tmp_path):
|
1291
|
+
yml = dedent(
|
1292
|
+
"""\
|
1293
|
+
name: test
|
1294
|
+
template_components:
|
1295
|
+
task_schemas:
|
1296
|
+
- objective: t1
|
1297
|
+
inputs:
|
1298
|
+
- parameter: p1
|
1299
|
+
tasks:
|
1300
|
+
- schema: t1
|
1301
|
+
inputs:
|
1302
|
+
p1: 100 # all subsequent tasks will source from this input
|
1303
|
+
|
1304
|
+
- schema: t1 # t1_2
|
1305
|
+
input_sources: # single source dict; by task insert ID
|
1306
|
+
p1:
|
1307
|
+
source_type: task
|
1308
|
+
task_source_type: input
|
1309
|
+
task_ref: 0
|
1310
|
+
|
1311
|
+
- schema: t1 # t1_3
|
1312
|
+
input_sources: # as a list of dicts; by task insert ID
|
1313
|
+
p1:
|
1314
|
+
- source_type: task
|
1315
|
+
task_source_type: input
|
1316
|
+
task_ref: 0
|
1317
|
+
|
1318
|
+
- schema: t1 # t1_4
|
1319
|
+
input_sources: # as a single source dict; by task unique name
|
1320
|
+
p1:
|
1321
|
+
source_type: task
|
1322
|
+
task_source_type: input
|
1323
|
+
task_ref: t1_1
|
1324
|
+
|
1325
|
+
- schema: t1 # t1_5
|
1326
|
+
input_sources: # as a list of dicts; by task unique name
|
1327
|
+
p1:
|
1328
|
+
- source_type: task
|
1329
|
+
task_source_type: input
|
1330
|
+
task_ref: t1_1
|
1331
|
+
|
1332
|
+
- schema: t1 # t1_6
|
1333
|
+
input_sources: # single source string; by task insert ID
|
1334
|
+
p1: task.0.input
|
1335
|
+
|
1336
|
+
- schema: t1 # t1_7
|
1337
|
+
input_sources: # as a list of strings; by task insert ID
|
1338
|
+
p1:
|
1339
|
+
- task.0.input
|
1340
|
+
|
1341
|
+
- schema: t1 # t1_8
|
1342
|
+
input_sources: # single source string; by task unique name
|
1343
|
+
p1: task.t1_1.input
|
1344
|
+
|
1345
|
+
- schema: t1 # t1_9
|
1346
|
+
input_sources: # as a list of strings; by task unique name
|
1347
|
+
p1:
|
1348
|
+
- task.t1_1.input
|
1349
|
+
|
1350
|
+
"""
|
1351
|
+
)
|
1352
|
+
wk = hf.Workflow.from_YAML_string(YAML_str=yml, path=tmp_path)
|
1353
|
+
|
1354
|
+
all_sources = (task.elements[0].input_sources["inputs.p1"] for task in wk.tasks[1:])
|
1355
|
+
all_task_refs = (src.task_ref for src in all_sources)
|
1356
|
+
assert all(task_ref == 0 for task_ref in all_task_refs)
|
@@ -1,9 +1,14 @@
|
|
1
1
|
from pathlib import Path
|
2
|
+
from textwrap import dedent
|
2
3
|
import pytest
|
3
4
|
import zarr # type: ignore
|
4
5
|
import numpy as np
|
5
6
|
from numpy.typing import NDArray
|
6
|
-
from hpcflow.sdk.core.errors import
|
7
|
+
from hpcflow.sdk.core.errors import (
|
8
|
+
InvalidIdentifier,
|
9
|
+
MissingVariableSubstitutionError,
|
10
|
+
YAMLError,
|
11
|
+
)
|
7
12
|
|
8
13
|
from hpcflow.sdk.core.utils import (
|
9
14
|
JSONLikeDirSnapShot,
|
@@ -16,6 +21,7 @@ from hpcflow.sdk.core.utils import (
|
|
16
21
|
nth_key,
|
17
22
|
nth_value,
|
18
23
|
process_string_nodes,
|
24
|
+
read_YAML_str,
|
19
25
|
replace_items,
|
20
26
|
check_valid_py_identifier,
|
21
27
|
reshape,
|
@@ -575,3 +581,36 @@ def test_nth_key_raises():
|
|
575
581
|
|
576
582
|
with pytest.raises(Exception):
|
577
583
|
nth_key(dct, -1)
|
584
|
+
|
585
|
+
|
586
|
+
def test_read_YAML_str():
|
587
|
+
good_yaml = dedent(
|
588
|
+
"""\
|
589
|
+
a: 1
|
590
|
+
b: 2
|
591
|
+
"""
|
592
|
+
)
|
593
|
+
assert read_YAML_str(good_yaml) == {"a": 1, "b": 2}
|
594
|
+
|
595
|
+
|
596
|
+
def test_read_YAML_str_raise_on_bad_indent():
|
597
|
+
bad_yaml = dedent(
|
598
|
+
"""\
|
599
|
+
a: 1
|
600
|
+
b: 2
|
601
|
+
"""
|
602
|
+
)
|
603
|
+
with pytest.raises(YAMLError):
|
604
|
+
read_YAML_str(bad_yaml)
|
605
|
+
|
606
|
+
|
607
|
+
def test_read_YAML_str_raise_on_mixed_tabs_spaces():
|
608
|
+
bad_yaml = dedent(
|
609
|
+
"""\
|
610
|
+
a:
|
611
|
+
a1: 2 # this has a space indent
|
612
|
+
a2: 3 # this has a tab indent
|
613
|
+
"""
|
614
|
+
)
|
615
|
+
with pytest.raises(YAMLError):
|
616
|
+
read_YAML_str(bad_yaml)
|
@@ -0,0 +1,97 @@
|
|
1
|
+
from textwrap import dedent
|
2
|
+
|
3
|
+
from hpcflow.sdk.utils.strings import extract_py_from_future_imports
|
4
|
+
|
5
|
+
|
6
|
+
def test_extract_py_from_future_imports_none():
|
7
|
+
py_str = dedent(
|
8
|
+
"""\
|
9
|
+
|
10
|
+
def my_function():
|
11
|
+
print("blah!")
|
12
|
+
"""
|
13
|
+
)
|
14
|
+
new_str, imports = extract_py_from_future_imports(py_str)
|
15
|
+
assert imports == set()
|
16
|
+
assert new_str == py_str
|
17
|
+
|
18
|
+
|
19
|
+
def test_extract_py_from_future_imports_single():
|
20
|
+
py_str = dedent(
|
21
|
+
"""\
|
22
|
+
from __future__ import annotations
|
23
|
+
|
24
|
+
def my_function():
|
25
|
+
print("blah!")
|
26
|
+
"""
|
27
|
+
)
|
28
|
+
new_str, imports = extract_py_from_future_imports(py_str)
|
29
|
+
assert imports == {"annotations"}
|
30
|
+
assert new_str == dedent(
|
31
|
+
"""\
|
32
|
+
|
33
|
+
def my_function():
|
34
|
+
print("blah!")
|
35
|
+
"""
|
36
|
+
)
|
37
|
+
|
38
|
+
|
39
|
+
def test_extract_py_from_future_imports_multi():
|
40
|
+
py_str = dedent(
|
41
|
+
"""\
|
42
|
+
from __future__ import annotations, feature_2
|
43
|
+
|
44
|
+
def my_function():
|
45
|
+
print("blah!")
|
46
|
+
"""
|
47
|
+
)
|
48
|
+
new_str, imports = extract_py_from_future_imports(py_str)
|
49
|
+
assert imports == {"annotations", "feature_2"}
|
50
|
+
assert new_str == dedent(
|
51
|
+
"""\
|
52
|
+
|
53
|
+
def my_function():
|
54
|
+
print("blah!")
|
55
|
+
"""
|
56
|
+
)
|
57
|
+
|
58
|
+
|
59
|
+
def test_extract_py_from_future_imports_trailing_comma():
|
60
|
+
py_str = dedent(
|
61
|
+
"""\
|
62
|
+
from __future__ import annotations,
|
63
|
+
|
64
|
+
def my_function():
|
65
|
+
print("blah!")
|
66
|
+
"""
|
67
|
+
)
|
68
|
+
new_str, imports = extract_py_from_future_imports(py_str)
|
69
|
+
assert imports == {"annotations"}
|
70
|
+
assert new_str == dedent(
|
71
|
+
"""\
|
72
|
+
|
73
|
+
def my_function():
|
74
|
+
print("blah!")
|
75
|
+
"""
|
76
|
+
)
|
77
|
+
|
78
|
+
|
79
|
+
def test_extract_py_from_future_imports_multi_lines():
|
80
|
+
py_str = dedent(
|
81
|
+
"""\
|
82
|
+
from __future__ import annotations, feature_2
|
83
|
+
from __future__ import feature_2, feature_3,
|
84
|
+
|
85
|
+
def my_function():
|
86
|
+
print("blah!")
|
87
|
+
"""
|
88
|
+
)
|
89
|
+
new_str, imports = extract_py_from_future_imports(py_str)
|
90
|
+
assert imports == {"annotations", "feature_2", "feature_3"}
|
91
|
+
assert new_str == dedent(
|
92
|
+
"""\
|
93
|
+
|
94
|
+
def my_function():
|
95
|
+
print("blah!")
|
96
|
+
"""
|
97
|
+
)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
[tool.poetry]
|
3
3
|
name = "hpcflow-new2"
|
4
|
-
version = "0.2.
|
4
|
+
version = "0.2.0a220"
|
5
5
|
|
6
6
|
description = "Computational workflow management"
|
7
7
|
authors = ["aplowman <adam.plowman@manchester.ac.uk>"]
|
@@ -100,7 +100,7 @@ hook-dirs = "hpcflow.__pyinstaller:get_hook_dirs"
|
|
100
100
|
|
101
101
|
[tool.commitizen]
|
102
102
|
name = "cz_conventional_commits"
|
103
|
-
version = "0.2.
|
103
|
+
version = "0.2.0a220"
|
104
104
|
tag_format = "v$version"
|
105
105
|
version_files = [
|
106
106
|
"pyproject.toml:version",
|
@@ -1 +0,0 @@
|
|
1
|
-
__version__ = "0.2.0a218"
|