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,223 @@
1
+ from pathlib import Path
2
+ from typing import List, Dict, Optional, Iterator, Any, Union
3
+
4
+ from simstack.util.directory_iterator import DirectoryPath
5
+ from simstack.util.project_root_finder import find_project_root
6
+
7
+
8
+ class PathManager:
9
+ """
10
+ Manages paths for the SimStack application, providing mechanisms to find Python files
11
+ for nodes and models. Will read only .py files.
12
+ By default travers all directories below the project root.
13
+ Uses DirectoryPath for efficient directory traversal.
14
+ """
15
+ _instance = None
16
+
17
+ def __new__(cls, *args, **kwargs):
18
+ if cls._instance is None:
19
+ cls._instance = super(PathManager, cls).__new__(cls)
20
+ cls._instance._initialized = False
21
+ return cls._instance
22
+
23
+ def default_excludes(self):
24
+ return [
25
+ "__pycache__",
26
+ "*.pyc",
27
+ ".git",
28
+ ".venv",
29
+ "venv",
30
+ ]
31
+
32
+ def reset(self):
33
+ self._initialized = False
34
+ self.paths.clear()
35
+ self._excluded_patterns = self.default_excludes()
36
+
37
+ def __init__(self, use_pickle: bool = False, include_project_root: bool = False):
38
+ """
39
+ Initialize the PathManager.
40
+
41
+ Args:
42
+ use_pickle: Boolean flag indicating whether to use pickle for serialization
43
+ """
44
+ if self._initialized:
45
+ return
46
+
47
+ self._initialized = True
48
+ self.use_pickle = use_pickle
49
+ self.root_dir = find_project_root()
50
+ self.paths: Dict[str, Dict[str, Union[Path, str, bool]]] = {}
51
+
52
+ if include_project_root:
53
+ self.add_path("project_root", self.root_dir)
54
+
55
+ self._excluded_patterns = self.default_excludes()
56
+
57
+ def add_path(self, name: str, path: Path, drops: str = "", use_pickle: bool = False) -> None:
58
+ """
59
+ Add a path to the manager.
60
+
61
+ Args:
62
+ name: Name identifier for the path
63
+ path: The directory path relative to the project root
64
+ drops: Prefix to drop from module names (for import paths)
65
+ use_pickle: Whether to use pickle for this path
66
+ """
67
+ # Convert relative paths to absolute paths
68
+ if not path.is_absolute():
69
+ path = self.root_dir / path
70
+
71
+ if not path.is_dir():
72
+ raise ValueError(f"'{path}' is not a valid directory")
73
+
74
+ self.paths[name] = {"path": path, "drops": drops, "use_pickle": use_pickle}
75
+
76
+ def get_path(self, name: str) -> Dict[str, str]:
77
+ """
78
+ Get a path by name.
79
+
80
+ Args:
81
+ name: Name of the path to retrieve
82
+
83
+ Returns:
84
+ Dictionary containing path information
85
+ """
86
+ if name not in self.paths:
87
+ raise KeyError(f"Path '{name}' not found in PathManager")
88
+
89
+ return self.paths[name]
90
+
91
+ def find_python_files(self, path_name: str, excluded_patterns: Optional[List[str]] = None) -> List[str]:
92
+ """
93
+ Find Python files in the specified path, excluding __init__.py files.
94
+
95
+ Args:
96
+ path_name: Name of the path to search in
97
+ excluded_patterns: Additional patterns to exclude
98
+
99
+ Returns:
100
+ List of absolute file paths to Python files
101
+ """
102
+ path_info = self.get_path(path_name)
103
+ path = path_info["path"]
104
+
105
+ # Combine default and additional exclusion patterns
106
+ all_excluded_patterns = self._excluded_patterns.copy()
107
+ if excluded_patterns:
108
+ all_excluded_patterns.extend(excluded_patterns)
109
+
110
+ # Add __init__.py to excluded patterns
111
+ all_excluded_patterns.append("__init__.py")
112
+
113
+ # Use DirectoryPath to find Python files
114
+ dir_path = DirectoryPath(path, all_excluded_patterns, [".py"])
115
+
116
+ # Convert Path objects to strings for compatibility with existing code
117
+ return [str(file_path) for file_path in dir_path.get_files_list()]
118
+
119
+ def iterate_python_files(self, path_name: str, excluded_patterns: Optional[List[str]] = None) -> Iterator[Path]:
120
+ """
121
+ Iterate over Python files in the specified path, excluding __init__.py files.
122
+
123
+ Args:
124
+ path_name: Name of the path to search in
125
+ excluded_patterns: Additional patterns to exclude
126
+
127
+ Returns:
128
+ Iterator of Path objects for Python files
129
+ """
130
+ path_info = self.get_path(path_name)
131
+ path = path_info["path"]
132
+
133
+ # Combine default and additional exclusion patterns
134
+ all_excluded_patterns = self._excluded_patterns.copy()
135
+ if excluded_patterns:
136
+ all_excluded_patterns.extend(excluded_patterns)
137
+
138
+ # Add __init__.py to excluded patterns
139
+ all_excluded_patterns.append("__init__.py")
140
+
141
+ # Use DirectoryPath to iterate over Python files
142
+ dir_path = DirectoryPath(path, all_excluded_patterns, [".py"])
143
+ return dir_path.iterate_files()
144
+
145
+ def get_drops(self, path_name: str) -> str:
146
+ """
147
+ Get the drops value for a path.
148
+
149
+ Args:
150
+ path_name: Name of the path
151
+
152
+ Returns:
153
+ The drops value for the path
154
+ """
155
+ path_info = self.get_path(path_name)
156
+ return path_info["drops"]
157
+
158
+ @classmethod
159
+ def from_config(cls, config: Dict[str, Any]) -> "PathManager":
160
+ """
161
+ Create a PathManager from configuration.
162
+
163
+ Args:
164
+ config: Configuration object containing path information
165
+
166
+ Returns:
167
+ Initialized PathManager instance
168
+ """
169
+ # Get use_pickle from config
170
+
171
+ use_pickle = config.get("parameters", {}).get("general", {}).get("use_pickle", False)
172
+
173
+ path_manager = cls(use_pickle=use_pickle)
174
+
175
+ config_paths = config.get("paths", {})
176
+ for path_name, path_info in config_paths.items():
177
+ path_manager.add_path(path_name, path_info["path"], path_info.get("drops", ""))
178
+
179
+ return path_manager
180
+
181
+ def find_parent_path(self, path: str) -> Optional[str]:
182
+ """
183
+ Find the parent path from self.paths that contains the given path.
184
+
185
+ Args:
186
+ path: The path to find the parent of
187
+
188
+ Returns:
189
+ The name of the parent path from self.paths that contains the given path,
190
+ or None if no parent is found
191
+ """
192
+ path_obj = Path(path)
193
+
194
+ # Convert to the absolute path if needed
195
+ if not path_obj.is_absolute():
196
+ path_obj = self.root_dir / path_obj
197
+
198
+ # Search through all paths in self.paths to find the one that contains path_obj
199
+ best_match = None
200
+ best_match_depth = -1
201
+
202
+ for path_name, path_info in self.paths.items():
203
+ registered_path = Path(path_info["path"])
204
+
205
+ try:
206
+ # Check if path_obj is under this registered path
207
+ path_obj.relative_to(registered_path)
208
+
209
+ # Calculate depth (number of parent directories)
210
+ depth = len(registered_path.parts)
211
+
212
+ # Keep the deepest match (the most specific parent)
213
+ if depth > best_match_depth:
214
+ best_match = path_name
215
+ best_match_depth = depth
216
+
217
+ except ValueError:
218
+ # path_obj is not under this registered path, continue
219
+ continue
220
+
221
+ return best_match
222
+
223
+ path_manager = PathManager()
@@ -0,0 +1,41 @@
1
+ import os
2
+ from pathlib import Path
3
+
4
+
5
+ def find_project_root(current_file=None, marker_files=(".git", "simstack.toml", "setup.py"),
6
+ skip_files=("simstack_marker.com",)) -> Path:
7
+ """
8
+ Find the project root directory by searching for common marker files
9
+
10
+ Args:
11
+ skip_files: directories with these names will be skipped
12
+ current_file: Path to the current file, defaults to __file__ if None
13
+ marker_files: Tuple of files/directories that indicate the project root
14
+
15
+ Returns:
16
+ Absolute path to the project root directory
17
+ """
18
+ if "SIMSTACK_PROJECT_ROOT" in os.environ:
19
+ return Path(os.environ["SIMSTACK_PROJECT_ROOT"]).resolve()
20
+
21
+ if current_file is None:
22
+ current_file = __file__
23
+
24
+ # Get the directory from where the process was started
25
+ current_dir = Path.cwd()
26
+
27
+ # Walk up the directory tree until we find a marker file
28
+ prev_dir = None
29
+ while current_dir != prev_dir:
30
+ # Check if any marker files/directories exist in the current directory
31
+ found_marker = any((current_dir / marker).exists() for marker in marker_files)
32
+ found_skip = any((current_dir / marker).exists() for marker in skip_files)
33
+ if found_marker and not found_skip:
34
+ return current_dir
35
+
36
+ # Move up one directory
37
+ prev_dir = current_dir
38
+ current_dir = current_dir.parent
39
+
40
+ # If we can't find any markers, return the directory of the current file
41
+ return Path(current_file).resolve().parent
@@ -0,0 +1,262 @@
1
+ #!/usr/bin/env python3
2
+ import argparse
3
+ import os
4
+ import signal
5
+ import subprocess
6
+ import sys
7
+ import time
8
+ from concurrent.futures import ThreadPoolExecutor, as_completed
9
+ from pathlib import Path
10
+ from typing import Dict, Optional, Tuple
11
+
12
+
13
+ def parse_spec(
14
+ cmd_file: Path
15
+ ) -> Tuple[Optional[str], Dict[str, str], Optional[str], Optional[int]]:
16
+ cwd: Optional[str] = None
17
+ env: Dict[str, str] = {}
18
+ command: Optional[str] = None
19
+ timeout: Optional[int] = None
20
+
21
+ with cmd_file.open("r", encoding="utf-8") as f:
22
+ for raw in f:
23
+ line = raw.strip()
24
+ if not line:
25
+ continue
26
+ if line.startswith("CWD="):
27
+ cwd = line[len("CWD=") :]
28
+ elif line.startswith("ENV[") and "]=" in line:
29
+ key = line[len("ENV[") : line.index("]=", len("ENV["))]
30
+ val = line.split("]=", 1)[1]
31
+ env[key] = val
32
+ elif line.startswith("CMD="):
33
+ command = line[len("CMD=") :]
34
+ elif line.startswith("TIMEOUT="):
35
+ try:
36
+ timeout = int(line[len("TIMEOUT=") :])
37
+ except ValueError:
38
+ timeout = None
39
+ return cwd, env, command, timeout
40
+
41
+
42
+ def run_command(
43
+ command: str,
44
+ cwd: Optional[str],
45
+ env: Dict[str, str],
46
+ timeout: Optional[int],
47
+ out_file: Path,
48
+ err_file: Path,
49
+ ) -> int:
50
+ proc_env = os.environ.copy()
51
+ proc_env.update(env)
52
+ with out_file.open("w", encoding="utf-8") as out, err_file.open(
53
+ "w", encoding="utf-8"
54
+ ) as err:
55
+ try:
56
+ proc = subprocess.run(
57
+ command,
58
+ shell=True,
59
+ cwd=cwd if cwd else None,
60
+ env=proc_env,
61
+ stdout=out,
62
+ stderr=err,
63
+ text=True,
64
+ timeout=timeout if (timeout and timeout > 0) else None,
65
+ check=False,
66
+ )
67
+ return proc.returncode
68
+ except subprocess.TimeoutExpired:
69
+ err.write("\n[watcher] Command timed out\n")
70
+ return 124
71
+ except Exception as e:
72
+ err.write(f"\n[watcher] Execution error: {e}\n")
73
+ return 1
74
+
75
+
76
+ def process_job_from_claim(base_stem: Path, claimed_by_lock: bool) -> None:
77
+ """
78
+ Process a job using base path (without suffix). If claimed_by_lock is True, we did not
79
+ rename .ready; we will remove it at the end. Otherwise, we operate on .claim and remove it.
80
+ """
81
+ cmd_file = base_stem.with_suffix(".cmd")
82
+ ready_file = base_stem.with_suffix(".ready")
83
+ claim_file = base_stem.with_suffix(".claim")
84
+ lock_file = base_stem.with_suffix(".lock")
85
+ out_file = base_stem.with_suffix(".stdout")
86
+ err_file = base_stem.with_suffix(".stderr")
87
+ exit_file = base_stem.with_suffix(".exit")
88
+ done_file = base_stem.with_suffix(".done")
89
+ fail_file = base_stem.with_suffix(".fail")
90
+
91
+ try:
92
+ if not cmd_file.exists():
93
+ fail_file.touch()
94
+ return
95
+
96
+ cwd, env, command, timeout = parse_spec(cmd_file)
97
+ if not command:
98
+ err_file.write_text("[watcher] CMD missing in spec\n", encoding="utf-8")
99
+ exit_file.write_text("2\n", encoding="utf-8")
100
+ fail_file.touch()
101
+ return
102
+
103
+ rc = run_command(command, cwd, env, timeout, out_file, err_file)
104
+ print(f"[watcher] Job {base_stem.name} finished with rc={rc}")
105
+ exit_file.write_text(f"{rc}\n", encoding="utf-8")
106
+ print(f"[watcher] Job {base_stem.name} finished")
107
+ (done_file if rc == 0 else fail_file).touch()
108
+ finally:
109
+ # Cleanup markers depending on the claim type
110
+ if claimed_by_lock:
111
+ try:
112
+ if ready_file.exists():
113
+ ready_file.unlink()
114
+ except Exception as e:
115
+ print(
116
+ f"[watcher] Warning: failed to remove ready {ready_file}: {e}",
117
+ file=sys.stderr,
118
+ )
119
+ try:
120
+ if lock_file.exists():
121
+ lock_file.unlink()
122
+ except Exception as e:
123
+ print(
124
+ f"[watcher] Warning: failed to remove lock {lock_file}: {e}",
125
+ file=sys.stderr,
126
+ )
127
+ else:
128
+ try:
129
+ if claim_file.exists():
130
+ claim_file.unlink()
131
+ except Exception as e:
132
+ print(
133
+ f"[watcher] Warning: failed to remove claim {claim_file}: {e}",
134
+ file=sys.stderr,
135
+ )
136
+
137
+
138
+ def try_claim(
139
+ ready_file: Path, retries: int = 5, backoff: float = 0.05
140
+ ) -> Optional[Path]:
141
+ """
142
+ Atomically rename *.ready -> *.claim. Retries on transient failures.
143
+ Returns claim path if successful, else None.
144
+ """
145
+ claim = ready_file.with_suffix(".claim")
146
+ for i in range(max(1, retries)):
147
+ try:
148
+ os.replace(ready_file, claim)
149
+ return claim
150
+ except FileNotFoundError:
151
+ return None
152
+ except PermissionError:
153
+ # Windows/NFS transient locks: backoff and retry
154
+ time.sleep(backoff * (i + 1))
155
+ except OSError:
156
+ # Other transient FS issues: backoff and retry
157
+ time.sleep(backoff * (i + 1))
158
+ return None
159
+
160
+
161
+ def try_lock(base_stem: Path) -> bool:
162
+ """
163
+ Create an exclusive lock file <id>.lock using O_CREAT|O_EXCL.
164
+ Returns True if lock acquired, False otherwise.
165
+ """
166
+ lock_path = base_stem.with_suffix(".lock")
167
+ flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY
168
+ try:
169
+ fd = os.open(lock_path, flags, 0o644)
170
+ os.close(fd)
171
+ return True
172
+ except FileExistsError:
173
+ return False
174
+ except OSError as e:
175
+ print(f"[watcher] Lock create failed for {lock_path}: {e}", file=sys.stderr)
176
+ return False
177
+
178
+
179
+ def main() -> int:
180
+ parser = argparse.ArgumentParser(
181
+ description="Concurrent file-queue watcher with robust claiming"
182
+ )
183
+ parser.add_argument(
184
+ "--queue-dir", type=Path, required=False, default=Path.cwd() / "queue"
185
+ )
186
+ parser.add_argument("--poll-interval", type=float, default=1.0)
187
+ parser.add_argument("--workers", type=int, default=min(4, (os.cpu_count() or 2)))
188
+ parser.add_argument(
189
+ "--use-srun",
190
+ action="store_true",
191
+ help="(deprecated; wrapper removed for simplicity)",
192
+ )
193
+ parser.add_argument("--srun-args", type=str, default="")
194
+ parser.add_argument(
195
+ "--once", action="store_true", help="Process current claims and exit"
196
+ )
197
+ args = parser.parse_args()
198
+
199
+ qd = args.queue_dir
200
+ qd.mkdir(parents=True, exist_ok=True)
201
+ print(f"[watcher] Watching {qd}, workers={args.workers}")
202
+
203
+ stop = {"flag": False}
204
+
205
+ def handle_sig(_s, _f):
206
+ stop["flag"] = True
207
+
208
+ signal.signal(signal.SIGINT, handle_sig)
209
+ signal.signal(signal.SIGTERM, handle_sig)
210
+
211
+ with ThreadPoolExecutor(max_workers=max(1, args.workers)) as pool:
212
+ futures = set()
213
+
214
+ while not stop["flag"]:
215
+ claimed_any = False
216
+
217
+ # Scan for all .ready files
218
+ ready_list = list(qd.glob("*.ready"))
219
+ if ready_list:
220
+ print(f"[watcher] Found {len(ready_list)} ready file(s)")
221
+ for ready in ready_list:
222
+ base_stem = ready.with_suffix("") # drop .ready
223
+ # 1) Preferred: atomic rename to .claim (with retries)
224
+ claim = try_claim(ready)
225
+ if claim is not None:
226
+ claimed_any = True
227
+ futures.add(pool.submit(process_job_from_claim, base_stem, False))
228
+ continue
229
+
230
+ # 2) Fallback: exclusive lock file if rename failed
231
+ if try_lock(base_stem):
232
+ print(f"[watcher] Claimed by lock: {base_stem.name}")
233
+ claimed_any = True
234
+ futures.add(pool.submit(process_job_from_claim, base_stem, True))
235
+
236
+ # Reap done tasks
237
+ done_now = {f for f in futures if f.done()}
238
+ for f in done_now:
239
+ try:
240
+ f.result()
241
+ except Exception as e:
242
+ print(f"[watcher] Job raised: {e}", file=sys.stderr)
243
+ futures.discard(f)
244
+
245
+ if args.once:
246
+ # Drain: wait for all running tasks to finish
247
+ for f in as_completed(list(futures)):
248
+ try:
249
+ f.result()
250
+ except Exception as e:
251
+ print(f"[watcher] Job raised: {e}", file=sys.stderr)
252
+ break
253
+
254
+ if not claimed_any:
255
+ time.sleep(max(0.05, args.poll_interval))
256
+
257
+ print("[watcher] Exiting")
258
+ return 0
259
+
260
+
261
+ if __name__ == "__main__":
262
+ sys.exit(main())