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"/>
-