abstra 3.23.7__py3-none-any.whl → 3.23.9__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 (249) hide show
  1. {abstra-3.23.7.dist-info → abstra-3.23.9.dist-info}/METADATA +1 -1
  2. {abstra-3.23.7.dist-info → abstra-3.23.9.dist-info}/RECORD +196 -191
  3. abstra_internals/consts/filepaths.py +1 -1
  4. abstra_internals/controllers/codebase.py +9 -18
  5. abstra_internals/controllers/codebase_events.py +6 -4
  6. abstra_internals/controllers/main.py +56 -67
  7. abstra_internals/entities/forms/template.py +84 -5
  8. abstra_internals/interface/cli/deploy.py +1 -3
  9. abstra_internals/modules_test.py +2 -2
  10. abstra_internals/repositories/linter/repository.py +9 -40
  11. abstra_internals/repositories/linter/rules/__init__.py +2 -0
  12. abstra_internals/repositories/linter/rules/big_py_files.py +40 -0
  13. abstra_internals/repositories/linter/rules/big_py_files_test.py +93 -0
  14. abstra_internals/repositories/linter/rules/duplicate_package_in_requirements.py +16 -4
  15. abstra_internals/repositories/linter/rules/duplicate_package_in_requirements_test.py +24 -3
  16. abstra_internals/repositories/linter/rules/env_in_bundle.py +3 -6
  17. abstra_internals/repositories/linter/rules/env_in_bundle_test.py +4 -3
  18. abstra_internals/repositories/linter/rules/missing_packages_in_requirements.py +12 -1
  19. abstra_internals/repositories/linter/rules/syntax_errors.py +2 -19
  20. abstra_internals/repositories/linter/rules/venv_in_bundle.py +3 -2
  21. abstra_internals/server/routes/requirements.py +7 -3
  22. abstra_internals/server/routes/workspace.py +0 -10
  23. abstra_internals/services/file_watcher.py +17 -14
  24. abstra_internals/services/fs.py +144 -99
  25. abstra_internals/services/fs_test.py +303 -8
  26. abstra_internals/services/requirements.py +271 -81
  27. abstra_internals/services/requirements_test.py +528 -164
  28. abstra_internals/templates/__init__.py +3 -1
  29. abstra_internals/utils/file.py +62 -0
  30. abstra_internals/utils/platform.py +5 -0
  31. abstra_statics/dist/assets/{AbstraButton.vue_vue_type_script_setup_true_lang.9812dba9.js → AbstraButton.vue_vue_type_script_setup_true_lang.e74c1d9b.js} +2 -2
  32. abstra_statics/dist/assets/AbstraLogo.vue_vue_type_script_setup_true_lang.f9d9018b.js +2 -0
  33. abstra_statics/dist/assets/{ApiKeys.902caf82.js → ApiKeys.1d2b9051.js} +2 -2
  34. abstra_statics/dist/assets/App.24328bec.js +2 -0
  35. abstra_statics/dist/assets/App.vue_vue_type_style_index_0_lang.13b52476.js +2 -0
  36. abstra_statics/dist/assets/BaseLayout.e2546be2.js +2 -0
  37. abstra_statics/dist/assets/{Billing.877a4614.js → Billing.ed96ff6d.js} +2 -2
  38. abstra_statics/dist/assets/{Breadcrumb.f312111a.js → Breadcrumb.21c760be.js} +2 -2
  39. abstra_statics/dist/assets/{Builds.a2c45c39.js → Builds.e0882931.js} +2 -2
  40. abstra_statics/dist/assets/{Card.5f504e7b.js → Card.714646f7.js} +5 -5
  41. abstra_statics/dist/assets/{CircularLoading.6f511e29.js → CircularLoading.5ac43298.js} +2 -2
  42. abstra_statics/dist/assets/CloseCircleOutlined.04918c3d.js +2 -0
  43. abstra_statics/dist/assets/ConnectorsView.78da900d.js +2 -0
  44. abstra_statics/dist/assets/{ConnectorsView.17764dde.css → ConnectorsView.aeb00ce8.css} +1 -1
  45. abstra_statics/dist/assets/ConsoleOmniChat.vue_vue_type_script_setup_true_lang.d7b17e09.js +2 -0
  46. abstra_statics/dist/assets/ContentLayout.3a69fd49.js +2 -0
  47. abstra_statics/dist/assets/{CrudView.5a642b48.js → CrudView.77b58a5a.js} +2 -2
  48. abstra_statics/dist/assets/DocsButton.vue_vue_type_script_setup_true_lang.836e8f5e.js +2 -0
  49. abstra_statics/dist/assets/{EditorLogin.d2224782.js → EditorLogin.4d61e44f.js} +2 -2
  50. abstra_statics/dist/assets/{EditorsView.5e769180.js → EditorsView.08a5cd3a.js} +2 -2
  51. abstra_statics/dist/assets/EnvVars.2f33a113.js +2 -0
  52. abstra_statics/dist/assets/{Error.dd899e38.js → Error.8a6cfa01.js} +2 -2
  53. abstra_statics/dist/assets/ExclamationCircleOutlined.93703670.js +2 -0
  54. abstra_statics/dist/assets/Files.6d07f9be.js +2 -0
  55. abstra_statics/dist/assets/Form.c1a08fa6.js +2 -0
  56. abstra_statics/dist/assets/{FormRunner.1c6a88dd.js → FormRunner.a00b296a.js} +2 -2
  57. abstra_statics/dist/assets/Home.332f8b44.js +2 -0
  58. abstra_statics/dist/assets/Home.964d824d.js +2 -0
  59. abstra_statics/dist/assets/{Live.a691b0eb.js → Live.ee0e73dd.js} +2 -2
  60. abstra_statics/dist/assets/LoadingContainer.cbb8e068.js +2 -0
  61. abstra_statics/dist/assets/LoadingOutlined.644899c2.js +2 -0
  62. abstra_statics/dist/assets/{Login.f96858b0.js → Login.0182140b.js} +2 -2
  63. abstra_statics/dist/assets/Login.ebf77147.js +2 -0
  64. abstra_statics/dist/assets/{Login.vue_vue_type_script_setup_true_lang.7d56cca3.js → Login.vue_vue_type_script_setup_true_lang.721bd893.js} +2 -2
  65. abstra_statics/dist/assets/Logo.c74119c7.js +2 -0
  66. abstra_statics/dist/assets/{Logs.8426d360.js → Logs.7a458aba.js} +2 -2
  67. abstra_statics/dist/assets/{LogsController.318117fd.js → LogsController.e7ad74db.js} +2 -2
  68. abstra_statics/dist/assets/Main.a2c84f7b.js +2 -0
  69. abstra_statics/dist/assets/{MockForm.f8600bec.js → MockForm.50f237ad.js} +2 -2
  70. abstra_statics/dist/assets/Navbar.0cf9e650.js +2 -0
  71. abstra_statics/dist/assets/{NewEditor.d3300cf0.css → NewEditor.0044878f.css} +1 -1
  72. abstra_statics/dist/assets/NewEditor.4519d8ac.js +8 -0
  73. abstra_statics/dist/assets/OidcLoginCallback.073de6bd.js +2 -0
  74. abstra_statics/dist/assets/{OidcLogoutCallback.485fb0b9.js → OidcLogoutCallback.e474320b.js} +2 -2
  75. abstra_statics/dist/assets/{OmniChat.097bec71.js → OmniChat.1a6ad90c.js} +4 -4
  76. abstra_statics/dist/assets/{OmniChat.7660057c.css → OmniChat.8a35a659.css} +1 -1
  77. abstra_statics/dist/assets/{OnboardingView.c9a3343e.js → OnboardingView.c4e859f1.js} +2 -2
  78. abstra_statics/dist/assets/{Organization.0833b7fe.js → Organization.432776d6.js} +2 -2
  79. abstra_statics/dist/assets/Organizations.cd6f9f61.js +2 -0
  80. abstra_statics/dist/assets/{PhArrowCounterClockwise.vue.aaa06bc0.js → PhArrowCounterClockwise.vue.72ed46a0.js} +2 -2
  81. abstra_statics/dist/assets/{PhArrowSquareOut.vue.ee4af292.js → PhArrowSquareOut.vue.ddb450f1.js} +2 -2
  82. abstra_statics/dist/assets/{PhBookBookmark.vue.681c5036.js → PhBookBookmark.vue.4a6ba053.js} +2 -2
  83. abstra_statics/dist/assets/{PhChats.vue.d61c3615.js → PhChats.vue.870bbba9.js} +2 -2
  84. abstra_statics/dist/assets/{PhClockCounterClockwise.vue.0457e9b2.js → PhClockCounterClockwise.vue.123f0e9b.js} +2 -2
  85. abstra_statics/dist/assets/{PhCopy.vue.391b0ef7.js → PhCopy.vue.70df4792.js} +2 -2
  86. abstra_statics/dist/assets/{PhCopySimple.vue.e887b43c.js → PhCopySimple.vue.f5246c24.js} +2 -2
  87. abstra_statics/dist/assets/{PhCube.vue.d070a184.js → PhCube.vue.8900248a.js} +2 -2
  88. abstra_statics/dist/assets/{PhDotsThreeVertical.vue.f4b60771.js → PhDotsThreeVertical.vue.14516e2e.js} +2 -2
  89. abstra_statics/dist/assets/{PhDownloadSimple.vue.3444d06b.js → PhDownloadSimple.vue.f8dc6a01.js} +2 -2
  90. abstra_statics/dist/assets/{PhFolderPlus.vue.d5788203.js → PhFolderPlus.vue.65855487.js} +2 -2
  91. abstra_statics/dist/assets/{PhGear.vue.e2b120bb.js → PhGear.vue.172823d3.js} +2 -2
  92. abstra_statics/dist/assets/{PhKey.vue.cf1e08ca.js → PhKey.vue.9d78ceda.js} +2 -2
  93. abstra_statics/dist/assets/{PhPencil.vue.20f1b3c4.js → PhPencil.vue.f326c6d0.js} +2 -2
  94. abstra_statics/dist/assets/{PhPencilSimple.vue.ec2125f5.js → PhPencilSimple.vue.31afd9b3.js} +2 -2
  95. abstra_statics/dist/assets/{PhPencilSimpleLine.vue.22e75a5a.js → PhPencilSimpleLine.vue.394969fe.js} +2 -2
  96. abstra_statics/dist/assets/{PhRocket.vue.27c6f935.js → PhRocket.vue.b69be43b.js} +2 -2
  97. abstra_statics/dist/assets/{PhSignOut.vue.61b63ec0.js → PhSignOut.vue.6be07f90.js} +2 -2
  98. abstra_statics/dist/assets/{PhSparkle.vue.fd6a9ad7.js → PhSparkle.vue.ca8d1014.js} +2 -2
  99. abstra_statics/dist/assets/{PhUserList.vue.abdd6da1.js → PhUserList.vue.43e7a38a.js} +2 -2
  100. abstra_statics/dist/assets/{PhUsersThree.vue.85d1a1f0.js → PhUsersThree.vue.fb26521a.js} +2 -2
  101. abstra_statics/dist/assets/{PhWebhooksLogo.vue.00b65b2c.js → PhWebhooksLogo.vue.ea667ed2.js} +2 -2
  102. abstra_statics/dist/assets/{PlayerConfigProvider.10f46997.js → PlayerConfigProvider.5aec8c16.js} +2 -2
  103. abstra_statics/dist/assets/{PlayerNavbar.f2f66852.js → PlayerNavbar.21883569.js} +2 -2
  104. abstra_statics/dist/assets/Project.a987ec46.js +2 -0
  105. abstra_statics/dist/assets/ProjectLogin.583ce83f.js +2 -0
  106. abstra_statics/dist/assets/{ProjectSettings.50027450.js → ProjectSettings.b949f40f.js} +2 -2
  107. abstra_statics/dist/assets/{ProjectsView.107f5e34.js → ProjectsView.7de28b8a.js} +2 -2
  108. abstra_statics/dist/assets/{SaveButton.cd025dae.js → SaveButton.f6870993.js} +2 -2
  109. abstra_statics/dist/assets/{files.f66880c3.js → ScrollArea.vue_vue_type_script_setup_true_lang.21ea9f38.js} +2 -2
  110. abstra_statics/dist/assets/{Sidebar.c3d5d187.js → Sidebar.1250b960.js} +2 -2
  111. abstra_statics/dist/assets/Sql.3cdc910a.css +1 -0
  112. abstra_statics/dist/assets/Sql.a8abfb57.js +5 -0
  113. abstra_statics/dist/assets/Steps.2f177a1f.js +2 -0
  114. abstra_statics/dist/assets/{TableEditor.7b07ece4.js → TableEditor.25046840.js} +2 -2
  115. abstra_statics/dist/assets/Tables.f0ea43f5.js +2 -0
  116. abstra_statics/dist/assets/{TablesDiagram.6736c045.js → TablesDiagram.eb43ee41.js} +3 -3
  117. abstra_statics/dist/assets/TablesTabs.vue_vue_type_script_setup_true_lang.a35b4471.js +2 -0
  118. abstra_statics/dist/assets/Tasks.5fde94ea.js +2 -0
  119. abstra_statics/dist/assets/{UploadOutlined.732440a5.js → UploadOutlined.3828d031.js} +2 -2
  120. abstra_statics/dist/assets/{View.283e52c1.js → View.f0be1038.js} +2 -2
  121. abstra_statics/dist/assets/View.vue_vue_type_script_setup_true_lang.bddece9c.js +2 -0
  122. abstra_statics/dist/assets/{Watermark.6076ef47.js → Watermark.1655dad8.js} +2 -2
  123. abstra_statics/dist/assets/{WebEditor.6a012d5b.js → WebEditor.f6ba39c6.js} +2 -2
  124. abstra_statics/dist/assets/WidgetPreview.70e3ecb3.js +2 -0
  125. abstra_statics/dist/assets/{ant-design.4302db30.js → ant-design.1cc60cde.js} +2 -2
  126. abstra_statics/dist/assets/{apiKey.1c96dd66.js → apiKey.49b8decf.js} +2 -2
  127. abstra_statics/dist/assets/asyncComputed.92146382.js +2 -0
  128. abstra_statics/dist/assets/{build.656c5601.js → build.261de82e.js} +2 -2
  129. abstra_statics/dist/assets/{colorHelpers.2a607581.js → colorHelpers.59e5ee56.js} +2 -2
  130. abstra_statics/dist/assets/{console.9b13e1da.js → console.0548c6a0.js} +2 -2
  131. abstra_statics/dist/assets/constants.f352d89b.js +2 -0
  132. abstra_statics/dist/assets/contracts.generated.ac0bb8ea.js +2 -0
  133. abstra_statics/dist/assets/{cssMode.6d17ca95.js → cssMode.c55f29d9.js} +2 -2
  134. abstra_statics/dist/assets/datetime.780c5d6b.js +2 -0
  135. abstra_statics/dist/assets/dayjs.b139fc88.js +2 -0
  136. abstra_statics/dist/assets/editor.f7e5ca32.js +2 -0
  137. abstra_statics/dist/assets/editor.main.eea24677.js +2 -0
  138. abstra_statics/dist/assets/fetch.e3c8dd68.js +2 -0
  139. abstra_statics/dist/assets/{folder.9092348a.js → folder.934b13ce.js} +2 -2
  140. abstra_statics/dist/assets/{freemarker2.82f2cb8c.js → freemarker2.72aea997.js} +2 -2
  141. abstra_statics/dist/assets/{handlebars.36ec2a3c.js → handlebars.607ed329.js} +2 -2
  142. abstra_statics/dist/assets/{html.845da565.js → html.1cebc021.js} +3 -3
  143. abstra_statics/dist/assets/{htmlMode.980f76b4.js → htmlMode.b3ab207b.js} +2 -2
  144. abstra_statics/dist/assets/{index.0f357aec.js → index.2efe5ae2.js} +2 -2
  145. abstra_statics/dist/assets/{index.55d10b71.js → index.424d5056.js} +2 -2
  146. abstra_statics/dist/assets/{index.1e12c884.js → index.61710ff9.js} +2 -2
  147. abstra_statics/dist/assets/{index.a2b9d34b.js → index.8ea8b90b.js} +2 -2
  148. abstra_statics/dist/assets/{index.1551abd6.js → index.a5ddebc4.js} +3 -3
  149. abstra_statics/dist/assets/{index.5dbe93c3.js → index.cef986ac.js} +3 -3
  150. abstra_statics/dist/assets/{index.81a2ae08.js → index.d73f307c.js} +2 -2
  151. abstra_statics/dist/assets/{index.b3b62f71.js → index.ded0f9ab.js} +2 -2
  152. abstra_statics/dist/assets/{index.4ecba4f7.js → index.e189c6ed.js} +2 -2
  153. abstra_statics/dist/assets/{javascript.b0154182.js → javascript.595850d9.js} +3 -3
  154. abstra_statics/dist/assets/{jsonMode.f86e9042.js → jsonMode.e3ed1113.js} +2 -2
  155. abstra_statics/dist/assets/{jwt-decode.esm.d86c27e0.js → jwt-decode.esm.1b301f74.js} +8 -8
  156. abstra_statics/dist/assets/linters.0018d44c.js +2 -0
  157. abstra_statics/dist/assets/{liquid.029287f8.js → liquid.3ef83be8.js} +3 -3
  158. abstra_statics/dist/assets/member.3c3a769a.js +2 -0
  159. abstra_statics/dist/assets/{metadata.18d0a278.js → metadata.e6e62729.js} +2 -2
  160. abstra_statics/dist/assets/omniChatStore.8902e4de.js +8 -0
  161. abstra_statics/dist/assets/{organization.8b2c1c53.js → organization.c92a9190.js} +2 -2
  162. abstra_statics/dist/assets/player.82dc9e2f.js +2 -0
  163. abstra_statics/dist/assets/{plotly.min.10467de2.js → plotly.min.ac699f04.js} +2 -2
  164. abstra_statics/dist/assets/polling.a3bae209.js +2 -0
  165. abstra_statics/dist/assets/{project.33809d47.js → project.48a235c1.js} +2 -2
  166. abstra_statics/dist/assets/{python.ee23fd86.js → python.b69cfb2a.js} +2 -2
  167. abstra_statics/dist/assets/{razor.4ae6d2a2.js → razor.0f42fef9.js} +2 -2
  168. abstra_statics/dist/assets/{record.d087b37e.js → record.b96b0275.js} +2 -2
  169. abstra_statics/dist/assets/{redirect.c06a7828.js → redirect.ebab71ed.js} +2 -2
  170. abstra_statics/dist/assets/repository.4920fb3b.js +2 -0
  171. abstra_statics/dist/assets/{repository.6fa74dff.js → repository.c0dc4e22.js} +2 -2
  172. abstra_statics/dist/assets/router.8882099a.js +2 -0
  173. abstra_statics/dist/assets/{router.7936fd78.js → router.99a53337.js} +3 -3
  174. abstra_statics/dist/assets/string.4796d44a.js +2 -0
  175. abstra_statics/dist/assets/{tables.d580be9d.js → tables.13d62f0c.js} +2 -2
  176. abstra_statics/dist/assets/tasksController.3255c760.js +4 -0
  177. abstra_statics/dist/assets/{toggleHighContrast.510bdb1d.js → toggleHighContrast.cb5e834a.js} +7 -7
  178. abstra_statics/dist/assets/{tsMode.5c0f732d.js → tsMode.95528ab9.js} +2 -2
  179. abstra_statics/dist/assets/{typescript.0643a053.js → typescript.b90bd43d.js} +3 -3
  180. abstra_statics/dist/assets/url.84e62ca8.js +2 -0
  181. abstra_statics/dist/assets/useCodebaseEvents.c47ad36d.js +2 -0
  182. abstra_statics/dist/assets/userStore.843da693.js +2 -0
  183. abstra_statics/dist/assets/uuid.58d26ff2.js +2 -0
  184. abstra_statics/dist/assets/{vue-flow-background.011d27ef.js → vue-flow-background.6b5566a8.js} +2 -2
  185. abstra_statics/dist/assets/{vue-quill.esm-bundler.487756a5.js → vue-quill.esm-bundler.3c4b0230.js} +2 -2
  186. abstra_statics/dist/assets/{workspaceStore.87f8dbc6.js → workspaceStore.6a20c00d.js} +2 -2
  187. abstra_statics/dist/assets/{xml.c3c548ab.js → xml.070d3630.js} +3 -3
  188. abstra_statics/dist/assets/{yaml.0d909e29.js → yaml.2a862691.js} +3 -3
  189. abstra_statics/dist/console.html +15 -15
  190. abstra_statics/dist/editor.html +13 -11
  191. abstra_statics/dist/player.html +9 -9
  192. tests/e2e/test_crud_files.py +0 -1
  193. tests/e2e/test_requirements.py +41 -4
  194. abstra_statics/dist/assets/AbstraLogo.vue_vue_type_script_setup_true_lang.0c707a8b.js +0 -2
  195. abstra_statics/dist/assets/App.f0468c7f.js +0 -2
  196. abstra_statics/dist/assets/App.vue_vue_type_style_index_0_lang.864018b5.js +0 -2
  197. abstra_statics/dist/assets/BaseLayout.8bd18c5f.js +0 -2
  198. abstra_statics/dist/assets/CloseCircleOutlined.2815d641.js +0 -2
  199. abstra_statics/dist/assets/ConnectorsView.4e22242f.js +0 -2
  200. abstra_statics/dist/assets/ConsoleOmniChat.vue_vue_type_script_setup_true_lang.17b546d4.js +0 -2
  201. abstra_statics/dist/assets/ContentLayout.c7733a0e.js +0 -2
  202. abstra_statics/dist/assets/DocsButton.vue_vue_type_script_setup_true_lang.2c5aae83.js +0 -2
  203. abstra_statics/dist/assets/EnvVars.74e357b2.js +0 -2
  204. abstra_statics/dist/assets/ExclamationCircleOutlined.9b25ffda.js +0 -2
  205. abstra_statics/dist/assets/Files.d9621f31.js +0 -2
  206. abstra_statics/dist/assets/Form.9eebd960.js +0 -2
  207. abstra_statics/dist/assets/Home.9531e545.js +0 -2
  208. abstra_statics/dist/assets/Home.b12bb81a.js +0 -2
  209. abstra_statics/dist/assets/LoadingContainer.c40ae513.js +0 -2
  210. abstra_statics/dist/assets/LoadingOutlined.b607eff2.js +0 -2
  211. abstra_statics/dist/assets/Login.5f104bb1.js +0 -2
  212. abstra_statics/dist/assets/Logo.a34929e1.js +0 -2
  213. abstra_statics/dist/assets/Main.7030ea1d.js +0 -2
  214. abstra_statics/dist/assets/Navbar.a1055174.js +0 -2
  215. abstra_statics/dist/assets/NewEditor.6b2cb8e6.js +0 -8
  216. abstra_statics/dist/assets/OidcLoginCallback.445dd392.js +0 -2
  217. abstra_statics/dist/assets/Organizations.1c35b6b8.js +0 -2
  218. abstra_statics/dist/assets/Project.2fdca57c.js +0 -2
  219. abstra_statics/dist/assets/ProjectLogin.7660cd84.js +0 -2
  220. abstra_statics/dist/assets/Sql.1afe0bac.css +0 -1
  221. abstra_statics/dist/assets/Sql.7d92acbb.js +0 -5
  222. abstra_statics/dist/assets/Steps.b12e16c6.js +0 -2
  223. abstra_statics/dist/assets/Tables.aa6b418c.js +0 -2
  224. abstra_statics/dist/assets/TablesTabs.vue_vue_type_script_setup_true_lang.95ea10aa.js +0 -2
  225. abstra_statics/dist/assets/Tasks.6660de00.js +0 -2
  226. abstra_statics/dist/assets/View.vue_vue_type_script_setup_true_lang.483e52f9.js +0 -2
  227. abstra_statics/dist/assets/WidgetPreview.b01eed73.js +0 -2
  228. abstra_statics/dist/assets/asyncComputed.59410422.js +0 -2
  229. abstra_statics/dist/assets/constants.733c6549.js +0 -2
  230. abstra_statics/dist/assets/datetime.adbf692e.js +0 -2
  231. abstra_statics/dist/assets/dayjs.9e279491.js +0 -2
  232. abstra_statics/dist/assets/editor.7e30500a.js +0 -2
  233. abstra_statics/dist/assets/editor.main.4675b13a.js +0 -2
  234. abstra_statics/dist/assets/fetch.13b54f0f.js +0 -2
  235. abstra_statics/dist/assets/linters.9ba6d5f8.js +0 -2
  236. abstra_statics/dist/assets/member.3aca30ee.js +0 -2
  237. abstra_statics/dist/assets/omniChatStore.16b8f156.js +0 -10
  238. abstra_statics/dist/assets/player.7f570660.js +0 -2
  239. abstra_statics/dist/assets/polling.0b08b681.js +0 -2
  240. abstra_statics/dist/assets/repository.02efcdbd.js +0 -2
  241. abstra_statics/dist/assets/router.7071f838.js +0 -2
  242. abstra_statics/dist/assets/string.360236ba.js +0 -2
  243. abstra_statics/dist/assets/tasksController.b66c85ee.js +0 -4
  244. abstra_statics/dist/assets/url.b31d406a.js +0 -2
  245. abstra_statics/dist/assets/userStore.f2537ff3.js +0 -2
  246. abstra_statics/dist/assets/uuid.d6d43ef5.js +0 -2
  247. {abstra-3.23.7.dist-info → abstra-3.23.9.dist-info}/WHEEL +0 -0
  248. {abstra-3.23.7.dist-info → abstra-3.23.9.dist-info}/entry_points.txt +0 -0
  249. {abstra-3.23.7.dist-info → abstra-3.23.9.dist-info}/top_level.txt +0 -0
