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
@@ -0,0 +1,40 @@
1
+ from pathlib import Path
2
+ from typing import List
3
+
4
+ from abstra_internals.repositories.linter.models import LinterIssue, LinterRule
5
+ from abstra_internals.repositories.project.project import LocalProjectRepository
6
+
7
+ # Constant to make it easy to change the line limit
8
+ MAX_LINES_THRESHOLD = 500
9
+
10
+
11
+ class BigPyFileFound(LinterIssue):
12
+ def __init__(self, file_path: Path, line_count: int):
13
+ self.label = f"File {file_path.name} has {line_count} lines (limit: {MAX_LINES_THRESHOLD}). Consider splitting this file into multiple smaller files organized by responsibilities to improve code maintainability."
14
+ self.fixes = [] # No automatic fix available for this type of issue
15
+
16
+
17
+ class BigPyFiles(LinterRule):
18
+ label = "Large Python files"
19
+ type = "info"
20
+
21
+ def find_issues(self) -> List[LinterIssue]:
22
+ project = LocalProjectRepository().load()
23
+ issues = []
24
+
25
+ # Check all Python files in the project
26
+ for py_file in project.iter_py_files():
27
+ try:
28
+ if py_file.exists() and py_file.is_file():
29
+ # Count the number of lines in the file
30
+ with open(py_file, "r", encoding="utf-8") as f:
31
+ line_count = sum(1 for _ in f)
32
+
33
+ # If it exceeds the limit, add an issue
34
+ if line_count > MAX_LINES_THRESHOLD:
35
+ issues.append(BigPyFileFound(py_file, line_count))
36
+ except (UnicodeDecodeError, OSError):
37
+ # Ignore files that cannot be read as text
38
+ continue
39
+
40
+ return issues
@@ -0,0 +1,93 @@
1
+ from abstra_internals.repositories.linter.rules.big_py_files import (
2
+ MAX_LINES_THRESHOLD,
3
+ BigPyFiles,
4
+ )
5
+ from tests.fixtures import BaseTest
6
+
7
+
8
+ class BigPyFilesTest(BaseTest):
9
+ def test_big_py_files_valid_small_file(self):
10
+ """Tests that small files don't generate issues"""
11
+ # Create a small file
12
+ script = self.controller.create_tasklet("Small script", "small_script.py")
13
+ script.file_path.write_text("print('hello world')\n" * 10) # 10 lines
14
+
15
+ rule = BigPyFiles()
16
+ issues = rule.find_issues()
17
+ self.assertEqual(len(issues), 0)
18
+
19
+ def test_big_py_files_valid_threshold_file(self):
20
+ """Tests that files exactly at the limit don't generate issues"""
21
+ script = self.controller.create_tasklet(
22
+ "Threshold script", "threshold_script.py"
23
+ )
24
+ script.file_path.write_text(
25
+ "print('line')\n" * MAX_LINES_THRESHOLD
26
+ ) # Exactly at the limit
27
+
28
+ rule = BigPyFiles()
29
+ issues = rule.find_issues()
30
+ self.assertEqual(len(issues), 0)
31
+
32
+ def test_big_py_files_invalid_large_file(self):
33
+ """Tests that large files generate issues"""
34
+ script = self.controller.create_tasklet("Large script", "large_script.py")
35
+ # Create a file with more lines than the limit
36
+ script.file_path.write_text("print('line')\n" * (MAX_LINES_THRESHOLD + 50))
37
+
38
+ rule = BigPyFiles()
39
+ issues = rule.find_issues()
40
+ self.assertEqual(len(issues), 1)
41
+
42
+ issue = issues[0]
43
+ self.assertIn("large_script.py", issue.label)
44
+ self.assertIn(str(MAX_LINES_THRESHOLD + 50), issue.label)
45
+ self.assertIn("lines", issue.label)
46
+ self.assertIn("responsibilities", issue.label)
47
+
48
+ def test_big_py_files_multiple_files(self):
49
+ """Tests behavior with multiple files"""
50
+ # Small file
51
+ small_script = self.controller.create_tasklet("Small script", "small.py")
52
+ small_script.file_path.write_text("print('small')\n" * 10)
53
+
54
+ # Large file
55
+ large_script = self.controller.create_tasklet("Large script", "large.py")
56
+ large_script.file_path.write_text(
57
+ "print('large')\n" * (MAX_LINES_THRESHOLD + 100)
58
+ )
59
+
60
+ # Another large file
61
+ another_large_script = self.controller.create_tasklet(
62
+ "Another large", "another_large.py"
63
+ )
64
+ another_large_script.file_path.write_text(
65
+ "print('another')\n" * (MAX_LINES_THRESHOLD + 200)
66
+ )
67
+
68
+ rule = BigPyFiles()
69
+ issues = rule.find_issues()
70
+ self.assertEqual(len(issues), 2) # Only the 2 large files
71
+
72
+ # Check if the file names are in the issues
73
+ issue_labels = [issue.label for issue in issues]
74
+ self.assertTrue(any("large.py" in label for label in issue_labels))
75
+ self.assertTrue(any("another_large.py" in label for label in issue_labels))
76
+
77
+ def test_big_py_files_empty_file(self):
78
+ """Tests behavior with empty file"""
79
+ script = self.controller.create_tasklet("Empty script", "empty.py")
80
+ script.file_path.touch() # Create empty file
81
+
82
+ rule = BigPyFiles()
83
+ issues = rule.find_issues()
84
+ self.assertEqual(len(issues), 0)
85
+
86
+ def test_big_py_files_nonexistent_file(self):
87
+ """Tests behavior when file doesn't exist"""
88
+ script = self.controller.create_tasklet("Nonexistent script", "nonexistent.py")
89
+ script.file_path.unlink() # Remove the file
90
+
91
+ rule = BigPyFiles()
92
+ issues = rule.find_issues()
93
+ self.assertEqual(len(issues), 0) # Should not generate error or issue
@@ -5,7 +5,10 @@ from abstra_internals.repositories.linter.models import (
5
5
  LinterIssue,
6
6
  LinterRule,
7
7
  )
