From 04bfda48d3384bae45a6693a47ad58ff076d27f9 Mon Sep 17 00:00:00 2001 From: "lada.gagina" Date: Wed, 7 Feb 2024 19:00:38 +0100 Subject: [PATCH] [python] requirements.txt: Add a quickfix to install missing packages (PY-65403, PY-70139) GitOrigin-RevId: 2501161e7f217070b17871089a1f11be44247938 --- ... => UnsatisfiedRequirementInspection.html} | 2 +- .../messages/PyBundle.properties | 7 +- .../python/packaging/common/packages.kt | 27 ++-- .../python/packaging/conda/common.kt | 7 +- .../management/PythonPackageManagerExt.kt | 6 +- .../pyproject/PyInstallPackageQuickFix.kt | 49 ------- .../PyInstallProjectAsEditableQuickfix.kt | 53 ------- .../pyproject/PyprojectPackageInspection.kt | 41 ------ .../repository/PyPackageRepository.kt | 7 + .../PyPackagingToolWindowService.kt | 2 +- .../RequirementsCompletionUtil.kt | 2 +- .../UnsatisfiedRequirementInspection.kt | 136 ++++++++++++++++-- python/src/intellij.python.community.impl.xml | 11 +- 13 files changed, 164 insertions(+), 186 deletions(-) rename python/pluginResources/inspectionDescriptions/{RequirementsUnsatisfiedRequirementInspection.html => UnsatisfiedRequirementInspection.html} (55%) delete mode 100644 python/src/com/jetbrains/python/packaging/pyproject/PyInstallPackageQuickFix.kt delete mode 100644 python/src/com/jetbrains/python/packaging/pyproject/PyInstallProjectAsEditableQuickfix.kt delete mode 100644 python/src/com/jetbrains/python/packaging/pyproject/PyprojectPackageInspection.kt diff --git a/python/pluginResources/inspectionDescriptions/RequirementsUnsatisfiedRequirementInspection.html b/python/pluginResources/inspectionDescriptions/UnsatisfiedRequirementInspection.html similarity index 55% rename from python/pluginResources/inspectionDescriptions/RequirementsUnsatisfiedRequirementInspection.html rename to python/pluginResources/inspectionDescriptions/UnsatisfiedRequirementInspection.html index 2fc8a220964a..6d08bcd9c13d 100644 --- a/python/pluginResources/inspectionDescriptions/RequirementsUnsatisfiedRequirementInspection.html +++ b/python/pluginResources/inspectionDescriptions/UnsatisfiedRequirementInspection.html @@ -1,7 +1,7 @@

- Reports packages mentioned in requirements files (for example, requirements.txt) but not installed, + Reports packages mentioned in requirements files (for example, requirements.txt, or dependencies section in pyproject.toml files) but not installed, or imported but not mentioned in requirements files.

\ No newline at end of file diff --git a/python/pluginResources/messages/PyBundle.properties b/python/pluginResources/messages/PyBundle.properties index 5912da4ca38f..900cb8b0288a 100644 --- a/python/pluginResources/messages/PyBundle.properties +++ b/python/pluginResources/messages/PyBundle.properties @@ -1487,8 +1487,9 @@ inlay.parameters.python.hints.blacklist.explanation=\ Qualified method names must include class names, or placeholders for them.
\ Use the "Do not show hints for current method" {0} action to add patterns from the editor.