@@ -11,5 +11,5 @@ CREDENTIALS_FILEPATH = f"{DOT_ABSTRA_DIR}/credentials"
11
11
  TEST_DATA_FILEPATH = f"{DOT_ABSTRA_DIR}/test_data.json"
12
12
 
13
13
  GIT_DIR_PATH = ".git"
14
- ABSTRA_IGNORE_FILEPATH = ".abstraignore"
14
+ ABSTRA_IGNORE_FILEPATH = ".gitignore"
15
15
  ABSTRA_TABLES_FILEPATH = "abstra-tables.json"
@@ -20,7 +20,7 @@ from abstra_internals.contracts_generated import (
20
20
  )
21
21
  from abstra_internals.repositories.factory import Repositories
22
22
  from abstra_internals.services.file_watcher import crdt_managers
23
- from abstra_internals.services.fs import SKIPPED_DIRNAMES, FileSystemService
23
+ from abstra_internals.services.fs import FileSystemService
24
24
  from abstra_internals.utils.crdt import CRDTManager
25
25
 
26
26
 
@@ -28,29 +28,20 @@ class CodebaseController:
28
28
  def __init__(self, repos: Repositories):
29
29
  self.repos = repos
30
30
 
31
- def file_node(self, child_path: Path, base_path: Path) -> CommonFileNode:
32
- is_dir = child_path.is_dir()
33
- stats = child_path.stat()
34
-
35
- grand_children = []
36
- if is_dir:
37
- grand_children = [
38
- list(c.relative_to(base_path).parts)
39
- for c in child_path.iterdir()
40
- if c.name not in SKIPPED_DIRNAMES
41
- ]
31
+ def file_node(self, path: Path) -> CommonFileNode:
32
+ is_dir = path.is_dir()
33
+ stats = path.stat()
42
34
 
