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.
- {abstra-3.23.7.dist-info → abstra-3.23.9.dist-info}/METADATA +1 -1
- {abstra-3.23.7.dist-info → abstra-3.23.9.dist-info}/RECORD +196 -191
- abstra_internals/consts/filepaths.py +1 -1
- abstra_internals/controllers/codebase.py +9 -18
- abstra_internals/controllers/codebase_events.py +6 -4
- abstra_internals/controllers/main.py +56 -67
- abstra_internals/entities/forms/template.py +84 -5
- abstra_internals/interface/cli/deploy.py +1 -3
- abstra_internals/modules_test.py +2 -2
- abstra_internals/repositories/linter/repository.py +9 -40
- abstra_internals/repositories/linter/rules/__init__.py +2 -0
- abstra_internals/repositories/linter/rules/big_py_files.py +40 -0
- abstra_internals/repositories/linter/rules/big_py_files_test.py +93 -0
- abstra_internals/repositories/linter/rules/duplicate_package_in_requirements.py +16 -4
- abstra_internals/repositories/linter/rules/duplicate_package_in_requirements_test.py +24 -3
- abstra_internals/repositories/linter/rules/env_in_bundle.py +3 -6
- abstra_internals/repositories/linter/rules/env_in_bundle_test.py +4 -3
- abstra_internals/repositories/linter/rules/missing_packages_in_requirements.py +12 -1
- abstra_internals/repositories/linter/rules/syntax_errors.py +2 -19
- abstra_internals/repositories/linter/rules/venv_in_bundle.py +3 -2
- abstra_internals/server/routes/requirements.py +7 -3
- abstra_internals/server/routes/workspace.py +0 -10
- abstra_internals/services/file_watcher.py +17 -14
- abstra_internals/services/fs.py +144 -99
- abstra_internals/services/fs_test.py +303 -8
- abstra_internals/services/requirements.py +271 -81
- abstra_internals/services/requirements_test.py +528 -164
- abstra_internals/templates/__init__.py +3 -1
- abstra_internals/utils/file.py +62 -0
- abstra_internals/utils/platform.py +5 -0
- 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
- abstra_statics/dist/assets/AbstraLogo.vue_vue_type_script_setup_true_lang.f9d9018b.js +2 -0
- abstra_statics/dist/assets/{ApiKeys.902caf82.js → ApiKeys.1d2b9051.js} +2 -2
- abstra_statics/dist/assets/App.24328bec.js +2 -0
- abstra_statics/dist/assets/App.vue_vue_type_style_index_0_lang.13b52476.js +2 -0
- abstra_statics/dist/assets/BaseLayout.e2546be2.js +2 -0
- abstra_statics/dist/assets/{Billing.877a4614.js → Billing.ed96ff6d.js} +2 -2
- abstra_statics/dist/assets/{Breadcrumb.f312111a.js → Breadcrumb.21c760be.js} +2 -2
- abstra_statics/dist/assets/{Builds.a2c45c39.js → Builds.e0882931.js} +2 -2
- abstra_statics/dist/assets/{Card.5f504e7b.js → Card.714646f7.js} +5 -5
- abstra_statics/dist/assets/{CircularLoading.6f511e29.js → CircularLoading.5ac43298.js} +2 -2
- abstra_statics/dist/assets/CloseCircleOutlined.04918c3d.js +2 -0
- abstra_statics/dist/assets/ConnectorsView.78da900d.js +2 -0
- abstra_statics/dist/assets/{ConnectorsView.17764dde.css → ConnectorsView.aeb00ce8.css} +1 -1
- abstra_statics/dist/assets/ConsoleOmniChat.vue_vue_type_script_setup_true_lang.d7b17e09.js +2 -0
- abstra_statics/dist/assets/ContentLayout.3a69fd49.js +2 -0
- abstra_statics/dist/assets/{CrudView.5a642b48.js → CrudView.77b58a5a.js} +2 -2
- abstra_statics/dist/assets/DocsButton.vue_vue_type_script_setup_true_lang.836e8f5e.js +2 -0
- abstra_statics/dist/assets/{EditorLogin.d2224782.js → EditorLogin.4d61e44f.js} +2 -2
- abstra_statics/dist/assets/{EditorsView.5e769180.js → EditorsView.08a5cd3a.js} +2 -2
- abstra_statics/dist/assets/EnvVars.2f33a113.js +2 -0
- abstra_statics/dist/assets/{Error.dd899e38.js → Error.8a6cfa01.js} +2 -2
- abstra_statics/dist/assets/ExclamationCircleOutlined.93703670.js +2 -0
- abstra_statics/dist/assets/Files.6d07f9be.js +2 -0
- abstra_statics/dist/assets/Form.c1a08fa6.js +2 -0
- abstra_statics/dist/assets/{FormRunner.1c6a88dd.js → FormRunner.a00b296a.js} +2 -2
- abstra_statics/dist/assets/Home.332f8b44.js +2 -0
- abstra_statics/dist/assets/Home.964d824d.js +2 -0
- abstra_statics/dist/assets/{Live.a691b0eb.js → Live.ee0e73dd.js} +2 -2
- abstra_statics/dist/assets/LoadingContainer.cbb8e068.js +2 -0
- abstra_statics/dist/assets/LoadingOutlined.644899c2.js +2 -0
- abstra_statics/dist/assets/{Login.f96858b0.js → Login.0182140b.js} +2 -2
- abstra_statics/dist/assets/Login.ebf77147.js +2 -0
- 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
- abstra_statics/dist/assets/Logo.c74119c7.js +2 -0
- abstra_statics/dist/assets/{Logs.8426d360.js → Logs.7a458aba.js} +2 -2
- abstra_statics/dist/assets/{LogsController.318117fd.js → LogsController.e7ad74db.js} +2 -2
- abstra_statics/dist/assets/Main.a2c84f7b.js +2 -0
- abstra_statics/dist/assets/{MockForm.f8600bec.js → MockForm.50f237ad.js} +2 -2
- abstra_statics/dist/assets/Navbar.0cf9e650.js +2 -0
- abstra_statics/dist/assets/{NewEditor.d3300cf0.css → NewEditor.0044878f.css} +1 -1
- abstra_statics/dist/assets/NewEditor.4519d8ac.js +8 -0
- abstra_statics/dist/assets/OidcLoginCallback.073de6bd.js +2 -0
- abstra_statics/dist/assets/{OidcLogoutCallback.485fb0b9.js → OidcLogoutCallback.e474320b.js} +2 -2
- abstra_statics/dist/assets/{OmniChat.097bec71.js → OmniChat.1a6ad90c.js} +4 -4
- abstra_statics/dist/assets/{OmniChat.7660057c.css → OmniChat.8a35a659.css} +1 -1
- abstra_statics/dist/assets/{OnboardingView.c9a3343e.js → OnboardingView.c4e859f1.js} +2 -2
- abstra_statics/dist/assets/{Organization.0833b7fe.js → Organization.432776d6.js} +2 -2
- abstra_statics/dist/assets/Organizations.cd6f9f61.js +2 -0
- abstra_statics/dist/assets/{PhArrowCounterClockwise.vue.aaa06bc0.js → PhArrowCounterClockwise.vue.72ed46a0.js} +2 -2
- abstra_statics/dist/assets/{PhArrowSquareOut.vue.ee4af292.js → PhArrowSquareOut.vue.ddb450f1.js} +2 -2
- abstra_statics/dist/assets/{PhBookBookmark.vue.681c5036.js → PhBookBookmark.vue.4a6ba053.js} +2 -2
- abstra_statics/dist/assets/{PhChats.vue.d61c3615.js → PhChats.vue.870bbba9.js} +2 -2
- abstra_statics/dist/assets/{PhClockCounterClockwise.vue.0457e9b2.js → PhClockCounterClockwise.vue.123f0e9b.js} +2 -2
- abstra_statics/dist/assets/{PhCopy.vue.391b0ef7.js → PhCopy.vue.70df4792.js} +2 -2
- abstra_statics/dist/assets/{PhCopySimple.vue.e887b43c.js → PhCopySimple.vue.f5246c24.js} +2 -2
- abstra_statics/dist/assets/{PhCube.vue.d070a184.js → PhCube.vue.8900248a.js} +2 -2
- abstra_statics/dist/assets/{PhDotsThreeVertical.vue.f4b60771.js → PhDotsThreeVertical.vue.14516e2e.js} +2 -2
- abstra_statics/dist/assets/{PhDownloadSimple.vue.3444d06b.js → PhDownloadSimple.vue.f8dc6a01.js} +2 -2
- abstra_statics/dist/assets/{PhFolderPlus.vue.d5788203.js → PhFolderPlus.vue.65855487.js} +2 -2
- abstra_statics/dist/assets/{PhGear.vue.e2b120bb.js → PhGear.vue.172823d3.js} +2 -2
- abstra_statics/dist/assets/{PhKey.vue.cf1e08ca.js → PhKey.vue.9d78ceda.js} +2 -2
- abstra_statics/dist/assets/{PhPencil.vue.20f1b3c4.js → PhPencil.vue.f326c6d0.js} +2 -2
- abstra_statics/dist/assets/{PhPencilSimple.vue.ec2125f5.js → PhPencilSimple.vue.31afd9b3.js} +2 -2
- abstra_statics/dist/assets/{PhPencilSimpleLine.vue.22e75a5a.js → PhPencilSimpleLine.vue.394969fe.js} +2 -2
- abstra_statics/dist/assets/{PhRocket.vue.27c6f935.js → PhRocket.vue.b69be43b.js} +2 -2
- abstra_statics/dist/assets/{PhSignOut.vue.61b63ec0.js → PhSignOut.vue.6be07f90.js} +2 -2
- abstra_statics/dist/assets/{PhSparkle.vue.fd6a9ad7.js → PhSparkle.vue.ca8d1014.js} +2 -2
- abstra_statics/dist/assets/{PhUserList.vue.abdd6da1.js → PhUserList.vue.43e7a38a.js} +2 -2
- abstra_statics/dist/assets/{PhUsersThree.vue.85d1a1f0.js → PhUsersThree.vue.fb26521a.js} +2 -2
- abstra_statics/dist/assets/{PhWebhooksLogo.vue.00b65b2c.js → PhWebhooksLogo.vue.ea667ed2.js} +2 -2
- abstra_statics/dist/assets/{PlayerConfigProvider.10f46997.js → PlayerConfigProvider.5aec8c16.js} +2 -2
- abstra_statics/dist/assets/{PlayerNavbar.f2f66852.js → PlayerNavbar.21883569.js} +2 -2
- abstra_statics/dist/assets/Project.a987ec46.js +2 -0
- abstra_statics/dist/assets/ProjectLogin.583ce83f.js +2 -0
- abstra_statics/dist/assets/{ProjectSettings.50027450.js → ProjectSettings.b949f40f.js} +2 -2
- abstra_statics/dist/assets/{ProjectsView.107f5e34.js → ProjectsView.7de28b8a.js} +2 -2
- abstra_statics/dist/assets/{SaveButton.cd025dae.js → SaveButton.f6870993.js} +2 -2
- abstra_statics/dist/assets/{files.f66880c3.js → ScrollArea.vue_vue_type_script_setup_true_lang.21ea9f38.js} +2 -2
- abstra_statics/dist/assets/{Sidebar.c3d5d187.js → Sidebar.1250b960.js} +2 -2
- abstra_statics/dist/assets/Sql.3cdc910a.css +1 -0
- abstra_statics/dist/assets/Sql.a8abfb57.js +5 -0
- abstra_statics/dist/assets/Steps.2f177a1f.js +2 -0
- abstra_statics/dist/assets/{TableEditor.7b07ece4.js → TableEditor.25046840.js} +2 -2
- abstra_statics/dist/assets/Tables.f0ea43f5.js +2 -0
- abstra_statics/dist/assets/{TablesDiagram.6736c045.js → TablesDiagram.eb43ee41.js} +3 -3
- abstra_statics/dist/assets/TablesTabs.vue_vue_type_script_setup_true_lang.a35b4471.js +2 -0
- abstra_statics/dist/assets/Tasks.5fde94ea.js +2 -0
- abstra_statics/dist/assets/{UploadOutlined.732440a5.js → UploadOutlined.3828d031.js} +2 -2
- abstra_statics/dist/assets/{View.283e52c1.js → View.f0be1038.js} +2 -2
- abstra_statics/dist/assets/View.vue_vue_type_script_setup_true_lang.bddece9c.js +2 -0
- abstra_statics/dist/assets/{Watermark.6076ef47.js → Watermark.1655dad8.js} +2 -2
- abstra_statics/dist/assets/{WebEditor.6a012d5b.js → WebEditor.f6ba39c6.js} +2 -2
- abstra_statics/dist/assets/WidgetPreview.70e3ecb3.js +2 -0
- abstra_statics/dist/assets/{ant-design.4302db30.js → ant-design.1cc60cde.js} +2 -2
- abstra_statics/dist/assets/{apiKey.1c96dd66.js → apiKey.49b8decf.js} +2 -2
- abstra_statics/dist/assets/asyncComputed.92146382.js +2 -0
- abstra_statics/dist/assets/{build.656c5601.js → build.261de82e.js} +2 -2
- abstra_statics/dist/assets/{colorHelpers.2a607581.js → colorHelpers.59e5ee56.js} +2 -2
- abstra_statics/dist/assets/{console.9b13e1da.js → console.0548c6a0.js} +2 -2
- abstra_statics/dist/assets/constants.f352d89b.js +2 -0
- abstra_statics/dist/assets/contracts.generated.ac0bb8ea.js +2 -0
- abstra_statics/dist/assets/{cssMode.6d17ca95.js → cssMode.c55f29d9.js} +2 -2
- abstra_statics/dist/assets/datetime.780c5d6b.js +2 -0
- abstra_statics/dist/assets/dayjs.b139fc88.js +2 -0
- abstra_statics/dist/assets/editor.f7e5ca32.js +2 -0
- abstra_statics/dist/assets/editor.main.eea24677.js +2 -0
- abstra_statics/dist/assets/fetch.e3c8dd68.js +2 -0
- abstra_statics/dist/assets/{folder.9092348a.js → folder.934b13ce.js} +2 -2
- abstra_statics/dist/assets/{freemarker2.82f2cb8c.js → freemarker2.72aea997.js} +2 -2
- abstra_statics/dist/assets/{handlebars.36ec2a3c.js → handlebars.607ed329.js} +2 -2
- abstra_statics/dist/assets/{html.845da565.js → html.1cebc021.js} +3 -3
- abstra_statics/dist/assets/{htmlMode.980f76b4.js → htmlMode.b3ab207b.js} +2 -2
- abstra_statics/dist/assets/{index.0f357aec.js → index.2efe5ae2.js} +2 -2
- abstra_statics/dist/assets/{index.55d10b71.js → index.424d5056.js} +2 -2
- abstra_statics/dist/assets/{index.1e12c884.js → index.61710ff9.js} +2 -2
- abstra_statics/dist/assets/{index.a2b9d34b.js → index.8ea8b90b.js} +2 -2
- abstra_statics/dist/assets/{index.1551abd6.js → index.a5ddebc4.js} +3 -3
- abstra_statics/dist/assets/{index.5dbe93c3.js → index.cef986ac.js} +3 -3
- abstra_statics/dist/assets/{index.81a2ae08.js → index.d73f307c.js} +2 -2
- abstra_statics/dist/assets/{index.b3b62f71.js → index.ded0f9ab.js} +2 -2
- abstra_statics/dist/assets/{index.4ecba4f7.js → index.e189c6ed.js} +2 -2
- abstra_statics/dist/assets/{javascript.b0154182.js → javascript.595850d9.js} +3 -3
- abstra_statics/dist/assets/{jsonMode.f86e9042.js → jsonMode.e3ed1113.js} +2 -2
- abstra_statics/dist/assets/{jwt-decode.esm.d86c27e0.js → jwt-decode.esm.1b301f74.js} +8 -8
- abstra_statics/dist/assets/linters.0018d44c.js +2 -0
- abstra_statics/dist/assets/{liquid.029287f8.js → liquid.3ef83be8.js} +3 -3
- abstra_statics/dist/assets/member.3c3a769a.js +2 -0
- abstra_statics/dist/assets/{metadata.18d0a278.js → metadata.e6e62729.js} +2 -2
- abstra_statics/dist/assets/omniChatStore.8902e4de.js +8 -0
- abstra_statics/dist/assets/{organization.8b2c1c53.js → organization.c92a9190.js} +2 -2
- abstra_statics/dist/assets/player.82dc9e2f.js +2 -0
- abstra_statics/dist/assets/{plotly.min.10467de2.js → plotly.min.ac699f04.js} +2 -2
- abstra_statics/dist/assets/polling.a3bae209.js +2 -0
- abstra_statics/dist/assets/{project.33809d47.js → project.48a235c1.js} +2 -2
- abstra_statics/dist/assets/{python.ee23fd86.js → python.b69cfb2a.js} +2 -2
- abstra_statics/dist/assets/{razor.4ae6d2a2.js → razor.0f42fef9.js} +2 -2
- abstra_statics/dist/assets/{record.d087b37e.js → record.b96b0275.js} +2 -2
- abstra_statics/dist/assets/{redirect.c06a7828.js → redirect.ebab71ed.js} +2 -2
- abstra_statics/dist/assets/repository.4920fb3b.js +2 -0
- abstra_statics/dist/assets/{repository.6fa74dff.js → repository.c0dc4e22.js} +2 -2
- abstra_statics/dist/assets/router.8882099a.js +2 -0
- abstra_statics/dist/assets/{router.7936fd78.js → router.99a53337.js} +3 -3
- abstra_statics/dist/assets/string.4796d44a.js +2 -0
- abstra_statics/dist/assets/{tables.d580be9d.js → tables.13d62f0c.js} +2 -2
- abstra_statics/dist/assets/tasksController.3255c760.js +4 -0
- abstra_statics/dist/assets/{toggleHighContrast.510bdb1d.js → toggleHighContrast.cb5e834a.js} +7 -7
- abstra_statics/dist/assets/{tsMode.5c0f732d.js → tsMode.95528ab9.js} +2 -2
- abstra_statics/dist/assets/{typescript.0643a053.js → typescript.b90bd43d.js} +3 -3
- abstra_statics/dist/assets/url.84e62ca8.js +2 -0
- abstra_statics/dist/assets/useCodebaseEvents.c47ad36d.js +2 -0
- abstra_statics/dist/assets/userStore.843da693.js +2 -0
- abstra_statics/dist/assets/uuid.58d26ff2.js +2 -0
- abstra_statics/dist/assets/{vue-flow-background.011d27ef.js → vue-flow-background.6b5566a8.js} +2 -2
- abstra_statics/dist/assets/{vue-quill.esm-bundler.487756a5.js → vue-quill.esm-bundler.3c4b0230.js} +2 -2
- abstra_statics/dist/assets/{workspaceStore.87f8dbc6.js → workspaceStore.6a20c00d.js} +2 -2
- abstra_statics/dist/assets/{xml.c3c548ab.js → xml.070d3630.js} +3 -3
- abstra_statics/dist/assets/{yaml.0d909e29.js → yaml.2a862691.js} +3 -3
- abstra_statics/dist/console.html +15 -15
- abstra_statics/dist/editor.html +13 -11
- abstra_statics/dist/player.html +9 -9
- tests/e2e/test_crud_files.py +0 -1
- tests/e2e/test_requirements.py +41 -4
- abstra_statics/dist/assets/AbstraLogo.vue_vue_type_script_setup_true_lang.0c707a8b.js +0 -2
- abstra_statics/dist/assets/App.f0468c7f.js +0 -2
- abstra_statics/dist/assets/App.vue_vue_type_style_index_0_lang.864018b5.js +0 -2
- abstra_statics/dist/assets/BaseLayout.8bd18c5f.js +0 -2
- abstra_statics/dist/assets/CloseCircleOutlined.2815d641.js +0 -2
- abstra_statics/dist/assets/ConnectorsView.4e22242f.js +0 -2
- abstra_statics/dist/assets/ConsoleOmniChat.vue_vue_type_script_setup_true_lang.17b546d4.js +0 -2
- abstra_statics/dist/assets/ContentLayout.c7733a0e.js +0 -2
- abstra_statics/dist/assets/DocsButton.vue_vue_type_script_setup_true_lang.2c5aae83.js +0 -2
- abstra_statics/dist/assets/EnvVars.74e357b2.js +0 -2
- abstra_statics/dist/assets/ExclamationCircleOutlined.9b25ffda.js +0 -2
- abstra_statics/dist/assets/Files.d9621f31.js +0 -2
- abstra_statics/dist/assets/Form.9eebd960.js +0 -2
- abstra_statics/dist/assets/Home.9531e545.js +0 -2
- abstra_statics/dist/assets/Home.b12bb81a.js +0 -2
- abstra_statics/dist/assets/LoadingContainer.c40ae513.js +0 -2
- abstra_statics/dist/assets/LoadingOutlined.b607eff2.js +0 -2
- abstra_statics/dist/assets/Login.5f104bb1.js +0 -2
- abstra_statics/dist/assets/Logo.a34929e1.js +0 -2
- abstra_statics/dist/assets/Main.7030ea1d.js +0 -2
- abstra_statics/dist/assets/Navbar.a1055174.js +0 -2
- abstra_statics/dist/assets/NewEditor.6b2cb8e6.js +0 -8
- abstra_statics/dist/assets/OidcLoginCallback.445dd392.js +0 -2
- abstra_statics/dist/assets/Organizations.1c35b6b8.js +0 -2
- abstra_statics/dist/assets/Project.2fdca57c.js +0 -2
- abstra_statics/dist/assets/ProjectLogin.7660cd84.js +0 -2
- abstra_statics/dist/assets/Sql.1afe0bac.css +0 -1
- abstra_statics/dist/assets/Sql.7d92acbb.js +0 -5
- abstra_statics/dist/assets/Steps.b12e16c6.js +0 -2
- abstra_statics/dist/assets/Tables.aa6b418c.js +0 -2
- abstra_statics/dist/assets/TablesTabs.vue_vue_type_script_setup_true_lang.95ea10aa.js +0 -2
- abstra_statics/dist/assets/Tasks.6660de00.js +0 -2
- abstra_statics/dist/assets/View.vue_vue_type_script_setup_true_lang.483e52f9.js +0 -2
- abstra_statics/dist/assets/WidgetPreview.b01eed73.js +0 -2
- abstra_statics/dist/assets/asyncComputed.59410422.js +0 -2
- abstra_statics/dist/assets/constants.733c6549.js +0 -2
- abstra_statics/dist/assets/datetime.adbf692e.js +0 -2
- abstra_statics/dist/assets/dayjs.9e279491.js +0 -2
- abstra_statics/dist/assets/editor.7e30500a.js +0 -2
- abstra_statics/dist/assets/editor.main.4675b13a.js +0 -2
- abstra_statics/dist/assets/fetch.13b54f0f.js +0 -2
- abstra_statics/dist/assets/linters.9ba6d5f8.js +0 -2
- abstra_statics/dist/assets/member.3aca30ee.js +0 -2
- abstra_statics/dist/assets/omniChatStore.16b8f156.js +0 -10
- abstra_statics/dist/assets/player.7f570660.js +0 -2
- abstra_statics/dist/assets/polling.0b08b681.js +0 -2
- abstra_statics/dist/assets/repository.02efcdbd.js +0 -2
- abstra_statics/dist/assets/router.7071f838.js +0 -2
- abstra_statics/dist/assets/string.360236ba.js +0 -2
- abstra_statics/dist/assets/tasksController.b66c85ee.js +0 -4
- abstra_statics/dist/assets/url.b31d406a.js +0 -2
- abstra_statics/dist/assets/userStore.f2537ff3.js +0 -2
- abstra_statics/dist/assets/uuid.d6d43ef5.js +0 -2
- {abstra-3.23.7.dist-info → abstra-3.23.9.dist-info}/WHEEL +0 -0
- {abstra-3.23.7.dist-info → abstra-3.23.9.dist-info}/entry_points.txt +0 -0
- {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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
213
|
+
# Skip -r, -c, --find-links, etc. (pip options)
|
|
214
|
+
if line.startswith(("-r", "-c", "--", "-f", "-i", "-e")):
|
|
158
215
|
continue
|
|
159
216
|
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
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=[
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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}=={
|
|
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=
|
|
327
|
-
|
|
328
|
-
version
|
|
516
|
+
requirement=create_requirement(
|
|
517
|
+
lib_name,
|
|
518
|
+
version,
|
|
329
519
|
),
|
|
330
520
|
reason_file=python_file,
|
|
331
521
|
reason_line=node.lineno,
|