8
- from abstra_internals.services.requirements import RequirementsRepository
8
+ from abstra_internals.services.requirements import (
9
+ RequirementsRepository,
10
+ requirement_to_dict,
11
+ )
9
12
 
10
13
 
11
14
  class MergeDuplicatePackages(LinterFix):
@@ -52,9 +55,18 @@ class DuplicatePackagesInRequirements(LinterRule):
52
55
  duplicates = requirements.get_duplicates()
53
56
  issues = []
54
57
  for name, versions in duplicates.items():
58
+ # Extract exact versions from specifiers
59
+ version_list = []
60
+ for r in versions:
61
+ req_dict = requirement_to_dict(r)
62
+ exact_version = None
63
+ for spec in req_dict.get("specifiers", []):
64
+ if spec["operator"] == "==":
65
+ exact_version = spec["version"]
66
+ break
67
+ version_list.append(exact_version)
68
+
55
69
  issues.append(
56
- DuplicatePackagesInRequirementsFound(
57
- name=name, versions=[r.version for r in versions]
58
- )
70
+ DuplicatePackagesInRequirementsFound(name=name, versions=version_list)
59
71
  )
60
72
  return issues
@@ -1,7 +1,10 @@
1
1
  from abstra_internals.repositories.linter.rules.duplicate_package_in_requirements import (
2
2
  DuplicatePackagesInRequirements,
3
3
  )
4
- from abstra_internals.services.requirements import RequirementsRepository
4
+ from abstra_internals.services.requirements import (
5
+ RequirementsRepository,
6
+ requirement_to_dict,
7
+ )
5
8
  from tests.fixtures import BaseTest
6
9
 
7
10
 
@@ -49,7 +52,16 @@ class TestDuplicatePackagesInRequirements(BaseTest):
49
52
  requirements = RequirementsRepository.load()
50
53
  self.assertEqual(len(requirements.libraries), 1)
51
54
  self.assertEqual(requirements.libraries[0].name, "abstra")