43
35
  return CommonFileNode(
44
- path_parts=list(child_path.relative_to(base_path).parts),
36
+ path_parts=list(path.parts),
45
37
  size=stats.st_size,
46
38
  last_modified=datetime.fromtimestamp(stats.st_mtime),
47
39
  type="directory" if is_dir else "file",
48
- children=grand_children,
49
40
  )
50
41
 
51
42
  def list_files(self, path) -> AbstraLibApiEditorFilesListResponse:
52
43
  if path is None:
53
- path = Path.cwd()
44
+ path = Path()
54
45
  elif isinstance(path, str):
55
46
  path = Path(path)
56
47
  elif not isinstance(path, Path):
@@ -60,17 +51,17 @@ class CodebaseController:
60
51
 
61
52
  return [
62
53
  AbstraLibApiEditorFilesListResponseItem(
63
- file=self.file_node(child_path, path),
54
+ file=self.file_node(child_path),
64
55
  stages=[
65
56
  AbstraLibApiEditorFilesListResponseItemStagesItem(
66
57
  id=stage.id,
67
- type=stage.type_name, # type: ignore
58
+ type=stage.type_name,
68
59
  )
69
60
  for stage in project.get_stages_by_file_path(child_path)
70
61
  ],
71
62
  )
72
63
  for child_path in FileSystemService.list_files(
73
- path, include_dirs=True, use_ignore=False
64
+ path, include_dirs=True, use_ignore=False, recursive=False
74
65
  )
75
66
  ]
