diff --git a/python/pluginResources/intellij.python.community.impl.xml b/python/pluginResources/intellij.python.community.impl.xml index 30058f506bfc..ce2a4462fc37 100644 --- a/python/pluginResources/intellij.python.community.impl.xml +++ b/python/pluginResources/intellij.python.community.impl.xml @@ -1090,6 +1090,15 @@ + + + + + diff --git a/python/pluginResources/messages/PyBundle.properties b/python/pluginResources/messages/PyBundle.properties index 1e187a6feee4..0d9278961499 100644 --- a/python/pluginResources/messages/PyBundle.properties +++ b/python/pluginResources/messages/PyBundle.properties @@ -1604,6 +1604,9 @@ action.CondaUpdateEnvAction.text=Conda Update from "environment.yml" python.sdk.intention.family.name.sync.project=Sync project python.sdk.sync.project.text=Syncing project +action.PipSetDefaultRequirementsAction.text=Set This Requirements File as Default for SDK +action.PipUpdateEnvAction.text=Install Packages from This File + python.survey.user.job.notification.group=PyCharm Job Survey python.survey.user.job.notification.title=Feedback In IDE python.survey.user.job.notification.content=Tell us about your Python development experience. Just one question. diff --git a/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerAction.kt b/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerAction.kt index dff9e54569c1..8799e290dbae 100644 --- a/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerAction.kt +++ b/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerAction.kt @@ -12,6 +12,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.module.ModuleUtil import com.intellij.openapi.project.DumbAwareAction import com.intellij.openapi.util.NlsContexts.ProgressTitle +import com.intellij.openapi.vfs.VirtualFile import com.intellij.python.pyproject.PY_PROJECT_TOML import com.jetbrains.python.errorProcessing.ErrorSink import com.jetbrains.python.errorProcessing.PyResult @@ -56,7 +57,7 @@ abstract class PythonPackageManagerAction : DumbAwa override fun update(e: AnActionEvent) { val virtualFile = e.getData(PlatformDataKeys.VIRTUAL_FILE) - val isWatchedFile = virtualFile?.name?.let { fileNamesPattern.matches(it) } ?: false + val isWatchedFile = isWatchedFile(virtualFile) val manager = if (isWatchedFile) getManager(e) else null with(e.presentation) { @@ -65,6 +66,8 @@ abstract class PythonPackageManagerAction : DumbAwa } } + protected open fun isWatchedFile(virtualFile: VirtualFile?): Boolean = virtualFile?.name?.let { fileNamesPattern.matches(it) } ?: false + /** * This action saves the current document on fs because tools are command line tools, and they need actual files to be up to date * Handles errors via [errorSink] diff --git a/python/src/com/jetbrains/python/packaging/pip/PipPythonPackageManager.kt b/python/src/com/jetbrains/python/packaging/pip/PipPythonPackageManager.kt index cc012fd508b4..7f8339b88b87 100644 --- a/python/src/com/jetbrains/python/packaging/pip/PipPythonPackageManager.kt +++ b/python/src/com/jetbrains/python/packaging/pip/PipPythonPackageManager.kt @@ -7,6 +7,7 @@ import com.intellij.execution.process.CapturingProcessHandler import com.intellij.execution.process.ProcessOutput import com.intellij.openapi.project.Project import com.intellij.openapi.projectRoots.Sdk +import com.intellij.openapi.vfs.VirtualFile import com.intellij.python.community.helpersLocator.PythonHelpersLocator.Companion.findPathInHelpers import com.jetbrains.python.errorProcessing.PyResult import com.jetbrains.python.packaging.PyPackageUtil @@ -62,6 +63,13 @@ open class PipPythonPackageManager(project: Project, sdk: Sdk) : PythonPackageMa } } + suspend fun syncRequirementsTxt(requirementsFile: VirtualFile): PyResult { + engine.syncRequirementsTxt(requirementsFile).getOr { + return it + } + return reloadPackages().mapSuccess { } + } + override suspend fun updatePackageCommand( vararg specifications: PythonRepositoryPackageSpecification, ): PyResult = engine.updatePackageCommand(*specifications) diff --git a/python/src/com/jetbrains/python/packaging/pip/actions/PipPackageManagerAction.kt b/python/src/com/jetbrains/python/packaging/pip/actions/PipPackageManagerAction.kt new file mode 100644 index 000000000000..2e75d4a3ea4c --- /dev/null +++ b/python/src/com/jetbrains/python/packaging/pip/actions/PipPackageManagerAction.kt @@ -0,0 +1,58 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.jetbrains.python.packaging.pip.actions + +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.PlatformDataKeys +import com.intellij.openapi.vfs.VirtualFile +import com.jetbrains.python.errorProcessing.PyResult +import com.jetbrains.python.packaging.management.PythonPackageManagerAction +import com.jetbrains.python.packaging.management.getPythonPackageManager +import com.jetbrains.python.packaging.pip.PipPythonPackageManager +import com.jetbrains.python.packaging.requirementsTxt.PythonRequirementTxtSdkUtils +import com.jetbrains.python.packaging.requirementsTxt.PythonRequirementsTxtManager +import com.jetbrains.python.requirements.RequirementsFileType + +internal sealed class PipPackageManagerAction : PythonPackageManagerAction() { + override fun isWatchedFile(virtualFile: VirtualFile?): Boolean { + return virtualFile?.fileType is RequirementsFileType + } + + override fun getManager(e: AnActionEvent): PipPythonPackageManager? = e.getPythonPackageManager() +} + + +internal class PipSetDefaultRequirementsAction() : PipPackageManagerAction() { + override suspend fun execute(e: AnActionEvent, manager: PipPythonPackageManager): PyResult { + val envFile = e.getData(PlatformDataKeys.VIRTUAL_FILE) ?: return PyResult.success(Unit) + val project = e.project ?: return PyResult.success(Unit) + + val newFilePath = envFile.toNioPath() + val requirementsTxtManager = PythonRequirementsTxtManager.getInstance(project, manager.sdk) + requirementsTxtManager.getDependenciesFile() + + PythonRequirementTxtSdkUtils.saveRequirementsTxtPath(project, manager.sdk, newFilePath) + return PyResult.success(Unit) + } + + override fun update(e: AnActionEvent) { + super.update(e) + if (!e.presentation.isEnabledAndVisible) + return + val manager = e.getPythonPackageManager() ?: return + val savedFile = PythonRequirementTxtSdkUtils.findRequirementsTxt(manager.sdk) + val currentFile = e.getData(PlatformDataKeys.VIRTUAL_FILE) ?: return + + if (savedFile == currentFile) { + e.presentation.isEnabledAndVisible = false + } + + } +} + + +internal class PipUpdateEnvAction() : PipPackageManagerAction() { + override suspend fun execute(e: AnActionEvent, manager: PipPythonPackageManager): PyResult { + val currentFile = e.getData(PlatformDataKeys.VIRTUAL_FILE) ?: return PyResult.success(Unit) + return manager.syncRequirementsTxt(currentFile) + } +} \ No newline at end of file