diff --git a/python/pluginCore/resources/META-INF/plugin.xml b/python/pluginCore/resources/META-INF/plugin.xml
index a9932707b227..7f84a1664962 100644
--- a/python/pluginCore/resources/META-INF/plugin.xml
+++ b/python/pluginCore/resources/META-INF/plugin.xml
@@ -414,8 +414,8 @@ The Python plug-in provides smart editing for Python scripts. The feature set of
-
+
diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java
index 9b33df4214e1..6ca12e94b658 100644
--- a/python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java
+++ b/python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java
@@ -9,7 +9,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.Disposer;
import com.intellij.util.ArrayUtil;
-import com.jetbrains.python.packaging.common.PackageManagerHolder;
+import com.jetbrains.python.packaging.management.PythonPackageManagerService;
import com.jetbrains.python.packaging.ui.PyPackageManagementService;
import com.jetbrains.python.sdk.PythonSdkType;
import com.jetbrains.python.sdk.PythonSdkUtil;
@@ -89,7 +89,7 @@ public class PyPackageManagersImpl extends PyPackageManagers {
LOG.assertTrue(!Disposer.isDisposed((Disposable)sdk),
"Requesting a package service for an already disposed SDK " + sdk + " (" + sdk.getClass() + ")");
}
- return project.getService(PackageManagerHolder.class).bridgeForSdk(project, sdk);
+ return project.getService(PythonPackageManagerService.class).bridgeForSdk(project, sdk);
}
@Override
diff --git a/python/src/com/jetbrains/python/packaging/common/util.kt b/python/src/com/jetbrains/python/packaging/common/util.kt
index 1c252ee35238..0257bdff76cf 100644
--- a/python/src/com/jetbrains/python/packaging/common/util.kt
+++ b/python/src/com/jetbrains/python/packaging/common/util.kt
@@ -1,79 +1,21 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.packaging.common
-import com.intellij.openapi.Disposable
import com.intellij.openapi.application.writeIntentReadAction
import com.intellij.openapi.components.service
-import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
-import com.intellij.openapi.util.Disposer
import com.intellij.openapi.util.NlsContexts
import com.intellij.webcore.packaging.PackagesNotificationPanel
import com.jetbrains.python.packaging.PyExecutionException
import com.jetbrains.python.packaging.PyPIPackageRanking
import com.jetbrains.python.packaging.PyPackagesNotificationPanel
import com.jetbrains.python.packaging.bridge.PythonPackageManagementServiceBridge
-import com.jetbrains.python.packaging.management.PythonPackageManager
-import com.jetbrains.python.packaging.management.PythonPackageManagerProvider
import com.jetbrains.python.packaging.ui.PyPackageManagementService
-import com.jetbrains.python.sdk.PythonSdkAdditionalData
-import com.jetbrains.python.sdk.getOrCreateAdditionalData
import com.jetbrains.python.statistics.PyPackagesUsageCollector
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jetbrains.annotations.ApiStatus
import java.util.*
-import java.util.concurrent.ConcurrentHashMap
-
-
-interface PackageManagerHolder {
- fun forSdk(project: Project, sdk: Sdk): PythonPackageManager
-
- /**
- * Provides an implementation bridge for Python package management operations
- * specific to the given project and SDK. The bridge serves as a connection point
- * to enable advanced management tasks, potentially extending or adapting functionalities
- * provided by the [PythonPackageManager].
- */
- fun bridgeForSdk(project: Project, sdk: Sdk): PythonPackageManagementServiceBridge
-
- fun getServiceScope(): CoroutineScope
-}
-
-class PackageManagerHolderImpl(private val serviceScope: CoroutineScope) : PackageManagerHolder, Disposable {
- private val cache = ConcurrentHashMap()
-
- private val bridgeCache = ConcurrentHashMap()
-
- /**
- * Requires Sdk to be Python Sdk and have PythonSdkAdditionalData.
- */
- override fun forSdk(project: Project, sdk: Sdk): PythonPackageManager {
- val cacheKey = (sdk.getOrCreateAdditionalData()).uuid
-
- return cache.computeIfAbsent(cacheKey) {
- PythonPackageManagerProvider.EP_NAME.extensionList
- .firstNotNullOf { it.createPackageManagerForSdk(project, sdk) }
- }
- }
-
- override fun bridgeForSdk(project: Project, sdk: Sdk): PythonPackageManagementServiceBridge {
- val cacheKey = (sdk.sdkAdditionalData as PythonSdkAdditionalData).uuid
- return bridgeCache.computeIfAbsent(cacheKey) {
- val bridge = PythonPackageManagementServiceBridge(project, sdk)
- Disposer.register(this@PackageManagerHolderImpl, bridge)
- bridge
- }
- }
-
- override fun getServiceScope(): CoroutineScope = serviceScope
-
- override fun dispose() {
- cache.clear()
- }
-}
-
@ApiStatus.Experimental
interface PythonPackageManagementListener {
diff --git a/python/src/com/jetbrains/python/packaging/conda/CompositePythonPackageManager.kt b/python/src/com/jetbrains/python/packaging/conda/CompositePythonPackageManager.kt
index 10ce9b17ac60..4eda95eb6a0d 100644
--- a/python/src/com/jetbrains/python/packaging/conda/CompositePythonPackageManager.kt
+++ b/python/src/com/jetbrains/python/packaging/conda/CompositePythonPackageManager.kt
@@ -15,12 +15,14 @@ class CompositePythonPackageManager(
) : PythonPackageManager(project, sdk) {
@Volatile
override var installedPackages: List = emptyList()
+
+ // TODO: composite one
override var repositoryManager: PythonRepositoryManager = managers.first().repositoryManager
private fun isInRepository(repositoryManager: PythonRepositoryManager, pkgName: String) =
repositoryManager.allPackages().contains(pkgName)
- override suspend fun installPackageCommand(specification: PythonPackageSpecification, options: List): Result {
+ override suspend fun installPackageCommand(specification: PythonPackageSpecification, options: List): Result {
val exceptionList = mutableListOf()
for (manager in managers) {
repositoryManager = manager.repositoryManager
@@ -28,17 +30,15 @@ class CompositePythonPackageManager(
if (!isInRepository(repositoryManager, specification.name)) continue
val executionResult = manager.installPackage(specification, options)
- val executionOutcome = executionResult.getOrElse { exceptionList.add(it) }
-
- if (executionResult.isSuccess) {
- return Result.success(executionOutcome.toString())
- }
+ executionResult
+ .onSuccess { return Result.success(Unit) }
+ .onFailure { exceptionList.add(it) }
}
- return Result.failure(exceptionList.last())
+
+ return Result.failure(exceptionList.lastOrNull() ?: RuntimeException("No package managers found for package $specification"))
}
-
- override suspend fun updatePackageCommand(specification: PythonPackageSpecification): Result {
+ override suspend fun updatePackageCommand(specification: PythonPackageSpecification): Result {
val exceptionList = mutableListOf()
for (manager in managers) {
@@ -47,33 +47,32 @@ class CompositePythonPackageManager(
if (!isInRepository(repositoryManager, specification.name)) continue
val executionResult = manager.updatePackage(specification)
- val executionOutcome = executionResult.getOrElse { exceptionList.add(it) }
-
- if (executionResult.isSuccess) {
- return Result.success(executionOutcome.toString())
- }
+ executionResult
+ .onSuccess { return Result.success(Unit) }
+ .onFailure { exceptionList.add(it) }
}
- return Result.failure(exceptionList.last())
+ return Result.failure(exceptionList.lastOrNull() ?: RuntimeException("No package managers found for package $specification"))
}
- override suspend fun uninstallPackageCommand(pkg: PythonPackage): Result {
+ override suspend fun uninstallPackageCommand(pkg: PythonPackage): Result {
val exceptionList = mutableListOf()
for (manager in managers) {
repositoryManager = manager.repositoryManager
installedPackages = manager.installedPackages
- if (!isInRepository(repositoryManager, pkg.name)) continue
+ if (!isInRepository(repositoryManager, pkg.name)) {
+ continue
+ }
val executionResult = manager.uninstallPackage(pkg)
- val executionOutcome = executionResult.getOrElse { exceptionList.add(it) }
- if (executionResult.isSuccess) {
- return Result.success(executionOutcome.toString())
- }
+ executionResult
+ .onSuccess { return Result.success(Unit) }
+ .onFailure { exceptionList.add(it) }
}
- return Result.failure(exceptionList.last())
+ return Result.failure(exceptionList.lastOrNull() ?: RuntimeException("No package managers found for package $pkg"))
}
override suspend fun reloadPackagesCommand(): Result> {
@@ -83,15 +82,11 @@ class CompositePythonPackageManager(
repositoryManager = manager.repositoryManager
installedPackages = manager.installedPackages
- val executionResult = manager.reloadPackages()
- val executionOutcome = executionResult.getOrElse {
- exceptionList.add(it)
- emptyList()
- }
- if (executionResult.isSuccess) {
- return Result.success(executionOutcome)
- }
+ manager.reloadPackages()
+ .onSuccess { return Result.success(it) }
+ .onFailure { exceptionList.add(it) }
}
- return Result.failure(exceptionList.last())
+
+ return Result.failure(exceptionList.lastOrNull() ?: RuntimeException("No package managers found"))
}
}
\ No newline at end of file
diff --git a/python/src/com/jetbrains/python/packaging/conda/CondaPackageManager.kt b/python/src/com/jetbrains/python/packaging/conda/CondaPackageManager.kt
index 51855cad962f..acadbb06fe3c 100644
--- a/python/src/com/jetbrains/python/packaging/conda/CondaPackageManager.kt
+++ b/python/src/com/jetbrains/python/packaging/conda/CondaPackageManager.kt
@@ -32,25 +32,28 @@ class CondaPackageManager(project: Project, sdk: Sdk) : PythonPackageManager(pro
override var installedPackages: List = emptyList()
override val repositoryManager = CondaRepositoryManger(project, sdk)
- override suspend fun installPackageCommand(specification: PythonPackageSpecification, options: List): Result =
+ override suspend fun installPackageCommand(specification: PythonPackageSpecification, options: List): Result =
try {
- Result.success(runConda("install", specification.buildInstallationString() + "-y" + options, message("conda.packaging.install.progress", specification.name)))
+ runConda("install", specification.buildInstallationString() + "-y" + options, message("conda.packaging.install.progress", specification.name))
+ Result.success(Unit)
}
catch (ex: ExecutionException) {
Result.failure(ex)
}
- override suspend fun updatePackageCommand(specification: PythonPackageSpecification): Result =
+ override suspend fun updatePackageCommand(specification: PythonPackageSpecification): Result =
try {
- Result.success(runConda("update", listOf(specification.name, "-y"), message("conda.packaging.update.progress", specification.name)))
+ runConda("update", listOf(specification.name, "-y"), message("conda.packaging.update.progress", specification.name))
+ Result.success(Unit)
}
catch (ex: ExecutionException) {
Result.failure(ex)
}
- override suspend fun uninstallPackageCommand(pkg: PythonPackage): Result =
+ override suspend fun uninstallPackageCommand(pkg: PythonPackage): Result =
try {
- Result.success(runConda("uninstall", listOf(pkg.name, "-y"), message("conda.packaging.uninstall.progress", pkg.name)))
+ runConda("uninstall", listOf(pkg.name, "-y"), message("conda.packaging.uninstall.progress", pkg.name))
+ Result.success(Unit)
}
catch (ex: ExecutionException) {
Result.failure(ex)
diff --git a/python/src/com/jetbrains/python/packaging/management/PythonPackageManager.kt b/python/src/com/jetbrains/python/packaging/management/PythonPackageManager.kt
index da4f4e523ef9..db6f4d99f350 100644
--- a/python/src/com/jetbrains/python/packaging/management/PythonPackageManager.kt
+++ b/python/src/com/jetbrains/python/packaging/management/PythonPackageManager.kt
@@ -73,9 +73,9 @@ abstract class PythonPackageManager(val project: Project, val sdk: Sdk) {
return Result.success(packages)
}
- protected abstract suspend fun installPackageCommand(specification: PythonPackageSpecification, options: List): Result
- protected abstract suspend fun updatePackageCommand(specification: PythonPackageSpecification): Result
- protected abstract suspend fun uninstallPackageCommand(pkg: PythonPackage): Result
+ protected abstract suspend fun installPackageCommand(specification: PythonPackageSpecification, options: List): Result
+ protected abstract suspend fun updatePackageCommand(specification: PythonPackageSpecification): Result
+ protected abstract suspend fun uninstallPackageCommand(pkg: PythonPackage): Result
protected abstract suspend fun reloadPackagesCommand(): Result>
internal suspend fun refreshPaths() {
@@ -93,9 +93,9 @@ abstract class PythonPackageManager(val project: Project, val sdk: Sdk) {
companion object {
fun forSdk(project: Project, sdk: Sdk): PythonPackageManager {
- val packageManagerHolder = project.service()
- val manager = packageManagerHolder.forSdk(project, sdk)
- packageManagerHolder.getServiceScope().launch(Dispatchers.IO) {
+ val pythonPackageManagerService = project.service()
+ val manager = pythonPackageManagerService.forSdk(project, sdk)
+ pythonPackageManagerService.getServiceScope().launch(Dispatchers.IO) {
manager.repositoryManager.initCaches()
}
return manager
diff --git a/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerProvider.kt b/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerService.kt
similarity index 55%
rename from python/src/com/jetbrains/python/packaging/management/PythonPackageManagerProvider.kt
rename to python/src/com/jetbrains/python/packaging/management/PythonPackageManagerService.kt
index c292fa62cad1..76eff8ecc1c5 100644
--- a/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerProvider.kt
+++ b/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerService.kt
@@ -4,6 +4,8 @@ package com.jetbrains.python.packaging.management
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
+import com.jetbrains.python.packaging.bridge.PythonPackageManagementServiceBridge
+import kotlinx.coroutines.CoroutineScope
import org.jetbrains.annotations.ApiStatus
@ApiStatus.Experimental
@@ -19,4 +21,19 @@ interface PythonPackageManagerProvider {
companion object {
val EP_NAME = ExtensionPointName.create("Pythonid.pythonPackageManagerProvider")
}
+}
+
+@ApiStatus.Experimental
+interface PythonPackageManagerService {
+ fun forSdk(project: Project, sdk: Sdk): PythonPackageManager
+
+ /**
+ * Provides an implementation bridge for Python package management operations
+ * specific to the given project and SDK. The bridge serves as a connection point
+ * to enable advanced management tasks, potentially extending or adapting functionalities
+ * provided by the [PythonPackageManager].
+ */
+ fun bridgeForSdk(project: Project, sdk: Sdk): PythonPackageManagementServiceBridge
+
+ fun getServiceScope(): CoroutineScope
}
\ No newline at end of file
diff --git a/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerServiceImpl.kt b/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerServiceImpl.kt
new file mode 100644
index 000000000000..343b7c580574
--- /dev/null
+++ b/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerServiceImpl.kt
@@ -0,0 +1,46 @@
+// 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.management
+
+import com.intellij.openapi.Disposable
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.projectRoots.Sdk
+import com.intellij.openapi.util.Disposer
+import com.jetbrains.python.packaging.bridge.PythonPackageManagementServiceBridge
+import com.jetbrains.python.sdk.PythonSdkAdditionalData
+import com.jetbrains.python.sdk.getOrCreateAdditionalData
+import kotlinx.coroutines.CoroutineScope
+import java.util.UUID
+import java.util.concurrent.ConcurrentHashMap
+
+class PythonPackageManagerServiceImpl(private val serviceScope: CoroutineScope) : PythonPackageManagerService, Disposable {
+ private val cache = ConcurrentHashMap()
+
+ private val bridgeCache = ConcurrentHashMap()
+
+ /**
+ * Requires Sdk to be Python Sdk and have PythonSdkAdditionalData.
+ */
+ override fun forSdk(project: Project, sdk: Sdk): PythonPackageManager {
+ val cacheKey = (sdk.getOrCreateAdditionalData()).uuid
+
+ return cache.computeIfAbsent(cacheKey) {
+ PythonPackageManagerProvider.EP_NAME.extensionList
+ .firstNotNullOf { it.createPackageManagerForSdk(project, sdk) }
+ }
+ }
+
+ override fun bridgeForSdk(project: Project, sdk: Sdk): PythonPackageManagementServiceBridge {
+ val cacheKey = (sdk.sdkAdditionalData as PythonSdkAdditionalData).uuid
+ return bridgeCache.computeIfAbsent(cacheKey) {
+ val bridge = PythonPackageManagementServiceBridge(project, sdk)
+ Disposer.register(this@PythonPackageManagerServiceImpl, bridge)
+ bridge
+ }
+ }
+
+ override fun getServiceScope(): CoroutineScope = serviceScope
+
+ override fun dispose() {
+ cache.clear()
+ }
+}
\ No newline at end of file
diff --git a/python/src/com/jetbrains/python/packaging/pip/PipPythonPackageManager.kt b/python/src/com/jetbrains/python/packaging/pip/PipPythonPackageManager.kt
index 7b03b2cb052b..2ba3fb8dfdc0 100644
--- a/python/src/com/jetbrains/python/packaging/pip/PipPythonPackageManager.kt
+++ b/python/src/com/jetbrains/python/packaging/pip/PipPythonPackageManager.kt
@@ -19,31 +19,40 @@ open class PipPythonPackageManager(project: Project, sdk: Sdk) : PythonPackageMa
override var installedPackages: List = emptyList()
override val repositoryManager: PythonRepositoryManager = PipRepositoryManager(project, sdk)
- override suspend fun installPackageCommand(specification: PythonPackageSpecification, options: List): Result =
+ override suspend fun installPackageCommand(specification: PythonPackageSpecification, options: List): Result {
try {
- Result.success(runPackagingTool("install", specification.buildInstallationString() + options, PyBundle.message("python.packaging.install.progress", specification.name)))
+ runPackagingTool("install", specification.buildInstallationString() + options, PyBundle.message("python.packaging.install.progress", specification.name))
}
catch (ex: ExecutionException) {
- Result.failure(ex)
+ return Result.failure(ex)
}
- override suspend fun updatePackageCommand(specification: PythonPackageSpecification): Result =
+ return Result.success(Unit)
+ }
+
+ override suspend fun updatePackageCommand(specification: PythonPackageSpecification): Result {
try {
- Result.success(runPackagingTool("install", listOf("--upgrade") + specification.buildInstallationString(), PyBundle.message("python.packaging.update.progress", specification.name)))
+ runPackagingTool("install", listOf("--upgrade") + specification.buildInstallationString(), PyBundle.message("python.packaging.update.progress", specification.name))
}
catch (ex: ExecutionException) {
- Result.failure(ex)
+ return Result.failure(ex)
}
- override suspend fun uninstallPackageCommand(pkg: PythonPackage): Result =
+ return Result.success(Unit)
+ }
+
+ override suspend fun uninstallPackageCommand(pkg: PythonPackage): Result {
try {
- Result.success(runPackagingTool("uninstall", listOf(pkg.name), PyBundle.message("python.packaging.uninstall.progress", pkg.name)))
+ runPackagingTool("uninstall", listOf(pkg.name), PyBundle.message("python.packaging.uninstall.progress", pkg.name))
}
catch (ex: ExecutionException) {
- Result.failure(ex)
+ return Result.failure(ex)
}
- override suspend fun reloadPackagesCommand(): Result> =
+ return Result.success(Unit)
+ }
+
+ override suspend fun reloadPackagesCommand(): Result> {
try {
val output = runPackagingTool("list", emptyList(), PyBundle.message("python.packaging.list.progress"))
val packages = output.lineSequence()
@@ -54,9 +63,11 @@ open class PipPythonPackageManager(project: Project, sdk: Sdk) : PythonPackageMa
}
.sortedWith(compareBy(PythonPackage::name))
.toList()
- Result.success(packages)
+
+ return Result.success(packages)
}
catch (ex: ExecutionException) {
- Result.failure(ex)
+ return Result.failure(ex)
}
+ }
}
\ No newline at end of file
diff --git a/python/src/com/jetbrains/python/sdk/poetry/PoetryPackageManager.kt b/python/src/com/jetbrains/python/sdk/poetry/PoetryPackageManager.kt
index bf5385602047..331f80786f19 100644
--- a/python/src/com/jetbrains/python/sdk/poetry/PoetryPackageManager.kt
+++ b/python/src/com/jetbrains/python/sdk/poetry/PoetryPackageManager.kt
@@ -18,13 +18,26 @@ class PoetryPackageManager(project: Project, sdk: Sdk) : PythonPackageManager(pr
@Volatile
private var outdatedPackages: Map = emptyMap()
- override suspend fun installPackageCommand(specification: PythonPackageSpecification, options: List): Result =
+ override suspend fun installPackageCommand(specification: PythonPackageSpecification, options: List): Result {
poetryInstallPackage(sdk, specification.getVersionForPoetry(), options)
+ .onFailure { return Result.failure(it) }
- override suspend fun updatePackageCommand(specification: PythonPackageSpecification): Result =
+ return Result.success(Unit)
+ }
+
+ override suspend fun updatePackageCommand(specification: PythonPackageSpecification): Result {
poetryInstallPackage(sdk, specification.getVersionForPoetry(), emptyList())
+ .onFailure { return Result.failure(it) }
- override suspend fun uninstallPackageCommand(pkg: PythonPackage): Result = poetryUninstallPackage(sdk, pkg.name)
+ return Result.success(Unit)
+ }
+
+ override suspend fun uninstallPackageCommand(pkg: PythonPackage): Result {
+ poetryUninstallPackage(sdk, pkg.name)
+ .onFailure { return Result.failure(it) }
+
+ return Result.success(Unit)
+ }
override suspend fun reloadPackagesCommand(): Result> {
val (installed, _) = poetryListPackages(sdk).getOrElse {
diff --git a/python/src/com/jetbrains/python/sdk/uv/UvPackageManager.kt b/python/src/com/jetbrains/python/sdk/uv/UvPackageManager.kt
index 6ce66db29a07..5e4d5d529d53 100644
--- a/python/src/com/jetbrains/python/sdk/uv/UvPackageManager.kt
+++ b/python/src/com/jetbrains/python/sdk/uv/UvPackageManager.kt
@@ -21,7 +21,7 @@ internal class UvPackageManager(project: Project, sdk: Sdk, private val uv: UvLo
@Volatile
var outdatedPackages: Map = emptyMap()
- override suspend fun installPackageCommand(specification: PythonPackageSpecification, options: List): Result {
+ override suspend fun installPackageCommand(specification: PythonPackageSpecification, options: List): Result {
val result = if (sdk.uvUsePackageManagement) {
uv.installPackage(specification, emptyList())
}
@@ -34,19 +34,19 @@ internal class UvPackageManager(project: Project, sdk: Sdk, private val uv: UvLo
}
// FIXME: refactor command return value, it's not used
- return Result.success("")
+ return Result.success(Unit)
}
- override suspend fun updatePackageCommand(specification: PythonPackageSpecification): Result {
+ override suspend fun updatePackageCommand(specification: PythonPackageSpecification): Result {
installPackageCommand(specification, emptyList()).getOrElse {
return Result.failure(it)
}
// FIXME: refactor command return value, it's not used
- return Result.success("")
+ return Result.success(Unit)
}
- override suspend fun uninstallPackageCommand(pkg: PythonPackage): Result {
+ override suspend fun uninstallPackageCommand(pkg: PythonPackage): Result {
val result = if (sdk.uvUsePackageManagement) {
uv.uninstallPackage(pkg)
}
@@ -59,7 +59,7 @@ internal class UvPackageManager(project: Project, sdk: Sdk, private val uv: UvLo
}
// FIXME: refactor command return value, it's not used
- return Result.success("")
+ return Result.success(Unit)
}
override suspend fun reloadPackagesCommand(): Result> {
diff --git a/python/testSrc/com/jetbrains/python/inspections/PyPackageRequirementsInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyPackageRequirementsInspectionTest.java
index 903fdd3506ac..d26d4abadd06 100644
--- a/python/testSrc/com/jetbrains/python/inspections/PyPackageRequirementsInspectionTest.java
+++ b/python/testSrc/com/jetbrains/python/inspections/PyPackageRequirementsInspectionTest.java
@@ -7,8 +7,8 @@ import com.intellij.testFramework.ServiceContainerUtil;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.fixtures.PyInspectionTestCase;
import com.jetbrains.python.packaging.PyRequirement;
-import com.jetbrains.python.packaging.common.PackageManagerHolder;
-import com.jetbrains.python.packaging.mocks.MockPackageManagerHolder;
+import com.jetbrains.python.packaging.management.PythonPackageManagerService;
+import com.jetbrains.python.packaging.management.TestPythonPackageManagerService;
import com.jetbrains.python.psi.LanguageLevel;
import com.jetbrains.python.sdk.PythonSdkUtil;
import com.jetbrains.python.sdk.pipenv.PipenvFilesUtilsKt;
@@ -31,8 +31,8 @@ public class PyPackageRequirementsInspectionTest extends PyInspectionTestCase {
ServiceContainerUtil.replaceService(
myFixture.getProject(),
- PackageManagerHolder.class,
- new MockPackageManagerHolder(),
+ PythonPackageManagerService.class,
+ new TestPythonPackageManagerService(),
myFixture.getProject()
);
}
diff --git a/python/testSrc/com/jetbrains/python/packaging/management/TestPackageManager.kt b/python/testSrc/com/jetbrains/python/packaging/management/TestPackageManager.kt
new file mode 100644
index 000000000000..9aea86941586
--- /dev/null
+++ b/python/testSrc/com/jetbrains/python/packaging/management/TestPackageManager.kt
@@ -0,0 +1,114 @@
+// 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.management
+
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.projectRoots.Sdk
+import com.jetbrains.python.packaging.bridge.PythonPackageManagementServiceBridge
+import com.jetbrains.python.packaging.common.PythonPackage
+import com.jetbrains.python.packaging.common.PythonPackageDetails
+import com.jetbrains.python.packaging.common.PythonPackageSpecification
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import org.jetbrains.annotations.TestOnly
+
+@TestOnly
+class TestPythonPackageManager(project: Project, sdk: Sdk) : PythonPackageManager(project, sdk) {
+
+ override var installedPackages: List = DEFAULT_PACKAGES.toMutableList()
+
+ private var packageNames: List = emptyList()
+ private var packageDetails: PythonPackageDetails? = null
+
+ override val repositoryManager: PythonRepositoryManager
+ get() = TestPythonRepositoryManager(project, sdk).withPackageNames(packageNames).withPackageDetails(packageDetails)
+
+ override suspend fun installPackageCommand(specification: PythonPackageSpecification, options: List): Result {
+ return if (repositoryManager.allPackages().contains(specification.name)) {
+ installedPackages += PythonPackage(specification.name, specification.versionSpecs.orEmpty(), false)
+ Result.success(Unit)
+ } else {
+ Result.failure(Exception(PACKAGE_INSTALL_FAILURE_MESSAGE))
+ }
+ }
+
+ override suspend fun updatePackageCommand(specification: PythonPackageSpecification): Result {
+ return Result.success(Unit)
+ }
+
+ override suspend fun uninstallPackageCommand(pkg: PythonPackage): Result {
+ val packageToRemove = findPackageByName(pkg.name)
+ return if (packageToRemove != null) {
+ installedPackages -= packageToRemove
+ Result.success(Unit)
+ } else {
+ Result.failure(Exception(PACKAGE_UNINSTALL_FAILURE_MESSAGE))
+ }
+ }
+
+ override suspend fun reloadPackagesCommand(): Result> {
+ return Result.success(installedPackages)
+ }
+
+ private fun findPackageByName(name: String): PythonPackage? {
+ return installedPackages.find { it.name == name }
+ }
+
+ fun withPackageNames(packageNames: List): TestPythonPackageManager {
+ this.packageNames = packageNames
+ return this
+ }
+
+ fun withPackageDetails(details: PythonPackageDetails?): TestPythonPackageManager {
+ this.packageDetails = details
+ return this
+ }
+
+ companion object {
+ private val DEFAULT_PACKAGES = listOf(
+ PythonPackage(PIP_PACKAGE, EMPTY_STRING, false),
+ PythonPackage(SETUP_TOOLS_PACKAGE, EMPTY_STRING, false)
+ )
+
+ private const val PIP_PACKAGE = "pip"
+ private const val SETUP_TOOLS_PACKAGE = "setuptools"
+ private const val PACKAGE_INSTALL_FAILURE_MESSAGE = "Failed to install package"
+ private const val PACKAGE_UNINSTALL_FAILURE_MESSAGE = "No such package found"
+ private const val EMPTY_STRING = ""
+ }
+}
+
+@TestOnly
+class TestPythonPackageManagerService(): PythonPackageManagerService {
+
+ override fun forSdk(project: Project, sdk: Sdk): PythonPackageManager {
+ return TestPythonPackageManager(project, sdk)
+ }
+
+ override fun bridgeForSdk(project: Project, sdk: Sdk): PythonPackageManagementServiceBridge {
+ return PythonPackageManagementServiceBridge(project, sdk)
+ }
+
+ override fun getServiceScope(): CoroutineScope {
+ return CoroutineScope(Job())
+ }
+}
+
+@TestOnly
+class TestPackageManagerProvider : PythonPackageManagerProvider {
+ private var packageNames: List = emptyList()
+ private var packageDetails: PythonPackageDetails? = null
+
+ fun withPackageNames(packageNames: List): TestPackageManagerProvider {
+ this.packageNames = packageNames
+ return this
+ }
+
+ fun withPackageDetails(details: PythonPackageDetails): TestPackageManagerProvider {
+ this.packageDetails = details
+ return this
+ }
+
+ override fun createPackageManagerForSdk(project: Project, sdk: Sdk): PythonPackageManager {
+ return TestPythonPackageManager(project, sdk).withPackageNames(packageNames).withPackageDetails(packageDetails)
+ }
+}
\ No newline at end of file
diff --git a/python/testSrc/com/jetbrains/python/packaging/management/TestPackageManagerProvider.kt b/python/testSrc/com/jetbrains/python/packaging/management/TestPackageManagerProvider.kt
deleted file mode 100644
index 64e68e3211a5..000000000000
--- a/python/testSrc/com/jetbrains/python/packaging/management/TestPackageManagerProvider.kt
+++ /dev/null
@@ -1,27 +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.management
-
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.projectRoots.Sdk
-import com.jetbrains.python.packaging.common.PythonPackageDetails
-import org.jetbrains.annotations.TestOnly
-
-@TestOnly
-class TestPackageManagerProvider : PythonPackageManagerProvider {
- private var packageNames: List = emptyList()
- private var packageDetails: PythonPackageDetails? = null
-
- fun withPackageNames(packageNames: List): TestPackageManagerProvider {
- this.packageNames = packageNames
- return this
- }
-
- fun withPackageDetails(details: PythonPackageDetails): TestPackageManagerProvider {
- this.packageDetails = details
- return this
- }
-
- override fun createPackageManagerForSdk(project: Project, sdk: Sdk): PythonPackageManager {
- return TestPythonPackageManager(project, sdk).withPackageNames(packageNames).withPackageDetails(packageDetails)
- }
-}
\ No newline at end of file
diff --git a/python/testSrc/com/jetbrains/python/packaging/management/TestPythonPackageManager.kt b/python/testSrc/com/jetbrains/python/packaging/management/TestPythonPackageManager.kt
deleted file mode 100644
index 726f8c07833b..000000000000
--- a/python/testSrc/com/jetbrains/python/packaging/management/TestPythonPackageManager.kt
+++ /dev/null
@@ -1,50 +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.management
-
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.projectRoots.Sdk
-import com.jetbrains.python.packaging.common.PythonPackage
-import com.jetbrains.python.packaging.common.PythonPackageDetails
-import com.jetbrains.python.packaging.common.PythonPackageSpecification
-import org.jetbrains.annotations.TestOnly
-
-@TestOnly
-class TestPythonPackageManager(project: Project, sdk: Sdk) : PythonPackageManager(project, sdk) {
- private var packageNames: List = emptyList()
- private var packageDetails: PythonPackageDetails? = null
-
- override var installedPackages: List = emptyList()
- get() = TODO("Not yet implemented")
- override val repositoryManager: PythonRepositoryManager
- get() = TestPythonRepositoryManager(project, sdk).withPackageNames(packageNames).withPackageDetails(packageDetails)
-
- fun withPackageNames(packageNames: List): TestPythonPackageManager {
- this.packageNames = packageNames
- return this
- }
-
- fun withPackageDetails(details: PythonPackageDetails?): TestPythonPackageManager {
- this.packageDetails = details
- return this
- }
-
- override suspend fun reloadPackages(): Result> {
- TODO("Not yet implemented")
- }
-
- override suspend fun installPackageCommand(specification: PythonPackageSpecification, options: List): Result {
- TODO("Not yet implemented")
- }
-
- override suspend fun updatePackageCommand(specification: PythonPackageSpecification): Result {
- TODO("Not yet implemented")
- }
-
- override suspend fun uninstallPackageCommand(pkg: PythonPackage): Result {
- TODO("Not yet implemented")
- }
-
- override suspend fun reloadPackagesCommand(): Result> {
- TODO("Not yet implemented")
- }
-}
\ No newline at end of file
diff --git a/python/testSrc/com/jetbrains/python/packaging/management/TestPythonRepositoryManager.kt b/python/testSrc/com/jetbrains/python/packaging/management/TestPythonRepositoryManager.kt
index 36fc346e3c5f..fb19f3a68f0d 100644
--- a/python/testSrc/com/jetbrains/python/packaging/management/TestPythonRepositoryManager.kt
+++ b/python/testSrc/com/jetbrains/python/packaging/management/TestPythonRepositoryManager.kt
@@ -59,10 +59,8 @@ class TestPythonRepositoryManager(project: Project, sdk: Sdk) : PythonRepository
}
override suspend fun refreshCashes() {
- TODO("Not yet implemented")
}
override suspend fun initCaches() {
- TODO("Not yet implemented")
}
}
\ No newline at end of file
diff --git a/python/testSrc/com/jetbrains/python/packaging/mocks/MockPackageManager.kt b/python/testSrc/com/jetbrains/python/packaging/mocks/MockPackageManager.kt
deleted file mode 100644
index 37dbd7e806d9..000000000000
--- a/python/testSrc/com/jetbrains/python/packaging/mocks/MockPackageManager.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-// 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.packaging.mocks
-
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.projectRoots.Sdk
-import com.jetbrains.python.packaging.common.PythonPackage
-import com.jetbrains.python.packaging.common.PythonPackageSpecification
-import com.jetbrains.python.packaging.management.PythonPackageManager
-import com.jetbrains.python.packaging.management.PythonRepositoryManager
-import com.jetbrains.python.packaging.pip.PipRepositoryManager
-import org.jetbrains.annotations.TestOnly
-
-@TestOnly
-class MockPythonPackageManager(project: Project, sdk: Sdk) : PythonPackageManager(project, sdk) {
-
- override var installedPackages: List = DEFAULT_PACKAGES.toMutableList()
-
- override val repositoryManager: PythonRepositoryManager = PipRepositoryManager(project, sdk)
-
- override suspend fun installPackageCommand(specification: PythonPackageSpecification, options: List): Result {
- return if (repositoryManager.allPackages().contains(specification.name)) {
- installedPackages += PythonPackage(specification.name, specification.versionSpecs.orEmpty(), false)
- Result.success(PACKAGE_INSTALLED_MESSAGE)
- } else {
- Result.failure(Exception(PACKAGE_INSTALL_FAILURE_MESSAGE))
- }
- }
-
- override suspend fun updatePackageCommand(specification: PythonPackageSpecification): Result {
- return Result.success(PACKAGE_UPDATED_MESSAGE)
- }
-
- override suspend fun uninstallPackageCommand(pkg: PythonPackage): Result {
- val packageToRemove = findPackageByName(pkg.name)
- return if (packageToRemove != null) {
- installedPackages -= packageToRemove
- Result.success(PACKAGE_UNINSTALLED_MESSAGE)
- } else {
- Result.failure(Exception(PACKAGE_UNINSTALL_FAILURE_MESSAGE))
- }
- }
-
- override suspend fun reloadPackagesCommand(): Result> {
- return Result.success(installedPackages)
- }
-
- private fun findPackageByName(name: String): PythonPackage? {
- return installedPackages.find { it.name == name }
- }
-
- companion object {
- private val DEFAULT_PACKAGES = listOf(
- PythonPackage(PIP_PACKAGE, EMPTY_STRING, false),
- PythonPackage(SETUP_TOOLS_PACKAGE, EMPTY_STRING, false)
- )
-
- private const val PIP_PACKAGE = "pip"
- private const val SETUP_TOOLS_PACKAGE = "setuptools"
- private const val PACKAGE_INSTALLED_MESSAGE = "Successfully installed package"
- private const val PACKAGE_INSTALL_FAILURE_MESSAGE = "Failed to install package"
- private const val PACKAGE_UPDATED_MESSAGE = "Successfully updated package"
- private const val PACKAGE_UNINSTALLED_MESSAGE = "Successfully uninstalled package"
- private const val PACKAGE_UNINSTALL_FAILURE_MESSAGE = "No such package found"
- private const val EMPTY_STRING = ""
- }
-}
\ No newline at end of file
diff --git a/python/testSrc/com/jetbrains/python/packaging/mocks/MockPackageManagerHolder.kt b/python/testSrc/com/jetbrains/python/packaging/mocks/MockPackageManagerHolder.kt
deleted file mode 100644
index 14ee6d3f6376..000000000000
--- a/python/testSrc/com/jetbrains/python/packaging/mocks/MockPackageManagerHolder.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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.packaging.mocks
-
-import com.intellij.openapi.project.Project
-import com.intellij.openapi.projectRoots.Sdk
-import com.jetbrains.python.packaging.bridge.PythonPackageManagementServiceBridge
-import com.jetbrains.python.packaging.common.PackageManagerHolder
-import com.jetbrains.python.packaging.management.PythonPackageManager
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import org.jetbrains.annotations.TestOnly
-
-
-@TestOnly
-class MockPackageManagerHolder() : PackageManagerHolder {
-
- override fun forSdk(project: Project, sdk: Sdk): PythonPackageManager {
- return MockPythonPackageManager(project, sdk)
- }
-
- override fun bridgeForSdk(project: Project, sdk: Sdk): PythonPackageManagementServiceBridge {
- return PythonPackageManagementServiceBridge(project, sdk)
- }
-
- override fun getServiceScope(): CoroutineScope {
- return CoroutineScope(Job())
- }
-}
\ No newline at end of file