76
67
 
@@ -6,13 +6,12 @@ from typing import List, Optional
6
6
  import flask_sock
7
7
  from dotenv import load_dotenv
8
8
 
9
- from abstra_internals.contracts_generated import (
10
- AbstraLibApiEditorCodebaseEventsMessage,
11
- )
9
+ from abstra_internals.contracts_generated import AbstraLibApiEditorCodebaseEventsMessage
12
10
  from abstra_internals.logger import AbstraLogger
13
11
  from abstra_internals.modules import reload_module
14
12
  from abstra_internals.repositories.factory import Repositories
15
13
  from abstra_internals.services.file_watcher import FSEventType
14
+ from abstra_internals.settings import Settings
16
15
 
17
16
 
18
17
  class CodebaseEventController:
@@ -37,8 +36,11 @@ class CodebaseEventController:
37
36
  def broadcast_changes(
38
37
  cls, filepath: Path, event: FSEventType, content: Optional[str]
39
38
  ):
39
+ absolute_root_path = Settings.root_path.resolve()
40
40
  message = AbstraLibApiEditorCodebaseEventsMessage(
41
- filepath=str(filepath), event=event, content=content
41
+ filepath=str(filepath.relative_to(absolute_root_path)),
42
+ event=event,
43
+ content=content,
42
44
  )