52
- self.assertEqual(requirements.libraries[0].version, "1.0.0")
55
+
56
+ # Use helper function to get version from Requirement
57
+ req_dict = requirement_to_dict(requirements.libraries[0])
58
+ # Extract exact version from specifiers
59
+ exact_version = None
60
+ for spec in req_dict.get("specifiers", []):
61
+ if spec["operator"] == "==":
62
+ exact_version = spec["version"]
63
+ break
64
+ self.assertEqual(exact_version, "1.0.0")
53
65
 
54
66
  def test_invalid_with_distinct_versions_choose_second(self):
55
67
  requirements_txt = self.root / "requirements.txt"
@@ -66,4 +78,13 @@ class TestDuplicatePackagesInRequirements(BaseTest):
66
78
  requirements = RequirementsRepository.load()
67
79
  self.assertEqual(len(requirements.libraries), 1)
68
80
  self.assertEqual(requirements.libraries[0].name, "abstra")
69
- self.assertEqual(requirements.libraries[0].version, "2.0.0")
81
+
82
+ # Use helper function to get version from Requirement
83
+ req_dict = requirement_to_dict(requirements.libraries[0])
84
+ # Extract exact version from specifiers
85
+ exact_version = None
86
+ for spec in req_dict.get("specifiers", []):
87
+ if spec["operator"] == "==":
88
+ exact_version = spec["version"]
89
+ break
90
+ self.assertEqual(exact_version, "2.0.0")
@@ -1,5 +1,6 @@
1
1
  from typing import List
2
2
 
