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.
Files changed (354) hide show
  1. simstack/__init__.py +0 -0
  2. simstack/_version.py +24 -0
  3. simstack/core/__init__.py +0 -0
  4. simstack/core/artifacts.py +406 -0
  5. simstack/core/asnyc_helper.py +14 -0
  6. simstack/core/config_file.py +60 -0
  7. simstack/core/context.py +283 -0
  8. simstack/core/definitions.py +22 -0
  9. simstack/core/engine.py +115 -0
  10. simstack/core/find_simstack_modules.py +41 -0
  11. simstack/core/hash.py +179 -0
  12. simstack/core/node.py +1137 -0
  13. simstack/core/node_claim.py +31 -0
  14. simstack/core/node_runner.py +362 -0
  15. simstack/core/process_results.py +102 -0
  16. simstack/core/recompute_artifacts.py +117 -0
  17. simstack/core/reset_db.py +12 -0
  18. simstack/core/resource_assignment.py +222 -0
  19. simstack/core/resources.py +90 -0
  20. simstack/core/route_table.py +24 -0
  21. simstack/core/run_docker.py +109 -0
  22. simstack/core/run_node.py +71 -0
  23. simstack/core/runner.py +169 -0
  24. simstack/core/services/__init__.py +11 -0
  25. simstack/core/services/base_service.py +191 -0
  26. simstack/core/services/file_transfer_service.py +117 -0
  27. simstack/core/services/git_restart_service.py +12 -0
  28. simstack/core/services/git_uv_update_service.py +105 -0
  29. simstack/core/services/node_execution_service.py +182 -0
  30. simstack/core/services/resource_branch_monitor_service.py +71 -0
  31. simstack/core/services/runner_cleanup_service.py +149 -0
  32. simstack/core/services/runner_manager.py +163 -0
  33. simstack/core/services/runner_status_service.py +10 -0
  34. simstack/core/services/slurm_status_service.py +67 -0
  35. simstack/core/services/stop_check_service.py +27 -0
  36. simstack/core/services/timeout_restart_service.py +26 -0
  37. simstack/core/simstack_result.py +39 -0
  38. simstack/core/submit_node.py +217 -0
  39. simstack/core/task_id.py +22 -0
  40. simstack/methods/__init__.py +0 -0
  41. simstack/methods/archive_files.py +292 -0
  42. simstack/methods/fire_and_forget_runner.py +113 -0
  43. simstack/methods/generate_test.py +125 -0
  44. simstack/methods/get_file.py +26 -0
  45. simstack/methods/mass_runner.py +154 -0
  46. simstack/methods/multiple_executor.py +307 -0
  47. simstack/methods/switch_git.py +16 -0
  48. simstack/methods/upload_helpers.py +81 -0
  49. simstack/models/__init__.py +74 -0
  50. simstack/models/array_list.py +21 -0
  51. simstack/models/array_storage.py +83 -0
  52. simstack/models/artifact_models.py +127 -0
  53. simstack/models/base_lists.py +51 -0
  54. simstack/models/base_types.py +209 -0
  55. simstack/models/charts_artifact.py +690 -0
  56. simstack/models/dataset.py +451 -0
  57. simstack/models/dataset_metadata.py +390 -0
  58. simstack/models/datasettuple.py +587 -0
  59. simstack/models/file_instance.py +150 -0
  60. simstack/models/file_list.py +45 -0
  61. simstack/models/files.py +424 -0
  62. simstack/models/fire_and_forget_result.py +19 -0
  63. simstack/models/images2d.py +113 -0
  64. simstack/models/log_entry_model.py +37 -0
  65. simstack/models/models.py +52 -0
  66. simstack/models/named_data_reference.py +36 -0
  67. simstack/models/node_registry.py +96 -0
  68. simstack/models/pandas_model.py +239 -0
  69. simstack/models/parameters.py +402 -0
  70. simstack/models/pickle_models.py +300 -0
  71. simstack/models/project.py +13 -0
  72. simstack/models/resource_assignment.py +170 -0
  73. simstack/models/resource_definition.py +125 -0
  74. simstack/models/runner_model.py +39 -0
  75. simstack/models/simple_table.py +79 -0
  76. simstack/models/simstack_model.py +97 -0
  77. simstack/models/slurm_info.py +17 -0
  78. simstack/models/table_artifact.py +87 -0
  79. simstack/models/tag.py +12 -0
  80. simstack/models/unused_test_file.py +5 -0
  81. simstack/simstack_marker.com +1 -0
  82. simstack/static/docs/.buildinfo +4 -0
  83. simstack/static/docs/_images/GUI Concept.webp +0 -0
  84. simstack/static/docs/_modules/index.html +434 -0
  85. simstack/static/docs/_modules/simstack/core/artifacts.html +743 -0
  86. simstack/static/docs/_modules/simstack/core/asnyc_helper.html +330 -0
  87. simstack/static/docs/_modules/simstack/core/config_file.html +379 -0
  88. simstack/static/docs/_modules/simstack/core/definitions.html +341 -0
  89. simstack/static/docs/_modules/simstack/core/engine.html +437 -0
  90. simstack/static/docs/_modules/simstack/core/find_simstack_modules.html +360 -0
  91. simstack/static/docs/_modules/simstack/core/hash.html +528 -0
  92. simstack/static/docs/_modules/simstack/core/node.html +1392 -0
  93. simstack/static/docs/_modules/simstack/core/node_claim.html +347 -0
  94. simstack/static/docs/_modules/simstack/core/node_runner.html +711 -0
  95. simstack/static/docs/_modules/simstack/core/process_results.html +418 -0
  96. simstack/static/docs/_modules/simstack/core/recompute_artifacts.html +433 -0
  97. simstack/static/docs/_modules/simstack/core/reset_db.html +328 -0
  98. simstack/static/docs/_modules/simstack/core/resource_assignment.html +553 -0
  99. simstack/static/docs/_modules/simstack/core/resources.html +424 -0
  100. simstack/static/docs/_modules/simstack/core/route_table.html +349 -0
  101. simstack/static/docs/_modules/simstack/core/run_docker.html +425 -0
  102. simstack/static/docs/_modules/simstack/core/run_node.html +390 -0
  103. simstack/static/docs/_modules/simstack/core/runner.html +491 -0
  104. simstack/static/docs/_modules/simstack/core/services/base_service.html +522 -0
  105. simstack/static/docs/_modules/simstack/core/services/file_transfer_service.html +436 -0
  106. simstack/static/docs/_modules/simstack/core/services/git_restart_service.html +331 -0
  107. simstack/static/docs/_modules/simstack/core/services/git_uv_update_service.html +427 -0
  108. simstack/static/docs/_modules/simstack/core/services/node_execution_service.html +507 -0
  109. simstack/static/docs/_modules/simstack/core/services/resource_branch_monitor_service.html +390 -0
  110. simstack/static/docs/_modules/simstack/core/services/runner_cleanup_service.html +468 -0
  111. simstack/static/docs/_modules/simstack/core/services/runner_manager.html +485 -0
  112. simstack/static/docs/_modules/simstack/core/services/runner_status_service.html +329 -0
  113. simstack/static/docs/_modules/simstack/core/services/slurm_status_service.html +386 -0
  114. simstack/static/docs/_modules/simstack/core/services/stop_check_service.html +346 -0
  115. simstack/static/docs/_modules/simstack/core/services/timeout_restart_service.html +345 -0
  116. simstack/static/docs/_modules/simstack/core/simstack_result.html +355 -0
  117. simstack/static/docs/_modules/simstack/core/submit_node.html +536 -0
  118. simstack/static/docs/_modules/simstack/core/task_id.html +344 -0
  119. simstack/static/docs/_modules/simstack/methods/archive_files.html +598 -0
  120. simstack/static/docs/_modules/simstack/methods/fire_and_forget_runner.html +432 -0
  121. simstack/static/docs/_modules/simstack/methods/generate_test.html +443 -0
  122. simstack/static/docs/_modules/simstack/methods/get_file.html +342 -0
  123. simstack/static/docs/_modules/simstack/methods/mass_runner.html +476 -0
  124. simstack/static/docs/_modules/simstack/methods/multiple_executor.html +674 -0
  125. simstack/static/docs/_modules/simstack/methods/switch_git.html +332 -0
  126. simstack/static/docs/_modules/simstack/methods/upload_helpers.html +403 -0
  127. simstack/static/docs/_modules/simstack/models/array_list.html +337 -0
  128. simstack/static/docs/_modules/simstack/models/array_storage.html +417 -0
  129. simstack/static/docs/_modules/simstack/models/base_lists.html +374 -0
  130. simstack/static/docs/_modules/simstack/models/base_types.html +588 -0
  131. simstack/static/docs/_modules/simstack/models/charts_artifact.html +1035 -0
  132. simstack/static/docs/_modules/simstack/models/dataset.html +866 -0
  133. simstack/static/docs/_modules/simstack/models/dataset_metadata.html +711 -0
  134. simstack/static/docs/_modules/simstack/models/datasettuple.html +1005 -0
  135. simstack/static/docs/_modules/simstack/models/file_instance.html +472 -0
  136. simstack/static/docs/_modules/simstack/models/file_list.html +367 -0
  137. simstack/static/docs/_modules/simstack/models/files.html +773 -0
  138. simstack/static/docs/_modules/simstack/models/fire_and_forget_result.html +335 -0
  139. simstack/static/docs/_modules/simstack/models/images2d.html +447 -0
  140. simstack/static/docs/_modules/simstack/models/log_entry_model.html +356 -0
  141. simstack/static/docs/_modules/simstack/models/models.html +374 -0
  142. simstack/static/docs/_modules/simstack/models/named_data_reference.html +355 -0
  143. simstack/static/docs/_modules/simstack/models/node_registry.html +415 -0
  144. simstack/static/docs/_modules/simstack/models/pandas_model.html +570 -0
  145. simstack/static/docs/_modules/simstack/models/parameters.html +739 -0
  146. simstack/static/docs/_modules/simstack/models/pickle_models.html +631 -0
  147. simstack/static/docs/_modules/simstack/models/project.html +329 -0
  148. simstack/static/docs/_modules/simstack/models/resource_assignment.html +501 -0
  149. simstack/static/docs/_modules/simstack/models/resource_definition.html +477 -0
  150. simstack/static/docs/_modules/simstack/models/runner_model.html +361 -0
  151. simstack/static/docs/_modules/simstack/models/simple_table.html +413 -0
  152. simstack/static/docs/_modules/simstack/models/simstack_model.html +416 -0
  153. simstack/static/docs/_modules/simstack/models/slurm_info.html +333 -0
  154. simstack/static/docs/_modules/simstack/models/table_artifact.html +406 -0
  155. simstack/static/docs/_modules/simstack/models/tag.html +328 -0
  156. simstack/static/docs/_modules/simstack/tables/model_table.html +519 -0
  157. simstack/static/docs/_modules/simstack/tables/node_children.html +538 -0
  158. simstack/static/docs/_modules/simstack/tables/node_table.html +854 -0
  159. simstack/static/docs/_modules/simstack/tables/table_builder.html +642 -0
  160. simstack/static/docs/_modules/simstack/util/async_zip_utils.html +337 -0
  161. simstack/static/docs/_modules/simstack/util/b64mixin.html +339 -0
  162. simstack/static/docs/_modules/simstack/util/cleaned_json_schema.html +336 -0
  163. simstack/static/docs/_modules/simstack/util/config_reader.html +498 -0
  164. simstack/static/docs/_modules/simstack/util/custom_model_dump.html +391 -0
  165. simstack/static/docs/_modules/simstack/util/database_information.html +488 -0
  166. simstack/static/docs/_modules/simstack/util/db.html +1392 -0
  167. simstack/static/docs/_modules/simstack/util/db_logger.html +448 -0
  168. simstack/static/docs/_modules/simstack/util/default_from_dict.html +376 -0
  169. simstack/static/docs/_modules/simstack/util/directory_iterator.html +553 -0
  170. simstack/static/docs/_modules/simstack/util/docstring_parser.html +526 -0
  171. simstack/static/docs/_modules/simstack/util/file_hashing.html +394 -0
  172. simstack/static/docs/_modules/simstack/util/file_transfer_client.html +791 -0
  173. simstack/static/docs/_modules/simstack/util/files.html +733 -0
  174. simstack/static/docs/_modules/simstack/util/find_python_files.html +349 -0
  175. simstack/static/docs/_modules/simstack/util/generate_ui_schema.html +396 -0
  176. simstack/static/docs/_modules/simstack/util/generic_list_mixin.html +487 -0
  177. simstack/static/docs/_modules/simstack/util/get_module_path.html +336 -0
  178. simstack/static/docs/_modules/simstack/util/git_pull.html +381 -0
  179. simstack/static/docs/_modules/simstack/util/git_repository_status.html +411 -0
  180. simstack/static/docs/_modules/simstack/util/import_module.html +377 -0
  181. simstack/static/docs/_modules/simstack/util/importer.html +637 -0
  182. simstack/static/docs/_modules/simstack/util/init_data_source.html +357 -0
  183. simstack/static/docs/_modules/simstack/util/make_graph.html +423 -0
  184. simstack/static/docs/_modules/simstack/util/make_table.html +1171 -0
  185. simstack/static/docs/_modules/simstack/util/mappings.html +378 -0
  186. simstack/static/docs/_modules/simstack/util/minimal_route_finder.html +388 -0
  187. simstack/static/docs/_modules/simstack/util/mock_db.html +472 -0
  188. simstack/static/docs/_modules/simstack/util/module_path_checker.html +354 -0
  189. simstack/static/docs/_modules/simstack/util/object_list_mixin.html +649 -0
  190. simstack/static/docs/_modules/simstack/util/path_manager.html +566 -0
  191. simstack/static/docs/_modules/simstack/util/project_root_finder.html +357 -0
  192. simstack/static/docs/_modules/simstack/util/queue_watcher.html +593 -0
  193. simstack/static/docs/_modules/simstack/util/resource_config.html +532 -0
  194. simstack/static/docs/_modules/simstack/util/route_finder.html +390 -0
  195. simstack/static/docs/_modules/simstack/util/runner_util.html +487 -0
  196. simstack/static/docs/_modules/simstack/util/runner_utils.html +468 -0
  197. simstack/static/docs/_modules/simstack/util/safe_code_executor.html +510 -0
  198. simstack/static/docs/_modules/simstack/util/sample_config.html +414 -0
  199. simstack/static/docs/_modules/simstack/util/sensitive_content_filter.html +364 -0
  200. simstack/static/docs/_modules/simstack/util/setup_logging.html +365 -0
  201. simstack/static/docs/_modules/simstack/util/submit_to_watchdog.html +425 -0
  202. simstack/static/docs/_modules/simstack/util/toml_reader.html +482 -0
  203. simstack/static/docs/_modules/simstack/util/transform_file_name.html +357 -0
  204. simstack/static/docs/_modules/simstack/util/ui_tools.html +541 -0
  205. simstack/static/docs/_sources/architecture.rst.txt +25 -0
  206. simstack/static/docs/_sources/array_lists.rst.txt +60 -0
  207. simstack/static/docs/_sources/ci_pipeline.rst.txt +34 -0
  208. simstack/static/docs/_sources/data.rst.txt +175 -0
  209. simstack/static/docs/_sources/database_migrations.rst.txt +310 -0
  210. simstack/static/docs/_sources/datasets.rst.txt +93 -0
  211. simstack/static/docs/_sources/developer_guide.rst.txt +25 -0
  212. simstack/static/docs/_sources/docker_nodes.rst.txt +149 -0
  213. simstack/static/docs/_sources/files.rst.txt +159 -0
  214. simstack/static/docs/_sources/fire_and_forget_runner.rst.txt +67 -0
  215. simstack/static/docs/_sources/index.rst.txt +25 -0
  216. simstack/static/docs/_sources/installation.rst.txt +213 -0
  217. simstack/static/docs/_sources/lists.rst.txt +111 -0
  218. simstack/static/docs/_sources/mass_runner.rst.txt +91 -0
  219. simstack/static/docs/_sources/misc.rst.txt +106 -0
  220. simstack/static/docs/_sources/overview.rst.txt +91 -0
  221. simstack/static/docs/_sources/recovery.rst.txt +38 -0
  222. simstack/static/docs/_sources/resource_config.rst.txt +141 -0
  223. simstack/static/docs/_sources/simstack/modules/modules.rst.txt +7 -0
  224. simstack/static/docs/_sources/simstack/modules/simstack.core.rst.txt +205 -0
  225. simstack/static/docs/_sources/simstack/modules/simstack.core.services.rst.txt +109 -0
  226. simstack/static/docs/_sources/simstack/modules/simstack.methods.rst.txt +77 -0
  227. simstack/static/docs/_sources/simstack/modules/simstack.models.rst.txt +261 -0
  228. simstack/static/docs/_sources/simstack/modules/simstack.rst.txt +22 -0
  229. simstack/static/docs/_sources/simstack/modules/simstack.tables.rst.txt +45 -0
  230. simstack/static/docs/_sources/simstack/modules/simstack.util.rst.txt +381 -0
  231. simstack/static/docs/_sources/submit.rst.txt +141 -0
  232. simstack/static/docs/_sources/tables.rst.txt +50 -0
  233. simstack/static/docs/_sources/testing_complex_nodes.rst.txt +92 -0
  234. simstack/static/docs/_sources/ui_documentation.rst.txt +272 -0
  235. simstack/static/docs/_sources/workflows.rst.txt +446 -0
  236. simstack/static/docs/_static/base-stemmer.js +476 -0
  237. simstack/static/docs/_static/basic.css +906 -0
  238. simstack/static/docs/_static/cobalt2-theme.css +1 -0
  239. simstack/static/docs/_static/debug.css +69 -0
  240. simstack/static/docs/_static/docs-build-badge.css +51 -0
  241. simstack/static/docs/_static/docs-build-badge.js +51 -0
  242. simstack/static/docs/_static/doctools.js +150 -0
  243. simstack/static/docs/_static/documentation_options.js +13 -0
  244. simstack/static/docs/_static/english-stemmer.js +1066 -0
  245. simstack/static/docs/_static/file.png +0 -0
  246. simstack/static/docs/_static/language_data.js +13 -0
  247. simstack/static/docs/_static/minus.png +0 -0
  248. simstack/static/docs/_static/plus.png +0 -0
  249. simstack/static/docs/_static/pygments.css +250 -0
  250. simstack/static/docs/_static/scripts/furo-extensions.js +0 -0
  251. simstack/static/docs/_static/scripts/furo.js +3 -0
  252. simstack/static/docs/_static/scripts/furo.js.LICENSE.txt +7 -0
  253. simstack/static/docs/_static/scripts/furo.js.map +1 -0
  254. simstack/static/docs/_static/searchtools.js +693 -0
  255. simstack/static/docs/_static/skeleton.css +296 -0
  256. simstack/static/docs/_static/sphinx_highlight.js +159 -0
  257. simstack/static/docs/_static/styles/furo-extensions.css +2 -0
  258. simstack/static/docs/_static/styles/furo-extensions.css.map +1 -0
  259. simstack/static/docs/_static/styles/furo.css +2 -0
  260. simstack/static/docs/_static/styles/furo.css.map +1 -0
  261. simstack/static/docs/architecture.html +340 -0
  262. simstack/static/docs/array_lists.html +678 -0
  263. simstack/static/docs/ci_pipeline.html +385 -0
  264. simstack/static/docs/data.html +545 -0
  265. simstack/static/docs/database_migrations.html +652 -0
  266. simstack/static/docs/datasets.html +1268 -0
  267. simstack/static/docs/developer_guide.html +359 -0
  268. simstack/static/docs/docker_nodes.html +503 -0
  269. simstack/static/docs/files.html +718 -0
  270. simstack/static/docs/fire_and_forget_runner.html +641 -0
  271. simstack/static/docs/genindex.html +6390 -0
  272. simstack/static/docs/index.html +516 -0
  273. simstack/static/docs/installation.html +553 -0
  274. simstack/static/docs/lists.html +478 -0
  275. simstack/static/docs/mass_runner.html +481 -0
  276. simstack/static/docs/misc.html +454 -0
  277. simstack/static/docs/objects.inv +0 -0
  278. simstack/static/docs/overview.html +469 -0
  279. simstack/static/docs/py-modindex.html +1111 -0
  280. simstack/static/docs/recovery.html +371 -0
  281. simstack/static/docs/resource_config.html +590 -0
  282. simstack/static/docs/search.html +327 -0
  283. simstack/static/docs/searchindex.js +1 -0
  284. simstack/static/docs/simstack/modules/modules.html +489 -0
  285. simstack/static/docs/simstack/modules/simstack.core.html +1883 -0
  286. simstack/static/docs/simstack/modules/simstack.core.services.html +698 -0
  287. simstack/static/docs/simstack/modules/simstack.html +3060 -0
  288. simstack/static/docs/simstack/modules/simstack.methods.html +1150 -0
  289. simstack/static/docs/simstack/modules/simstack.models.html +13113 -0
  290. simstack/static/docs/simstack/modules/simstack.tables.html +635 -0
  291. simstack/static/docs/simstack/modules/simstack.util.html +3320 -0
  292. simstack/static/docs/submit.html +487 -0
  293. simstack/static/docs/tables.html +539 -0
  294. simstack/static/docs/testing_complex_nodes.html +458 -0
  295. simstack/static/docs/ui_documentation.html +788 -0
  296. simstack/static/docs/workflows.html +766 -0
  297. simstack/tables/__init__.py +0 -0
  298. simstack/tables/model_table.py +191 -0
  299. simstack/tables/node_children.py +219 -0
  300. simstack/tables/node_table.py +517 -0
  301. simstack/tables/table_builder.py +314 -0
  302. simstack/util/__init__.py +0 -0
  303. simstack/util/async_zip_utils.py +18 -0
  304. simstack/util/b64mixin.py +23 -0
  305. simstack/util/cleaned_json_schema.py +20 -0
  306. simstack/util/config_reader.py +179 -0
  307. simstack/util/custom_model_dump.py +75 -0
  308. simstack/util/database_information.py +160 -0
  309. simstack/util/db.py +1013 -0
  310. simstack/util/db_logger.py +117 -0
  311. simstack/util/default_from_dict.py +57 -0
  312. simstack/util/directory_iterator.py +216 -0
  313. simstack/util/docstring_parser.py +186 -0
  314. simstack/util/file_hashing.py +72 -0
  315. simstack/util/file_transfer_client.py +427 -0
  316. simstack/util/files.py +396 -0
  317. simstack/util/find_python_files.py +33 -0
  318. simstack/util/generate_ui_schema.py +80 -0
  319. simstack/util/generic_list_mixin.py +120 -0
  320. simstack/util/geometry_utils.py +118 -0
  321. simstack/util/get_module_path.py +20 -0
  322. simstack/util/git_pull.py +65 -0
  323. simstack/util/git_repository_status.py +95 -0
  324. simstack/util/import_module.py +63 -0
  325. simstack/util/importer.py +312 -0
  326. simstack/util/init_data_source.py +41 -0
  327. simstack/util/make_graph.py +107 -0
  328. simstack/util/make_table.py +837 -0
  329. simstack/util/mappings.py +41 -0
  330. simstack/util/minimal_route_finder.py +72 -0
  331. simstack/util/mock_db.py +144 -0
  332. simstack/util/module_path_checker.py +38 -0
  333. simstack/util/object_list_mixin.py +282 -0
  334. simstack/util/path_manager.py +223 -0
  335. simstack/util/project_root_finder.py +41 -0
  336. simstack/util/queue_watcher.py +262 -0
  337. simstack/util/resource_config.py +198 -0
  338. simstack/util/route_finder.py +74 -0
  339. simstack/util/runner_util.py +166 -0
  340. simstack/util/runner_utils.py +143 -0
  341. simstack/util/safe_code_executor.py +194 -0
  342. simstack/util/sample_config.py +98 -0
  343. simstack/util/sensitive_content_filter.py +48 -0
  344. simstack/util/setup_logging.py +49 -0
  345. simstack/util/submit_to_watchdog.py +106 -0
  346. simstack/util/toml_reader.py +142 -0
  347. simstack/util/transform_file_name.py +41 -0
  348. simstack/util/ui_tools.py +213 -0
  349. simstack-2.0.1.dist-info/METADATA +454 -0
  350. simstack-2.0.1.dist-info/RECORD +354 -0
  351. simstack-2.0.1.dist-info/WHEEL +5 -0
  352. simstack-2.0.1.dist-info/entry_points.txt +12 -0
  353. simstack-2.0.1.dist-info/licenses/LICENSE +21 -0
  354. simstack-2.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,149 @@