43
45
  for listener in cls.listeners:
44
46
  try:
@@ -1,6 +1,5 @@
1
1
  import datetime
2
2
  import pkgutil
3
- import webbrowser
4
3
  from pathlib import Path
5
4
  from shutil import move
6
5
  from tempfile import mkdtemp, mktemp
@@ -65,7 +64,7 @@ from abstra_internals.templates import (
65
64
  )
66
65
  from abstra_internals.utils.ai import AiWs
67
66
  from abstra_internals.utils.diff import compute_updated_code_from_replacements
68
- from abstra_internals.utils.file import module2path, path2module
67
+ from abstra_internals.utils.file import path2module
69
68
  from abstra_internals.utils.validate import validate_json
70
69
 
71
70
 
@@ -169,7 +168,7 @@ class MainController:
169
168
 
170
169
  This method looks up and returns a single stage from the project
171
170
  based on the provided ID. The stage can be of any type (form, hook,
172
- job, or script).
171
+ job, or tasklet).
173
172
 
174
173
  Args:
175
174
  id (str): Unique identifier of the stage to retrieve.
@@ -202,7 +201,7 @@ class MainController:
202
201
  elif isinstance(stage, JobStage):
203
202
  print("This is a job stage")
204
203
  elif isinstance(stage, ScriptStage):
205
- print("This is a script stage")
204
+ print("This is a tasklet stage")
206
205
  else:
207
206
  print("Stage not found")
208
207
  ```
@@ -287,16 +286,6 @@ class MainController:
287
286
  file_path.parent.mkdir(parents=True, exist_ok=True)
288
287
  file_path.write_text(code, encoding="utf-8")
289
288
 
290
- def open_file(self, file_path: str, mode: str, create_if_not_exists: bool = False):
291
- if mode == "module" or mode == "package":
292
- file_path = str(module2path(file_path, mode == "package"))
293
- complete_file_path = Settings.root_path.joinpath(file_path)
294
-
295
- if create_if_not_exists and not complete_file_path.is_file():
296
- complete_file_path.touch()
297
-
298
- webbrowser.open(complete_file_path.absolute().as_uri())
299
-
300
289
  def read_file(self, file: str):
301
290
  """
302
291
  Read the contents of a file from the project workspace.
@@ -316,13 +305,13 @@ class MainController:
316
305
  ```python
317
306
  controller = MainController(repositories)
318
307
 
319
- # Read a Python script file
320
- script_content = controller.read_file("scripts/data_processor.py")
321
- if script_content:
322
- print("Script content:")
323
- print(script_content)
308
+ # Read a Python tasklet file
309
+ tasklet_content = controller.read_file("tasklet_data_processor.py")
310
+ if tasklet_content:
311
+ print("Tasklet content:")
312
+ print(tasklet_content)
324
313
  else:
325
- print("Script file not found")
314
+ print("Tasklet file not found")
326
315
 
327
316
  # Read configuration files
328
317
  config_content = controller.read_file("config.json")
@@ -383,13 +372,13 @@ class MainController:
383
372
  else:
384
373
  print("No requirements file found")
385
374
 
386
- # Check script files before reading
387
- script_path = "scripts/data_processor.py"
388
- if controller.check_file(script_path):
389
- content = controller.read_file(script_path)
390
- print("Script loaded successfully")
375
+ # Check tasklet files before reading
376
+ tasklet_path = "tasklet_data_processor.py"
377
+ if controller.check_file(tasklet_path):
378
+ content = controller.read_file(tasklet_path)
379
+ print("Tasklet loaded successfully")
391
380
  else:
392
- print(f"Script {script_path} not found")
381
+ print(f"Tasklet {tasklet_path} not found")
393
382
 