-# Requirements todo lada +# Requirements INSP.GROUP.requirements=Requirements INSP.requirement.uninstalled.name=Requirement is not satisfied -INSP.requirements.package.requirements.not.satisfied=Package requirement {0} is not satisfied -QFIX.NAME.install.requirement=Install requirement +INSP.requirements.package.requirements.not.satisfied=Package {0} is not installed +QFIX.NAME.install.requirement=Install package {0} +QFIX.NAME.install.all.requirements=Install all missing packages diff --git a/python/src/com/jetbrains/python/packaging/common/packages.kt b/python/src/com/jetbrains/python/packaging/common/packages.kt index 1e707e379706..89d06acb6f07 100644 --- a/python/src/com/jetbrains/python/packaging/common/packages.kt +++ b/python/src/com/jetbrains/python/packaging/common/packages.kt @@ -52,16 +52,22 @@ class EmptyPythonPackageDetails(override val name: String, @Nls override val des override fun toPackageSpecification(version: String?) = error("Using EmptyPythonPackageDetails for specification") } +open class PythonPackageSpecificationBase(override val name: String, + val version: String?, + val relation: PyRequirementRelation? = null, + override val repository: PyPackageRepository?) : PythonPackageSpecification { + override val versionSpecs: String? + get() = if (version != null) "${relation?.presentableText ?: "=="}$version" else "" +} + interface PythonPackageSpecification { // todo[akniazev]: add version specs and use them in buildInstallationString val name: String - val version: String? val repository: PyPackageRepository? - val relation: PyRequirementRelation? + val versionSpecs: String? - fun buildInstallationString(): List = buildList { - val versionString = if (version != null) "${relation?.presentableText ?: "=="}$version" else "" - add("$name$versionString") + fun buildInstallationString(): List = buildList { + add("$name$versionSpecs") if (repository == PyEmptyPackagePackageRepository) { thisLogger().warn("PyEmptyPackagePackageRepository used as source repository for package installation!") return@buildList @@ -77,19 +83,20 @@ interface PythonLocationBasedPackageSpecification : PythonPackageSpecification { val location: String val editable: Boolean val prefix: String - override val version: String? - get() = null override val repository: PyPackageRepository? get() = null - override val relation: PyRequirementRelation? + override val versionSpecs: String? get() = null override fun buildInstallationString(): List = if (editable) listOf("-e", "$prefix$location") else listOf("$prefix$location") } data class PythonSimplePackageSpecification(override val name: String, - override val version: String?, + val version: String?, override val repository: PyPackageRepository?, - override val relation: PyRequirementRelation? = null) : PythonPackageSpecification + val relation: PyRequirementRelation? = null) : PythonPackageSpecification { + override var versionSpecs: String? = null + get() = if (field == null && version != null) "${relation?.presentableText ?: "=="}$version" else "" +} data class PythonLocalPackageSpecification(override val name: String, override val location: String, diff --git a/python/src/com/jetbrains/python/packaging/conda/common.kt b/python/src/com/jetbrains/python/packaging/conda/common.kt index f4de82c4cd00..469536c218de 100644 --- a/python/src/com/jetbrains/python/packaging/conda/common.kt +++ b/python/src/com/jetbrains/python/packaging/conda/common.kt @@ -4,6 +4,7 @@ package com.jetbrains.python.packaging.conda import com.jetbrains.python.packaging.common.PythonPackage import com.jetbrains.python.packaging.common.PythonPackageDetails import com.jetbrains.python.packaging.common.PythonPackageSpecification +import com.jetbrains.python.packaging.common.PythonPackageSpecificationBase import com.jetbrains.python.packaging.repository.PyPackageRepository import com.jetbrains.python.packaging.requirement.PyRequirementRelation @@ -13,9 +14,9 @@ class CondaPackage(name: String, version: String, val installedWithPip: Boolean } } -class CondaPackageSpecification(override val name: String, - override val version: String?, - override val relation: PyRequirementRelation? = null) : PythonPackageSpecification { +class CondaPackageSpecification(name: String, + version: String?, + relation: PyRequirementRelation? = null) : PythonPackageSpecificationBase(name, version, relation, CondaPackageRepository) { override val repository: PyPackageRepository = CondaPackageRepository override fun buildInstallationString(): List { diff --git a/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerExt.kt b/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerExt.kt index 81ee95617f70..9c5e8f3c742a 100644 --- a/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerExt.kt +++ b/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerExt.kt @@ -23,7 +23,6 @@ import com.jetbrains.python.PythonHelper import com.jetbrains.python.packaging.PyExecutionException import com.jetbrains.python.packaging.common.PythonPackageSpecification import com.jetbrains.python.packaging.repository.PyPackageRepository -import com.jetbrains.python.packaging.requirement.PyRequirementRelation import com.jetbrains.python.run.PythonInterpreterTargetEnvironmentFactory import com.jetbrains.python.run.buildTargetedCommandLine import com.jetbrains.python.run.ensureProjectSdkAndModuleDirsAreOnTarget @@ -141,8 +140,7 @@ fun PythonPackageManager.isInstalled(name: String): Boolean { } fun PythonRepositoryManager.createSpecification(name: String, - version: String? = null, - relation: PyRequirementRelation? = null): PythonPackageSpecification? { + versionSpec: String? = null): PythonPackageSpecification? { val repository = packagesByRepository().firstOrNull { it.second.any { pkg -> pkg.lowercase() == name.lowercase() } }?.first - return repository?.createPackageSpecification(name, version, relation) + return repository?.createPackageSpecification(name, versionSpec) } \ No newline at end of file diff --git a/python/src/com/jetbrains/python/packaging/pyproject/PyInstallPackageQuickFix.kt b/python/src/com/jetbrains/python/packaging/pyproject/PyInstallPackageQuickFix.kt deleted file mode 100644 index 275a3d15310e..000000000000 --- a/python/src/com/jetbrains/python/packaging/pyproject/PyInstallPackageQuickFix.kt +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.jetbrains.python.packaging.pyproject - -import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer -import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo -import com.intellij.codeInspection.LocalQuickFix -import com.intellij.codeInspection.ProblemDescriptor -import com.intellij.openapi.components.service -import com.intellij.openapi.module.ModuleUtilCore -import com.intellij.openapi.project.Project -import com.jetbrains.python.PyBundle -import com.jetbrains.python.packaging.PyRequirementParser -import com.jetbrains.python.packaging.management.PythonPackageManager -import com.jetbrains.python.packaging.management.createSpecification -import com.jetbrains.python.packaging.toolwindow.PyPackagingToolWindowService -import com.jetbrains.python.sdk.pythonSdk -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch - -class PyInstallPackageQuickFix(val packageName: String) : LocalQuickFix { - - - override fun getFamilyName(): String { - return PyBundle.message("python.pyproject.install.package", packageName) - } - - override fun applyFix(project: Project, descriptor: ProblemDescriptor) { - val element = descriptor.psiElement - val file = descriptor.psiElement.containingFile ?: return - val sdk = ModuleUtilCore.findModuleForPsiElement(element)?.pythonSdk ?: return - val manager = PythonPackageManager.forSdk(project, sdk) - val requirement = PyRequirementParser.fromLine(element.text.removeSurrounding("\"")) ?: return - - project.service().serviceScope.launch(Dispatchers.IO) { - val versionSpec = requirement.versionSpecs.firstOrNull() - val specification = manager.repositoryManager.createSpecification(requirement.name, versionSpec?.version, versionSpec?.relation) ?: return@launch - manager.installPackage(specification) - DaemonCodeAnalyzer.getInstance(project).restart(file) - } - } - - override fun generatePreview(project: Project, previewDescriptor: ProblemDescriptor): IntentionPreviewInfo { - return IntentionPreviewInfo.EMPTY - } - - override fun startInWriteAction(): Boolean { - return false - } -} \ No newline at end of file diff --git a/python/src/com/jetbrains/python/packaging/pyproject/PyInstallProjectAsEditableQuickfix.kt b/python/src/com/jetbrains/python/packaging/pyproject/PyInstallProjectAsEditableQuickfix.kt deleted file mode 100644 index 4188d7b00d52..000000000000 --- a/python/src/com/jetbrains/python/packaging/pyproject/PyInstallProjectAsEditableQuickfix.kt +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.jetbrains.python.packaging.pyproject - -import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer -import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo -import com.intellij.codeInspection.LocalQuickFix -import com.intellij.codeInspection.ProblemDescriptor -import com.intellij.openapi.components.service -import com.intellij.openapi.fileEditor.FileDocumentManager -import com.intellij.openapi.module.ModuleUtilCore -import com.intellij.openapi.project.Project -import com.intellij.openapi.vfs.findDocument -import com.jetbrains.python.PyBundle -import com.jetbrains.python.packaging.common.runPackagingOperationOrShowErrorDialog -import com.jetbrains.python.packaging.management.PythonPackageManager -import com.jetbrains.python.packaging.management.runPackagingTool -import com.jetbrains.python.packaging.toolwindow.PyPackagingToolWindowService -import com.jetbrains.python.sdk.pythonSdk -import kotlinx.coroutines.launch - -class PyInstallProjectAsEditableQuickfix : LocalQuickFix { - - override fun getFamilyName(): String { - return PyBundle.message("python.pyproject.install.self.as.editable") - } - - @Suppress("DialogTitleCapitalization") - override fun applyFix(project: Project, descriptor: ProblemDescriptor) { - val element = descriptor.psiElement - val file = descriptor.psiElement.containingFile ?: return - val sdk = ModuleUtilCore.findModuleForPsiElement(element)?.pythonSdk ?: return - val manager = PythonPackageManager.forSdk(project, sdk) - FileDocumentManager.getInstance().saveDocument(file.virtualFile.findDocument() ?: return) - - project.service().serviceScope.launch { - runPackagingOperationOrShowErrorDialog(sdk, PyBundle.message("python.pyproject.install.self.error"), null) { - manager.runPackagingTool("install", listOf("-e", "."), PyBundle.message("python.pyproject.install.self.as.editable.progress")) - manager.refreshPaths() - manager.reloadPackages() - } - DaemonCodeAnalyzer.getInstance(project).restart(file) - } - - } - - override fun startInWriteAction(): Boolean { - return false - } - - override fun generatePreview(project: Project, previewDescriptor: ProblemDescriptor): IntentionPreviewInfo { - return IntentionPreviewInfo.EMPTY - } -} \ No newline at end of file diff --git a/python/src/com/jetbrains/python/packaging/pyproject/PyprojectPackageInspection.kt b/python/src/com/jetbrains/python/packaging/pyproject/PyprojectPackageInspection.kt deleted file mode 100644 index 944e60d021ce..000000000000 --- a/python/src/com/jetbrains/python/packaging/pyproject/PyprojectPackageInspection.kt +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package com.jetbrains.python.packaging.pyproject - -import com.intellij.codeInspection.LocalInspectionTool -import com.intellij.codeInspection.ProblemsHolder -import com.intellij.openapi.module.ModuleUtilCore -import com.intellij.psi.PsiElementVisitor -import com.intellij.psi.util.elementType -import com.intellij.psi.util.parentOfType -import com.jetbrains.python.PyBundle -import com.jetbrains.python.packaging.PyRequirementParser -import com.jetbrains.python.packaging.management.PythonPackageManager -import com.jetbrains.python.packaging.management.isInstalled -import com.jetbrains.python.sdk.pythonSdk -import org.toml.lang.psi.* -import org.toml.lang.psi.ext.name - -class PyprojectPackageInspection : LocalInspectionTool() { - - override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { - return object : TomlVisitor() { - override fun visitLiteral(element: TomlLiteral) { - if (element.parent is TomlArray && TOML_STRING_LITERALS.contains(element.firstChild.elementType)) { - if (element.parentOfType()?.key?.name == "dependencies") { - val elementText = element.text.removeSurrounding("'").removeSurrounding("\"") - val requirement = PyRequirementParser.fromLine(elementText) ?: return - - val module = ModuleUtilCore.findModuleForPsiElement(element.originalElement) ?: return - val packageManager = PythonPackageManager.forSdk(element.project, module.pythonSdk ?: return) - - if (!packageManager.isInstalled(requirement.name)) { - holder.registerProblem(element, PyBundle.message("python.pyproject.package.not.installed", requirement.name), PyInstallPackageQuickFix(requirement.name), PyInstallProjectAsEditableQuickfix()) - } - } - } - } - } - } - - -} \ No newline at end of file diff --git a/python/src/com/jetbrains/python/packaging/repository/PyPackageRepository.kt b/python/src/com/jetbrains/python/packaging/repository/PyPackageRepository.kt index 3afaf7d2c537..51a256fee9b3 100644 --- a/python/src/com/jetbrains/python/packaging/repository/PyPackageRepository.kt +++ b/python/src/com/jetbrains/python/packaging/repository/PyPackageRepository.kt @@ -64,4 +64,11 @@ open class PyPackageRepository() : BaseState() { relation: PyRequirementRelation? = null): PythonPackageSpecification { return PythonSimplePackageSpecification(packageName, version, this, relation) } + + open fun createPackageSpecification(packageName: String, + versionSpecs: String? = null): PythonPackageSpecification { + val spec = PythonSimplePackageSpecification(packageName, null, this) + spec.versionSpecs = versionSpecs + return spec + } } \ No newline at end of file diff --git a/python/src/com/jetbrains/python/packaging/toolwindow/PyPackagingToolWindowService.kt b/python/src/com/jetbrains/python/packaging/toolwindow/PyPackagingToolWindowService.kt index b0ba09c7df6e..f8f4ad8fb95d 100644 --- a/python/src/com/jetbrains/python/packaging/toolwindow/PyPackagingToolWindowService.kt +++ b/python/src/com/jetbrains/python/packaging/toolwindow/PyPackagingToolWindowService.kt @@ -120,7 +120,7 @@ class PyPackagingToolWindowService(val project: Project, val serviceScope: Corou suspend fun updatePackage(specification: PythonPackageSpecification) { val result = manager.updatePackage(specification) - if (result.isSuccess) showPackagingNotification(message("python.packaging.notification.updated", specification.name, specification.version)) + if (result.isSuccess) showPackagingNotification(message("python.packaging.notification.updated", specification.name, specification.versionSpecs)) } internal suspend fun initForSdk(sdk: Sdk?) { diff --git a/python/src/com/jetbrains/python/requirements/RequirementsCompletionUtil.kt b/python/src/com/jetbrains/python/requirements/RequirementsCompletionUtil.kt index 25a2e5a23a2d..76d3dd08288b 100644 --- a/python/src/com/jetbrains/python/requirements/RequirementsCompletionUtil.kt +++ b/python/src/com/jetbrains/python/requirements/RequirementsCompletionUtil.kt @@ -27,7 +27,7 @@ fun completePackageNames(project: Project, result: CompletionResultSet) { fun completeVersions(name: String, project: Project, result: CompletionResultSet, addQuotes: Boolean) { val repositoryManager = PythonPackageManager.forSdk(project, project.pythonSdk ?: return).repositoryManager - val packageSpecification = repositoryManager.createSpecification(name, null, null) ?: return + val packageSpecification = repositoryManager.createSpecification(name, null) ?: return val versions = ApplicationUtil.runWithCheckCanceled({ runBlockingCancellable { repositoryManager.getPackageDetails(packageSpecification).availableVersions diff --git a/python/src/com/jetbrains/python/requirements/UnsatisfiedRequirementInspection.kt b/python/src/com/jetbrains/python/requirements/UnsatisfiedRequirementInspection.kt index b3fb58114b83..d8dee874fc45 100644 --- a/python/src/com/jetbrains/python/requirements/UnsatisfiedRequirementInspection.kt +++ b/python/src/com/jetbrains/python/requirements/UnsatisfiedRequirementInspection.kt @@ -1,18 +1,31 @@ // Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.jetbrains.python.requirements -import com.intellij.codeInspection.LocalInspectionTool -import com.intellij.codeInspection.LocalInspectionToolSession -import com.intellij.codeInspection.ProblemHighlightType -import com.intellij.codeInspection.ProblemsHolder +import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer +import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo +import com.intellij.codeInspection.* +import com.intellij.openapi.components.service +import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.module.ModuleUtilCore +import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.openapi.vfs.findDocument import com.intellij.psi.PsiElementVisitor +import com.intellij.psi.SmartPointerManager +import com.intellij.psi.SmartPsiElementPointer import com.jetbrains.python.PyBundle import com.jetbrains.python.packaging.PyPackageRequirementsSettings +import com.jetbrains.python.packaging.common.runPackagingOperationOrShowErrorDialog import com.jetbrains.python.packaging.management.PythonPackageManager +import com.jetbrains.python.packaging.management.createSpecification +import com.jetbrains.python.packaging.management.runPackagingTool +import com.jetbrains.python.packaging.toolwindow.PyPackagingToolWindowService +import com.jetbrains.python.requirements.psi.NameReq +import com.jetbrains.python.requirements.psi.Requirement import com.jetbrains.python.sdk.pythonSdk +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch class UnsatisfiedRequirementInspection : LocalInspectionTool() { @@ -44,13 +57,116 @@ private class RequirementsUnresolvedRequirementInspectionVisitor(holder: Problem val sdk = project.pythonSdk ?: return val packageManager = PythonPackageManager.forSdk(project, sdk) val packages = packageManager.installedPackages.map { it.name } - element.requirements().forEach { requirement -> - if (requirement.displayName !in packages) { - holder.registerProblem(requirement, - PyBundle.message("INSP.requirements.package.requirements.not.satisfied", requirement.displayName), - ProblemHighlightType.WARNING) - } + val unsatisfiedRequirements = element.requirements().filter { requirement -> requirement.displayName !in packages } + unsatisfiedRequirements.forEach { requirement -> + holder.registerProblem(requirement, + PyBundle.message("INSP.requirements.package.requirements.not.satisfied", requirement.displayName), + ProblemHighlightType.WARNING, + InstallRequirementQuickFix(requirement), + InstallAllRequirementsQuickFix(unsatisfiedRequirements), + InstallProjectAsEditableQuickfix()) } } } } + +class InstallAllRequirementsQuickFix(requirements: List) : LocalQuickFix { + val requirements: List> = requirements.map { SmartPointerManager.createPointer(it) }.toList() + + override fun getFamilyName(): String { + return PyBundle.message("QFIX.NAME.install.all.requirements") + } + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val element = descriptor.psiElement + val file = descriptor.psiElement.containingFile ?: return + val sdk = ModuleUtilCore.findModuleForPsiElement(element)?.pythonSdk ?: return + val manager = PythonPackageManager.forSdk(project, sdk) + + requirements.forEach { + val req = it.element ?: return@forEach + val versionSpec = if (req is NameReq) req.versionspec?.text else "" + val name = req.displayName + project.service().serviceScope.launch(Dispatchers.IO) { + val specification = manager.repositoryManager.createSpecification(name, versionSpec) + ?: return@launch + manager.installPackage(specification) + DaemonCodeAnalyzer.getInstance(project).restart(file) + } + } + } + + override fun generatePreview(project: Project, previewDescriptor: ProblemDescriptor): IntentionPreviewInfo { + return IntentionPreviewInfo.EMPTY + } + + override fun startInWriteAction(): Boolean { + return false + } +} + +class InstallRequirementQuickFix(requirement: Requirement) : LocalQuickFix { + + val requirement: SmartPsiElementPointer = SmartPointerManager.createPointer(requirement) + + override fun getFamilyName(): String { + return PyBundle.message("QFIX.NAME.install.requirement", requirement.element?.displayName ?: "") + } + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val element = descriptor.psiElement + val file = descriptor.psiElement.containingFile ?: return + val sdk = ModuleUtilCore.findModuleForPsiElement(element)?.pythonSdk ?: return + val manager = PythonPackageManager.forSdk(project, sdk) + val req = requirement.element ?: return + + val versionSpec = if (req is NameReq) req.versionspec?.text else "" + val name = req.displayName + project.service().serviceScope.launch(Dispatchers.IO) { + manager.installPackage(manager.repositoryManager.createSpecification(name, versionSpec) ?: return@launch) + DaemonCodeAnalyzer.getInstance(project).restart(file) + } + } + + override fun generatePreview(project: Project, previewDescriptor: ProblemDescriptor): IntentionPreviewInfo { + return IntentionPreviewInfo.EMPTY + } + + override fun startInWriteAction(): Boolean { + return false + } +} + +class InstallProjectAsEditableQuickfix : LocalQuickFix { + + override fun getFamilyName(): String { + return PyBundle.message("python.pyproject.install.self.as.editable") + } + + @Suppress("DialogTitleCapitalization") + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val element = descriptor.psiElement + val file = descriptor.psiElement.containingFile ?: return + val sdk = ModuleUtilCore.findModuleForPsiElement(element)?.pythonSdk ?: return + val manager = PythonPackageManager.forSdk(project, sdk) + FileDocumentManager.getInstance().saveDocument(file.virtualFile.findDocument() ?: return) + + project.service().serviceScope.launch { + runPackagingOperationOrShowErrorDialog(sdk, PyBundle.message("python.pyproject.install.self.error"), null) { + manager.runPackagingTool("install", listOf("-e", "."), PyBundle.message("python.pyproject.install.self.as.editable.progress")) + manager.refreshPaths() + manager.reloadPackages() + } + DaemonCodeAnalyzer.getInstance(project).restart(file) + } + + } + + override fun startInWriteAction(): Boolean { + return false + } + + override fun generatePreview(project: Project, previewDescriptor: ProblemDescriptor): IntentionPreviewInfo { + return IntentionPreviewInfo.EMPTY + } +} \ No newline at end of file diff --git a/python/src/intellij.python.community.impl.xml b/python/src/intellij.python.community.impl.xml index 0224be3ca164..87fe7c8c7867 100644 --- a/python/src/intellij.python.community.impl.xml +++ b/python/src/intellij.python.community.impl.xml @@ -45,7 +45,7 @@ - @@ -520,15 +520,6 @@ key="python.sdk.installation.notification.group"/> -