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
simstack/util/files.py ADDED
@@ -0,0 +1,396 @@
1
+ import os
2
+ from datetime import datetime
3
+ from typing import Optional, Dict, Any, List
4
+
5
+ from odmantic import Field
6
+ from pydantic import BaseModel
7
+
8
+
9
+ class SshAuthMethod(BaseModel):
10
+ """ """
11
+
12
+ # Authentication type
13
+ auth_type: str = Field(
14
+ description="Authentication type (password, key, agent, none)"
15
+ )
16
+
17
+ # Password authentication
18
+ password: Optional[str] = Field(
19
+ default=None,
20
+ description="Password for authentication (should be stored securely)",
21
+ )
22
+
23
+ # Key-based authentication
24
+ key_filename: Optional[str] = Field(
25
+ default=None, description="Path to the private key file"
26
+ )
27
+ key_passphrase: Optional[str] = Field(
28
+ default=None, description="Passphrase for the private key"
29
+ )
30
+
31
+ # Allow host key to be added automatically to known_hosts
32
+ allow_host_key_add: bool = Field(
33
+ default=True, description="Whether to automatically add host key to known_hosts"
34
+ )
35
+
36
+ # Host key verification options
37
+ host_key_policy: str = Field(
38
+ default="ask", description="Host key policy (ask, auto_add, strict)"
39
+ )
40
+ known_hosts_file: Optional[str] = Field(
41
+ default=None, description="Path to known_hosts file"
42
+ )
43
+
44
+ model_config = {"extra": "allow"}
45
+
46
+
47
+ class FfspecFile(BaseModel):
48
+ """
49
+ Representation of a file with protocol-specific details, metadata, and utility methods
50
+ for remote or local operations.
51
+
52
+ This class facilitates working with files across different storage protocols such as
53
+ local filesystem, SFTP, SCP, or cloud-based protocols. It provides attributes for
54
+ authenticating, accessing, and manipulating files as well as methods for operations
55
+ like copying files, creating URIs, and testing connections.
56
+
57
+ :ivar path: Full path to the file.
58
+ :type path: str
59
+ :ivar name: Name of the file.
60
+ :type name: str
61
+ :ivar extension: File extension.
62
+ :type extension: str
63
+ :ivar protocol: Storage protocol (e.g., 'file', 's3', 'http', 'sftp', 'scp').
64
+ :type protocol: str
65
+ :ivar host: Host name or address where the file is located.
66
+ :type host: Optional[str]
67
+ :ivar port: Port number if applicable.
68
+ :type port: Optional[int]
69
+ :ivar username: Username for authentication if required.
70
+ :type username: Optional[str]
71
+ :ivar ssh_auth: SSH authentication configuration.
72
+ :type ssh_auth: Optional[SshAuthMethod]
73
+ :ivar size: Size of the file in bytes.
74
+ :type size: int
75
+ :ivar created_at: Creation timestamp.
76
+ :type created_at: datetime
77
+ :ivar modified_at: Last modification timestamp.
78
+ :type modified_at: datetime
79
+ :ivar storage_options: Protocol-specific storage options.
80
+ :type storage_options: Dict[str, Any]
81
+ :ivar checksum: File content checksum.
82
+ :type checksum: Optional[str]
83
+ :ivar content_type: MIME type of the file.
84
+ :type content_type: Optional[str]
85
+ :ivar parent_path: Path to parent directory.
86
+ :type parent_path: Optional[str]
87
+ :ivar is_directory: Whether this is a directory.
88
+ :type is_directory: bool
89
+ :ivar children: Child file/directory paths if this is a directory.
90
+ :type children: List[str]
91
+ :ivar last_accessed: Last access timestamp.
92
+ :type last_accessed: Optional[datetime]
93
+ :ivar access_count: Number of times this file has been accessed.
94
+ :type access_count: int
95
+ :ivar metadata: Additional metadata as key-value pairs.
96
+ :type metadata: Dict[str, Any]
97
+ """
98
+
99
+ # Basic file information
100
+ path: str = Field(description="Full path to the file")
101
+ name: str = Field(description="Name of the file")
102
+ extension: str = Field(description="File extension")
103
+
104
+ # Protocol and location information
105
+ protocol: str = Field(
106
+ description="Storage protocol (e.g., 'file', 's3', 'http', 'sftp', 'scp')"
107
+ )
108
+ host: Optional[str] = Field(
109
+ default=None, description="Host name or address where the file is located"
110
+ )
111
+ port: Optional[int] = Field(default=None, description="Port number if applicable")
112
+
113
+ # Authentication information
114
+ username: Optional[str] = Field(
115
+ default=None, description="Username for authentication if required"
116
+ )
117
+ ssh_auth: Optional[SshAuthMethod] = Field(
118
+ default=None, description="SSH authentication configuration"
119
+ )
120
+
121
+ # File metadata
122
+ size: int = Field(description="Size of the file in bytes")
123
+ created_at: datetime = Field(description="Creation timestamp")
124
+ modified_at: datetime = Field(description="Last modification timestamp")
125
+
126
+ # Ffspec-specific attributes
127
+ storage_options: Dict[str, Any] = Field(
128
+ default_factory=dict, description="Protocol-specific storage options"
129
+ )
130
+
131
+ # Content addressing
132
+ checksum: Optional[str] = Field(default=None, description="File content checksum")
133
+ content_type: Optional[str] = Field(
134
+ default=None, description="MIME type of the file"
135
+ )
136
+
137
+ # Hierarchical structure support
138
+ parent_path: Optional[str] = Field(
139
+ default=None, description="Path to parent directory"
140
+ )
141
+ is_directory: bool = Field(default=False, description="Whether this is a directory")
142
+ children: List[str] = Field(
143
+ default_factory=list,
144
+ description="Child file/directory paths if this is a directory",
145
+ )
146
+
147
+ # Access metadata
148
+ last_accessed: Optional[datetime] = Field(
149
+ default=None, description="Last access timestamp"
150
+ )
151
+ access_count: int = Field(
152
+ default=0, description="Number of times this file has been accessed"
153
+ )
154
+
155
+ # Custom metadata
156
+ metadata: Dict[str, Any] = Field(
157
+ default_factory=dict, description="Additional metadata as key-value pairs"
158
+ )
159
+
160
+ @classmethod
161
+ def from_ssh_key(
162
+ cls,
163
+ host: str,
164
+ path: str,
165
+ username: str,
166
+ key_filename: str,
167
+ protocol: str = "sftp",
168
+ port: int = 22,
169
+ key_passphrase: Optional[str] = None,
170
+ host_key_policy: str = "auto_add",
171
+ **kwargs,
172
+ ) -> "FfspecFile":
173
+ """
174
+ Create a FfspecFile instance with SSH key authentication.
175
+
176
+ Args:
177
+ host: Remote host name or IP address
178
+ path: Path to the file on the remote host
179
+ username: SSH username for authentication
180
+ key_filename: Path to the SSH private key file
181
+ protocol: Protocol to use, either 'sftp' or 'scp' (default: 'sftp')
182
+ port: SSH port on the remote host (default: 22)
183
+ key_passphrase: Passphrase for the SSH key if it's encrypted (default: None)
184
+ host_key_policy: Host key policy - 'auto_add', 'strict', or 'ask' (default: 'auto_add')
185
+ **kwargs: Additional attributes to set on the model
186
+
187
+ Returns:
188
+ FfspecFile: A new instance representing the remote file
189
+ """
190
+ from pathlib import Path
191
+
192
+ # Validate protocol
193
+ if protocol not in ["sftp", "scp"]:
194
+ raise ValueError(f"Protocol must be 'sftp' or 'scp', got '{protocol}'")
195
+
196
+ # Validate that the key file exists
197
+ if not os.path.exists(key_filename):
198
+ raise FileNotFoundError(f"SSH key file not found: {key_filename}")
199
+
200
+ # Ensure path is absolute (starts with /)
201
+ if not path.startswith("/"):
202
+ path = "/" + path
203
+
204
+ # Create SSH authentication config with proper host key policy
205
+ ssh_auth = SshAuthMethod(
206
+ auth_type="key",
207
+ key_filename=key_filename,
208
+ key_passphrase=key_passphrase,
209
+ host_key_policy=host_key_policy,
210
+ allow_host_key_add=(host_key_policy == "auto_add"),
211
+ )
212
+
213
+ # Extract file name and extension from path
214
+ p = Path(path)
215
+ name = p.name
216
+ extension = p.suffix.lstrip(".") if p.suffix else ""
217
+ parent_path = str(p.parent) if p.parent != p else None
218
+
219
+ # Create comprehensive storage options for fsspec
220
+ # Note: Removed missing_host_key_policy as it's not a valid paramiko connection parameter
221
+ storage_options = {
222
+ "host": host,
223
+ "port": port,
224
+ "username": username,
225
+ "key_filename": key_filename,
226
+ # Connection timeouts
227
+ "timeout": 30,
228
+ "banner_timeout": 30,
229
+ "auth_timeout": 30,
230
+ }
231
+
232
+ # Add key passphrase if provided
233
+ if key_passphrase:
234
+ storage_options["password"] = key_passphrase
235
+
236
+ # Create the instance
237
+ return cls(
238
+ path=path,
239
+ name=name,
240
+ extension=extension,
241
+ protocol=protocol,
242
+ host=host,
243
+ port=port,
244
+ username=username,
245
+ ssh_auth=ssh_auth,
246
+ storage_options=storage_options,
247
+ parent_path=parent_path,
248
+ # Use values from kwargs or sensible defaults for required fields
249
+ size=kwargs.pop("size", 0),
250
+ created_at=kwargs.pop("created_at", datetime.now()),
251
+ modified_at=kwargs.pop("modified_at", datetime.now()),
252
+ is_directory=kwargs.pop("is_directory", False),
253
+ **kwargs,
254
+ )
255
+
256
+ def get_filesystem(self):
257
+ """
258
+ Create and return a fsspec filesystem object for this file.
259
+
260
+ Returns:
261
+ object: An fsspec filesystem instance
262
+ """
263
+ import importlib
264
+
265
+ try:
266
+ # Import the appropriate filesystem module
267
+ fsspec = importlib.import_module("fsspec")
268
+
269
+ # Handle SSH-specific protocols
270
+ if self.protocol in ["sftp", "scp"]:
271
+ # Create a copy of storage options to avoid modifying the original
272
+ storage_options = dict(self.storage_options)
273
+
274
+ # Add SSH auth options if available
275
+ if self.ssh_auth:
276
+ if self.ssh_auth.auth_type == "key":
277
+ storage_options["key_filename"] = self.ssh_auth.key_filename
278
+ if self.ssh_auth.key_passphrase:
279
+ storage_options["password"] = self.ssh_auth.key_passphrase
280
+ elif self.ssh_auth.auth_type == "password":
281
+ storage_options["password"] = self.ssh_auth.password
282
+
283
+ # Create the filesystem without the problematic missing_host_key_policy parameter
284
+ return fsspec.filesystem(self.protocol, **storage_options)
285
+ else:
286
+ # Handle other protocols
287
+ return fsspec.filesystem(self.protocol, **self.storage_options)
288
+
289
+ except (ImportError, KeyError) as e:
290
+ raise ValueError(
291
+ f"Could not create filesystem for protocol '{self.protocol}': {e}"
292
+ )
293
+
294
+ def can_connect(self) -> bool:
295
+ """
296
+ Test if we can connect to the remote host using the provided credentials.
297
+
298
+ Returns:
299
+ bool: True if connection is successful, False otherwise
300
+ """
301
+ if self.protocol not in ["sftp", "scp"]:
302
+ # Only applicable for SSH-based protocols
303
+ return False
304
+
305
+ try:
306
+ fs = self.get_filesystem()
307
+ # Try to list the parent directory
308
+ fs.ls(os.path.dirname(self.path))
309
+ return True
310
+ except Exception:
311
+ return False
312
+
313
+ def to_uri(self) -> str:
314
+ """
315
+ Convert this model to a URI/URL representation.
316
+
317
+ Note: SSH private key information is not included in the URI.
318
+
319
+ Returns:
320
+ str: URI string representing this file
321
+ """
322
+ import urllib.parse
323
+
324
+ # Build the netloc part
325
+ netloc = ""
326
+ if self.username:
327
+ netloc = f"{self.username}@"
328
+ if self.host:
329
+ netloc += self.host
330
+ if self.port and self.port != 22: # Only include non-standard SSH port
331
+ netloc += f":{self.port}"
332
+
333
+ # Assemble the URI
334
+ return urllib.parse.urlunparse(
335
+ (
336
+ self.protocol, # scheme
337
+ netloc, # netloc
338
+ self.path, # path
339
+ "", # params
340
+ "", # query (not including auth info for security)
341
+ "", # fragment
342
+ )
343
+ )
344
+
345
+ def copy_to_local(self, local_path: Optional[str] = None) -> str:
346
+ """
347
+ Copy the remote file to a local path.
348
+
349
+ Args:
350
+ local_path: Local path to copy the file to. If None, a temporary file is created.
351
+
352
+ Returns:
353
+ str: Path to the local copy of the file
354
+ """
355
+ import tempfile
356
+
357
+ if self.is_directory:
358
+ raise ValueError(
359
+ "Cannot copy a directory, only individual files are supported"
360
+ )
361
+
362
+ # Create a destination path if none is provided
363
+ if local_path is None:
364
+ temp_dir = tempfile.gettempdir()
365
+ local_path = os.path.join(temp_dir, self.name)
366
+
367
+ # Get the filesystem and copy the file
368
+ fs = self.get_filesystem()
369
+ fs.get(self.path, local_path)
370
+
371
+ return local_path
372
+
373
+ def copy_from_local(self, local_path: str) -> None:
374
+ """
375
+ Copy a local file to the remote location represented by this model.
376
+
377
+ Args:
378
+ local_path: Path to the local file
379
+
380
+ Returns:
381
+ None
382
+ """
383
+ if not os.path.exists(local_path):
384
+ raise FileNotFoundError(f"Local file {local_path} does not exist")
385
+
386
+ # Get the filesystem and copy the file
387
+ fs = self.get_filesystem()
388
+ fs.put(local_path, self.path)
389
+
390
+ # Update file size and modification time
391
+ try:
392
+ info = fs.info(self.path)
393
+ self.size = info.get("size", 0)
394
+ self.modified_at = datetime.now()
395
+ except Exception:
396
+ pass
@@ -0,0 +1,33 @@
1
+ import os
2
+ from typing import List
3
+
4
+
5
+ def find_python_files(directory: str) -> List[str]:
6
+ """
7
+ Returns full paths of all .py files in the given directory and its subdirectories,
8
+ excluding __init__.py files.
9
+
10
+ Args:
11
+ directory: The root directory to search from
12
+
13
+ Returns:
14
+ List of absolute file paths to all .py files that aren't __init__.py
15
+ """
16
+ python_files = []
17
+
18
+ # Ensure the directory exists
19
+ if not os.path.isdir(directory):
20
+ raise ValueError(f"'{directory}' is not a valid directory")
21
+
22
+ # Convert to absolute path
23
+ directory = os.path.abspath(directory)
24
+
25
+ # Walk through directory structure
26
+ for root, dirs, files in os.walk(directory):
27
+ for file in files:
28
+ # Check if it's a Python file but not __init__.py
29
+ if file.endswith(".py") and file != "__init__.py":
30
+ full_path = os.path.join(root, file)
31
+ python_files.append(full_path)
32
+
33
+ return python_files
@@ -0,0 +1,80 @@
1
+ from typing import Union
2
+
3
+
4
+ def generate_ui_schema(cls):
5
+ """
6
+ Generates a UI schema that uses GenericForm for fields with ui_schema function.
7
+ Also preserves any existing UI schema configurations from the class.
8
+
9
+ Args:
10
+ cls: The model class to generate UI schema for
11
+
12
+ Returns:
13
+ dict: The generated UI schema
14
+ """
15
+ # Get base UI schema from the class if it exists
16
+
17
+ base_schema = cls.ui_base_schema() if hasattr(cls, "ui_base_schema") else {}
18
+
19
+ # Process each field in the model
20
+ for field_name, field in cls.model_fields.items():
21
+ field_type = field.annotation
22
+
23
+ # Unwrap Optional type to get the underlying type
24
+ if hasattr(field_type, "__origin__") and field_type.__origin__ is Union:
25
+ # Optional[X] is implemented as Union[X, None]
26
+ args = field_type.__args__
27
+ if len(args) == 2 and args[1] is type(None):
28
+ field_type = args[0]
29
+
30
+ # Handle Lists by checking their inner type
31
+ is_list = hasattr(field_type, "__origin__") and field_type.__origin__ is list
32
+ if is_list:
33
+ field_type = field_type.__args__[0]
34
+
35
+ # Check for Optional inside List
36
+ if hasattr(field_type, "__origin__") and field_type.__origin__ is Union:
37
+ args = field_type.__args__
38
+ if len(args) == 2 and args[1] is type(None):
39
+ field_type = args[0]
40
+
41
+ # Check if field type has ui_schema method (this is true for all simstack_model classes)
42
+ if hasattr(field_type, "ui_schema"):
43
+ module_path = field_type.__module__
44
+ full_path = f"{module_path}.{field_type.__name__}"
45
+ # if simstack is in the module path, skip all elements after the last simstack occurrence
46
+ if "simstack" in module_path:
47
+ full_path = ".".join(
48
+ module_path[module_path.rfind("simstack") :].split(".")
49
+ + [field_type.__name__]
50
+ )
51
+
52
+ field_schema = field_type.ui_schema() # get the field schema for the field
53
+ # Don't override if there's already a UI configuration for this field
54
+ if field_name not in base_schema:
55
+ if "ui:field" in field_schema:
56
+ # If the field schema already has a ui:field, use it
57
+ if is_list:
58
+ base_schema[field_name] = {"items": field_schema}
59
+ else:
60
+ base_schema[field_name] = field_schema
61
+ else:
62
+ # Check if field type is a list
63
+ ui_ref = {
64
+ "ui:field": "GenericFormField",
65
+ "ui:options": {
66
+ "model": full_path,
67
+ },
68
+ }
69
+ # in rjsf the ui_scheme for list element must be in "items" key
70
+ if is_list:
71
+ base_schema[field_name] = {"items": ui_ref}
72
+ else:
73
+ ui_ref["ui:options"]["accordion"] = "true"
74
+ base_schema[field_name] = ui_ref
75
+
76
+ # Always hide the id field if it exists
77
+ if "id" not in base_schema:
78
+ base_schema["id"] = {"ui:widget": "hidden"}
79
+
80
+ return base_schema
@@ -0,0 +1,120 @@
1
+ import re
2
+ from typing import Generic, Union, List, Iterable, Iterator, Optional, Any, TypeVar
3
+
4
+ T = TypeVar("T")
5
+
6
+
7
+ class GenericListMixin(Generic[T]):
8
+ """
9
+ Mixin class containing common functionality for list operations.
10
+
11
+ Notes on typing:
12
+ - Pure list-like operations (append/extend/...) are fully generic over `T`.
13
+ - Convenience helpers like `find()` / `filter_by_size()` rely on optional attributes
14
+ (e.g. `.name`, `.size`). For arbitrary `T`, we use `getattr()` to keep runtime
15
+ behavior flexible while remaining type-safe-ish.
16
+ """
17
+
18
+ def __len__(self):
19
+ return len(self.elements)
20
+
21
+ def append(self, elements: T):
22
+ self.elements.append(elements)
23
+
24
+ def extend(self, elements: Union[List[T], Iterable[T], "GenericListMixin[T]"]):
25
+ if isinstance(elements, GenericListMixin):
26
+ # It's another GenericListMixin object
27
+ self.elements.extend(elements.elements)
28
+ else:
29
+ # It's a list or iterable
30
+ self.elements.extend(list(elements))
31
+
32
+ def insert(self, index: int, elements: T):
33
+ self.elements.insert(index, elements)
34
+
35
+ def remove(self, elements: T):
36
+ self.elements.remove(elements)
37
+
38
+ def pop(self, index: int = -1) -> T:
39
+ return self.elements.pop(index)
40
+
41
+ def clear(self):
42
+ self.elements.clear()
43
+
44
+ def index(self, elements: T, start: int = 0, stop: int = None) -> int:
45
+ if stop is None:
46
+ return self.elements.index(elements, start)
47
+ return self.elements.index(elements, start, stop)
48
+
49
+ def count(self, elements: T) -> int:
50
+ return self.elements.count(elements)
51
+
52
+ def reverse(self):
53
+ self.elements.reverse()
54
+
55
+ def sort(self, key=None, reverse: bool = False):
56
+ self.elements.sort(key=key, reverse=reverse)
57
+
58
+ def copy(self) -> List[T]:
59
+ return self.elements.copy()
60
+
61
+ def __getitem__(self, index: Union[int, slice]) -> Union[T, List[T]]:
62
+ return self.elements[index]
63
+
64
+ def __setitem__(self, index: Union[int, slice], value: Union[T, List[T], Iterable[T]]):
65
+ if isinstance(index, slice):
66
+ self.elements[index] = list(value)
67
+ else:
68
+ self.elements[index] = value
69
+
70
+ def __delitem__(self, index: Union[int, slice]):
71
+ del self.elements[index]
72
+
73
+ def __iter__(self) -> Iterator[T]:
74
+ return iter(self.elements)
75
+
76
+ def __contains__(self, element: T) -> bool:
77
+ return element in self.elements
78
+
79
+ def __bool__(self) -> bool:
80
+ return bool(self.elements)
81
+
82
+ def __repr__(self) -> str:
83
+ return f"{self.__class__.__name__}(elements={self.elements!r})"
84
+
85
+ def find(self, pattern: str) -> Optional[T]:
86
+ regex = re.compile(pattern)
87
+ for element in self.elements:
88
+ name = getattr(element, "name", None)
89
+ if name and regex.search(name):
90
+ return element
91
+ return None
92
+
93
+ def find_all(self, pattern: str) -> Iterator[T]:
94
+ regex = re.compile(pattern)
95
+ for element in self.elements:
96
+ name = getattr(element, "name", None)
97
+ if name and regex.search(name):
98
+ yield element
99
+
100
+ def filter_by_size(self, min_size: int = None, max_size: int = None) -> Iterator[T]:
101
+ for element in self.elements:
102
+ size = getattr(element, "size", None)
103
+ if size is None:
104
+ continue
105
+ if min_size is not None and size < min_size:
106
+ continue
107
+ if max_size is not None and size > max_size:
108
+ continue
109
+ yield element
110
+
111
+ def filter_by_property(self, property_name: str, value: Any) -> Iterator[T]:
112
+ for element in self.elements:
113
+ if hasattr(element, property_name) and getattr(element, property_name) == value:
114
+ yield element
115
+
116
+ def sort_by_name(self, reverse: bool = False):
117
+ self.elements.sort(key=lambda x: getattr(x, "name", "") or "", reverse=reverse)
118
+
119
+ def sort_by_size(self, reverse: bool = False):
120
+ self.elements.sort(key=lambda x: getattr(x, "size", 0) or 0, reverse=reverse)