394
383
  # Check configuration files
395
384
  config_files = ["config.json", "settings.yaml", ".env"]
@@ -401,8 +390,8 @@ class MainController:
401
390
  print("No configuration files found")
402
391
 
403
392
  # Directories return False
404
- is_file = controller.check_file("scripts") # Returns False (directory)
405
- is_file = controller.check_file("scripts/") # Returns False (directory)
393
+ is_file = controller.check_file("tasklets") # Returns False (directory)
394
+ is_file = controller.check_file("tasklets/") # Returns False (directory)
406
395
  ```
407
396
 
408
397
  Note:
@@ -499,7 +488,7 @@ class MainController:
499
488
 
500
489
  This method provides different listing modes to browse the project filesystem,
501
490
  supporting various file types and Python module discovery. It respects
502
- ignore patterns defined in .abstraignore and .gitignore files.
491
+ ignore patterns defined in .gitignore file.
503
492
 
504
493
  Args:
505
494
  path (str, optional): Relative path from project root to list contents.
@@ -531,8 +520,8 @@ class MainController:
531
520
  for item in all_files:
532
521
  print(f"{item['type']}: {item['name']} -> {item['path']}")
533
522
 
534
- # List only Python files in scripts directory
535
- python_files = controller.list_files("scripts", mode="python-file")
523
+ # List only Python files in tasklets directory
524
+ python_files = controller.list_files("tasklets", mode="python-file")
536
525
  for file in python_files:
537
526
  if file['type'] == 'file':
538
527
  print(f"Python file: {file['path']}")
@@ -552,7 +541,7 @@ class MainController:
552
541
  ```
553
542
 
554
543
  Note:
555
- - Respects .abstraignore and .gitignore patterns when use_ignore=True
544
+ - Respects .gitignore and .gitignore patterns when use_ignore=True
556
545
  - Image mode supports: .png, .jpg, .jpeg, .gif, .svg, .webp, .jfif, .pjp, .pjpeg
557
546
  - Module mode uses Python's pkgutil.iter_modules for discovery
558
547
  - Paths are always relative to the project root directory
@@ -841,49 +830,49 @@ class MainController:
841
830
  id: Optional[str] = None,
842
831
  ) -> ScriptStage:
843
832
  """
844
- Create a new script stage in the project workflow.
833
+ Create a new tasklet stage in the project workflow.
845
834
 
846
- Scripts are programmatic workflow stages that execute Python code
835
+ Tasklets are programmatic workflow stages that execute Python code
847
836
  without user interaction. They are used for data processing, business
848
837
  logic, integrations, and automation tasks within workflows.
849
838
 
850
839
  Args:
851
- title (str): Display name for the script stage.
852
- file (str): Relative path where the script's Python code will be stored.
840
+ title (str): Display name for the tasklet stage.
841
+ file (str): Relative path where the tasklet's Python code will be stored.
853
842
  Must end with .py extension.
854
843
  workflow_position (Tuple[int, int], optional): X, Y coordinates for the
855
- script's position in the visual workflow editor. Defaults to (0, 0).
856
- id (Optional[str], optional): Custom identifier for the script. If None,
844
+ tasklet's position in the visual workflow editor. Defaults to (0, 0).
845
+ id (Optional[str], optional): Custom identifier for the tasklet. If None,
857
846
  a unique ID will be automatically generated.
858
847
 
859
848
  Returns:
860
- ScriptStage: The newly created script stage object containing all script metadata.
849
+ ScriptStage: The newly created tasklet stage object containing all tasklet metadata.
861
850
 
862
851
  Example:
863
852
  ```python
864
853
  controller = MainController(repositories)
865
854
 
866
- # Create a data processing script
867
- processor = controller.create_script(
855
+ # Create a data processing tasklet
856
+ processor = controller.create_tasklet(
868
857
  title="Data Processor",
869
- file="scripts/process_data.py"
858
+ file="tasklet_process_data.py"
870
859
  )
871
- print(f"Created script: {processor.id}")
860
+ print(f"Created tasklet: {processor.id}")
872
861
 
873
- # Create script with custom positioning
874
- validator = controller.create_script(
862
+ # Create tasklet with custom positioning
863
+ validator = controller.create_tasklet(
875
864
  title="Input Validator",
876
- file="scripts/validate_input.py",
865
+ file="tasklet_validate_input.py",
877
866
  workflow_position=(200, 300),
878
867
  id="input-validator"
879
868
  )
880
869
  ```
881
870
 
882
871
  Note:
883
- - The script file will be initialized with default script template code
884
- - Scripts can receive data from previous workflow stages
885
- - Scripts can send tasks to trigger subsequent workflow stages
886
- - Scripts run without user interaction and are ideal for automation
872
+ - The tasklet file will be initialized with default tasklet template code
873
+ - Tasklets can receive data from previous workflow stages
874
+ - Tasklets can send tasks to trigger subsequent workflow stages
875
+ - Tasklets run without user interaction and are ideal for automation
887
876
 
888
877
  Copywritings:
889
878
  Create a new tasklet stage
@@ -909,13 +898,13 @@ class MainController:
909
898
 
910
899
  def delete_tasklet(self, id: str, remove_file: bool = False):
911
900
  """
912
- Delete a script stage from the project workflow.
901
+ Delete a tasklet stage from the project workflow.
913
902
 
914
- This method removes a script stage from the project configuration and
903
+ This method removes a tasklet stage from the project configuration and
915
904
  optionally deletes the associated Python file from the filesystem.
916
905
 
917
906
  Args:
918
- id (str): Unique identifier of the script stage to delete.
907
+ id (str): Unique identifier of the tasklet stage to delete.
919
908
  remove_file (bool, optional): Whether to also delete the associated
920
909
  Python file from the filesystem. Defaults to False.
921
910
 
@@ -923,15 +912,15 @@ class MainController:
923
912
  ```python
924
913
  controller = MainController(repositories)
925
914
 
926
- # Delete script but preserve the file
927
- controller.delete_script("data-processor")
915
+ # Delete tasklet but preserve the file
916
+ controller.delete_tasklet("data-processor")
928
917
 
929
- # Delete script and its file completely
930
- controller.delete_script("validator-script", remove_file=True)
918
+ # Delete tasklet and its file completely
919
+ controller.delete_tasklet("validator-tasklet", remove_file=True)
931
920
  ```
932
921
 
933
922
  Warning:
934
- - Deleting a script that is referenced by workflow transitions may
923
+ - Deleting a tasklet that is referenced by workflow transitions may
935
924
  break the workflow flow
936
925
  - If remove_file=True, the Python file will be permanently deleted
937
926
  - This operation cannot be undone
@@ -976,14 +965,14 @@ class MainController:
976
965
  # Create a simple form
977
966
  form = controller.create_form(
978
967
  title="User Registration",
979
- file="forms/registration.py"
968
+ file="form_registration.py"
980
969
  )
981
970
  print(f"Created form with ID: {form.id}")
