simstack 2.0.1__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.
- simstack/__init__.py +0 -0
- simstack/_version.py +24 -0
- simstack/core/__init__.py +0 -0
- simstack/core/artifacts.py +406 -0
- simstack/core/asnyc_helper.py +14 -0
- simstack/core/config_file.py +60 -0
- simstack/core/context.py +283 -0
- simstack/core/definitions.py +22 -0
- simstack/core/engine.py +115 -0
- simstack/core/find_simstack_modules.py +41 -0
- simstack/core/hash.py +179 -0
- simstack/core/node.py +1137 -0
- simstack/core/node_claim.py +31 -0
- simstack/core/node_runner.py +362 -0
- simstack/core/process_results.py +102 -0
- simstack/core/recompute_artifacts.py +117 -0
- simstack/core/reset_db.py +12 -0
- simstack/core/resource_assignment.py +222 -0
- simstack/core/resources.py +90 -0
- simstack/core/route_table.py +24 -0
- simstack/core/run_docker.py +109 -0
- simstack/core/run_node.py +71 -0
- simstack/core/runner.py +169 -0
- simstack/core/services/__init__.py +11 -0
- simstack/core/services/base_service.py +191 -0
- simstack/core/services/file_transfer_service.py +117 -0
- simstack/core/services/git_restart_service.py +12 -0
- simstack/core/services/git_uv_update_service.py +105 -0
- simstack/core/services/node_execution_service.py +182 -0
- simstack/core/services/resource_branch_monitor_service.py +71 -0
- simstack/core/services/runner_cleanup_service.py +149 -0
- simstack/core/services/runner_manager.py +163 -0
- simstack/core/services/runner_status_service.py +10 -0
- simstack/core/services/slurm_status_service.py +67 -0
- simstack/core/services/stop_check_service.py +27 -0
- simstack/core/services/timeout_restart_service.py +26 -0
- simstack/core/simstack_result.py +39 -0
- simstack/core/submit_node.py +217 -0
- simstack/core/task_id.py +22 -0
- simstack/methods/__init__.py +0 -0
- simstack/methods/archive_files.py +292 -0
- simstack/methods/fire_and_forget_runner.py +113 -0
- simstack/methods/generate_test.py +125 -0
- simstack/methods/get_file.py +26 -0
- simstack/methods/mass_runner.py +154 -0
- simstack/methods/multiple_executor.py +307 -0
- simstack/methods/switch_git.py +16 -0
- simstack/methods/upload_helpers.py +81 -0
- simstack/models/__init__.py +74 -0
- simstack/models/array_list.py +21 -0
- simstack/models/array_storage.py +83 -0
- simstack/models/artifact_models.py +127 -0
- simstack/models/base_lists.py +51 -0
- simstack/models/base_types.py +209 -0
- simstack/models/charts_artifact.py +690 -0
- simstack/models/dataset.py +451 -0
- simstack/models/dataset_metadata.py +390 -0
- simstack/models/datasettuple.py +587 -0
- simstack/models/file_instance.py +150 -0
- simstack/models/file_list.py +45 -0
- simstack/models/files.py +424 -0
- simstack/models/fire_and_forget_result.py +19 -0
- simstack/models/images2d.py +113 -0
- simstack/models/log_entry_model.py +37 -0
- simstack/models/models.py +52 -0
- simstack/models/named_data_reference.py +36 -0
- simstack/models/node_registry.py +96 -0
- simstack/models/pandas_model.py +239 -0
- simstack/models/parameters.py +402 -0
- simstack/models/pickle_models.py +300 -0
- simstack/models/project.py +13 -0
- simstack/models/resource_assignment.py +170 -0
- simstack/models/resource_definition.py +125 -0
- simstack/models/runner_model.py +39 -0
- simstack/models/simple_table.py +79 -0
- simstack/models/simstack_model.py +97 -0
- simstack/models/slurm_info.py +17 -0
- simstack/models/table_artifact.py +87 -0
- simstack/models/tag.py +12 -0
- simstack/models/unused_test_file.py +5 -0
- simstack/simstack_marker.com +1 -0
- simstack/static/docs/.buildinfo +4 -0
- simstack/static/docs/_images/GUI Concept.webp +0 -0
- simstack/static/docs/_modules/index.html +434 -0
- simstack/static/docs/_modules/simstack/core/artifacts.html +743 -0
- simstack/static/docs/_modules/simstack/core/asnyc_helper.html +330 -0
- simstack/static/docs/_modules/simstack/core/config_file.html +379 -0
- simstack/static/docs/_modules/simstack/core/definitions.html +341 -0
- simstack/static/docs/_modules/simstack/core/engine.html +437 -0
- simstack/static/docs/_modules/simstack/core/find_simstack_modules.html +360 -0
- simstack/static/docs/_modules/simstack/core/hash.html +528 -0
- simstack/static/docs/_modules/simstack/core/node.html +1392 -0
- simstack/static/docs/_modules/simstack/core/node_claim.html +347 -0
- simstack/static/docs/_modules/simstack/core/node_runner.html +711 -0
- simstack/static/docs/_modules/simstack/core/process_results.html +418 -0
- simstack/static/docs/_modules/simstack/core/recompute_artifacts.html +433 -0
- simstack/static/docs/_modules/simstack/core/reset_db.html +328 -0
- simstack/static/docs/_modules/simstack/core/resource_assignment.html +553 -0
- simstack/static/docs/_modules/simstack/core/resources.html +424 -0
- simstack/static/docs/_modules/simstack/core/route_table.html +349 -0
- simstack/static/docs/_modules/simstack/core/run_docker.html +425 -0
- simstack/static/docs/_modules/simstack/core/run_node.html +390 -0
- simstack/static/docs/_modules/simstack/core/runner.html +491 -0
- simstack/static/docs/_modules/simstack/core/services/base_service.html +522 -0
- simstack/static/docs/_modules/simstack/core/services/file_transfer_service.html +436 -0
- simstack/static/docs/_modules/simstack/core/services/git_restart_service.html +331 -0
- simstack/static/docs/_modules/simstack/core/services/git_uv_update_service.html +427 -0
- simstack/static/docs/_modules/simstack/core/services/node_execution_service.html +507 -0
- simstack/static/docs/_modules/simstack/core/services/resource_branch_monitor_service.html +390 -0
- simstack/static/docs/_modules/simstack/core/services/runner_cleanup_service.html +468 -0
- simstack/static/docs/_modules/simstack/core/services/runner_manager.html +485 -0
- simstack/static/docs/_modules/simstack/core/services/runner_status_service.html +329 -0
- simstack/static/docs/_modules/simstack/core/services/slurm_status_service.html +386 -0
- simstack/static/docs/_modules/simstack/core/services/stop_check_service.html +346 -0
- simstack/static/docs/_modules/simstack/core/services/timeout_restart_service.html +345 -0
- simstack/static/docs/_modules/simstack/core/simstack_result.html +355 -0
- simstack/static/docs/_modules/simstack/core/submit_node.html +536 -0
- simstack/static/docs/_modules/simstack/core/task_id.html +344 -0
- simstack/static/docs/_modules/simstack/methods/archive_files.html +598 -0
- simstack/static/docs/_modules/simstack/methods/fire_and_forget_runner.html +432 -0
- simstack/static/docs/_modules/simstack/methods/generate_test.html +443 -0
- simstack/static/docs/_modules/simstack/methods/get_file.html +342 -0
- simstack/static/docs/_modules/simstack/methods/mass_runner.html +476 -0
- simstack/static/docs/_modules/simstack/methods/multiple_executor.html +674 -0
- simstack/static/docs/_modules/simstack/methods/switch_git.html +332 -0
- simstack/static/docs/_modules/simstack/methods/upload_helpers.html +403 -0
- simstack/static/docs/_modules/simstack/models/array_list.html +337 -0
- simstack/static/docs/_modules/simstack/models/array_storage.html +417 -0
- simstack/static/docs/_modules/simstack/models/base_lists.html +374 -0
- simstack/static/docs/_modules/simstack/models/base_types.html +588 -0
- simstack/static/docs/_modules/simstack/models/charts_artifact.html +1035 -0
- simstack/static/docs/_modules/simstack/models/dataset.html +866 -0
- simstack/static/docs/_modules/simstack/models/dataset_metadata.html +711 -0
- simstack/static/docs/_modules/simstack/models/datasettuple.html +1005 -0
- simstack/static/docs/_modules/simstack/models/file_instance.html +472 -0
- simstack/static/docs/_modules/simstack/models/file_list.html +367 -0
- simstack/static/docs/_modules/simstack/models/files.html +773 -0
- simstack/static/docs/_modules/simstack/models/fire_and_forget_result.html +335 -0
- simstack/static/docs/_modules/simstack/models/images2d.html +447 -0
- simstack/static/docs/_modules/simstack/models/log_entry_model.html +356 -0
- simstack/static/docs/_modules/simstack/models/models.html +374 -0
- simstack/static/docs/_modules/simstack/models/named_data_reference.html +355 -0
- simstack/static/docs/_modules/simstack/models/node_registry.html +415 -0
- simstack/static/docs/_modules/simstack/models/pandas_model.html +570 -0
- simstack/static/docs/_modules/simstack/models/parameters.html +739 -0
- simstack/static/docs/_modules/simstack/models/pickle_models.html +631 -0
- simstack/static/docs/_modules/simstack/models/project.html +329 -0
- simstack/static/docs/_modules/simstack/models/resource_assignment.html +501 -0
- simstack/static/docs/_modules/simstack/models/resource_definition.html +477 -0
- simstack/static/docs/_modules/simstack/models/runner_model.html +361 -0
- simstack/static/docs/_modules/simstack/models/simple_table.html +413 -0
- simstack/static/docs/_modules/simstack/models/simstack_model.html +416 -0
- simstack/static/docs/_modules/simstack/models/slurm_info.html +333 -0
- simstack/static/docs/_modules/simstack/models/table_artifact.html +406 -0
- simstack/static/docs/_modules/simstack/models/tag.html +328 -0
- simstack/static/docs/_modules/simstack/tables/model_table.html +519 -0
- simstack/static/docs/_modules/simstack/tables/node_children.html +538 -0
- simstack/static/docs/_modules/simstack/tables/node_table.html +854 -0
- simstack/static/docs/_modules/simstack/tables/table_builder.html +642 -0
- simstack/static/docs/_modules/simstack/util/async_zip_utils.html +337 -0
- simstack/static/docs/_modules/simstack/util/b64mixin.html +339 -0
- simstack/static/docs/_modules/simstack/util/cleaned_json_schema.html +336 -0
- simstack/static/docs/_modules/simstack/util/config_reader.html +498 -0
- simstack/static/docs/_modules/simstack/util/custom_model_dump.html +391 -0
- simstack/static/docs/_modules/simstack/util/database_information.html +488 -0
- simstack/static/docs/_modules/simstack/util/db.html +1392 -0
- simstack/static/docs/_modules/simstack/util/db_logger.html +448 -0
- simstack/static/docs/_modules/simstack/util/default_from_dict.html +376 -0
- simstack/static/docs/_modules/simstack/util/directory_iterator.html +553 -0
- simstack/static/docs/_modules/simstack/util/docstring_parser.html +526 -0
- simstack/static/docs/_modules/simstack/util/file_hashing.html +394 -0
- simstack/static/docs/_modules/simstack/util/file_transfer_client.html +791 -0
- simstack/static/docs/_modules/simstack/util/files.html +733 -0
- simstack/static/docs/_modules/simstack/util/find_python_files.html +349 -0
- simstack/static/docs/_modules/simstack/util/generate_ui_schema.html +396 -0
- simstack/static/docs/_modules/simstack/util/generic_list_mixin.html +487 -0
- simstack/static/docs/_modules/simstack/util/get_module_path.html +336 -0
- simstack/static/docs/_modules/simstack/util/git_pull.html +381 -0
- simstack/static/docs/_modules/simstack/util/git_repository_status.html +411 -0
- simstack/static/docs/_modules/simstack/util/import_module.html +377 -0
- simstack/static/docs/_modules/simstack/util/importer.html +637 -0
- simstack/static/docs/_modules/simstack/util/init_data_source.html +357 -0
- simstack/static/docs/_modules/simstack/util/make_graph.html +423 -0
- simstack/static/docs/_modules/simstack/util/make_table.html +1171 -0
- simstack/static/docs/_modules/simstack/util/mappings.html +378 -0
- simstack/static/docs/_modules/simstack/util/minimal_route_finder.html +388 -0
- simstack/static/docs/_modules/simstack/util/mock_db.html +472 -0
- simstack/static/docs/_modules/simstack/util/module_path_checker.html +354 -0
- simstack/static/docs/_modules/simstack/util/object_list_mixin.html +649 -0
- simstack/static/docs/_modules/simstack/util/path_manager.html +566 -0
- simstack/static/docs/_modules/simstack/util/project_root_finder.html +357 -0
- simstack/static/docs/_modules/simstack/util/queue_watcher.html +593 -0
- simstack/static/docs/_modules/simstack/util/resource_config.html +532 -0
- simstack/static/docs/_modules/simstack/util/route_finder.html +390 -0
- simstack/static/docs/_modules/simstack/util/runner_util.html +487 -0
- simstack/static/docs/_modules/simstack/util/runner_utils.html +468 -0
- simstack/static/docs/_modules/simstack/util/safe_code_executor.html +510 -0
- simstack/static/docs/_modules/simstack/util/sample_config.html +414 -0
- simstack/static/docs/_modules/simstack/util/sensitive_content_filter.html +364 -0
- simstack/static/docs/_modules/simstack/util/setup_logging.html +365 -0
- simstack/static/docs/_modules/simstack/util/submit_to_watchdog.html +425 -0
- simstack/static/docs/_modules/simstack/util/toml_reader.html +482 -0
- simstack/static/docs/_modules/simstack/util/transform_file_name.html +357 -0
- simstack/static/docs/_modules/simstack/util/ui_tools.html +541 -0
- simstack/static/docs/_sources/architecture.rst.txt +25 -0
- simstack/static/docs/_sources/array_lists.rst.txt +60 -0
- simstack/static/docs/_sources/ci_pipeline.rst.txt +34 -0
- simstack/static/docs/_sources/data.rst.txt +175 -0
- simstack/static/docs/_sources/database_migrations.rst.txt +310 -0
- simstack/static/docs/_sources/datasets.rst.txt +93 -0
- simstack/static/docs/_sources/developer_guide.rst.txt +25 -0
- simstack/static/docs/_sources/docker_nodes.rst.txt +149 -0
- simstack/static/docs/_sources/files.rst.txt +159 -0
- simstack/static/docs/_sources/fire_and_forget_runner.rst.txt +67 -0
- simstack/static/docs/_sources/index.rst.txt +25 -0
- simstack/static/docs/_sources/installation.rst.txt +213 -0
- simstack/static/docs/_sources/lists.rst.txt +111 -0
- simstack/static/docs/_sources/mass_runner.rst.txt +91 -0
- simstack/static/docs/_sources/misc.rst.txt +106 -0
- simstack/static/docs/_sources/overview.rst.txt +91 -0
- simstack/static/docs/_sources/recovery.rst.txt +38 -0
- simstack/static/docs/_sources/resource_config.rst.txt +141 -0
- simstack/static/docs/_sources/simstack/modules/modules.rst.txt +7 -0
- simstack/static/docs/_sources/simstack/modules/simstack.core.rst.txt +205 -0
- simstack/static/docs/_sources/simstack/modules/simstack.core.services.rst.txt +109 -0
- simstack/static/docs/_sources/simstack/modules/simstack.methods.rst.txt +77 -0
- simstack/static/docs/_sources/simstack/modules/simstack.models.rst.txt +261 -0
- simstack/static/docs/_sources/simstack/modules/simstack.rst.txt +22 -0
- simstack/static/docs/_sources/simstack/modules/simstack.tables.rst.txt +45 -0
- simstack/static/docs/_sources/simstack/modules/simstack.util.rst.txt +381 -0
- simstack/static/docs/_sources/submit.rst.txt +141 -0
- simstack/static/docs/_sources/tables.rst.txt +50 -0
- simstack/static/docs/_sources/testing_complex_nodes.rst.txt +92 -0
- simstack/static/docs/_sources/ui_documentation.rst.txt +272 -0
- simstack/static/docs/_sources/workflows.rst.txt +446 -0
- simstack/static/docs/_static/base-stemmer.js +476 -0
- simstack/static/docs/_static/basic.css +906 -0
- simstack/static/docs/_static/cobalt2-theme.css +1 -0
- simstack/static/docs/_static/debug.css +69 -0
- simstack/static/docs/_static/docs-build-badge.css +51 -0
- simstack/static/docs/_static/docs-build-badge.js +51 -0
- simstack/static/docs/_static/doctools.js +150 -0
- simstack/static/docs/_static/documentation_options.js +13 -0
- simstack/static/docs/_static/english-stemmer.js +1066 -0
- simstack/static/docs/_static/file.png +0 -0
- simstack/static/docs/_static/language_data.js +13 -0
- simstack/static/docs/_static/minus.png +0 -0
- simstack/static/docs/_static/plus.png +0 -0
- simstack/static/docs/_static/pygments.css +250 -0
- simstack/static/docs/_static/scripts/furo-extensions.js +0 -0
- simstack/static/docs/_static/scripts/furo.js +3 -0
- simstack/static/docs/_static/scripts/furo.js.LICENSE.txt +7 -0
- simstack/static/docs/_static/scripts/furo.js.map +1 -0
- simstack/static/docs/_static/searchtools.js +693 -0
- simstack/static/docs/_static/skeleton.css +296 -0
- simstack/static/docs/_static/sphinx_highlight.js +159 -0
- simstack/static/docs/_static/styles/furo-extensions.css +2 -0
- simstack/static/docs/_static/styles/furo-extensions.css.map +1 -0
- simstack/static/docs/_static/styles/furo.css +2 -0
- simstack/static/docs/_static/styles/furo.css.map +1 -0
- simstack/static/docs/architecture.html +340 -0
- simstack/static/docs/array_lists.html +678 -0
- simstack/static/docs/ci_pipeline.html +385 -0
- simstack/static/docs/data.html +545 -0
- simstack/static/docs/database_migrations.html +652 -0
- simstack/static/docs/datasets.html +1268 -0
- simstack/static/docs/developer_guide.html +359 -0
- simstack/static/docs/docker_nodes.html +503 -0
- simstack/static/docs/files.html +718 -0
- simstack/static/docs/fire_and_forget_runner.html +641 -0
- simstack/static/docs/genindex.html +6390 -0
- simstack/static/docs/index.html +516 -0
- simstack/static/docs/installation.html +553 -0
- simstack/static/docs/lists.html +478 -0
- simstack/static/docs/mass_runner.html +481 -0
- simstack/static/docs/misc.html +454 -0
- simstack/static/docs/objects.inv +0 -0
- simstack/static/docs/overview.html +469 -0
- simstack/static/docs/py-modindex.html +1111 -0
- simstack/static/docs/recovery.html +371 -0
- simstack/static/docs/resource_config.html +590 -0
- simstack/static/docs/search.html +327 -0
- simstack/static/docs/searchindex.js +1 -0
- simstack/static/docs/simstack/modules/modules.html +489 -0
- simstack/static/docs/simstack/modules/simstack.core.html +1883 -0
- simstack/static/docs/simstack/modules/simstack.core.services.html +698 -0
- simstack/static/docs/simstack/modules/simstack.html +3060 -0
- simstack/static/docs/simstack/modules/simstack.methods.html +1150 -0
- simstack/static/docs/simstack/modules/simstack.models.html +13113 -0
- simstack/static/docs/simstack/modules/simstack.tables.html +635 -0
- simstack/static/docs/simstack/modules/simstack.util.html +3320 -0
- simstack/static/docs/submit.html +487 -0
- simstack/static/docs/tables.html +539 -0
- simstack/static/docs/testing_complex_nodes.html +458 -0
- simstack/static/docs/ui_documentation.html +788 -0
- simstack/static/docs/workflows.html +766 -0
- simstack/tables/__init__.py +0 -0
- simstack/tables/model_table.py +191 -0
- simstack/tables/node_children.py +219 -0
- simstack/tables/node_table.py +517 -0
- simstack/tables/table_builder.py +314 -0
- simstack/util/__init__.py +0 -0
- simstack/util/async_zip_utils.py +18 -0
- simstack/util/b64mixin.py +23 -0
- simstack/util/cleaned_json_schema.py +20 -0
- simstack/util/config_reader.py +179 -0
- simstack/util/custom_model_dump.py +75 -0
- simstack/util/database_information.py +160 -0
- simstack/util/db.py +1013 -0
- simstack/util/db_logger.py +117 -0
- simstack/util/default_from_dict.py +57 -0
- simstack/util/directory_iterator.py +216 -0
- simstack/util/docstring_parser.py +186 -0
- simstack/util/file_hashing.py +72 -0
- simstack/util/file_transfer_client.py +427 -0
- simstack/util/files.py +396 -0
- simstack/util/find_python_files.py +33 -0
- simstack/util/generate_ui_schema.py +80 -0
- simstack/util/generic_list_mixin.py +120 -0
- simstack/util/geometry_utils.py +118 -0
- simstack/util/get_module_path.py +20 -0
- simstack/util/git_pull.py +65 -0
- simstack/util/git_repository_status.py +95 -0
- simstack/util/import_module.py +63 -0
- simstack/util/importer.py +312 -0
- simstack/util/init_data_source.py +41 -0
- simstack/util/make_graph.py +107 -0
- simstack/util/make_table.py +837 -0
- simstack/util/mappings.py +41 -0
- simstack/util/minimal_route_finder.py +72 -0
- simstack/util/mock_db.py +144 -0
- simstack/util/module_path_checker.py +38 -0
- simstack/util/object_list_mixin.py +282 -0
- simstack/util/path_manager.py +223 -0
- simstack/util/project_root_finder.py +41 -0
- simstack/util/queue_watcher.py +262 -0
- simstack/util/resource_config.py +198 -0
- simstack/util/route_finder.py +74 -0
- simstack/util/runner_util.py +166 -0
- simstack/util/runner_utils.py +143 -0
- simstack/util/safe_code_executor.py +194 -0
- simstack/util/sample_config.py +98 -0
- simstack/util/sensitive_content_filter.py +48 -0
- simstack/util/setup_logging.py +49 -0
- simstack/util/submit_to_watchdog.py +106 -0
- simstack/util/toml_reader.py +142 -0
- simstack/util/transform_file_name.py +41 -0
- simstack/util/ui_tools.py +213 -0
- simstack-2.0.1.dist-info/METADATA +454 -0
- simstack-2.0.1.dist-info/RECORD +354 -0
- simstack-2.0.1.dist-info/WHEEL +5 -0
- simstack-2.0.1.dist-info/entry_points.txt +12 -0
- simstack-2.0.1.dist-info/licenses/LICENSE +21 -0
- simstack-2.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
from typing import Dict
|
|
3
|
+
|
|
4
|
+
from simstack.core.artifacts import ArtifactArguments
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def safe_code_executor(
|
|
8
|
+
code_string: str, artifact_arguments: ArtifactArguments, timeout: int = 30
|
|
9
|
+
) -> Dict:
|
|
10
|
+
"""
|
|
11
|
+
Safely executes Python code from a string with a controlled environment.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
code_string (str): The Python code to execute
|
|
15
|
+
artifact_arguments (ArtifactArguments): Artifact arguments to be used in the execution environment
|
|
16
|
+
timeout (int, optional): Maximum execution time in seconds before timeout
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
Dict: A dictionary containing:
|
|
20
|
+
'success' (bool): Whether execution was successful
|
|
21
|
+
'result': The return value if successful
|
|
22
|
+
'error': Error message if unsuccessful
|
|
23
|
+
'error_type': Type of error if unsuccessful
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
import ast
|
|
27
|
+
import traceback
|
|
28
|
+
|
|
29
|
+
# Default result structure
|
|
30
|
+
result = {"success": False, "result": None, "error": None, "error_type": None}
|
|
31
|
+
|
|
32
|
+
# Verify code is not empty
|
|
33
|
+
if not code_string or not code_string.strip():
|
|
34
|
+
result["error"] = "Empty code string provided"
|
|
35
|
+
result["error_type"] = "ValueError"
|
|
36
|
+
return result
|
|
37
|
+
|
|
38
|
+
# Create a safe globals dictionary
|
|
39
|
+
safe_globals = {"__builtins__": {}}
|
|
40
|
+
|
|
41
|
+
# Add only whitelisted builtins that are considered safe
|
|
42
|
+
for name in [
|
|
43
|
+
"abs",
|
|
44
|
+
"all",
|
|
45
|
+
"any",
|
|
46
|
+
"bool",
|
|
47
|
+
"dict",
|
|
48
|
+
"enumerate",
|
|
49
|
+
"filter",
|
|
50
|
+
"float",
|
|
51
|
+
"frozenset",
|
|
52
|
+
"int",
|
|
53
|
+
"isinstance",
|
|
54
|
+
"issubclass",
|
|
55
|
+
"len",
|
|
56
|
+
"list",
|
|
57
|
+
"map",
|
|
58
|
+
"max",
|
|
59
|
+
"min",
|
|
60
|
+
"print",
|
|
61
|
+
"range",
|
|
62
|
+
"reversed",
|
|
63
|
+
"round",
|
|
64
|
+
"set",
|
|
65
|
+
"slice",
|
|
66
|
+
"sorted",
|
|
67
|
+
"str",
|
|
68
|
+
"sum",
|
|
69
|
+
"tuple",
|
|
70
|
+
"zip",
|
|
71
|
+
]:
|
|
72
|
+
safe_globals["__builtins__"][name] = __builtins__[name]
|
|
73
|
+
|
|
74
|
+
# Add artifact models to safe globals
|
|
75
|
+
from simstack.models.charts_artifact import (
|
|
76
|
+
ChartArtifactModel,
|
|
77
|
+
AGLineSeriesConfig,
|
|
78
|
+
AGChartAxisConfig,
|
|
79
|
+
AGChartTitleConfig,
|
|
80
|
+
AGChartLegendConfig,
|
|
81
|
+
)
|
|
82
|
+
from simstack.models.table_artifact import TableArtifactModel
|
|
83
|
+
from examples.science.electronic_structure.spectra.plot_spectra import (
|
|
84
|
+
make_multi_line_chart,
|
|
85
|
+
)
|
|
86
|
+
from simstack.models import ArtifactModel
|
|
87
|
+
|
|
88
|
+
safe_globals.update(
|
|
89
|
+
{
|
|
90
|
+
"ChartArtifactModel": ChartArtifactModel,
|
|
91
|
+
"TableArtifactModel": TableArtifactModel,
|
|
92
|
+
"AGLineSeriesConfig": AGLineSeriesConfig,
|
|
93
|
+
"AGChartAxisConfig": AGChartAxisConfig,
|
|
94
|
+
"AGChartTitleConfig": AGChartTitleConfig,
|
|
95
|
+
"AGChartLegendConfig": AGChartLegendConfig,
|
|
96
|
+
"make_multi_line_chart": make_multi_line_chart,
|
|
97
|
+
"ArtifactModel": ArtifactModel,
|
|
98
|
+
"ArtifactArguments": ArtifactArguments,
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Add artifact_arguments attributes to safe globals
|
|
103
|
+
# Extract all attributes from the artifact_arguments instance
|
|
104
|
+
for attr_name in dir(artifact_arguments):
|
|
105
|
+
if not attr_name.startswith("_"): # Skip private/magic methods
|
|
106
|
+
attr_value = getattr(artifact_arguments, attr_name)
|
|
107
|
+
if not callable(attr_value): # Skip methods, only include data attributes
|
|
108
|
+
safe_globals[attr_name] = attr_value
|
|
109
|
+
|
|
110
|
+
# Also add the full artifact_arguments object itself for backward compatibility
|
|
111
|
+
safe_globals["arg"] = artifact_arguments
|
|
112
|
+
|
|
113
|
+
# Verify code is syntactically correct
|
|
114
|
+
try:
|
|
115
|
+
ast.parse(code_string)
|
|
116
|
+
except SyntaxError as e:
|
|
117
|
+
result["error"] = str(e)
|
|
118
|
+
result["error_type"] = "SyntaxError"
|
|
119
|
+
return result
|
|
120
|
+
|
|
121
|
+
# Cross-platform timeout implementation using threading
|
|
122
|
+
execution_result = {
|
|
123
|
+
"completed": False,
|
|
124
|
+
"exception": None,
|
|
125
|
+
"local_vars": {},
|
|
126
|
+
"function_result": None,
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
def execute_code():
|
|
130
|
+
try:
|
|
131
|
+
local_vars = {}
|
|
132
|
+
exec(code_string, safe_globals, local_vars)
|
|
133
|
+
execution_result["local_vars"] = local_vars
|
|
134
|
+
|
|
135
|
+
# Look for a function in local_vars and call it with artifact_arguments
|
|
136
|
+
function_result = None
|
|
137
|
+
for var_name, var_value in local_vars.items():
|
|
138
|
+
if callable(var_value) and not var_name.startswith("_"):
|
|
139
|
+
# Found a function, call it with artifact_arguments
|
|
140
|
+
try:
|
|
141
|
+
function_result = var_value(artifact_arguments)
|
|
142
|
+
execution_result["function_result"] = function_result
|
|
143
|
+
break
|
|
144
|
+
except Exception as func_e:
|
|
145
|
+
execution_result["exception"] = func_e
|
|
146
|
+
execution_result["completed"] = True
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
execution_result["completed"] = True
|
|
150
|
+
except Exception as e:
|
|
151
|
+
execution_result["exception"] = e
|
|
152
|
+
execution_result["completed"] = True
|
|
153
|
+
|
|
154
|
+
try:
|
|
155
|
+
# Start execution in a separate thread
|
|
156
|
+
thread = threading.Thread(target=execute_code, daemon=True)
|
|
157
|
+
thread.start()
|
|
158
|
+
thread.join(timeout=timeout)
|
|
159
|
+
|
|
160
|
+
if thread.is_alive():
|
|
161
|
+
# Timeout occurred
|
|
162
|
+
result["error"] = f"Code execution timed out after {timeout} seconds"
|
|
163
|
+
result["error_type"] = "TimeoutError"
|
|
164
|
+
return result
|
|
165
|
+
|
|
166
|
+
if execution_result["exception"]:
|
|
167
|
+
# Exception occurred during execution
|
|
168
|
+
e = execution_result["exception"]
|
|
169
|
+
result["error"] = str(e)
|
|
170
|
+
result["error_type"] = e.__class__.__name__
|
|
171
|
+
result["traceback"] = traceback.format_exc()
|
|
172
|
+
return result
|
|
173
|
+
|
|
174
|
+
# Successful execution
|
|
175
|
+
# Return the function result if available, otherwise return local vars
|
|
176
|
+
if execution_result["function_result"] is not None:
|
|
177
|
+
result["result"] = execution_result["function_result"]
|
|
178
|
+
else:
|
|
179
|
+
local_vars = execution_result["local_vars"]
|
|
180
|
+
if "result" in local_vars:
|
|
181
|
+
result["result"] = local_vars["result"]
|
|
182
|
+
else:
|
|
183
|
+
result["result"] = {
|
|
184
|
+
k: v for k, v in local_vars.items() if not k.startswith("_")
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
result["success"] = True
|
|
188
|
+
|
|
189
|
+
except Exception as e:
|
|
190
|
+
result["error"] = str(e)
|
|
191
|
+
result["error_type"] = e.__class__.__name__
|
|
192
|
+
result["traceback"] = traceback.format_exc()
|
|
193
|
+
|
|
194
|
+
return result
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
_SAMPLE_SIMSTACK_TOML_DICT = {
|
|
2
|
+
"parameters": {
|
|
3
|
+
"common": {
|
|
4
|
+
"resources": [
|
|
5
|
+
"local",
|
|
6
|
+
"int-nano",
|
|
7
|
+
"horeka",
|
|
8
|
+
"justus",
|
|
9
|
+
"self",
|
|
10
|
+
"exchange",
|
|
11
|
+
"uploads",
|
|
12
|
+
],
|
|
13
|
+
"database": "wolfgang_data",
|
|
14
|
+
"test_database": "wolfgang_test",
|
|
15
|
+
"connection_string": "mongodb://localhost:27017/",
|
|
16
|
+
},
|
|
17
|
+
"local": {
|
|
18
|
+
"ssh-key": "C:\\Users\\bj7610\\Documents\\etc\\.ssh\\surface11_openssh",
|
|
19
|
+
"resource": "local",
|
|
20
|
+
"workdir": "C:\\Users\\bj7610\\simstack",
|
|
21
|
+
"python_path": [
|
|
22
|
+
"C:\\Users\\bj7610\\PyCharmProjects\\simstack-model",
|
|
23
|
+
"C:\\Users\\bj7610\\PyCharmProjects\\simstack-model\\src",
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
"self": {
|
|
27
|
+
"ssh-key": "C:\\Users\\bj7610\\Documents\\etc\\.ssh\\surface11_openssh",
|
|
28
|
+
"resource": "local",
|
|
29
|
+
"workdir": "C:\\Users\\bj7610\\simstack",
|
|
30
|
+
"python_path": [
|
|
31
|
+
"C:\\Users\\bj7610\\PyCharmProjects\\simstack-model",
|
|
32
|
+
"C:\\Users\\bj7610\\PyCharmProjects\\simstack-model\\src",
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
"uploads": {
|
|
36
|
+
"ssh-key": "C:\\Users\\bj7610\\Documents\\etc\\.ssh\\surface11_openssh",
|
|
37
|
+
"resource": "self",
|
|
38
|
+
"workdir": "C:\\Users\\bj7610\\simstack",
|
|
39
|
+
"python_path": [
|
|
40
|
+
"C:\\Users\\bj7610\\PyCharmProjects\\simstack-model",
|
|
41
|
+
"C:\\Users\\bj7610\\PyCharmProjects\\simstack-model\\src",
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
"int-nano": {
|
|
45
|
+
"ssh-key": "/home/ws/bj7610/.ssh/idrsa",
|
|
46
|
+
"workdir": "/home/ws/bj7610/simstack",
|
|
47
|
+
"python_path": [
|
|
48
|
+
"/home/ws/bj7610/projects/simstack-model",
|
|
49
|
+
"/home/ws/bj7610/projects/simstack-model/src",
|
|
50
|
+
],
|
|
51
|
+
"environment_start": "conda activate simstack-env",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
"server": {
|
|
55
|
+
"port": 8000,
|
|
56
|
+
"secret_key": "secret_key",
|
|
57
|
+
"upload_dir": "C:\\Users\\bj7610\\simstack\\uploads",
|
|
58
|
+
},
|
|
59
|
+
"hosts": {
|
|
60
|
+
"local": "localhost",
|
|
61
|
+
"int-nano": "int-nano.int.kit.edu",
|
|
62
|
+
"justus": "justus.int.kit.edu",
|
|
63
|
+
"horeka": "horeka.int.kit.edu",
|
|
64
|
+
},
|
|
65
|
+
"routes": [
|
|
66
|
+
{"source": "local", "target": "int-nano", "host": "local"},
|
|
67
|
+
{"source": "int-nano", "target": "local", "host": "local"},
|
|
68
|
+
{"source": "horeka", "target": "local", "host": "horeka"},
|
|
69
|
+
{"source": "local", "target": "horeka", "host": "horeka"},
|
|
70
|
+
{"source": "justus", "target": "local", "host": "justus"},
|
|
71
|
+
{"source": "local", "target": "justus", "host": "justus"},
|
|
72
|
+
],
|
|
73
|
+
"paths": {
|
|
74
|
+
"models": {"path": "src/simstack/models", "drops": "src"},
|
|
75
|
+
"methods": {"path": "src/simstack/methods", "drops": "src"},
|
|
76
|
+
"tests": {"path": "tests", "drops": "", "use_pickle": True},
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def sample_config_writer_entrypoint() -> None:
|
|
82
|
+
"""
|
|
83
|
+
Entry point to write a sample simstack.toml configuration file.
|
|
84
|
+
"""
|
|
85
|
+
import os
|
|
86
|
+
import toml
|
|
87
|
+
|
|
88
|
+
config_path = os.path.join(os.getcwd(), "simstack.toml")
|
|
89
|
+
if os.path.exists(config_path):
|
|
90
|
+
print(
|
|
91
|
+
f"simstack.toml already exists at {config_path}. Aborting to avoid overwrite."
|
|
92
|
+
)
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
with open(config_path, "w") as config_file:
|
|
96
|
+
toml.dump(_SAMPLE_SIMSTACK_TOML_DICT, config_file)
|
|
97
|
+
|
|
98
|
+
print(f"Sample simstack.toml written to {config_path}")
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from typing import Dict, Any
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def filter_file_content(data: Dict[str, Any]) -> Dict[str, Any]:
|
|
5
|
+
"""
|
|
6
|
+
Recursively searches through nested data structures to filter out sensitive content.
|
|
7
|
+
Specifically looks for 'content' fields in dictionaries, especially within 'files' structures.
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
data: Any Python data structure (dict, list, etc.)
|
|
11
|
+
|
|
12
|
+
Returns:
|
|
13
|
+
The filtered data structure with sensitive content removed
|
|
14
|
+
"""
|
|
15
|
+
if isinstance(data, dict):
|
|
16
|
+
# Create a new dict to avoid modifying the original during iteration
|
|
17
|
+
result = {}
|
|
18
|
+
|
|
19
|
+
# Check if this is a FileStack-like dict with content
|
|
20
|
+
if "content" in data:
|
|
21
|
+
# Make a copy of the dict without the content field
|
|
22
|
+
result = {
|
|
23
|
+
k: filter_file_content(v) for k, v in data.items() if k != "content"
|
|
24
|
+
}
|
|
25
|
+
else:
|
|
26
|
+
# Process all items recursively
|
|
27
|
+
for key, value in data.items():
|
|
28
|
+
result[key] = filter_file_content(value)
|
|
29
|
+
|
|
30
|
+
return result
|
|
31
|
+
|
|
32
|
+
elif isinstance(data, list):
|
|
33
|
+
# Process each item in the list recursively
|
|
34
|
+
return [filter_file_content(item) for item in data]
|
|
35
|
+
|
|
36
|
+
elif hasattr(data, "__dict__"):
|
|
37
|
+
# Handle object instances by converting to dict and processing
|
|
38
|
+
obj_dict = data.__dict__.copy()
|
|
39
|
+
filtered_dict = filter_file_content(obj_dict)
|
|
40
|
+
|
|
41
|
+
# This is tricky - we'd ideally create a new instance with the filtered data
|
|
42
|
+
# but that's complex without knowing the class structure
|
|
43
|
+
# So we'll return the filtered dict instead
|
|
44
|
+
return filtered_dict
|
|
45
|
+
|
|
46
|
+
else:
|
|
47
|
+
# For primitive types, return as is
|
|
48
|
+
return data
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from simstack.util.db_logger import DBLogHandler
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def setup_logging(
|
|
7
|
+
connection_string: str,
|
|
8
|
+
db_name: str,
|
|
9
|
+
log_level=logging.INFO,
|
|
10
|
+
console=True,
|
|
11
|
+
log_format: str = "%(asctime)s - %(name)-15s - %(levelname)-10s - %(filename)-20s:%(lineno)4d - %(message)s",
|
|
12
|
+
):
|
|
13
|
+
"""
|
|
14
|
+
Configures the root logger with specified logging level, handlers for both database
|
|
15
|
+
and optionally the console.
|
|
16
|
+
|
|
17
|
+
:param log_format:
|
|
18
|
+
:param connection_string: A string representing the database connection.
|
|
19
|
+
:param db_name: A name of the database to log into.
|
|
20
|
+
:param log_level: Logging verbosity level, defaulting to logging.INFO.
|
|
21
|
+
:param console: A boolean indicating whether to log messages to the console.
|
|
22
|
+
:return: Configured root logger instance.
|
|
23
|
+
"""
|
|
24
|
+
# Configure the root logger
|
|
25
|
+
root_logger = logging.getLogger()
|
|
26
|
+
root_logger.setLevel(log_level)
|
|
27
|
+
|
|
28
|
+
# Remove any existing handlers to avoid duplicates during reloads
|
|
29
|
+
for handler in root_logger.handlers[:]:
|
|
30
|
+
root_logger.removeHandler(handler)
|
|
31
|
+
|
|
32
|
+
# Create formatter
|
|
33
|
+
formatter = logging.Formatter(log_format, datefmt="%Y-%m-%d %H:%M:%S")
|
|
34
|
+
|
|
35
|
+
db_handler = DBLogHandler(connection_string, db_name, "logs")
|
|
36
|
+
|
|
37
|
+
# Add database handler
|
|
38
|
+
db_handler.setLevel(log_level)
|
|
39
|
+
db_handler.setFormatter(formatter)
|
|
40
|
+
root_logger.addHandler(db_handler)
|
|
41
|
+
|
|
42
|
+
# Optionally add console handler
|
|
43
|
+
if console:
|
|
44
|
+
console_handler = logging.StreamHandler()
|
|
45
|
+
console_handler.setFormatter(formatter)
|
|
46
|
+
console_handler.setLevel(log_level)
|
|
47
|
+
root_logger.addHandler(console_handler)
|
|
48
|
+
|
|
49
|
+
return root_logger
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# python
|
|
2
|
+
import os
|
|
3
|
+
import time
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from uuid import uuid4
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
|
|
9
|
+
POLL_INTERVAL = 1.0
|
|
10
|
+
TIMEOUT_SEC = 60 * 60 * 72 # adjust
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class WatchdogResult(BaseModel):
|
|
14
|
+
job_id: str
|
|
15
|
+
status: str
|
|
16
|
+
returncode: int | None
|
|
17
|
+
stdout: str
|
|
18
|
+
stderr: str
|
|
19
|
+
paths: dict[str, str]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def submit_to_watchdog(
|
|
23
|
+
command: str,
|
|
24
|
+
job_id: str,
|
|
25
|
+
queue_dir: Path = Path.cwd() / "queue",
|
|
26
|
+
env: dict | None = None,
|
|
27
|
+
cwd: str | None = None,
|
|
28
|
+
) -> WatchdogResult:
|
|
29
|
+
"""
|
|
30
|
+
Submit a command to be executed by the file-queue watcher.
|
|
31
|
+
Returns a WatchdogResult with status, exit_code, stdout, stderr, and paths.
|
|
32
|
+
"""
|
|
33
|
+
base = queue_dir / job_id
|
|
34
|
+
cmd_file_tmp = base.with_suffix(".cmd.tmp")
|
|
35
|
+
cmd_file = base.with_suffix(".cmd")
|
|
36
|
+
ready_file = base.with_suffix(".ready")
|
|
37
|
+
done_file = base.with_suffix(".done")
|
|
38
|
+
fail_file = base.with_suffix(".fail")
|
|
39
|
+
exit_file = base.with_suffix(".exit")
|
|
40
|
+
out_file = base.with_suffix(".stdout")
|
|
41
|
+
err_file = base.with_suffix(".stderr")
|
|
42
|
+
|
|
43
|
+
spec_lines = []
|
|
44
|
+
if cwd:
|
|
45
|
+
spec_lines.append(f"CWD={cwd}")
|
|
46
|
+
if env:
|
|
47
|
+
for k, v in env.items():
|
|
48
|
+
spec_lines.append(f"ENV[{k}]={v}")
|
|
49
|
+
spec_lines.append("CMD=" + command)
|
|
50
|
+
data = "\n".join(spec_lines) + "\n"
|
|
51
|
+
|
|
52
|
+
queue_dir.mkdir(parents=True, exist_ok=True)
|
|
53
|
+
print(f"[watcher] Submitting {data} to {cmd_file}")
|
|
54
|
+
with open(cmd_file_tmp, "w", encoding="utf-8") as f:
|
|
55
|
+
f.write(data)
|
|
56
|
+
f.flush()
|
|
57
|
+
os.fsync(f.fileno())
|
|
58
|
+
|
|
59
|
+
os.replace(cmd_file_tmp, cmd_file) # atomic
|
|
60
|
+
ready_file.touch() # signal
|
|
61
|
+
|
|
62
|
+
deadline = time.time() + TIMEOUT_SEC
|
|
63
|
+
while time.time() < deadline:
|
|
64
|
+
if done_file.exists() or fail_file.exists():
|
|
65
|
+
break
|
|
66
|
+
time.sleep(POLL_INTERVAL)
|
|
67
|
+
|
|
68
|
+
result = WatchdogResult(
|
|
69
|
+
job_id=job_id,
|
|
70
|
+
status="timeout",
|
|
71
|
+
returncode=None,
|
|
72
|
+
stdout="",
|
|
73
|
+
stderr="",
|
|
74
|
+
paths={
|
|
75
|
+
"cmd": str(cmd_file),
|
|
76
|
+
"ready": str(ready_file),
|
|
77
|
+
"done": str(done_file),
|
|
78
|
+
"fail": str(fail_file),
|
|
79
|
+
"exit": str(exit_file),
|
|
80
|
+
"stdout": str(out_file),
|
|
81
|
+
"stderr": str(err_file),
|
|
82
|
+
},
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
if done_file.exists() or fail_file.exists():
|
|
86
|
+
exit_code: int | None = None
|
|
87
|
+
if exit_file.exists():
|
|
88
|
+
try:
|
|
89
|
+
exit_code = int(exit_file.read_text().strip())
|
|
90
|
+
except Exception:
|
|
91
|
+
exit_code = None
|
|
92
|
+
out = out_file.read_text(errors="replace") if out_file.exists() else ""
|
|
93
|
+
err = err_file.read_text(errors="replace") if err_file.exists() else ""
|
|
94
|
+
ok = done_file.exists() and (exit_code == 0)
|
|
95
|
+
|
|
96
|
+
result.status = "ok" if ok else "failed"
|
|
97
|
+
result.returncode = exit_code
|
|
98
|
+
result.stdout = out
|
|
99
|
+
result.stderr = err
|
|
100
|
+
|
|
101
|
+
return result
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
if __name__ == "__main__":
|
|
105
|
+
res = submit_to_watchdog("dir", uuid4().hex)
|
|
106
|
+
print(res)
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import socket
|
|
5
|
+
import sys
|
|
6
|
+
import tomllib
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
from simstack.core.resources import allowed_resources
|
|
11
|
+
from simstack.core.route_table import route_table
|
|
12
|
+
|
|
13
|
+
from simstack.util.path_manager import path_manager
|
|
14
|
+
from simstack.util.transform_file_name import transform_file_name
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from simstack.models.resource_definition import ResourceDefinition
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class TomlReader:
|
|
23
|
+
def __init__(self, config_path: Path, config_file: Path = Path("simstack.toml")):
|
|
24
|
+
try:
|
|
25
|
+
if config_file.is_absolute():
|
|
26
|
+
toml_file = config_file
|
|
27
|
+
else:
|
|
28
|
+
toml_file = config_path / config_file
|
|
29
|
+
if toml_file.exists():
|
|
30
|
+
with open(toml_file, "rb") as f:
|
|
31
|
+
self._config = tomllib.load(f)
|
|
32
|
+
else:
|
|
33
|
+
print(f"Config file {toml_file} does not exist. Aborting.")
|
|
34
|
+
sys.exit(-1)
|
|
35
|
+
self._config_path = config_path
|
|
36
|
+
except tomllib.TOMLDecodeError:
|
|
37
|
+
print("There was an error decoding the TOML file.")
|
|
38
|
+
sys.exit(-1)
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def config(self):
|
|
42
|
+
return self._config
|
|
43
|
+
|
|
44
|
+
def get(self, key: str, default=None):
|
|
45
|
+
keys = key.split(".")
|
|
46
|
+
value = self._config
|
|
47
|
+
for k in keys:
|
|
48
|
+
if isinstance(value, dict) and k in value:
|
|
49
|
+
value = value[k]
|
|
50
|
+
else:
|
|
51
|
+
return default
|
|
52
|
+
return value
|
|
53
|
+
|
|
54
|
+
def get_git_list(self):
|
|
55
|
+
return self.get("parameters.general.git", [])
|
|
56
|
+
|
|
57
|
+
def use_db(self):
|
|
58
|
+
return self.get("parameters.general.use_db", False)
|
|
59
|
+
|
|
60
|
+
def get_allowed_resources(self):
|
|
61
|
+
allowed_resources_list = self.get("resources.allowed_resources", None)
|
|
62
|
+
if allowed_resources_list is None:
|
|
63
|
+
raise ValueError("Allowed resources must be specified in the config file")
|
|
64
|
+
return allowed_resources_list
|
|
65
|
+
|
|
66
|
+
def get_resource_definition(self, resource_str) -> "ResourceDefinition":
|
|
67
|
+
"""
|
|
68
|
+
Retrieves the resource definition for the given resource string from the TOML configuration.
|
|
69
|
+
Raises ValueError if the resource is not allowed or if the resource definition is not found.
|
|
70
|
+
Sets the allowed resources list from the config file.
|
|
71
|
+
"""
|
|
72
|
+
from simstack.models.resource_definition import ResourceDefinition
|
|
73
|
+
|
|
74
|
+
if not allowed_resources.has_resource(resource_str):
|
|
75
|
+
raise ValueError(
|
|
76
|
+
f"Illegal resource {resource_str}. Allowed resources are: {allowed_resources.get_resources()}."
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
resource_definition = self.get(f"resources.{resource_str}", None)
|
|
80
|
+
if resource_definition is None:
|
|
81
|
+
raise ValueError(f"Resource definition for {resource_str} not found.")
|
|
82
|
+
|
|
83
|
+
resource_definition["resource_str"] = resource_str
|
|
84
|
+
|
|
85
|
+
if "workdir" not in resource_definition:
|
|
86
|
+
raise ValueError(f"No workdir specified for resource {resource_str}.")
|
|
87
|
+
else:
|
|
88
|
+
resource_definition["workdir"] = transform_file_name(
|
|
89
|
+
resource_definition["workdir"]
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
if "python_paths" not in resource_definition:
|
|
93
|
+
logger.warning(f"No python paths specified for resource {resource_str}.")
|
|
94
|
+
else:
|
|
95
|
+
resource_definition["python_paths"] = [
|
|
96
|
+
transform_file_name(p) for p in resource_definition["python_paths"]
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
if "ssh_key" not in resource_definition:
|
|
100
|
+
logger.warning(f"No ssh key path specified for resource {resource_str}.")
|
|
101
|
+
else:
|
|
102
|
+
resource_definition["ssh_key"] = transform_file_name(
|
|
103
|
+
resource_definition["ssh_key"]
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
if "environment_start" not in resource_definition:
|
|
107
|
+
logger.warning(
|
|
108
|
+
f"No environment start command specified for resource {resource_str}."
|
|
109
|
+
)
|
|
110
|
+
if "routes" not in resource_definition:
|
|
111
|
+
logger.warning(f"No routes specified for resource {resource_str}.")
|
|
112
|
+
resource_definition["routes"] = []
|
|
113
|
+
if "hostname" not in resource_definition:
|
|
114
|
+
raise ValueError(f"No hostname specified for resource {resource_str}.")
|
|
115
|
+
elif (
|
|
116
|
+
resource_definition["hostname"] == "test_hostname"
|
|
117
|
+
or resource_definition["hostname"] == "localhost"
|
|
118
|
+
):
|
|
119
|
+
resource_definition["hostname"] = socket.gethostname()
|
|
120
|
+
logger.info(
|
|
121
|
+
f"Overriding hostname for tests: {resource_definition['hostname']} "
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return ResourceDefinition.model_validate(resource_definition)
|
|
125
|
+
|
|
126
|
+
def get_routes(self, resource_str: str):
|
|
127
|
+
return self.get(f"routes.{resource_str}", [])
|
|
128
|
+
|
|
129
|
+
def initialize_path_manager(self):
|
|
130
|
+
paths = self.get("paths", {})
|
|
131
|
+
for name, path_info in paths.items():
|
|
132
|
+
if isinstance(path_info, dict) and "path" in path_info:
|
|
133
|
+
path = Path(path_info["path"])
|
|
134
|
+
drops = path_info.get("drops", "")
|
|
135
|
+
path_use_pickle = path_info.get("use_pickle", False)
|
|
136
|
+
path_manager.add_path(name, path, drops, path_use_pickle)
|
|
137
|
+
|
|
138
|
+
def build_routes(self):
|
|
139
|
+
route_table.clear_routes()
|
|
140
|
+
for resource_str in allowed_resources.get_resources():
|
|
141
|
+
routes = self.get_routes(resource_str)
|
|
142
|
+
route_table.add_route_set(resource_str, routes)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from simstack.util.project_root_finder import find_project_root
|
|
6
|
+
|
|
7
|
+
def transform_file_name(path_str: str | Path, project_root: Path | None = None) -> Path:
|
|
8
|
+
"""
|
|
9
|
+
Transform a string path by substituting environment variables.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
path_str: String containing path with optional environment variables
|
|
13
|
+
($HOME, $PROJECT, $TEMP)
|
|
14
|
+
project_root: Optional project root directory. If not provided, it will be automatically detected.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
Path object with resolved environment variables
|
|
18
|
+
|
|
19
|
+
Raises:
|
|
20
|
+
FileNotFoundError: If the resolved path does not exist
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
if isinstance(path_str, Path):
|
|
24
|
+
path_str = str(path_str)
|
|
25
|
+
|
|
26
|
+
if project_root is None:
|
|
27
|
+
project_root = find_project_root()
|
|
28
|
+
|
|
29
|
+
replacements = {
|
|
30
|
+
"$HOME": os.environ.get("HOME", str(Path.home())),
|
|
31
|
+
"$TEMP": os.environ.get("TEMP", os.environ.get("TMP", "/tmp")),
|
|
32
|
+
"$PROJECT": os.environ.get("PROJECT", str(project_root))
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
for var, value in replacements.items():
|
|
36
|
+
path_str = path_str.replace(var, value)
|
|
37
|
+
|
|
38
|
+
path = Path(path_str)
|
|
39
|
+
if not path.exists():
|
|
40
|
+
raise FileNotFoundError(f"Path does not exist: {path}")
|
|
41
|
+
return path
|