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,283 @@
1
+ import logging
2
+ import sys
3
+ from pathlib import Path
4
+ from typing import TYPE_CHECKING
5
+ from urllib.parse import urlparse, urlunparse
6
+
7
+ from simstack.util.database_information import DatabaseInformation
8
+ from simstack.util.db import DBType
9
+ from simstack.util.project_root_finder import find_project_root
10
+ from simstack.util.toml_reader import TomlReader
11
+
12
+ from simstack.util.resource_config import ResourceConfig
13
+ from simstack.util.setup_logging import setup_logging
14
+ from simstack.util.mappings import ModelMappingTable, NodeMappingTable
15
+
16
+ if TYPE_CHECKING:
17
+ from simstack.util.db import Database
18
+
19
+
20
+ def remove_password_from_connection_string(connection_string):
21
+ parsed_url = urlparse(connection_string)
22
+
23
+ # Extract username and rebuild netloc without password
24
+ netloc = parsed_url.hostname
25
+ if parsed_url.username:
26
+ netloc = f"{parsed_url.username}@{netloc}"
27
+ if parsed_url.port:
28
+ netloc += f":{parsed_url.port}"
29
+
30
+ clean_url = parsed_url._replace(netloc=netloc)
31
+
32
+ return urlunparse(clean_url)
33
+
34
+
35
+ async def initialize_git_list(db: "Database", toml_reader: TomlReader | None):
36
+ from simstack.models.resource_definition import GitRepo
37
+ git_list = await db.find_all(GitRepo)
38
+ if git_list is None and toml_reader is not None:
39
+ git_list = toml_reader.get("parameters.common.git", [])
40
+ return git_list
41
+
42
+
43
+ class GlobalState:
44
+ _instance = None
45
+ _initialized = False
46
+
47
+ def __new__(cls, *args, **kwargs):
48
+ if not cls._instance:
49
+ cls._instance = super().__new__(cls)
50
+ cls._instance._initialized = False
51
+ cls._instance._db = None
52
+ cls._instance._log_handler = None
53
+ cls._instance._path_manager = None
54
+ cls._instance._config = None
55
+ cls._instance._model_mappings = None
56
+ cls._instance._node_mappings = None
57
+ cls._instance._resource_config = None
58
+ return cls._instance
59
+
60
+ def __init__(self, **kwargs):
61
+ """Initialize GlobalState instance.
62
+
63
+ This method only runs once due to the singleton pattern.
64
+ Use the initialize() method to set up the instance with database settings.
65
+ """
66
+ # We don't call self.initialize(**kwargs) here because it is async
67
+ # and __init__ must be sync. The user should call initialize() explicitly.
68
+
69
+ self._db = None
70
+ self._log_handler = None
71
+ self._path_manager = None
72
+ self._config = None
73
+ self._model_mappings = None
74
+ self._node_mappings = None
75
+ self._resource_config = None
76
+
77
+ def __getattribute__(self, name):
78
+ # These special attributes should always be accessible
79
+ if name in (
80
+ "_initialized",
81
+ "initialize",
82
+ "initialized",
83
+ ):
84
+ return object.__getattribute__(self, name)
85
+
86
+ # For other attributes, check initialization
87
+ if not object.__getattribute__(self, "_initialized"):
88
+ raise RuntimeError(
89
+ "GlobalState must be initialized with database settings before use"
90
+ )
91
+
92
+ return object.__getattribute__(self, name)
93
+
94
+ async def initialize(self, **kwargs):
95
+ """
96
+ Initializes the global state with the given configuration parameters.
97
+
98
+ Args:
99
+ **kwargs: Arbitrary keyword arguments for configuration. The following keys are expected:
100
+ - project_root (str, optional): The project root directory. If not provided, it will be
101
+ determined using `find_project_root`.
102
+ - db_name (str, optional): The name of the database. Required when not using a TOML
103
+ configuration.
104
+ - connection_string (str, optional): The connection string for the database. Required
105
+ when not using a TOML configuration.
106
+ - db_type (DBType, optional): The type of the database. Required when not using a TOML
107
+ configuration.
108
+ - is_test (bool, optional): Indicates whether the initialization is for testing purposes.
109
+ Defaults to `False`.
110
+ - log_level (str, optional): Specifies the logging level. Defaults to `"INFO"`.
111
+ - resource (str, optional): Specifies the resource identifier for the configuration reader.
112
+ Defaults to `"self"`.
113
+ - config_file (str, optional): Specifies the path to the configuration file, defaults to simstack.toml
114
+
115
+ Logic:
116
+ if all values for the DB are provided in the kwargs, use the provided values
117
+ otherwise the TOML file in the project root is used
118
+
119
+ The project root is determined using `find_project_root`, which by default looks for a
120
+ set of marker files, starting from the directory of find_project root and traversing up the directory tree.
121
+ In normal runs, it should skip the project root of the simstack package
122
+ This fails in the tests of the simstack package. The trick is to determine
123
+ project_root manually outside the call to initialize and pass it as a kwarg.
124
+
125
+ The TOML file decides whether the database or the file is used to set the variables
126
+ for the specific resource
127
+
128
+ """
129
+ is_test = kwargs.get("is_test", False)
130
+ resource_str = kwargs.get("resource", "self")
131
+
132
+ if self._initialized:
133
+ # If already initialized, we just return if not in test mode,
134
+ if not is_test:
135
+ return
136
+ self._initialized = True # required because some later functions use context
137
+
138
+ project_root = kwargs.get("project_root", find_project_root())
139
+ if project_root is None: # maybe None was passed
140
+ project_root = find_project_root()
141
+
142
+
143
+ project_root = Path(project_root)
144
+ kwargs["project_root"] = project_root
145
+
146
+ db_name: str | None = kwargs.get("db_name", None)
147
+ connection_string: str | None = kwargs.get("connection_string", None)
148
+ db_type: DBType | None = kwargs.get("db_type", None)
149
+
150
+ toml_reader = None
151
+ if db_name is None or connection_string is None or db_type is None:
152
+ # use not all info in the kwargs
153
+ simstack_toml_path = project_root / "simstack.toml"
154
+ toml_reader = TomlReader(project_root, config_file=Path(simstack_toml_path).resolve())
155
+ db_info = DatabaseInformation.from_config(toml_reader.config, **kwargs)
156
+ else:
157
+ db_info = DatabaseInformation(db_name, connection_string, db_type)
158
+
159
+ # check that the database can be reached and set logging up
160
+ self.initialize_database(db_info, is_test)
161
+ self.initialize_logging(db_info.connection_string, db_info.db_name, is_test, kwargs.get("log_level", "INFO"))
162
+
163
+ logger = logging.getLogger("Context")
164
+ if db_info.connection_string is not None:
165
+ db_type = DBType.MONGODB.value
166
+ safe_connection_string = remove_password_from_connection_string(db_info.connection_string)
167
+ logger.info(f"Database connection to {db_type} {safe_connection_string}/{db_name}")
168
+ else:
169
+ logger.info(f"Database connection in_memory {db_type}")
170
+
171
+ if not kwargs.get("skip_config", False):
172
+ await self.initialize_configs(self._db, toml_reader, **kwargs)
173
+
174
+ # Initialize memory-loaded mappings
175
+ await self.refresh_mappings()
176
+
177
+ self._initialized = True
178
+
179
+ async def initialize_configs(self, db, toml_reader, **kwargs):
180
+ project_root = kwargs["project_root"]
181
+ resource_str = kwargs.get("resource", "self")
182
+ is_test = kwargs.get("is_test", False)
183
+ try:
184
+ from simstack.util.config_reader import ConfigReader
185
+ from simstack.util.resource_config import ResourceConfig
186
+ resource_config_file = kwargs.get("config_file")
187
+ if resource_config_file is None:
188
+ resource_config_file = project_root / "config.toml"
189
+ else:
190
+ resource_config_file = Path(resource_config_file)
191
+ self._config = await ConfigReader.create(resource_str, db, toml_reader, **kwargs)
192
+ self._resource_config = ResourceConfig(resource_config_file, resource_str)
193
+ except Exception as e:
194
+ if is_test:
195
+ logger = logging.getLogger("Context")
196
+ logger.warning(f"Failed to initialize ConfigReader in test mode: {e}")
197
+ else:
198
+ raise e
199
+
200
+ async def refresh_mappings(self, *, models: bool = True, nodes: bool = True):
201
+ if models:
202
+ self._model_mappings = await ModelMappingTable.load(self._db)
203
+ if nodes:
204
+ self._node_mappings = await NodeMappingTable.load(self._db)
205
+
206
+ def initialize_logging(self, connection_string: str, db_name: str, is_test: bool, log_level: str = "INFO"):
207
+ if is_test:
208
+ # For tests, use simple console logging without the database handler
209
+ # We use force=True to ensure it overrides any existing configuration (e.g. from pytest or PyCharm)
210
+ # We also ensure the stream is sys.stderr so PyCharm/pytest can capture it properly if configured
211
+ logging.basicConfig(
212
+ level=log_level,
213
+ format="%(asctime)s - %(name)-15s - %(levelname)-10s - %(filename)-20s:%(lineno)4d - %(message)s",
214
+ stream=sys.stderr,
215
+ force=True,
216
+ )
217
+ self._log_handler = logging.getLogger()
218
+ # Flush stdout and stderr to ensure logs are not buffered
219
+ sys.stdout.flush()
220
+ sys.stderr.flush()
221
+ else:
222
+ self._log_handler = setup_logging(
223
+ connection_string,
224
+ db_name,
225
+ log_level,
226
+ )
227
+
228
+ def initialize_database(self, db_info: DatabaseInformation, is_test: bool):
229
+ from simstack.util.db import Database
230
+
231
+ try:
232
+ self._db = Database.from_db_info(db_info)
233
+ if self._db is not None and db_info.db_type == DBType.MONGODB:
234
+ # Only ping real MongoDB connections
235
+ self._db.client.admin.command("ping")
236
+
237
+ except ConnectionError as e:
238
+ if not is_test:
239
+ print(f"Could not connect to the database: {e}")
240
+ sys.exit(-1)
241
+ else:
242
+ # For tests, continue ignoring the database connection failure
243
+ print(f"Warning: Database connection failed in test mode: {e}")
244
+
245
+ @property
246
+ def db(self):
247
+ return self._db
248
+
249
+ @property
250
+ def config(self):
251
+ return self._config
252
+
253
+ @config.setter
254
+ def config(self, value):
255
+ self._config = value
256
+
257
+ @property
258
+ def model_mappings(self):
259
+ return self._model_mappings
260
+
261
+ @model_mappings.setter
262
+ def model_mappings(self, value):
263
+ self._model_mappings = value
264
+
265
+ @property
266
+ def node_mappings(self):
267
+ return self._node_mappings
268
+
269
+ @node_mappings.setter
270
+ def node_mappings(self, value):
271
+ self._node_mappings = value
272
+
273
+ @property
274
+ def resource_config(self) -> "ResourceConfig":
275
+ return self._resource_config
276
+
277
+ @property
278
+ def initialized(self):
279
+ return self._initialized
280
+
281
+
282
+ # Create the singleton instance, but it's not initialized yet
283
+ context = GlobalState()
@@ -0,0 +1,22 @@
1
+ from enum import Enum
2
+
3
+
4
+ class TaskStatus(str, Enum):
5
+ SUBMITTED = "submitted"
6
+ RUNNING = "running"
7
+ SLURM_QUEUED = "slurm_queued"
8
+ SLURM_RUNNING = "slurm_running"
9
+ RETRIEVED = "retrieved"
10
+ TIME_OUT = "timeout"
11
+ FAILED = "failed"
12
+ COMPLETED = "completed"
13
+ RECOVERED = "recovered"
14
+
15
+
16
+ # TODO: eliminate
17
+ class DBType(str, Enum):
18
+ SQLITE = "sqlite"
19
+ MONGODB = "mongodb"
20
+ POSTGRES = "postgres"
21
+ IN_MEMORY = "in_memory"
22
+ WITH_PATH = "with_path"
@@ -0,0 +1,115 @@
1
+ from contextvars import ContextVar
2
+ from typing import Optional, Any, Iterable
3
+ from odmantic import AIOEngine
4
+
5
+ # TDOD engine remove AIOEngineProxy
6
+ class AIOEngineProxy(AIOEngine):
7
+ """
8
+ A proxy engine that inherits all behavior from AIOEngine but overrides `save`.
9
+
10
+ The overridden `save` will:
11
+ - If the model has a `save` member function, call it with the engine instance.
12
+ - Else, if any direct part/attribute of the model has a `save` member function, call those with the engine instance.
13
+ - Otherwise, fall back to the original AIOEngine.save implementation.
14
+
15
+ Notes:
16
+ - Supports single model instances or iterables of model instances (list/tuple/set).
17
+ - Avoids recursion by delegating to super().save for the fallback.
18
+ """
19
+
20
+ async def save(self, obj: Any, *args, **kwargs) -> Any: # type: ignore[override]
21
+ # Handle collections of models
22
+ if isinstance(obj, (list, tuple, set)):
23
+ results = []
24
+ for item in obj:
25
+ results.append(await self._save_one(item, *args, **kwargs))
26
+ return results
27
+ # Single model
28
+ return await self._save_one(obj, *args, **kwargs)
29
+
30
+ async def save_unchecked(self, obj: Any, *args, **kwargs) -> Any:
31
+ """
32
+ Save without checking for custom save methods.
33
+ This is a direct call to the original AIOEngine.save.
34
+ """
35
+ return await super().save(obj, *args, **kwargs)
36
+
37
+ async def _save_one(self, model: Any, *args, **kwargs) -> Any:
38
+ # 1) Try model's own `save`
39
+ if await self._maybe_call_custom_save(model):
40
+ return None
41
+
42
+ # 2) Try parts' `save` (shallow scan of attributes and common containers)
43
+ parts_saved = await self._call_parts_saves(model)
44
+
45
+ # 3) If neither the model nor any part handled saving, fallback to AIOEngine
46
+ if not parts_saved:
47
+ return await super().save(model, *args, **kwargs)
48
+ return None
49
+
50
+ async def _maybe_call_custom_save(self, target: Any) -> bool:
51
+ """
52
+ If `target` has a callable `save`, invoke it with this engine.
53
+ Tries positional engine and keyword engine forms, awaits if coroutine.
54
+ Returns True if a save was invoked, False otherwise.
55
+ """
56
+ if not hasattr(target, "save"):
57
+ return False
58
+ save_attr = getattr(target, "save")
59
+ if not callable(save_attr):
60
+ raise AttributeError(f"Model {target} has no callable `save` method")
61
+ return await target.save(self)
62
+
63
+ async def _call_parts_saves(self, model: Any) -> bool:
64
+ """
65
+ Inspect immediate parts/attributes of the model and call their `save` if present.
66
+ Handles:
67
+ - Direct attributes from vars(model)
68
+ - Items inside lists/tuples/sets
69
+ - Values of dicts
70
+ Returns True if at least one part save was called.
71
+ """
72
+ any_saved = False
73
+
74
+ def iter_parts(root: Any) -> Iterable[Any]:
75
+ # Direct attributes
76
+ try:
77
+ for v in vars(root).values():
78
+ yield v
79
+ except TypeError:
80
+ # Objects without __dict__
81
+ pass
82
+
83
+ # For convenience, also scan common container-typed attributes one level deep
84
+ try:
85
+ for v in vars(root).values():
86
+ if isinstance(v, (list, tuple, set)):
87
+ for item in v:
88
+ yield item
89
+ elif isinstance(v, dict):
90
+ for item in v.values():
91
+ yield item
92
+ except TypeError:
93
+ pass
94
+
95
+ seen_ids = set()
96
+ for part in iter_parts(model):
97
+ pid = id(part)
98
+ if pid in seen_ids:
99
+ continue
100
+ seen_ids.add(pid)
101
+
102
+ if await self._maybe_call_custom_save(part):
103
+ any_saved = True
104
+
105
+ return any_saved
106
+
107
+ #
108
+ # current_engine_context: ContextVar[Optional[AIOEngineProxy]] = ContextVar(
109
+ # "current_engine", default=None
110
+ # )
111
+ #
112
+ #
113
+ # def get_current_engine_from_context() -> Optional[AIOEngineProxy]:
114
+ # """Get the current engine from context"""
115
+ # return current_engine_context.get()
@@ -0,0 +1,41 @@
1
+ import importlib
2
+ import pkgutil
3
+ from importlib.metadata import entry_points
4
+ from logging import getLogger
5
+
6
+ logger = getLogger("find_simstack_modules")
7
+
8
+
9
+ def walk_packages(package_name, all_modules):
10
+ """Walk through all packages and modules."""
11
+ try:
12
+ package = importlib.import_module(package_name)
13
+ package_path = package.__path__
14
+
15
+ logger.info(f"Package: {package_name}")
16
+ # print(f"Path: {package_path}")
17
+
18
+ for importer, modname, ispkg in pkgutil.walk_packages(
19
+ package_path, prefix=f"{package_name}."
20
+ ):
21
+ if ispkg:
22
+ logger.debug(f" Subpackage: {modname}")
23
+ else:
24
+ logger.debug(f" Module: {modname}")
25
+ # Split module name by periods
26
+ all_modules.append(modname)
27
+
28
+ except Exception as e:
29
+ logger.error(f"Error walking {package_name}: {e}")
30
+
31
+
32
+ def find_simstack_modules():
33
+ """Find all packages and modules within the simstack package."""
34
+
35
+ all_modules = []
36
+
37
+ entry_point_list = entry_points(group="simstack.modules")
38
+ logger.info(f"Entry points for simstack modules: {entry_point_list}.")
39
+ for entry_point in entry_point_list:
40
+ walk_packages(entry_point.value, all_modules)
41
+ return all_modules
simstack/core/hash.py ADDED
@@ -0,0 +1,179 @@
1
+ import hashlib
2
+ import inspect
3
+ from collections.abc import Callable, Iterable, Mapping
4
+ from typing import Any, cast
5
+
6
+ from odmantic import ObjectId
7
+
8
+ max_iterable_hash_count = 10000
9
+
10
+ hash_exclusions = [
11
+ "pandas",
12
+ "numpy",
13
+ "ase",
14
+ "indigo",
15
+ "openbabel",
16
+ "pybel",
17
+ "pymatgen",
18
+ "ase",
19
+ "numpy",
20
+ "sqlalchemy",
21
+ ]
22
+
23
+
24
+ HashResult = str | int
25
+
26
+
27
+ def is_iterable(obj: Any) -> bool:
28
+ try:
29
+ iter(obj)
30
+ return True
31
+ except TypeError:
32
+ return False
33
+
34
+
35
+ def is_primitive_type(obj: Any) -> bool:
36
+ return isinstance(obj, (int, float, bytes, bool, bytearray, type(None)))
37
+
38
+
39
+ def hash_value(value: Any) -> str:
40
+ return hashlib.sha256(str(value).encode()).hexdigest()
41
+
42
+
43
+ def hash_class_def(cls: type[Any]) -> str:
44
+ try:
45
+ source_code = inspect.getsource(cls)
46
+ source_hash = hashlib.sha256(source_code.encode("utf-8")).hexdigest()
47
+ return source_hash
48
+ except Exception:
49
+ mro = cls.__mro__
50
+ for mro_class in mro:
51
+ try:
52
+ source_code = inspect.getsource(mro_class)
53
+ source_hash = hashlib.sha256(source_code.encode("utf-8")).hexdigest()
54
+ return source_hash
55
+ except Exception:
56
+ pass
57
+ return "no source code"
58
+
59
+
60
+ def hash_iterable(iterable: Iterable[Any]) -> str:
61
+ hash_text = ""
62
+ for count, item in enumerate(iterable):
63
+ hash_text += str(complex_hash_function(item))
64
+ if count > max_iterable_hash_count:
65
+ break
66
+ return hashlib.sha256(hash_text.encode("utf-8")).hexdigest()
67
+
68
+
69
+ def hash_function_body(func: Callable[..., Any]) -> str:
70
+ # Get the source code of the function
71
+ source_code = inspect.getsource(func)
72
+ # Compute the hash of the source code
73
+ hash_object = hashlib.sha256(source_code.encode("utf-8"))
74
+ hash_digest = hash_object.hexdigest()
75
+ return hash_digest
76
+
77
+
78
+ def hash_non_callable_members(instance: Any) -> dict[str, str]:
79
+ hashed_values: dict[str, str] = {}
80
+ for attr_name, attr_value in vars(instance).items():
81
+ if not callable(attr_value):
82
+ hashed_values[attr_name] = hashlib.sha256(
83
+ str(attr_value).encode()
84
+ ).hexdigest()
85
+ return hashed_values
86
+
87
+
88
+ class ComplexHash:
89
+ def __init__(self, obj: Any) -> None:
90
+ self.hash_history: list[Any] = []
91
+
92
+ def hash_dict(self, obj: Mapping[Any, Any]) -> str:
93
+ hashed_values: dict[Any, HashResult] = {}
94
+ for k, v in obj.items():
95
+ hashed_values[k] = self.complex_hash(v)
96
+ combined_hash = "".join(f"{k}:{v}" for k, v in sorted(hashed_values.items()))
97
+ return hashlib.sha256(combined_hash.encode("utf-8")).hexdigest()
98
+
99
+ def hash_class(self, cls_obj: Any) -> str:
100
+ # check is the class name starts with a name in the hash_exclusions list
101
+ class_type = cls_obj.__class__
102
+ class_name = class_type.__module__ + "." + class_type.__name__
103
+ if class_type.__module__ == "builtins":
104
+ return "builtin"
105
+ if any([class_name.startswith(exclusion) for exclusion in hash_exclusions]):
106
+ return "excluded"
107
+ # print("hashing class", cls_obj.__class__.__name__)
108
+ class_hash = hash_class_def(cls_obj.__class__)
109
+ # TODO is __dict__ better than vars ?
110
+ # dict_hash = self.hash_dict(vars(cls_obj))
111
+
112
+ obj_dict = cls_obj.__dict__.copy()
113
+ if hasattr(cls_obj, "__class__") and hasattr(cls_obj.__class__, "__bases__"):
114
+ for base in cls_obj.__class__.__bases__:
115
+ if base.__name__ == "Model" and "odmantic.model" in str(
116
+ base.__module__
117
+ ):
118
+ if "id" in obj_dict:
119
+ del obj_dict["id"]
120
+
121
+ dict_hash = self.hash_dict(obj_dict)
122
+ if hasattr(cls_obj, "model_extra") and cls_obj.model_extra is not None:
123
+ dict_hash = dict_hash + self.hash_dict(cls_obj.model_extra)
124
+ combined_hash = class_hash + dict_hash
125
+ return hashlib.sha256(combined_hash.encode("utf-8")).hexdigest()
126
+
127
+ def complex_hash(self, obj: Any) -> HashResult:
128
+ # TODO: are the functions of a class hashed correctly?
129
+
130
+ if isinstance(obj, type):
131
+ return hash_class_def(obj)
132
+ elif is_primitive_type(obj):
133
+ return hash(obj)
134
+ elif isinstance(obj, ObjectId):
135
+ return str(obj)
136
+ elif isinstance(
137
+ obj, str
138
+ ): # strings are iterable but we want to hash them directly
139
+ return hash_value(obj)
140
+ elif inspect.isfunction(obj):
141
+ return hash_function_body(obj)
142
+ elif inspect.ismethod(obj):
143
+ if hasattr(obj, "__self__"):
144
+ if obj in self.hash_history:
145
+ return "recursive"
146
+ self.hash_history.append(obj)
147
+ if hasattr(obj, "complex_hash"):
148
+ return cast(HashResult, obj.complex_hash())
149
+ return self.hash_class(obj.__self__)
150
+ else:
151
+ return hash_function_body(obj)
152
+ # this should be instantiated classes
153
+ elif hasattr(obj, "__dict__"):
154
+ if obj in self.hash_history:
155
+ return "recursive"
156
+ self.hash_history.append(obj)
157
+ if hasattr(obj, "complex_hash"):
158
+ return cast(HashResult, obj.complex_hash())
159
+ return self.hash_class(obj)
160
+ elif isinstance(obj, dict):
161
+ return self.hash_dict(obj)
162
+ elif is_iterable(obj):
163
+ hashed_values: dict[int, HashResult] = {}
164
+ count = 0
165
+ for i, item in enumerate(obj):
166
+ hashed_values[i] = self.complex_hash(item)
167
+ count += 1
168
+ if count > max_iterable_hash_count:
169
+ break
170
+ combined_hash = "".join(
171
+ f"{k}:{v}" for k, v in sorted(hashed_values.items())
172
+ )
173
+ return hashlib.sha256(combined_hash.encode("utf-8")).hexdigest()
174
+ else:
175
+ return hash_value(obj)
176
+
177
+
178
+ def complex_hash_function(obj: Any) -> HashResult:
179
+ return ComplexHash(obj).complex_hash(obj)