982
971
 
983
972
  # Create form with custom position and ID
984
973
  custom_form = controller.create_form(
985
974
  title="Data Input Form",
986
- file="forms/data_input.py",
975
+ file="form_data_input.py",
987
976
  workflow_position=(100, 200),
988
977
  id="custom-form-id"
989
978
  )
@@ -1099,14 +1088,14 @@ class MainController:
1099
1088
  # Create a webhook for external API integration
1100
1089
  webhook = controller.create_hook(
1101
1090
  title="Payment Webhook",
1102
- file="hooks/payment_webhook.py"
1091
+ file="hook_payment_webhook.py"
1103
1092
  )
1104
1093
  print(f"Webhook URL: /hooks/{webhook.id}")
1105
1094
 
1106
1095
  # Create hook with custom positioning
1107
1096
  api_hook = controller.create_hook(
1108
1097
  title="User API Endpoint",
1109
- file="hooks/user_api.py",
1098
+ file="hook_user_api.py",
1110
1099
  workflow_position=(300, 150),
1111
1100
  id="user-api-hook"
1112
1101
  )
@@ -1242,14 +1231,14 @@ class MainController:
1242
1231
  # Create a daily data processing job
1243
1232
  daily_job = controller.create_job(
1244
1233
  title="Daily Data Sync",
1245
- file="jobs/daily_sync.py"
1234
+ file="job_daily_sync.py"
1246
1235
  )
1247
1236
  print(f"Created job: {daily_job.title}")
1248
1237
 
1249
1238
  # Create job with custom position and ID
1250
1239
  cleanup_job = controller.create_job(
1251
1240
  title="Weekly Cleanup",
1252
- file="jobs/cleanup.py",
1241
+ file="job_cleanup.py",
1253
1242
  workflow_position=(500, 100),
1254
1243
  id="weekly-cleanup"
1255
1244
  )
@@ -1390,7 +1379,7 @@ class MainController:
1390
1379
  Retrieve all workflow stages in the current project.
1391
1380
 
1392
1381
  This method returns a complete list of all stages (forms, hooks, jobs,
1393
- and scripts) that are part of the project workflow.
1382
+ and tasklets) that are part of the project workflow.
1394
1383
 
1395
1384
  Returns:
1396
1385
  List[Stage]: List containing all workflow stages, including:
@@ -1760,10 +1749,10 @@ class MainController:
1760
1749
 
1761
1750
  script = self.get_script(id)
1762
1751
  if not script:
1763
- raise Exception(f"Script with id {id} not found")
1752
+ raise Exception(f"Tasklet with id {id} not found")
1764
1753
 
1765
1754
  if not task_id:
1766
- raise Exception("Task ID is required for script execution")
1755
+ raise Exception("Task ID is required for tasklet execution")
1767
1756
 
