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