1
+ import logging
2
+ import os
3
+ from datetime import datetime, timedelta
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+ from simstack.core.context import context
8
+ from simstack.models.files import FileStack
9
+ from simstack.models.parameters import Resource
10
+ from simstack.models.runner_model import RunnerEvent, RunnerType
11
+ from simstack.core.services.base_service import BaseService
12
+
13
+ logger = logging.getLogger("NodeRunner")
14
+
15
+
16
+ def _resource_to_str(resource: Any) -> str:
17
+ if hasattr(resource, "resource_str"):
18
+ return str(getattr(resource, "resource_str"))
19
+ raw = getattr(resource, "__dict__", {}).get("value")
20
+ if raw is not None:
21
+ return str(raw)
22
+ return str(resource)
23
+
24
+
25
+ def _cache_ttl_days() -> int:
26
+ raw = os.environ.get("SIMSTACK_FILE_CACHE_TTL_DAYS")
27
+ if raw is None:
28
+ return 14
29
+ try:
30
+ value = int(raw)
31
+ except ValueError:
32
+ return 14
33
+ return max(value, 0)
34
+
35
+
36
+ def _cache_cleanup_enabled() -> bool:
37
+ return os.environ.get("SIMSTACK_FILE_CACHE_CLEANUP_ENABLED", "1").lower() not in {
38
+ "0",
39
+ "false",
40
+ "no",
41
+ }
42
+
43
+
44
+ def _as_naive_datetime(value: datetime | None) -> datetime | None:
45
+ if value is None:
46
+ return None
47
+ if value.tzinfo is None:
48
+ return value
49
+ return value.astimezone().replace(tzinfo=None)
50
+
51
+
52
+ class RunnerCleanupService(BaseService):
53
+ """
54
+ Service that cleans up old RunnerEvent logs and expired local file cache
55
+ copies for the current resource.
56
+ """
57
+
58
+ def __init__(self, resource: Resource, interval: int = 300) -> None:
59
+ # Default interval 5 minutes
60
+ super().__init__("RunnerCleanup", resource, interval)
61
+
62
+ async def execute(self) -> None:
63
+ cutoff_time = datetime.now() - timedelta(minutes=30)
64
+
65
+ # Find and delete events matching the criteria
66
+ old_events = await context.db.find(
67
+ RunnerEvent,
68
+ (RunnerEvent.runner_type == RunnerType.RESOURCE_RUNNER)
69
+ & (RunnerEvent.resource == self._resource)
70
+ & (RunnerEvent.timestamp < cutoff_time),
71
+ )
72
+
73
+ if old_events:
74
+ logger.info(
75
+ f"Cleaning up {len(old_events)} old RunnerEvent logs for resource {self._resource}"
76
+ )
77
+ for event in old_events:
78
+ await context.db.delete(event)
79
+
80
+ if _cache_cleanup_enabled():
81
+ await self._cleanup_file_cache()
82
+
83
+ async def _cleanup_file_cache(self) -> None:
84
+ ttl_days = _cache_ttl_days()
85
+ cutoff_time = datetime.now() - timedelta(days=ttl_days)
86
+ resource_name = _resource_to_str(self._resource)
87
+ workdir = Path(context.config.workdir).resolve()
88
+
89
+ file_stacks = await context.db.find_all(FileStack)
90
+ updated_count = 0
91
+ deleted_count = 0
92
+
93
+ for file_stack in file_stacks:
94
+ changed = False
95
+ for location in file_stack.locations or []:
96
+ if _resource_to_str(location.resource) != resource_name:
97
+ continue
98
+ if getattr(location, "location_type", "local_path") != "local_path":
99
+ continue
100
+ if getattr(location, "status", "available") != "available":
101
+ continue
102
+ if not getattr(location, "is_cached", False):
103
+ continue
104
+
105
+ accessed_at = _as_naive_datetime(
106
+ getattr(location, "last_accessed_at", None) or location.created_at
107
+ )
108
+ if accessed_at and accessed_at > cutoff_time:
109
+ continue
110
+
111
+ path = Path(location.path)
112
+ resolved_path = path if path.is_absolute() else workdir / path
113
+ try:
114
+ resolved_path = resolved_path.resolve()
115
+ if (
116
+ workdir != resolved_path
117
+ and workdir not in resolved_path.parents
118
+ ):
119
+ logger.warning(
120
+ "Skipping cached FileInstance outside workdir: file_stack=%s path=%s",
121
+ file_stack.id,
122
+ resolved_path,
123
+ )
124
+ continue
125
+ if resolved_path.exists():
126
+ resolved_path.unlink()
127
+ deleted_count += 1
128
+ location.status = "deleted"
129
+ location.expires_at = datetime.now()
130
+ changed = True
131
+ except Exception as exc:
132
+ logger.warning(
133
+ "Failed to clean cached FileInstance file_stack=%s path=%s: %s",
134
+ file_stack.id,
135
+ resolved_path,
136
+ exc,
137
+ )
138
+
139
+ if changed:
140
+ await context.db.save(file_stack)
141
+ updated_count += 1
142
+
143
+ if updated_count:
144
+ logger.info(
145
+ "Cleaned file cache for resource %s: marked %s FileStack(s), deleted %s file(s)",
146
+ resource_name,
147
+ updated_count,
148
+ deleted_count,
149
+ )
@@ -0,0 +1,163 @@
1
+ import asyncio
2
+ import logging
3
+ import os
4
+ import platform
5
+ from pathlib import Path
6
+
7
+ from simstack.core.services.base_service import BaseService
8
+ from simstack.core.context import context
9
+ from simstack.models.parameters import Resource
10
+ from simstack.core.services.node_execution_service import NodeExecutionService
11
+ from simstack.core.services.runner_status_service import RunnerStatusService
12
+ from simstack.core.services.runner_cleanup_service import RunnerCleanupService
13
+ from simstack.core.services.file_transfer_service import FileTransferService
14
+ from simstack.core.services.slurm_status_service import SlurmStatusService
15
+ from simstack.core.services.resource_branch_monitor_service import (
16
+ ResourceBranchMonitorService,
17
+ )
18
+ from simstack.core.services.stop_check_service import StopCheckService
19
+ from simstack.core.services.git_uv_update_service import GitUvUpdateService
20
+ from simstack.core.services.timeout_restart_service import TimeoutRestartService
21
+
22
+ logger = logging.getLogger("NodeRunner")
23
+
24
+
25
+ class RunnerManager:
26
+ def __init__(
27
+ self,
28
+ resource: Resource,
29
+ detach: bool = True,
30
+ no_pull: bool = False,
31
+ is_default: bool = False,
32
+ with_file_transfer: bool = True,
33
+ ) -> None:
34
+ self._resource = resource
35
+ self._detach = detach
36
+ self._no_pull = no_pull
37
+ self._pid = os.getpid()
38
+ self._services: list[BaseService] = []
39
+ self._shutdown_event: asyncio.Event = asyncio.Event()
40
+ self._pid_file: Path = Path(context.config.workdir) / f"runner_{resource}.pid"
41
+ self._is_default = is_default
42
+ self._with_file_transfer = with_file_transfer
43
+
44
+ def _is_process_running(self, pid: int) -> bool:
45
+ """Check if a process with given PID is running and is a simstack_runner process"""
46
+ try:
47
+ if platform.system() == "Windows":
48
+ # On Windows, os.kill with signal 0 doesn't work, use tasklist
49
+ import subprocess
50
+
51
+ result = subprocess.run(
52
+ ["tasklist", "/FI", f"PID eq {pid}", "/V", "/FO", "CSV"],
53
+ capture_output=True,
54
+ text=False, # keep bytes; avoid UnicodeDecodeError from console code pages
55
+ )
56
+ # Check if process exists and command line contains simstack_runner
57
+ stdout = result.stdout.decode("utf-8", errors="ignore")
58
+ return str(pid) in stdout and "simstack_runner" in stdout.lower()
59
+ else:
60
+ # Check if process exists
61
+ os.kill(pid, 0)
62
+ # Verify it's a simstack_runner process by checking command line
63
+ try:
64
+ with open(f"/proc/{pid}/cmdline", "r") as f:
65
+ cmdline = f.read()
66
+ return "simstack_runner" in cmdline
67
+ except (FileNotFoundError, PermissionError):
68
+ # If we can't read cmdline, fall back to just checking if process exists
69
+ return True
70
+ except (OSError, ProcessLookupError):
71
+ return False
72
+
73
+ def _check_existing_runner(self) -> None:
74
+ """Check if another runner for this resource is already running on this host"""
75
+ if self._pid_file.exists():
76
+ try:
77
+ existing_pid = int(self._pid_file.read_text().strip())
78
+ if existing_pid != self._pid and self._is_process_running(existing_pid):
79
+ error_msg = (
80
+ f"Another runner for resource '{self._resource}' is already running "
81
+ f"on this host with PID {existing_pid}. Exiting."
82
+ )
83
+ logger.error(error_msg)
84
+ raise RuntimeError(error_msg)
85
+ else:
86
+ logger.info(
87
+ f"Stale PID file found for resource '{self._resource}'. "
88
+ f"Overwriting with current PID {self._pid}."
89
+ )
90
+ except (ValueError, OSError) as e:
91
+ logger.warning(
92
+ f"Could not read PID file: {e}. Proceeding with startup."
93
+ )
94
+
95
+ async def stop_all_services(self) -> None:
96
+ """Gracefully stop all registered services"""
97
+ logger.info("Stopping all services...")
98
+ await asyncio.gather(
99
+ *(s.stop() for s in self._services), return_exceptions=True
100
+ )
101
+
102
+ async def run_nodes_for_resource(
103
+ self,
104
+ polling_interval: int = 5,
105
+ max_concurrent: int = 10,
106
+ timeout: int | None = None,
107
+ ) -> None:
108
+ """Orchestrates multiple independent services"""
109
+ # Check if another runner is already running for this resource
110
+ self._check_existing_runner()
111
+
112
+ # Save PID on startup
113
+ self._pid_file.write_text(str(self._pid))
114
+
115
+ self._services = [
116
+ NodeExecutionService(
117
+ self._resource,
118
+ polling_interval,
119
+ max_concurrent,
120
+ self._shutdown_event,
121
+ detach=self._detach,
122
+ is_default=self._is_default,
123
+ ),
124
+ RunnerStatusService(self._resource, interval=60),
125
+ RunnerCleanupService(self._resource, interval=300),
126
+ SlurmStatusService(self._resource, interval=60),
127
+ StopCheckService(
128
+ self._resource, interval=10, shutdown_event=self._shutdown_event
129
+ ),
130
+ ]
131
+
132
+ if self._with_file_transfer:
133
+ self._services.append(
134
+ FileTransferService(
135
+ self._resource,
136
+ interval=10,
137
+ max_concurrent=2,
138
+ shutdown_event=None,
139
+ )
140
+ )
141
+
142
+ if not self._no_pull:
143
+ self._services.append(GitUvUpdateService(self._resource, interval=60))
144
+ self._services.append(
145
+ ResourceBranchMonitorService(self._resource, interval=60)
146
+ )
147
+
148
+ # Add timeout restart service if timeout is specified
149
+ if timeout is not None:
150
+ self._services.append(TimeoutRestartService(self._resource, timeout))
151
+
152
+ # Start all services
153
+ service_tasks = [service.start() for service in self._services]
154
+
155
+ try:
156
+ # Wait for either a service task to finish or the shutdown event to be set
157
+ shutdown_task = asyncio.create_task(self._shutdown_event.wait())
158
+ tasks_to_wait = [*service_tasks, shutdown_task]
159
+ done, pending = await asyncio.wait(
160
+ tasks_to_wait, return_when=asyncio.FIRST_COMPLETED
161
+ )
162
+ finally:
163
+ await self.stop_all_services()
@@ -0,0 +1,10 @@
1
+ from simstack.models.parameters import Resource
2
+ from simstack.models.runner_model import RunnerEventEnum
3
+ from simstack.core.services.base_service import BaseService
4
+
5
+ class RunnerStatusService(BaseService):
6
+ def __init__(self, resource: Resource, interval):
7
+ super().__init__("RunnerStatus", resource, interval)
8
+
9
+ async def execute(self):
10
+ await self.write_resource_event(RunnerEventEnum.ALIVE)
@@ -0,0 +1,67 @@
1
+ import logging
2
+ from datetime import datetime
3
+
4
+ from simstack.core.context import context
5
+ from simstack.core.definitions import TaskStatus
6
+ from simstack.models import NodeRegistry
7
+ from simstack.models.parameters import Resource
8
+ from simstack.models.slurm_info import SlurmInfo
9
+ from simstack.util.runner_utils import get_job_info, clean_slurm_info
10
+ from simstack.core.services.base_service import BaseService
11
+
12
+ logger = logging.getLogger("NodeRunner")
13
+
14
+
15
+ class SlurmStatusService(BaseService):
16
+ def __init__(self, resource: Resource, interval: int) -> None:
17
+ super().__init__("SlurmStatus", resource, interval)
18
+ self._resource_name = str(resource)
19
+
20
+ async def execute(self) -> None:
21
+ try:
22
+ # logger.info(f"Running clean_slurm_info for {self._resource} and user {self._username}")
23
+ await clean_slurm_info(self._resource, user=self._username)
24
+
25
+ running_tasks = await context.db.find(
26
+ NodeRegistry,
27
+ (NodeRegistry.status == TaskStatus.RUNNING)
28
+ & (NodeRegistry.parameters.resource == self._resource),
29
+ )
30
+ queued_tasks = await context.db.find(
31
+ NodeRegistry,
32
+ (NodeRegistry.status == TaskStatus.SLURM_QUEUED)
33
+ & (NodeRegistry.parameters.resource == self._resource),
34
+ )
35
+ # logger.info(f"Checking Slurm status for {len(running_tasks)} running jobs on resource {self._resource}")
36
+
37
+ for task in list(running_tasks) + list(queued_tasks):
38
+ if task.job_id is not None:
39
+ slurm_info = get_job_info(
40
+ task.job_id, task.id, Resource(value=self._resource_name)
41
+ )
42
+ slurm_entry = await context.db.find_one(
43
+ SlurmInfo, SlurmInfo.job_id == task.job_id
44
+ )
45
+ if slurm_info:
46
+ if slurm_entry:
47
+ slurm_entry.code = slurm_info.code
48
+ slurm_entry.time = slurm_info.time
49
+ slurm_entry.updated = datetime.now()
50
+ await context.db.save(slurm_entry)
51
+ else:
52
+ await context.db.save(slurm_info)
53
+ else:
54
+ # Logic for finished/timed out jobs
55
+ check_job = await context.db.find_one(
56
+ NodeRegistry, NodeRegistry.id == task.id
57
+ )
58
+ if slurm_entry:
59
+ await context.db.delete(slurm_entry)
60
+ if check_job.status == TaskStatus.RUNNING:
61
+ task.job_id = None
62
+ task.status = TaskStatus.TIME_OUT
63
+ await context.db.save(task)
64
+
65
+ except Exception as e:
66
+ logger.exception(f"Error checking Slurm status: {e}")
67
+ raise e
@@ -0,0 +1,27 @@
1
+ import asyncio
2
+ import logging
3
+ from pathlib import Path
4
+
5
+ from simstack.core.context import context
6
+ from simstack.models.parameters import Resource
7
+ from simstack.models.runner_model import RunnerEventEnum
8
+ from simstack.core.services.base_service import BaseService
9
+
10
+ logger = logging.getLogger("NodeRunner")
11
+
12
+ class StopCheckService(BaseService):
13
+ """Service that checks for STOP file and triggers shutdown"""
14
+
15
+ def __init__(self, resource: Resource, interval: int, shutdown_event: asyncio.Event):
16
+ super().__init__("StopCheck", resource, interval, shutdown_event=shutdown_event)
17
+
18
+ async def execute(self):
19
+ # Check for STOP file
20
+ for path in [context.config.workdir, context.config.project_root]:
21
+ stop_file = Path(path) / "STOP"
22
+ if stop_file.exists():
23
+ await self.write_resource_event(RunnerEventEnum.SHUTDOWN, message="STOP file found")
24
+ logger.info(f"STOP file found at {stop_file}, signaling shutdown...")
25
+ if self._shutdown_event:
26
+ self._shutdown_event.set()
27
+ return
@@ -0,0 +1,26 @@
1
+ import logging
2
+
3
+ from simstack.core.context import context
4
+ from simstack.models.parameters import Resource
5
+ from simstack.models.runner_model import RunnerEventEnum
6
+ from simstack.core.services.base_service import RestartService
7
+
8
+ logger = logging.getLogger("NodeRunner")
9
+
10
+ class TimeoutRestartService(RestartService):
11
+ """Service that restarts the runner after a specified timeout"""
12
+
13
+ def __init__(self, resource: Resource, timeout_minutes: int):
14
+ super().__init__("TimeoutRestart", resource, interval=timeout_minutes * 60)
15
+ self._project_dir = context.config.project_root.resolve(strict=True)
16
+ self._timeout_minutes = timeout_minutes
17
+ self._executed = False
18
+
19
+ async def execute(self):
20
+ # Only execute once after the timeout interval
21
+ if not self._executed:
22
+ self._executed = True
23
+ logger.info(f"Timeout of {self._timeout_minutes} minutes reached. Triggering restart...")
24
+ await self.write_resource_event(RunnerEventEnum.SHUTDOWN,
25
+ message=f"Timeout restart after {self._timeout_minutes} minutes")
26
+ await self.trigger_restart()
@@ -0,0 +1,39 @@
1
+ from typing import Optional, List
2
+
3
+ from pydantic import BaseModel, Field, ConfigDict
4
+
5
+ from simstack.core.definitions import TaskStatus
6
+ from simstack.models.files import FileStack
7
+
8
+
9
+ class SimstackResult(BaseModel):
10
+ model_config = ConfigDict(extra="allow")
11
+ """
12
+ Represents the results from a Simstack operation.
13
+
14
+ This class serves to encapsulate the status, messages, and files resulting
15
+ from a Simstack operation. It provides structured attributes for the error
16
+ message, operation message, and categorized files, ensuring clarity in the
17
+ representation of these aspects. It also supports additional fields through
18
+ flexible configuration.
19
+
20
+ :ivar status: Indicates the status of the task, defaulting to `TaskStatus.COMPLETED`.
21
+ :type status: TaskStatus
22
+ :ivar error_message: Optionally stores an error message if the task encountered issues.
23
+ :type error_message: Optional[str]
24
+ :ivar message: An optional general message providing additional information about the task.
25
+ :type message: Optional[str]
26
+ :ivar files: A list of `FileStack` objects that represent the primary files involved in
27
+ the operation. Defaults to an empty list.
28
+ :type files: List[FileStack]
29
+ :ivar info_files: A list of `FileStack` objects that provide additional or informational
30
+ files pertaining to the operation. Defaults to an empty list. These files are shown in the results
31
+ but not passed to the calling function
32
+ :type info_files: List[FileStack]
33
+ """
34
+
35
+ status: TaskStatus = TaskStatus.RETRIEVED
36
+ error_message: Optional[str] = None
37
+ message: Optional[str] = None
38
+ files: List[FileStack] = Field(default_factory=list)
39
+ info_files: List[FileStack] = Field(default_factory=list)