1768
1757
  return ExecutionController(
1769
1758
  repositories=self.repositories,
@@ -18,7 +18,43 @@ from abstra_internals.entities.forms.widgets.widget_base import InputWidget, Wid
18
18
  @dataclass
19
19
  class Button:
20
20
  """
21
- Base class for form navigation buttons.
21
+ Render buttons with custom functionality in your form for conditional logic and branching paths.
22
+
23
+ Buttons allow users to trigger custom Python functions, processing or different form behaviors. When a button is pressed, its key becomes `True` in the form state, enabling conditional logic to determine the next steps in your workflow. This makes buttons essential for creating approval workflows, multi-path forms, and interactive decision trees.
24
+
25
+ Unlike automatic navigation buttons (NextButton/BackButton), custom Button widgets give you full control over widget behavior within a page and can be used to implement complex conditional logic based on user choices.
26
+
27
+ ## How Button State Works
28
+
29
+ When a user clicks a button, the button's key is set to `True` in the form state. You can then check this state using `state.get("button_key")` to determine which button was pressed and conditionally render different widgets or forms.
30
+
31
+ ## Usage Pattern
32
+
33
+ Buttons are optionally returned from your Page function (a function that returns a list of widgets). You have two options:
34
+
35
+ **Option 1: Return only widgets (default behavior)**
36
+ ```python
37
+ def my_page(state):
38
+ return [TextOutput("Hello"), TextOutput("world!")] # Default navigation buttons will be used
39
+ ```
40
+
41
+ **Option 2: Return widgets + custom buttons as a tuple**
42
+ ```python
43
+ def my_page(state):
44
+ widgets = [TextOutput("Hello"), TextOutput("world!")]
45
+ buttons = [Button("Label 1", key="key1"), Button("Label 2", key="key2")]
46
+ return widgets, buttons
47
+ ```
48
+
49
+ If you don't return buttons (Option 1), the system automatically provides default navigation buttons (Next/Back). If you want custom buttons, you must return a tuple with widgets as the first element and a list of Button objects as the second element.
50
+
51
+ ## Common Use Cases
52
+
53
+ - **Approval Workflows**: Create "Approve" and "Reject" buttons for document review processes
54
+ - **Data Reload**: Refresh page content with updated information from external APIs or databases
55
+ - **Multi-path Forms**: Allow users to choose between different information collection paths
56
+ - **Decision Trees**: Build complex conditional forms based on user choices
57
+ - **Custom Navigation**: Implement non-linear form progression with custom logic
22
58
 
23
59
  Args:
24
60
  label (str): The text displayed on the button.
@@ -50,8 +86,26 @@ class Button:
50
86
 
51
87
  class NextButton(Button):
52
88
  """
53
- Button used to navigate to the next step in a form.
54
- Automatically labeled with internationalized "Next" text.
89
+ Automatic navigation button for progressing to the next step in multi-step forms.
90
+
91
+ NextButton is automatically added to forms when using multi-step workflows created with the `run()` function. It handles forward navigation between form steps without requiring manual implementation. The button is automatically labeled with internationalized "Next" text that adapts to the user's language settings.
92
+
93
+ ## Automatic Behavior
94
+
95
+ - **Auto-added**: NextButton appears automatically on each step of a multi-step form (you don't need to import or create it)
96
+ - **Smart positioning**: Only shows when there are more steps ahead
97
+ - **Internationalized**: Button text automatically translates based on user locale
98
+ - **State preservation**: User input is preserved when navigating between steps
99
+
100
+ ## When to Use
101
+
102
+ NextButton is ideal for:
103
+ - **Linear workflows**: Step-by-step data collection processes
104
+ - **Wizards**: Multi-step onboarding or configuration flows
105
+ - **Progressive forms**: Breaking long forms into manageable sections
106
+ - **Data collection pipelines**: Structured information gathering processes
107
+
108
+ Unlike custom Button widgets, NextButton doesn't require state management or conditional logic - it's handled automatically by the form system.
55
109
  """
56
110
 
57
111
  def __init__(self):
@@ -61,8 +115,33 @@ class NextButton(Button):
61
115
 
62
116
  class BackButton(Button):
63
117
  """
64
- Button used to navigate to the previous step in a form.
65
- Automatically labeled with internationalized "Back" text.
118
+ Automatic navigation button for returning to previous steps in multi-step forms.
119
+
120
+ BackButton is automatically added to forms starting from step 2 onwards in multi-step workflows. It enables backward navigation while preserving all user input, allowing users to review and modify their previous responses without losing data. The button is automatically labeled with internationalized "Back" text that adapts to the user's language settings.
121
+
122
+ ## Automatic Behavior
123
+
124
+ - **Auto-added**: BackButton appears automatically from step 2 onwards in multi-step forms (you don't need to import or create it)
125
+ - **Smart positioning**: Only shows when there are previous steps to navigate to
126
+ - **Internationalized**: Button text automatically translates based on user locale
127
+ - **Data preservation**: All user input is maintained when navigating backwards
128
+ - **Seamless integration**: Works automatically with the form system's navigation logic
129
+
130
+ ## Key Features
131
+
132
+ - **Non-destructive navigation**: Users can go back without losing their progress
133
+ - **Form state management**: Previous inputs are automatically restored when returning
134
+ - **User-friendly**: Provides confidence for users to explore and correct their inputs
135
+
136
+ ## When BackButton Appears
137
+
138
+ BackButton is ideal for:
139
+ - **Multi-step wizards**: Allow users to review and modify previous steps
140
+ - **Data collection forms**: Enable correction of earlier responses
141
+ - **Progressive workflows**: Provide flexibility in non-linear completion
142
+ - **Review processes**: Allow users to double-check their inputs before submission
143
+
144
+ Unlike custom Button widgets, BackButton doesn't require any implementation - it's automatically managed by the form system.
66
145
  """
67
146
 
68
147
  def __init__(self):
@@ -17,9 +17,7 @@ def _generate_zip_file() -> pathlib.Path:
17
17
  zip_path = pathlib.Path(tempfile.gettempdir(), f"{uuid.uuid4()}.zip")
18
18
 
19
19
  with zipfile.ZipFile(zip_path, "w") as zip_file:
20
- for file in FileSystemService.list_files(
21
- root_path, use_ignore=True, ignore_dotenv=True
22
- ):
20
+ for file in FileSystemService.list_files(root_path, use_ignore=True):
23
21
  zip_file.write(file, file.relative_to(root_path))
24
22
 
25
23
  return zip_path
@@ -1,5 +1,4 @@
1
1
  import os
2
- import platform
3
2
  from unittest import TestCase, skipIf
4
3
 
5
4
  from abstra_internals.modules import import_as_new, reload_module
@@ -7,11 +6,12 @@ from abstra_internals.repositories.project.project import (
7
6
  LocalProjectRepository,
8
7
  ScriptStage,
9
8
  )
9
+ from abstra_internals.utils.platform import is_windows
10
10
  from tests.fixtures import clear_dir, init_dir
11
11
 
12
12
 
13
13
  def can_create_symlinks():
14
- if platform.system() != "Windows":
14
+ if not is_windows():
15
15
  return True
16
16
 
17
17
  try:
@@ -12,7 +12,7 @@ class LinterRepository(ABC):
12
12
  pass
13
13
 
14
14
  @abstractmethod
15
- def update_checks(self):
15
+ def update_checks(self) -> List[LinterCheck]:
16
16
  pass
17
17
 
18
18
  @abstractmethod
@@ -52,39 +52,6 @@ class LocalLinterRepository(LinterRepository):
52
52
  - description: Human-readable description of what the rule checks
53
53
  - issues: List of specific issues found by this rule
54
54
  - fixes: Available automatic fixes for the issues
55
-
56
- Example:
57
- ```python
58
- linter_repo = LocalLinterRepository()
59
- linter_repo.update_checks() # Run all checks first
60
-
61
- checks = linter_repo.get_checks()
62
- print(f"Found {len(checks)} linter checks")
63
-
64
- for check in checks:
65
- print(f"Rule: {check.name} ({check.type})")
66
- print(f"Description: {check.description}")
67
-
68
- if check.issues:
69
- print(f"Issues found: {len(check.issues)}")
70
- for issue in check.issues:
71
- print(f" - {issue.message}")
72
- print(f" File: {issue.file}:{issue.line}")
73
-
74
- if issue.fixes:
75
- print(f" Available fixes: {len(issue.fixes)}")
76
- for fix in issue.fixes:
77
- print(f" - {fix.name}: {fix.description}")
78
- else:
79
- print("✓ No issues found")
80
-
81
- # Filter by severity
82
- errors = [c for c in checks if c.type == 'error']
83
- warnings = [c for c in checks if c.type == 'warning']
84
- security_issues = [c for c in checks if c.type == 'security']
85
-
86
- print(f"Errors: {len(errors)}, Warnings: {len(warnings)}")
87
- print(f"Security issues: {len(security_issues)}")
88
55
  ```
89
56
 
90
57
  Note:
@@ -118,6 +85,8 @@ class LocalLinterRepository(LinterRepository):
118
85
 
119
86
  self.checks = new_checks
120
87
 
88
+ return self.checks
89
+
121
90
  def fix_issue_in_codebase(self, rule_name: str, fix_name: str):
122
91
  """
123
92
  Apply a specific automatic fix for a linter issue.
@@ -216,16 +185,16 @@ class ProductionLinterRepository(LinterRepository):
216
185
  """
217
186
 
218
187
  def find_issues_in_codebase(self) -> List[LinterCheck]:
219
- return []
188
+ raise NotImplementedError("Linters are not available in production.")
220
189
 
221
- def update_checks(self):
222
- pass
190
+ def update_checks(self) -> List[LinterCheck]:
191
+ raise NotImplementedError("Linters are not available in production.")
223
192
 
224
193
  def fix_issue_in_codebase(self, rule_name: str, fix_name: str) -> bool:
225
- return False
194
+ raise NotImplementedError("Linters are not available in production.")
226
195
 
227
196
  def fix_all_linters(self):
228
- pass
197
+ raise NotImplementedError("Linters are not available in production.")
229
198
 
230
199
  def get_blocking_checks(self) -> List[LinterCheck]:
231
- return []
200
+ raise NotImplementedError("Linters are not available in production.")
@@ -3,6 +3,7 @@ from typing import List
3
3
 
4
4
  from abstra_internals.repositories.linter.models import LinterRule
5
5
 
6
+ from .big_py_files import BigPyFiles
6
7
  from .conflicting_name import ConflictingName
7
8
  from .conflicting_path import ConflictingPath
8
9
  from .deprecated_functions import DeprecatedFunctionUsage
@@ -30,6 +31,7 @@ core_rules: List[LinterRule] = [
30
31
  Psycopg2MustBeBinary(),
31
32
  ConflictingName(),
32
33
  DeprecatedFunctionUsage(),
34
+ BigPyFiles(),
33
35
  ]
34
36
 
35
37
  conditional_rules: List[LinterRule] = []