3
+ from abstra_internals.consts.filepaths import ABSTRA_IGNORE_FILEPATH
3
4
  from abstra_internals.repositories.linter.models import (
4
5
  LinterFix,
5
6
  LinterIssue,
@@ -13,7 +14,7 @@ class AddEnvToAbstraIgnore(LinterFix):
13
14
  label = "Add env to abstra ignore"
14
15
 
15
16
  def fix(self):
16
- abstraignore_file = Settings.root_path / ".abstraignore"
17
+ abstraignore_file = Settings.root_path / ABSTRA_IGNORE_FILEPATH
17
18
  with abstraignore_file.open("a") as file:
18
19
  file.write("\n.env")
19
20
 
@@ -34,11 +35,7 @@ class EnvInBundle(LinterRule):
34
35
  if not env_file.exists():
35
36
  return []
36
37
 
37
- ignored_patterns = FileSystemService.load_ignore_patterns(Settings.root_path)
38
-
39
- if FileSystemService.is_ignored(
40
- ignored_patterns, env_file.relative_to(Settings.root_path)
41
- ):
38
+ if FileSystemService.is_ignored(env_file):
42
39
  return []
43
40
 
44
41
  return [EnvInBundleFound()]
@@ -1,5 +1,6 @@
1
1
  from unittest import TestCase
2
2
 
3
+ from abstra_internals.consts.filepaths import ABSTRA_IGNORE_FILEPATH
3
4
  from abstra_internals.repositories.linter.rules.env_in_bundle import EnvInBundle
4
5
  from tests.fixtures import clear_dir, init_dir
5
6
 
@@ -20,7 +21,7 @@ class EnvInBundleTest(TestCase):
20
21
  def test_env_on_bundle_valid_with_env(self):
21
22
  rule = EnvInBundle()
22
23
  env_file = self.root / ".env"
23
- abstraignore_file = self.root / ".abstraignore"
24
+ abstraignore_file = self.root / ABSTRA_IGNORE_FILEPATH
24
25
  abstraignore_file.write_text(".env")
25
26
  env_file.touch()
26
27
  self.assertEqual(len(rule.find_issues()), 0)
@@ -34,7 +35,7 @@ class EnvInBundleTest(TestCase):
34
35
  def test_env_on_bundle_invalid_with_abstraignore_file(self):
35
36
  env_file = self.root / ".env"
36
37
  env_file.touch()
37
- abstraignore_file = self.root / ".abstraignore"
38
+ abstraignore_file = self.root / ABSTRA_IGNORE_FILEPATH
38
39
  abstraignore_file.touch()
39
40
  rule = EnvInBundle()
40
41
  self.assertEqual(len(rule.find_issues()), 1)
@@ -46,7 +47,7 @@ class EnvInBundleTest(TestCase):
46
47
  self.assertEqual(len(rule.find_issues()), 1)
47
48
  rule.find_issues()[0].fixes[0].fix()
48
49
  self.assertEqual(len(rule.find_issues()), 0)
49
- abstraignore_file = self.root / ".abstraignore"
50
+ abstraignore_file = self.root / ABSTRA_IGNORE_FILEPATH
50
51
  self.assertTrue(abstraignore_file.exists())
51
52
  with abstraignore_file.open("r") as file:
52
53
  content = file.read()
@@ -8,6 +8,7 @@ from abstra_internals.repositories.linter.models import (
8
8
  from abstra_internals.services.requirements import (
9
9
  RequirementRecommendation,
10
10
  RequirementsRepository,
11
+ requirement_to_dict,
11
12
  )
12
13
 
13
14
 
@@ -21,7 +22,17 @@ class AddMissingPackagesToRequirements(LinterFix):
21
22
  def fix(self):
22
23
  requirements = RequirementsRepository.load()
23
24
  requirement_name = self.requirement_recommendation.requirement.name
24
- requirement_version = self.requirement_recommendation.requirement.version
25
+ requirement_dict = requirement_to_dict(
26
+ self.requirement_recommendation.requirement
27
+ )
28
+
29
+ # Extract exact version from specifiers if available
30
+ requirement_version = None
31
+ for spec in requirement_dict.get("specifiers", []):
32
+ if spec["operator"] == "==":
33
+ requirement_version = spec["version"]
34
+ break
35
+
25
36
  requirements.add(requirement_name, requirement_version)
26
37
  RequirementsRepository.save(requirements)
27
38
 
@@ -1,32 +1,15 @@
1
- import webbrowser
2
- from pathlib import Path
3
1
  from typing import List
4
2
 
5
- from abstra_internals.repositories.linter.models import (
6
- LinterFix,
7
- LinterIssue,
8
- LinterRule,
9
- )
3
+ from abstra_internals.repositories.linter.models import LinterIssue, LinterRule
10
4
  from abstra_internals.repositories.project.project import LocalProjectRepository
11
5
  from abstra_internals.utils.file import silent_traverse_code
12
6
 
13
7
 
14
- class OpenBrokenFile(LinterFix):
15
- file: Path
16
-
17
- def __init__(self, file: Path) -> None:
18
- self.label = f"Open {file}"
19
- self.file = file
20
-
21
- def fix(self):
22
- webbrowser.open(self.file.absolute().as_uri())
23
-
24
-
25
8
  class SyntaxErrorFound(LinterIssue):
26
9
  def __init__(self, error: SyntaxError) -> None:
27
10
  self.label = str(error)
28
11
  if error.filename is not None:
29
- self.fixes = [OpenBrokenFile(Path(error.filename))]
12
+ self.fixes = []
30
13
 
31
14
 
32
15
  class SyntaxErrors(LinterRule):
@@ -2,6 +2,7 @@ import sys
2
2
  from pathlib import Path
3
3
  from typing import List
4
4
 
5
+ from abstra_internals.consts.filepaths import ABSTRA_IGNORE_FILEPATH
5
6
  from abstra_internals.repositories.linter.models import (
6
7
  LinterFix,
7
8
  LinterIssue,
@@ -36,7 +37,7 @@ class AddVenvToAbstraIgnore(LinterFix):
36
37
  root_path, prefix_path = get_root_and_prefix_path()
37
38
  venv_folder = prefix_path.replace(root_path, "").lstrip("/")
38
39
 
39
- abstraignore_file = Settings.root_path / ".abstraignore"
40
+ abstraignore_file = Settings.root_path / ABSTRA_IGNORE_FILEPATH
40
41
  with abstraignore_file.open("a") as file:
41
42
  file.write("\n")
42
43
  file.write(venv_folder)
@@ -57,7 +58,7 @@ class VenvInBundle(LinterRule):
57
58
  return prefix_path.startswith(root_path)
58
59
 
59
60
  def virtualenv_in_abstraignore(self) -> bool:
60
- abstraignore_file = Settings.root_path / ".abstraignore"
61
+ abstraignore_file = Settings.root_path / ABSTRA_IGNORE_FILEPATH
61
62
  if not abstraignore_file.exists():
62
63
  return False
63
64
 
@@ -1,7 +1,11 @@
1
1
  import flask
2
2
 
3
3
  from abstra_internals.controllers.main import MainController
4
- from abstra_internals.services.requirements import Requirement, RequirementsRepository
4
+ from abstra_internals.services.requirements import (
5
+ RequirementsRepository,
6
+ create_requirement,
7
+ uninstall_requirement,
8
+ )
5
9
  from abstra_internals.usage import editor_usage
6
10
 
7
11
 
@@ -40,8 +44,8 @@ def get_editor_bp(controller: MainController):
40
44
 
41
45
  @bp.post("/<name>/uninstall")
42
46
  def _uninstall_requirement(name: str):
43
- req = Requirement(name=name)
44
- streamer = req.uninstall()
47
+ req = create_requirement(name)
48
+ streamer = uninstall_requirement(req)
45
49
  if streamer is None:
46
50
  flask.abort(403)
47
51
  reqs = RequirementsRepository.load()
@@ -41,16 +41,6 @@ def get_editor_bp(controller: MainController):
41
41
  except Exception as e:
42
42
  return {"error": f"Could not get version: {str(e)}"}, 500
43
43
 
44
- @bp.post("/open-file")
45
- @editor_usage
46
- def _open_file():
47
- if not flask.request.json:
48
- flask.abort(400)
49
- file_path = flask.request.json["path"]
50
- mode = flask.request.json.get("mode", "file")
51
- controller.open_file(file_path, create_if_not_exists=True, mode=mode)
52
- return {"success": True}
53
-
54
44
  @bp.post("/init-file")
55
45
  @editor_usage
56
46
  def _init_file():
@@ -15,6 +15,7 @@ from watchdog.observers import Observer
15
15
 
16
16
  from abstra_internals.settings import Settings
17
17
  from abstra_internals.utils.crdt import CRDTManager
18
+ from abstra_internals.utils.file import safe_read_file, safe_write_file
18
19
 
19
20
  IGNORED_PATHS = [
20
21
  ".abstra/",
@@ -55,19 +56,21 @@ class FileWatcher(FileSystemEventHandler):
55
56
  event_type = "moved"
56
57
  elif isinstance(event, FileModifiedEvent):
57
58
  event_type = "changed"
58
- content = (
59
- filepath.read_text(encoding="utf-8") if filepath.is_file() else None
60
- )
61
-
62
- if filepath_str not in crdt_managers or crdt_managers[filepath_str] is None:
63
- crdt_managers[filepath_str] = CRDTManager(file_path=filepath)
64
-
65
- old_content = crdt_managers[filepath_str].get_content()
66
- if content is not None and old_content != content:
67
- crdt_managers[filepath_str].apply_operations_from_diff(content)
68
- new_content = crdt_managers[filepath_str].get_content()
69
- if content != new_content:
70
- filepath.write_text(new_content, encoding="utf-8")
59
+ content = safe_read_file(filepath, 2.0)
60
+
61
+ if content is not None:
62
+ if (
63
+ filepath_str not in crdt_managers
64
+ or crdt_managers[filepath_str] is None
65
+ ):
66
+ crdt_managers[filepath_str] = CRDTManager(file_path=filepath)
67
+
68
+ old_content = crdt_managers[filepath_str].get_content()
69
+ if old_content != content:
70
+ crdt_managers[filepath_str].apply_operations_from_diff(content)
71
+ new_content = crdt_managers[filepath_str].get_content()
72
+ if content != new_content:
73
+ safe_write_file(filepath, new_content, 2.0)
71
74
 
72
75
  else:
73
76
  return
@@ -90,7 +93,7 @@ class FileWatcher(FileSystemEventHandler):
90
93
 
91
94
  self._debounce_timers[filepath_str] = threading.Timer(
92
95
  interval=0.5, function=execute_handlers
93
- ) # 500ms debounce
96
+ )
94
97
  self._debounce_timers[filepath_str].start()
95
98
 
96
99
  def should_ignore_path(self, path: Path) -> bool: