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
@@ -12,6 +12,7 @@ from tempfile import mkdtemp
12
12
  from typing import Dict, List, Literal, Optional, Set
13
13
 
14
14
  from importlib_metadata import packages_distributions
15
+ from packaging.requirements import Requirement
15
16
  from pip._internal.cli.main import main as pip_main
16
17
 
17
18
  from abstra_internals.repositories.project.project import LocalProjectRepository
@@ -48,75 +49,122 @@ def check_package(package_name) -> Literal["builtin", "installed", "unknown"]:
48
49
  return "unknown"
49
50
 
50
51
 
51
- @dataclass
52
- class Requirement:
53
- name: str
54
- version: Optional[str] = None
52
+ # Helper functions to extend packaging.requirements.Requirement functionality
53
+ def requirement_to_text(req: Requirement) -> str:
54
+ """Convert a Requirement to text format."""
55
+ return str(req)
55
56
 
56
- def to_text(self):
57
- if self.version is None:
58
- return self.name
59
- else:
60
- return f"{self.name}=={self.version}"
61
57
 
62
- @staticmethod
63
- def from_text(text: str):
64
- try:
65
- if "==" in text:
66
- name, version = text.split("==")
67
- return Requirement(name=name, version=version)
68
- else:
69
- return Requirement(name=text)
70
- except ValueError:
58
+ def requirement_from_text(text: str) -> Optional[Requirement]:
59
+ """Create a Requirement from text, handling simple cases gracefully."""
60
+ try:
61
+ text = text.strip()
62
+ if not text:
71
63
  return None
64
+ return Requirement(text)
65
+ except Exception:
66
+ return None
72
67
 
73
- def to_dict(self):
74
- return {
75
- "name": self.name,
76
- "version": self.version,
77
- "installed_version": self.installed_version(),
78
- }
79
68
 
80
- @staticmethod
81
- def from_dict(data: dict):
82
- return Requirement(name=data["name"], version=data["version"])
69
+ def requirement_to_dict(req: Requirement) -> dict:
70
+ """Convert a Requirement to comprehensive dictionary format."""
71
+ # Parse specifiers into a more detailed format
72
+ specifiers = []
73
+
74
+ if req.specifier:
75
+ for spec in req.specifier:
76
+ specifiers.append({"operator": spec.operator, "version": spec.version})
77
+
78
+ return {
79
+ "name": req.name,
80
+ "specifiers": specifiers,
81
+ "extras": list(req.extras) if req.extras else [],
82
+ "marker": str(req.marker) if req.marker else None,
83
+ "url": req.url,
84
+ "raw_requirement": str(req),
85
+ "installed_version": get_installed_version(req.name),
86
+ }
87
+
88
+
89
+ def requirement_from_dict(data: dict) -> Requirement:
90
+ """Create a Requirement from dictionary format."""
91
+ # Check if we have the raw requirement string (preferred)
92
+ if "raw_requirement" in data and data["raw_requirement"]:
93
+ return Requirement(data["raw_requirement"])
94
+
95
+ # Fallback: reconstruct from components
96
+ name = data["name"]
97
+
98
+ # Check for new specifiers format
99
+ if "specifiers" in data and data["specifiers"]:
100
+ spec_parts = []
101
+ for spec in data["specifiers"]:
102
+ spec_parts.append(f"{spec['operator']}{spec['version']}")
103
+ spec_string = ",".join(spec_parts)
104
+ req_string = f"{name}{spec_string}"
105
+ # Fallback to old version format for backward compatibility
106
+ elif "version" in data and data["version"]:
107
+ req_string = f"{name}=={data['version']}"
108
+ else:
109
+ req_string = name
110
+
111
+ # Add extras if present
112
+ if "extras" in data and data["extras"]:
113
+ extras_str = ",".join(data["extras"])
114
+ req_string = f"{name}[{extras_str}]{req_string[len(name) :]}"
115
+
116
+ # Add marker if present
117
+ if "marker" in data and data["marker"]:
118
+ req_string = f"{req_string}; {data['marker']}"
119
+
120
+ return Requirement(req_string)
121
+
122
+
123
+ def get_installed_version(package_name: str) -> Optional[str]:
124
+ """Get the installed version of a package."""
125
+ try:
126
+ return distribution(package_name).version
127
+ except PackageNotFoundError:
128
+ return None
83
129
 
84
- def __hash__(self) -> int:
85
- return hash(f"{self.name}/{self.version}")
86
130
 
87
- def installed_version(self):
88
- try:
89
- return distribution(self.name).version
90
- except PackageNotFoundError:
91
- return None
131
+ def create_requirement(name: str, version: Optional[str] = None) -> Requirement:
132
+ """Create a Requirement with optional version specification."""
133
+ if version:
134
+ return Requirement(f"{name}=={version}")
135
+ else:
136
+ return Requirement(name)
92
137
 
93
- def uninstall(self):
94
- if not self.installed_version():
95
- return
96
138
 
97
- if os.getenv("ABSTRA_RUNNING_IN_WINDOWS_APP"):
98
- yield from self.__uninstall_from_standalone()
99
- else:
100
- yield from self.__uninstall_from_lib()
101
-
102
- def __uninstall_from_standalone(self):
103
- cmd = [
104
- "uninstall",
105
- "-y",
106
- self.to_text(),
107
- "--target",
108
- os.environ["ABSTRA_BUNDLED_APP_PACKAGES_FOLDER"],
109
- ]
139
+ def uninstall_requirement(req: Requirement):
140
+ """Uninstall a requirement package."""
141
+ installed_version = get_installed_version(req.name)
142
+ if not installed_version:
143
+ return
110
144
 
111
- if pip_main(cmd) != 0:
112
- yield f"Failed to uninstall {self.to_text()} from standalone\n"
113
- else:
114
- yield "Uninstallation finished successfully\n\n"
145
+ if os.getenv("ABSTRA_RUNNING_IN_WINDOWS_APP"):
146
+ yield from __uninstall_from_standalone(req)
147
+ else:
148
+ yield from __uninstall_from_lib(req)
115
149
 
116
- def __uninstall_from_lib(self):
117
- yield from stream_output(
118
- [sys.executable, "-m", "pip", "uninstall", "-y", self.name]
119
- )
150
+
151
+ def __uninstall_from_standalone(req: Requirement):
152
+ cmd = [
153
+ "uninstall",
154
+ "-y",
155
+ requirement_to_text(req),
156
+ "--target",
157
+ os.environ["ABSTRA_BUNDLED_APP_PACKAGES_FOLDER"],
158
+ ]
159
+
160
+ if pip_main(cmd) != 0:
161
+ yield f"Failed to uninstall {requirement_to_text(req)} from standalone\n"
162
+ else:
163
+ yield "Uninstallation finished successfully\n\n"
164
+
165
+
166
+ def __uninstall_from_lib(req: Requirement):
167
+ yield from stream_output([sys.executable, "-m", "pip", "uninstall", "-y", req.name])
120
168
 
121
169
 
122
170
  @dataclass
@@ -127,8 +175,9 @@ class RequirementRecommendation:
127
175
  reason_code: str
128
176
 
129
177
  def to_dict(self):
178
+ """Convert to comprehensive dictionary format."""
130
179
  return {
131
- **self.requirement.to_dict(),
180
+ **requirement_to_dict(self.requirement),
132
181
  "reason_file": str(self.reason_file),
133
182
  "reason_line": self.reason_line,
134
183
  "reason_code": self.reason_code,
@@ -136,7 +185,7 @@ class RequirementRecommendation:
136
185
 
137
186
  def __hash__(self) -> int:
138
187
  return hash(
139
- f"{self.requirement.to_text()}/{self.reason_file}/{self.reason_line}"
188
+ f"{requirement_to_text(self.requirement)}/{self.reason_file}/{self.reason_line}"
140
189
  )
141
190
 
142
191
 
@@ -145,40 +194,134 @@ class Requirements:
145
194
  libraries: List[Requirement]
146
195
 
147
196
  def to_text(self):
148
- return "\n".join([lib.to_text() for lib in self.libraries])
197
+ return "\n".join([requirement_to_text(lib) for lib in self.libraries])
149
198
 
150
199
  @staticmethod
151
200
  def from_text(text: str):
152
201
  libraries = []
153
- for line in text.splitlines():
154
- if line.startswith("#"):
202
+ for line_num, line in enumerate(text.splitlines(), 1):
203
+ # Remove inline comments
204
+ if "#" in line:
205
+ line = line.split("#")[0]
206
+
207
+ line = line.strip()
208
+
209
+ # Skip empty lines and comments
210
+ if not line or line.startswith("#"):
155
211
  continue
156
212
 
157
- if line.strip() == "":
213
+ # Skip -r, -c, --find-links, etc. (pip options)
214
+ if line.startswith(("-r", "-c", "--", "-f", "-i", "-e")):
158
215
  continue
159
216
 
160
- requirement = Requirement.from_text(line)
161
- if requirement is None:
217
+ try:
218
+ requirement = requirement_from_text(line)
219
+ if requirement is not None:
220
+ libraries.append(requirement)
221
+ else:
222
+ # Check if this is a standalone URL that needs to be converted
223
+ if line.startswith(("http://", "https://", "git+", "file:///")):
224
+ try:
225
+ # Try to extract package name from URL
226
+ package_name = Requirements._extract_package_name_from_url(
227
+ line
228
+ )
229
+ if package_name:
230
+ # Convert to proper format: package @ url
231
+ proper_format = f"{package_name} @ {line}"
232
+ requirement = requirement_from_text(proper_format)
233
+ if requirement is not None:
234
+ libraries.append(requirement)
235
+ continue
236
+ except Exception:
237
+ pass
238
+
239
+ # Log parsing error but continue
240
+ print(
241
+ f"Warning: Could not parse requirement on line {line_num}: '{line}'"
242
+ )
243
+ except Exception as e:
244
+ # Check if this is a standalone URL that needs to be converted
245
+ if line.startswith(("http://", "https://", "git+", "file:///")):
246
+ try:
247
+ # Try to extract package name from URL
248
+ package_name = Requirements._extract_package_name_from_url(line)
249
+ if package_name:
250
+ # Convert to proper format: package @ url
251
+ proper_format = f"{package_name} @ {line}"
252
+ requirement = requirement_from_text(proper_format)
253
+ if requirement is not None:
254
+ libraries.append(requirement)
255
+ continue
256
+ except Exception:
257
+ pass
258
+
259
+ # Log parsing error but continue
260
+ print(
261
+ f"Warning: Could not parse requirement on line {line_num}: '{line}' - {e}"
262
+ )
162
263
  continue
163
264
 
164
- libraries.append(requirement)
165
265
  return Requirements(libraries=libraries)
166
266
 
267
+ @staticmethod
268
+ def _extract_package_name_from_url(url: str) -> Optional[str]:
269
+ """Extract a reasonable package name from a URL.
270
+
271
+ This is a heuristic approach since URLs don't inherently contain package names.
272
+ """
273
+ import re
274
+ from urllib.parse import urlparse
275
+
276
+ # Remove common URL prefixes
277
+ clean_url = url
278
+ if clean_url.startswith("git+"):
279
+ clean_url = clean_url[4:]
280
+
281
+ parsed = urlparse(clean_url)
282
+ path = parsed.path
283
+
284
+ # Try to extract from common patterns
285
+ patterns = [
286
+ r"/([^/]+)\.git$", # git repos: /package.git
287
+ r"/([^/]+)\.zip$", # zip files: /package.zip
288
+ r"/([^/]+)\.tar\.gz$", # tar.gz files: /package.tar.gz
289
+ r"/([^/]+)[-_][\d\.]", # versioned packages: /package-1.0.0
290
+ r"/([^/]+)$", # last path component
291
+ ]
292
+
293
+ for pattern in patterns:
294
+ match = re.search(pattern, path)
295
+ if match:
296
+ name = match.group(1)
297
+ # Clean up the name
298
+ name = re.sub(r"[-_]", "-", name) # normalize separators
299
+ name = re.sub(r"[^a-zA-Z0-9\-\.]", "", name) # remove invalid chars
300
+ if name and not name.startswith("."):
301
+ return name
302
+
303
+ # Fallback: use hostname
304
+ if parsed.hostname:
305
+ return f"package-from-{parsed.hostname.replace('.', '-')}"
306
+
307
+ return None
308
+
167
309
  def to_dict(self):
168
- return [lib.to_dict() for lib in self.libraries]
310
+ """Convert to comprehensive dictionary format."""
311
+ return [requirement_to_dict(lib) for lib in self.libraries]
169
312
 
170
313
  @staticmethod
171
314
  def from_dict(data: list):
172
- return Requirements(libraries=[Requirement.from_dict(lib) for lib in data])
315
+ return Requirements(libraries=[requirement_from_dict(lib) for lib in data])
173
316
 
174
317
  def add(self, name: str, version: Optional[str] = None):
175
318
  if self.has(name, version):
176
319
  return
177
- self.libraries.append(Requirement(name=name, version=version))
320
+ self.libraries.append(create_requirement(name, version))
178
321
 
179
322
  def update(self, name: str, version: str):
180
323
  self.libraries = [
181
- lib if lib.name != name else Requirement(name=name, version=version)
324
+ lib if lib.name != name else create_requirement(name, version)
182
325
  for lib in self.libraries
183
326
  ]
184
327
 
@@ -186,13 +329,54 @@ class Requirements:
186
329
  self.libraries = [lib for lib in self.libraries if lib.name != name]
187
330
 
188
331
  def delete_duplicates(self, name: str, version: Optional[str]):
332
+ # For packaging.requirements.Requirement, we need to extract version from specifier
189
333
  self.libraries = [
190
- lib for lib in self.libraries if lib.name != name or lib.version != version
334
+ lib
335
+ for lib in self.libraries
336
+ if not (
337
+ lib.name == name and self._get_version_from_requirement(lib) == version
338
+ )
191
339
  ]
192
340
 
341
+ def _get_version_from_requirement(self, req: Requirement) -> Optional[str]:
342
+ """Extract exact version from a Requirement's specifier.
343
+
344
+ Returns the exact version if the requirement has an == specifier,
345
+ otherwise returns None.
346
+ """
347
+ if req.specifier:
348
+ for spec in req.specifier:
349
+ if spec.operator == "==":
350
+ return spec.version
351
+ return None
352
+
353
+ def _get_requirement_signature(self, req: Requirement) -> str:
354
+ """Get a unique signature for a requirement that includes all specifiers.
355
+
356
+ This is used for more accurate duplicate detection.
357
+ """
358
+ return str(req)
359
+
193
360
  def has(self, lib_name: str, version: Optional[str] = None):
361
+ """Check if a requirement is present.
362
+
363
+ If version is None, checks for any requirement with the given name.
364
+ If version is provided, checks for exact version match (== specifier).
365
+ """
366
+ for lib in self.libraries:
367
+ if lib.name == lib_name:
368
+ if version is None:
369
+ return True
370
+ req_version = self._get_version_from_requirement(lib)
371
+ if req_version == version:
372
+ return True
373
+ return False
374
+
375
+ def has_requirement_like(self, req: Requirement) -> bool:
376
+ """Check if a requirement with the same signature already exists."""
377
+ req_signature = self._get_requirement_signature(req)
194
378
  for lib in self.libraries:
195
- if lib.name == lib_name and (lib.version == version or version is None):
379
+ if self._get_requirement_signature(lib) == req_signature:
196
380
  return True
197
381
  return False
198
382
 
@@ -209,10 +393,15 @@ class Requirements:
209
393
  def get(self, lib_name: str):
210
394
  for lib in self.libraries:
211
395
  if lib.name == lib_name:
212
- return lib.version
396
+ return self._get_version_from_requirement(lib)
213
397
  return None
214
398
 
215
399
  def get_duplicates(self) -> Dict[str, List[Requirement]]:
400
+ """Get requirements that have duplicate package names.
401
+
402
+ Groups requirements by package name, returns only groups with more than one requirement.
403
+ This allows for sophisticated duplicate detection that considers different version specifiers.
404
+ """
216
405
  duplicates = {}
217
406
  for lib in self.libraries:
218
407
  if not isinstance(duplicates.get(lib.name), list):
@@ -228,7 +417,7 @@ class Requirements:
228
417
  if lib.name == "abstra":
229
418
  continue
230
419
 
231
- cmd.append(lib.to_text())
420
+ cmd.append(requirement_to_text(lib))
232
421
 
233
422
  yield from stream_output(cmd)
234
423
 
@@ -242,16 +431,17 @@ class Requirements:
242
431
 
243
432
  cmd = [
244
433
  "install",
245
- lib.to_text(),
434
+ requirement_to_text(lib),
246
435
  "--target",
247
436
  os.environ["ABSTRA_BUNDLED_APP_PACKAGES_FOLDER"],
248
437
  ]
249
438
 
250
- yield f"Installing {lib.to_text()} in abstra standalone...\n"
439
+ yield f"Installing {requirement_to_text(lib)} in abstra standalone...\n"
251
440
 
252
441
  res = pip_main(cmd)
442
+ req_version = self._get_version_from_requirement(lib)
253
443
  if res != 0:
254
- yield f"Failed to install {lib.name}=={lib.version}\n"
444
+ yield f"Failed to install {lib.name}=={req_version}\n"
255
445
  else:
256
446
  yield "Installation finished successfully\n\n"
257
447
 
@@ -323,9 +513,9 @@ class RequirementsRepository:
323
513
 
324
514
  imported_modules.add(
325
515
  RequirementRecommendation(
326
- requirement=Requirement(
327
- name=lib_name,
328
- version=version,
516
+ requirement=create_requirement(
517
+ lib_name,
518
+ version,
329
519
  ),
330
520
  reason_file=python_file,
331
521
  reason_line=node.lineno,