mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-87081 Remove PythonDependenciesManager.kt and implementations and integrate the new API in PythonPackageManager.kt
GitOrigin-RevId: 7a8ba9c94f29746f59251e8095958f7cbb18bb9b
This commit is contained in:
committed by
intellij-monorepo-bot
parent
1856f3060f
commit
76cfde362a
@@ -3,6 +3,7 @@ package com.jetbrains.python.hatch.packaging
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.python.hatch.HatchService
|
||||
import com.intellij.python.hatch.getHatchService
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
@@ -10,6 +11,7 @@ import com.jetbrains.python.hatch.sdk.HatchSdkAdditionalData
|
||||
import com.jetbrains.python.hatch.sdk.isHatch
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManager
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManagerProvider
|
||||
import com.jetbrains.python.packaging.management.resolvePyProjectToml
|
||||
import com.jetbrains.python.packaging.pip.PipPythonPackageManager
|
||||
|
||||
internal class HatchPackageManager(project: Project, sdk: Sdk) : PipPythonPackageManager(project, sdk) {
|
||||
@@ -30,6 +32,12 @@ internal class HatchPackageManager(project: Project, sdk: Sdk) : PipPythonPackag
|
||||
val workingDirectory = data.hatchWorkingDirectory
|
||||
return workingDirectory.getHatchService(hatchEnvironmentName = data.hatchEnvironmentName)
|
||||
}
|
||||
|
||||
override fun getDependencyFile(): VirtualFile? {
|
||||
val data = sdk.sdkAdditionalData as? HatchSdkAdditionalData ?: return null
|
||||
val workingDirectory = data.hatchWorkingDirectory ?: return null
|
||||
return resolvePyProjectToml(workingDirectory)
|
||||
}
|
||||
}
|
||||
|
||||
internal class HatchPackageManagerProvider : PythonPackageManagerProvider {
|
||||
|
||||
+8
-3
@@ -6,11 +6,13 @@ import com.intellij.codeInspection.ProblemDescriptor
|
||||
import com.intellij.codeInspection.util.IntentionName
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.jetbrains.python.PyPsiBundle
|
||||
import com.jetbrains.python.packaging.dependencies.PythonDependenciesManager
|
||||
import com.jetbrains.python.packaging.PyRequirementParser
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManager
|
||||
import com.jetbrains.python.packaging.utils.PyPackageCoroutine
|
||||
import org.jetbrains.annotations.Nls
|
||||
|
||||
class PyAddToDeclaredPackagesQuickFix(
|
||||
private val manager: PythonDependenciesManager,
|
||||
private val manager: PythonPackageManager,
|
||||
val packageName: String,
|
||||
) : LocalQuickFix {
|
||||
override fun startInWriteAction(): Boolean = false
|
||||
@@ -19,6 +21,9 @@ class PyAddToDeclaredPackagesQuickFix(
|
||||
override fun getName(): @IntentionName String = PyPsiBundle.message("QFIX.add.imported.package.to.declared.packages", packageName)
|
||||
|
||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||
manager.addDependency(packageName)
|
||||
PyPackageCoroutine.launch(project) {
|
||||
val requirement = PyRequirementParser.fromLine(packageName) ?: return@launch
|
||||
manager.addDependencyToFile(requirement)
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -5,18 +5,20 @@ import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.jetbrains.python.packaging.PyRequirement
|
||||
import com.jetbrains.python.packaging.common.PythonPackage
|
||||
import com.jetbrains.python.packaging.common.toRequirements
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManager
|
||||
import com.jetbrains.python.packaging.management.extractDependenciesAsync
|
||||
import com.jetbrains.python.psi.PyUtil
|
||||
|
||||
class DeclaredButNotInstalledPackagesChecker(
|
||||
val ignoredPackages: Collection<String>,
|
||||
) {
|
||||
fun findUnsatisfiedRequirements(module: Module, manager: PythonPackageManager): List<PyRequirement> {
|
||||
val requirements = manager.getDependencyManager()?.getDependencies() ?: return emptyList()
|
||||
val requirements = manager.extractDependenciesAsync() ?: return emptyList()
|
||||
val installedPackages = manager.listInstalledPackagesSnapshot()
|
||||
val modulePackages = collectPackagesInModule(module)
|
||||
|
||||
return requirements.filter { requirement ->
|
||||
return requirements.toRequirements().filter { requirement ->
|
||||
isRequirementUnsatisfied(requirement, installedPackages, modulePackages)
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -1,13 +1,17 @@
|
||||
// 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.inspections.requirement
|
||||
|
||||
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
|
||||
import com.jetbrains.python.PyPsiPackageUtil
|
||||
import com.jetbrains.python.codeInsight.stdlib.PyStdlibUtil
|
||||
import com.jetbrains.python.packaging.PyPIPackageUtil.INSTANCE
|
||||
import com.jetbrains.python.packaging.PyPackageUtil
|
||||
import com.jetbrains.python.packaging.common.toRequirements
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManager
|
||||
import com.jetbrains.python.packaging.management.extractDependenciesAsync
|
||||
|
||||
class InstalledButNotDeclaredChecker(val ignoredPackages: Collection<String>, val pythonPackageManager: PythonPackageManager) {
|
||||
@RequiresBackgroundThread
|
||||
fun getUndeclaredPackageName(importedPyModule: String): String? {
|
||||
val packageName = PyPsiPackageUtil.moduleToPackageName(importedPyModule)
|
||||
if (isIgnoredOrStandardPackage(importedPyModule))
|
||||
@@ -16,8 +20,7 @@ class InstalledButNotDeclaredChecker(val ignoredPackages: Collection<String>, va
|
||||
if (!INSTANCE.isInPyPI(packageName))
|
||||
return null
|
||||
|
||||
|
||||
val requirements = pythonPackageManager.getDependencyManager()?.getDependencies() ?: emptyList()
|
||||
val requirements = pythonPackageManager.extractDependenciesAsync()?.toRequirements() ?: return null
|
||||
if (requirements.any { it.name == packageName }) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -56,19 +56,14 @@ class PyRequirementVisitor(
|
||||
|
||||
val sdk = module.pythonSdk ?: return
|
||||
val manager = PythonPackageManager.forSdk(module.project, sdk)
|
||||
val requirementsManager = manager.getDependencyManager() ?: return
|
||||
if (requirementsManager.getDependenciesFile() == null)
|
||||
if (manager.getDependencyFile() == null)
|
||||
return
|
||||
val installedNotDeclaredChecker = InstalledButNotDeclaredChecker(ignoredPackages, manager)
|
||||
val packageName = installedNotDeclaredChecker.getUndeclaredPackageName(importedPyModule = importedPyModule) ?: return
|
||||
|
||||
|
||||
val fixes = if (requirementsManager.isAddDependencyPossible()) {
|
||||
arrayOf(PyAddToDeclaredPackagesQuickFix(requirementsManager, packageName),
|
||||
IgnoreRequirementFix(setOf(packageName)))
|
||||
}
|
||||
else
|
||||
arrayOf()
|
||||
val fixes = arrayOf(PyAddToDeclaredPackagesQuickFix(manager, packageName),
|
||||
IgnoreRequirementFix(setOf(packageName)))
|
||||
|
||||
registerProblem(
|
||||
packageReferenceExpression,
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -19,6 +20,7 @@ import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.AsyncFileListener;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
@@ -27,16 +29,17 @@ import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFilePropertyChangeEvent;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.serviceContainer.AlreadyDisposedException;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread;
|
||||
import com.intellij.util.concurrency.annotations.RequiresReadLock;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.codeInsight.typing.PyTypeShed;
|
||||
import com.jetbrains.python.packaging.requirementsTxt.PythonRequirementTxtSdkUtils;
|
||||
import com.jetbrains.python.packaging.requirementsTxt.PythonRequirementsTxtManager;
|
||||
import com.jetbrains.python.packaging.requirementsTxt.RequirementsTxtManipulationHelper;
|
||||
import com.jetbrains.python.packaging.setupPy.SetupPyHelpers;
|
||||
import com.jetbrains.python.packaging.setupPy.SetupPyManager;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import com.jetbrains.python.psi.PyCallExpression;
|
||||
import com.jetbrains.python.psi.PyFile;
|
||||
@@ -59,6 +62,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.jetbrains.python.packaging.setupPy.SetupPyHelpers.SETUP_PY;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public final class PyPackageUtil {
|
||||
public static final String SETUPTOOLS = "setuptools";
|
||||
@@ -73,24 +78,31 @@ public final class PyPackageUtil {
|
||||
private PyPackageUtil() {
|
||||
}
|
||||
|
||||
@RequiresReadLock
|
||||
public static boolean hasSetupPy(@NotNull Module module) {
|
||||
return findSetupPy(module) != null;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static @Nullable VirtualFile findSetupPyFile(@NotNull Module module) {
|
||||
Sdk sdk = PythonSdkUtil.findPythonSdk(module);
|
||||
if (sdk == null) return null;
|
||||
return SetupPyManager.getInstance(module.getProject(), sdk).getDependenciesFile();
|
||||
var contentRoots = ModuleRootManager.getInstance(module).getContentRoots();
|
||||
for (VirtualFile root : contentRoots) {
|
||||
VirtualFile setupPy = VfsUtil.findRelativeFile(root, SETUP_PY);
|
||||
if (setupPy != null) {
|
||||
return setupPy;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@RequiresReadLock
|
||||
public static @Nullable PyFile findSetupPy(@NotNull Module module) {
|
||||
Sdk sdk = PythonSdkUtil.findPythonSdk(module);
|
||||
if (sdk == null) {
|
||||
return SetupPyHelpers.detectSetupPyInModule(module);
|
||||
}
|
||||
else {
|
||||
return SetupPyManager.getInstance(module.getProject(), sdk).getRequirementsPsiFile();
|
||||
return findSetupPyPsiFileForSdk(module);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,34 +117,50 @@ public final class PyPackageUtil {
|
||||
return PythonRequirementTxtSdkUtils.detectRequirementsTxtInModule(module);
|
||||
}
|
||||
else {
|
||||
return PythonRequirementsTxtManager.getInstance(module.getProject(), sdk).getDependenciesFile();
|
||||
return PythonRequirementTxtSdkUtils.findRequirementsTxt(sdk);
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresReadLock(generateAssertion = false)
|
||||
public static @Nullable List<PyRequirement> getRequirementsFromTxt(@NotNull Module module) {
|
||||
Sdk sdk = PythonSdkUtil.findPythonSdk(module);
|
||||
if (sdk == null) return null;
|
||||
|
||||
return PythonRequirementsTxtManager.getInstance(module.getProject(), sdk).getDependencies();
|
||||
VirtualFile requirementsFile = findRequirementsTxt(module);
|
||||
if (requirementsFile == null) return null;
|
||||
List<PyRequirement> requirements = ReadAction.compute(() -> PyRequirementParser.fromFile(requirementsFile));
|
||||
return requirements;
|
||||
}
|
||||
|
||||
|
||||
@RequiresReadLock
|
||||
public static @Nullable List<PyRequirement> findSetupPyRequires(@NotNull Module module) {
|
||||
Sdk sdk = PythonSdkUtil.findPythonSdk(module);
|
||||
if (sdk == null) return null;
|
||||
return SetupPyManager.getInstance(module.getProject(), sdk).getDependencies();
|
||||
PyFile pyFile = findSetupPyPsiFileForSdk(module);
|
||||
if (pyFile == null) return null;
|
||||
return SetupPyHelpers.parseSetupPy(pyFile);
|
||||
}
|
||||
|
||||
@RequiresReadLock
|
||||
public static @Nullable Map<String, List<PyRequirement>> findSetupPyExtrasRequire(@NotNull Module module) {
|
||||
Sdk sdk = PythonSdkUtil.findPythonSdk(module);
|
||||
if (sdk == null) return null;
|
||||
PyFile pyFile = SetupPyManager.getInstance(module.getProject(), sdk).getRequirementsPsiFile();
|
||||
PyFile pyFile = findSetupPyPsiFileForSdk(module);
|
||||
if (pyFile == null) return null;
|
||||
return SetupPyHelpers.findSetupPyExtrasRequire(pyFile);
|
||||
}
|
||||
|
||||
@RequiresReadLock
|
||||
@RequiresBackgroundThread(generateAssertion = false)
|
||||
private static @Nullable PyFile findSetupPyPsiFileForSdk(@NotNull Module module) {
|
||||
Sdk sdk = PythonSdkUtil.findPythonSdk(module);
|
||||
if (sdk == null) return null;
|
||||
|
||||
VirtualFile setupPyVFile = findSetupPyFile(module);
|
||||
if (setupPyVFile == null) return null;
|
||||
|
||||
Project project = module.getProject();
|
||||
return ReadAction.compute(() -> {
|
||||
if (!setupPyVFile.isValid()) return null;
|
||||
var psiFile = PsiManager.getInstance(project).findFile(setupPyVFile);
|
||||
return (psiFile instanceof PyFile pyFile) ? pyFile : null;
|
||||
});
|
||||
}
|
||||
|
||||
@RequiresReadLock
|
||||
public static @NotNull List<String> getPackageNames(@NotNull Module module) {
|
||||
// TODO: Cache found module packages, clear cache on module updates
|
||||
final List<String> packageNames = new ArrayList<>();
|
||||
@@ -152,6 +180,7 @@ public final class PyPackageUtil {
|
||||
}
|
||||
|
||||
|
||||
@RequiresReadLock
|
||||
public static @Nullable PyCallExpression findSetupCall(@NotNull Module module) {
|
||||
PyFile pyFile = findSetupPy(module);
|
||||
if (pyFile == null) {
|
||||
@@ -214,17 +243,21 @@ public final class PyPackageUtil {
|
||||
}.check(sdk);
|
||||
}
|
||||
|
||||
|
||||
@RequiresBackgroundThread(generateAssertion = false)
|
||||
public static void addRequirementToTxtOrSetupPy(@NotNull Module module,
|
||||
@NotNull String requirementName,
|
||||
@NotNull LanguageLevel languageLevel) {
|
||||
Sdk sdk = PythonSdkUtil.findPythonSdk(module);
|
||||
if (sdk == null) return;
|
||||
boolean isSuccess = PythonRequirementsTxtManager.getInstance(module.getProject(), sdk).addDependency(requirementName);
|
||||
if (isSuccess) {
|
||||
VirtualFile requirementsFile = findRequirementsTxt(module);
|
||||
if (requirementsFile != null) {
|
||||
RequirementsTxtManipulationHelper.addToRequirementsTxt(
|
||||
module.getProject(), requirementsFile, requirementName);
|
||||
return;
|
||||
}
|
||||
SetupPyManager.getInstance(module.getProject(), sdk).addDependency(requirementName);
|
||||
|
||||
PyFile setupPyFile = ReadAction.compute(() -> findSetupPy(module));
|
||||
if (setupPyFile != null) {
|
||||
SetupPyHelpers.addRequirementsToSetupPy(setupPyFile, requirementName, languageLevel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,10 +8,12 @@ import com.jetbrains.python.packaging.management.findPackageSpecification
|
||||
import com.jetbrains.python.packaging.pyRequirement
|
||||
import com.jetbrains.python.packaging.pyRequirementVersionSpec
|
||||
import com.jetbrains.python.packaging.repository.PyPackageRepository
|
||||
import com.jetbrains.python.packaging.requirement.PyRequirementRelation
|
||||
import com.jetbrains.python.packaging.requirement.PyRequirementVersionSpec
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.Nls
|
||||
import javax.swing.Icon
|
||||
import kotlin.collections.emptyList
|
||||
|
||||
open class PythonPackage(name: String, val version: String, val isEditableMode: Boolean) {
|
||||
companion object {
|
||||
@@ -28,17 +30,17 @@ open class PythonPackage(name: String, val version: String, val isEditableMode:
|
||||
open val sourceRepoIcon: Icon? = null
|
||||
|
||||
override fun toString(): String {
|
||||
return "PythonPackage(name='$name', version='$version')"
|
||||
return "PythonPackage(name='${this@PythonPackage.name}', version='$version')"
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is PythonPackage) return false
|
||||
return name == other.name && version == other.version && isEditableMode == other.isEditableMode
|
||||
return this@PythonPackage.name == other.name && version == other.version && isEditableMode == other.isEditableMode
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = name.hashCode()
|
||||
var result = this@PythonPackage.name.hashCode()
|
||||
result = HASH_MULTIPLIER * result + version.hashCode()
|
||||
result = HASH_MULTIPLIER * result + isEditableMode.hashCode()
|
||||
return result
|
||||
@@ -51,8 +53,44 @@ open class PythonPackage(name: String, val version: String, val isEditableMode:
|
||||
|
||||
@ApiStatus.Internal
|
||||
fun toPyPackage(): PyPackage {
|
||||
return PyPackage(name, version)
|
||||
return PyPackage(this@PythonPackage.name, version)
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
fun toPyRequirement(): PyRequirement {
|
||||
val versionSpec = if (version.isNotEmpty()) {
|
||||
pyRequirementVersionSpec(PyRequirementRelation.EQ, version)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
return pyRequirement(presentableName, versionSpec)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a list of [PythonPackage] to a list of [PyRequirement].
|
||||
* Each package is converted to a requirement with an exact version constraint.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
fun List<PythonPackage>.toRequirements(): List<PyRequirement> = map { it.toPyRequirement() }
|
||||
|
||||
/**
|
||||
* Converts a [PyRequirement] to a [PythonPackage].
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
fun PyRequirement.toPythonPackage(): PythonPackage {
|
||||
val exactVersionSpec = versionSpecs.firstOrNull { it.relation == PyRequirementRelation.EQ }
|
||||
return PythonPackage(presentableTextWithoutVersion, exactVersionSpec?.version ?: "", isEditable)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a list of [PyRequirement] to a list of [PythonPackage].
|
||||
* Only requirements with exact version constraints (==) are converted.
|
||||
* Requirements without exact versions are filtered out.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
fun List<PyRequirement>.toPythonPackages(): List<PythonPackage> {
|
||||
return mapNotNull { it.toPythonPackage() }
|
||||
}
|
||||
|
||||
class PythonOutdatedPackage(name: String, version: String, val latestVersion: String)
|
||||
|
||||
@@ -11,18 +11,23 @@ import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.getOrThrow
|
||||
import com.jetbrains.python.onFailure
|
||||
import com.jetbrains.python.packaging.PyRequirement
|
||||
import com.jetbrains.python.packaging.common.PythonOutdatedPackage
|
||||
import com.jetbrains.python.packaging.common.PythonPackage
|
||||
import com.jetbrains.python.packaging.common.PythonRepositoryPackageSpecification
|
||||
import com.jetbrains.python.packaging.conda.environmentYml.CondaEnvironmentYmlManager
|
||||
import com.jetbrains.python.packaging.dependencies.PythonDependenciesManager
|
||||
import com.jetbrains.python.packaging.common.toPythonPackages
|
||||
import com.jetbrains.python.packaging.conda.environmentYml.CondaEnvironmentYmlSdkUtils
|
||||
import com.jetbrains.python.packaging.conda.environmentYml.format.CondaEnvironmentYmlParser
|
||||
import com.jetbrains.python.packaging.conda.environmentYml.format.EnvironmentYmlModifier
|
||||
import com.jetbrains.python.packaging.management.PythonPackageInstallRequest
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManager
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManagerEngine
|
||||
import com.jetbrains.python.packaging.management.PythonRepositoryManager
|
||||
import com.jetbrains.python.packaging.pip.PipPackageManagerEngine
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class CondaPackageManager(project: Project, sdk: Sdk) : PythonPackageManager(project, sdk) {
|
||||
override val repositoryManager: PythonRepositoryManager = CondaRepositoryManger(project, sdk).also {
|
||||
@@ -32,13 +37,8 @@ class CondaPackageManager(project: Project, sdk: Sdk) : PythonPackageManager(pro
|
||||
private val condaPackageEngine = CondaPackageManagerEngine(sdk)
|
||||
private val pipPackageEngine = PipPackageManagerEngine(project, sdk)
|
||||
|
||||
override fun getDependencyManager(): PythonDependenciesManager {
|
||||
return CondaEnvironmentYmlManager.getInstance(project, sdk)
|
||||
}
|
||||
|
||||
override suspend fun syncCommand(): PyResult<Unit> {
|
||||
val requirementsFile = getDependencyManager().getDependenciesFile()
|
||||
?: return PyResult.localizedError(PyBundle.message("python.sdk.conda.requirements.file.not.found"))
|
||||
val requirementsFile = getDependencyFile() ?: return PyResult.localizedError(PyBundle.message("python.sdk.conda.requirements.file.not.found"))
|
||||
return updateEnv(requirementsFile)
|
||||
}
|
||||
|
||||
@@ -148,4 +148,21 @@ class CondaPackageManager(project: Project, sdk: Sdk) : PythonPackageManager(pro
|
||||
else
|
||||
pipPackageEngine
|
||||
}
|
||||
|
||||
override suspend fun extractDependencies(): PyResult<List<PythonPackage>>? {
|
||||
val envFile = getDependencyFile() ?: return null
|
||||
val requirements = withContext(Dispatchers.IO) {
|
||||
CondaEnvironmentYmlParser.fromFile (envFile)
|
||||
} ?: return null
|
||||
return PyResult.success(requirements.toPythonPackages())
|
||||
}
|
||||
|
||||
override fun getDependencyFile(): VirtualFile? {
|
||||
return CondaEnvironmentYmlSdkUtils.findFile(sdk)
|
||||
}
|
||||
|
||||
override suspend fun addDependencyImpl(requirement: PyRequirement): Boolean {
|
||||
val envFile = getDependencyFile() ?: return false
|
||||
return EnvironmentYmlModifier.addRequirement(project, envFile, requirement.presentableText)
|
||||
}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
// 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.conda.environmentYml
|
||||
|
||||
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.intellij.openapi.util.FilesModificationTrackerBase
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.getOrCreateUserDataUnsafe
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.jetbrains.python.PythonPluginDisposable
|
||||
import com.jetbrains.python.packaging.PyRequirement
|
||||
import com.jetbrains.python.packaging.conda.environmentYml.format.CondaEnvironmentYmlParser
|
||||
import com.jetbrains.python.packaging.conda.environmentYml.format.EnvironmentYmlModifier
|
||||
import com.jetbrains.python.packaging.dependencies.cache.PythonDependenciesManagerCached
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
|
||||
@ApiStatus.Internal
|
||||
class CondaEnvironmentYmlManager private constructor(project: Project, val sdk: Sdk) : PythonDependenciesManagerCached(project) {
|
||||
override fun getDependenciesFile(): VirtualFile? {
|
||||
return CondaEnvironmentYmlSdkUtils.findFile(sdk)
|
||||
}
|
||||
|
||||
override fun addDependency(packageName: String): Boolean {
|
||||
val envFile = getDependenciesFile() ?: return false
|
||||
return EnvironmentYmlModifier.addRequirement(project, envFile, packageName)
|
||||
}
|
||||
|
||||
override fun getModificationTracker(): FilesModificationTrackerBase {
|
||||
return CondaEnvYamlModificationTracker.getInstance(project)
|
||||
}
|
||||
|
||||
override fun parseRequirements(requirementsFile: VirtualFile): List<PyRequirement>? {
|
||||
return CondaEnvironmentYmlParser.fromFile(requirementsFile)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val KEY = Key<CondaEnvironmentYmlManager>(this::class.java.name)
|
||||
|
||||
fun getInstance(project: Project, sdk: Sdk): CondaEnvironmentYmlManager = sdk.getOrCreateUserDataUnsafe(KEY) {
|
||||
CondaEnvironmentYmlManager(project, sdk).also {
|
||||
Disposer.register(PythonPluginDisposable.getInstance(project), it)
|
||||
Disposer.register(it, Disposable { sdk.putUserData(KEY, null) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// 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.dependencies
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.jetbrains.python.packaging.PyRequirement
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
|
||||
@ApiStatus.Internal
|
||||
interface PythonDependenciesManager : Disposable.Default {
|
||||
fun isAddDependencyPossible(): Boolean = true
|
||||
fun addDependency(packageName: String): Boolean
|
||||
fun getDependencies(): List<PyRequirement>?
|
||||
fun getDependenciesFile(): VirtualFile?
|
||||
}
|
||||
Vendored
-35
@@ -1,35 +0,0 @@
|
||||
// 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.dependencies.cache
|
||||
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ProjectRootManager
|
||||
import com.intellij.openapi.util.FilesModificationTrackerBase
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.util.CachedValueProvider
|
||||
import com.intellij.psi.util.CachedValuesManager
|
||||
import com.jetbrains.python.packaging.PyRequirement
|
||||
import com.jetbrains.python.packaging.dependencies.PythonDependenciesManager
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@ApiStatus.Internal
|
||||
abstract class PythonDependenciesManagerCached(val project: Project) : PythonDependenciesManager {
|
||||
private val cachedEnvRequirements = CachedValuesManager.getManager(project).createCachedValue {
|
||||
val envFile = getDependenciesFile()
|
||||
val requirements = try {
|
||||
envFile?.let { parseRequirements(envFile) }
|
||||
}
|
||||
catch (t: Throwable) {
|
||||
thisLogger().warn("Failed to parse $envFile", t)
|
||||
null
|
||||
}
|
||||
CachedValueProvider.Result.create(requirements,
|
||||
ProjectRootManager.getInstance(project),
|
||||
getModificationTracker())
|
||||
}
|
||||
|
||||
final override fun getDependencies(): List<PyRequirement>? = cachedEnvRequirements.value
|
||||
|
||||
protected abstract fun getModificationTracker(): FilesModificationTrackerBase
|
||||
protected abstract fun parseRequirements(requirementsFile: VirtualFile): List<PyRequirement>?
|
||||
}
|
||||
@@ -6,12 +6,19 @@ package com.jetbrains.python.packaging.management
|
||||
import com.intellij.execution.ExecutionException
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.readAction
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.openapi.progress.runBlockingMaybeCancellable
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.psi.util.CachedValue
|
||||
import com.intellij.psi.util.CachedValueProvider
|
||||
import com.intellij.psi.util.CachedValuesManager
|
||||
import com.intellij.python.pyproject.PY_PROJECT_TOML
|
||||
import com.intellij.util.cancelOnDispose
|
||||
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
|
||||
import com.intellij.util.messages.Topic
|
||||
@@ -21,20 +28,23 @@ import com.jetbrains.python.getOrNull
|
||||
import com.jetbrains.python.onFailure
|
||||
import com.jetbrains.python.packaging.PyPackageManager
|
||||
import com.jetbrains.python.packaging.PyPackageName
|
||||
import com.jetbrains.python.packaging.PyRequirement
|
||||
import com.jetbrains.python.packaging.common.PythonOutdatedPackage
|
||||
import com.jetbrains.python.packaging.common.PythonPackage
|
||||
import com.jetbrains.python.packaging.common.PythonPackageManagementListener
|
||||
import com.jetbrains.python.packaging.common.PythonRepositoryPackageSpecification
|
||||
import com.jetbrains.python.packaging.dependencies.PythonDependenciesManager
|
||||
import com.jetbrains.python.packaging.utils.PyPackageCoroutine
|
||||
import com.jetbrains.python.sdk.PythonSdkType
|
||||
import com.jetbrains.python.sdk.isReadOnly
|
||||
import com.jetbrains.python.sdk.readOnlyErrorMessage
|
||||
import com.jetbrains.python.sdk.refreshPaths
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.async
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.CheckReturnValue
|
||||
import org.jetbrains.annotations.Nls
|
||||
import java.nio.file.Path
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
|
||||
@@ -66,14 +76,17 @@ abstract class PythonPackageManager(val project: Project, val sdk: Sdk) : Dispos
|
||||
@Volatile
|
||||
protected var outdatedPackages: Map<String, PythonOutdatedPackage> = emptyMap()
|
||||
|
||||
private fun createCachedDependencies(dependencyFile: VirtualFile): CachedValue<Deferred<PyResult<List<PythonPackage>>?>> =
|
||||
CachedValuesManager.getManager(project).createCachedValue {
|
||||
val scope = PyPackageCoroutine.getScope(project)
|
||||
val deferred = scope.async(NON_INTERACTIVE_ROOT_TRACE_CONTEXT, start = CoroutineStart.LAZY) {
|
||||
extractDependencies()
|
||||
}
|
||||
CachedValueProvider.Result.create(deferred, dependencyFile)
|
||||
}
|
||||
|
||||
abstract val repositoryManager: PythonRepositoryManager
|
||||
|
||||
@ApiStatus.Internal
|
||||
open fun getDependencyManager(): PythonDependenciesManager? {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@ApiStatus.Internal
|
||||
suspend fun sync(): PyResult<List<PythonPackage>> {
|
||||
if (sdk.isReadOnly) {
|
||||
@@ -158,7 +171,6 @@ abstract class PythonPackageManager(val project: Project, val sdk: Sdk) : Dispos
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ApiStatus.Internal
|
||||
suspend fun listInstalledPackages(): List<PythonPackage> {
|
||||
waitForInit()
|
||||
@@ -238,6 +250,47 @@ abstract class PythonPackageManager(val project: Project, val sdk: Sdk) : Dispos
|
||||
@ApiStatus.Internal
|
||||
open suspend fun extractDependencies(): PyResult<List<PythonPackage>>? = null
|
||||
|
||||
/**
|
||||
* Extracts project top-level dependencies with caching based on dependency file modification time.
|
||||
* Returns cached result if dependency file hasn't changed since last extraction.
|
||||
* Uses Platform's CachedValuesManager with the dependency file as an invalidation dependency.
|
||||
*
|
||||
* @return null if this package manager doesn't support dependency extraction,
|
||||
* PyResult.Failure if extraction is supported but failed (e.g., parsing error),
|
||||
* PyResult.Success with the list of dependencies if extraction succeeded.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
suspend fun extractDependenciesCached(): PyResult<List<PythonPackage>>? {
|
||||
val dependencyFile = getDependencyFile() ?: return null
|
||||
return createCachedDependencies(dependencyFile).value.await()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the dependency declaration file (e.g., requirements.txt, Pipfile.lock, environment.yml).
|
||||
* Returns null if no dependency file is associated with this package manager.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
open fun getDependencyFile(): VirtualFile? = null
|
||||
|
||||
|
||||
/**
|
||||
* Adds a dependency to the project's dependency declaration file.
|
||||
* Returns true if the dependency was successfully added, false if the operation
|
||||
* is not supported or failed.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
suspend fun addDependencyToFile(requirement: PyRequirement): Boolean {
|
||||
return getDependencyFile() != null && addDependencyImpl(requirement)
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of adding a dependency to the file.
|
||||
* Only called when getDependencyFile() returns non-null.
|
||||
* @param requirement The requirement to add
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
protected open suspend fun addDependencyImpl(requirement: PyRequirement): Boolean = false
|
||||
|
||||
@ApiStatus.Internal
|
||||
suspend fun waitForInit() {
|
||||
initializationJob?.join()
|
||||
@@ -289,3 +342,32 @@ abstract class PythonPackageManager(val project: Project, val sdk: Sdk) : Dispos
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extracts project top-level dependencies (blocking version for Java interop).
|
||||
* Returns null by default — this manager doesn't support dependency extraction.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
@RequiresBackgroundThread
|
||||
fun PythonPackageManager.extractDependenciesAsync(): List<PythonPackage>? = runBlockingMaybeCancellable {
|
||||
extractDependenciesCached()
|
||||
}?.getOrNull()
|
||||
|
||||
/**
|
||||
* Resolves pyproject.toml file from a working directory path.
|
||||
* Used by pyproject.toml-based package managers (Poetry, Hatch, UV).
|
||||
*
|
||||
* @param workingDirectory The directory path where pyproject.toml is expected
|
||||
* @return VirtualFile for pyproject.toml, or null if not found
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
@RequiresBackgroundThread
|
||||
internal fun resolvePyProjectToml(workingDirectory: Path): VirtualFile? {
|
||||
val pyprojectPath = workingDirectory.resolve(PY_PROJECT_TOML)
|
||||
return runBlockingMaybeCancellable {
|
||||
readAction {
|
||||
VirtualFileManager.getInstance().refreshAndFindFileByNioPath(pyprojectPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,23 +4,28 @@ package com.jetbrains.python.packaging.pip
|
||||
import com.intellij.execution.configurations.GeneralCommandLine
|
||||
import com.intellij.execution.process.CapturingProcessHandler
|
||||
import com.intellij.execution.process.ProcessOutput
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.application.readAction
|
||||
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
|
||||
import com.jetbrains.python.packaging.PyRequirement
|
||||
import com.jetbrains.python.packaging.common.PythonOutdatedPackage
|
||||
import com.jetbrains.python.packaging.common.PythonPackage
|
||||
import com.jetbrains.python.packaging.common.PythonRepositoryPackageSpecification
|
||||
import com.jetbrains.python.packaging.dependencies.PythonDependenciesManager
|
||||
import com.jetbrains.python.packaging.PyRequirementParser
|
||||
import com.jetbrains.python.packaging.common.toPythonPackage
|
||||
import com.jetbrains.python.packaging.management.PythonPackageInstallRequest
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManager
|
||||
import com.jetbrains.python.packaging.management.PythonRepositoryManager
|
||||
import com.jetbrains.python.packaging.management.hasInstalledPackage
|
||||
import com.jetbrains.python.packaging.requirementsTxt.PythonRequirementsTxtManager
|
||||
import com.jetbrains.python.packaging.setupPy.SetupPyManager
|
||||
import com.jetbrains.python.packaging.requirementsTxt.RequirementsTxtManipulationHelper
|
||||
import com.jetbrains.python.psi.LanguageLevel
|
||||
import com.jetbrains.python.sdk.PythonSdkAdditionalData
|
||||
import com.jetbrains.python.sdk.associatedModuleDir
|
||||
import com.jetbrains.python.sdk.legacy.PythonSdkUtil
|
||||
import com.jetbrains.python.statistics.version
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -41,19 +46,8 @@ open class PipPythonPackageManager(project: Project, sdk: Sdk) : PythonPackageMa
|
||||
options: List<String>,
|
||||
): PyResult<Unit> = engine.installPackageCommand(installRequest, options)
|
||||
|
||||
override fun getDependencyManager(): PythonDependenciesManager? {
|
||||
val requirementsTxtManager = PythonRequirementsTxtManager.getInstance(project, sdk)
|
||||
if (requirementsTxtManager.getDependenciesFile() != null)
|
||||
return requirementsTxtManager
|
||||
val setupPyManager = SetupPyManager.getInstance(project, sdk)
|
||||
if (setupPyManager.getDependenciesFile() != null)
|
||||
return setupPyManager
|
||||
return null
|
||||
}
|
||||
|
||||
override suspend fun syncCommand(): PyResult<Unit> {
|
||||
val requirementsManager = getDependencyManager() as? PythonRequirementsTxtManager
|
||||
val requirementsFile = requirementsManager?.getDependenciesFile()
|
||||
val requirementsFile = getDependencyFile()
|
||||
return if (requirementsFile != null) {
|
||||
engine.syncRequirementsTxt(requirementsFile)
|
||||
}
|
||||
@@ -76,6 +70,27 @@ open class PipPythonPackageManager(project: Project, sdk: Sdk) : PythonPackageMa
|
||||
override suspend fun uninstallPackageCommand(vararg pythonPackages: String): PyResult<Unit> = engine.uninstallPackageCommand(*pythonPackages)
|
||||
|
||||
override suspend fun loadPackagesCommand(): PyResult<List<PythonPackage>> = engine.loadPackagesCommand()
|
||||
|
||||
override suspend fun extractDependencies(): PyResult<List<PythonPackage>>? {
|
||||
val requirementsFile = getDependencyFile() ?: return null
|
||||
val requirements = readAction {
|
||||
PyRequirementParser.fromFile(requirementsFile)
|
||||
}
|
||||
return PyResult.success(requirements.mapNotNull { it.toPythonPackage() })
|
||||
}
|
||||
|
||||
override fun getDependencyFile(): VirtualFile? {
|
||||
val data = sdk.sdkAdditionalData as? PythonSdkAdditionalData ?: return null
|
||||
val requirementsPath = data.requiredTxtPath ?: Path.of(PythonSdkAdditionalData.REQUIREMENT_TXT_DEFAULT)
|
||||
return sdk.associatedModuleDir?.findFileByRelativePath(requirementsPath.toString())
|
||||
}
|
||||
|
||||
override suspend fun addDependencyImpl(requirement: PyRequirement): Boolean {
|
||||
val requirementsFile = getDependencyFile() ?: return false
|
||||
return withContext(Dispatchers.EDT) {
|
||||
RequirementsTxtManipulationHelper.addToRequirementsTxt(project, requirementsFile, requirement.presentableText)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
// 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.pipenv
|
||||
|
||||
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.intellij.openapi.util.FilesModificationTrackerBase
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.getOrCreateUserDataUnsafe
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.jetbrains.python.PythonPluginDisposable
|
||||
import com.jetbrains.python.packaging.PyRequirement
|
||||
import com.jetbrains.python.packaging.dependencies.cache.PythonDependenciesManagerCached
|
||||
import com.jetbrains.python.sdk.pipenv.PipEnvFileHelper
|
||||
import com.jetbrains.python.sdk.pipenv.PipEnvParser
|
||||
|
||||
internal class PipEnvDependenciesManager private constructor(project: Project, val sdk: Sdk) : PythonDependenciesManagerCached(project) {
|
||||
override fun getModificationTracker(): FilesModificationTrackerBase {
|
||||
return PipEnvLockModificationTracker.getInstance(project)
|
||||
}
|
||||
|
||||
override fun parseRequirements(requirementsFile: VirtualFile): List<PyRequirement>? {
|
||||
return PipEnvParser.getPipFileLockRequirements(requirementsFile)
|
||||
}
|
||||
|
||||
override fun isAddDependencyPossible(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun addDependency(packageName: String): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun getDependenciesFile(): VirtualFile? {
|
||||
return PipEnvFileHelper.getPipFileLock(sdk)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val KEY = Key<PipEnvDependenciesManager>(this::class.java.name)
|
||||
|
||||
fun getInstance(project: Project, sdk: Sdk): PipEnvDependenciesManager = sdk.getOrCreateUserDataUnsafe(KEY) {
|
||||
PipEnvDependenciesManager(project, sdk).also {
|
||||
Disposer.register(PythonPluginDisposable.getInstance(project), it)
|
||||
Disposer.register(it, Disposable { sdk.putUserData(KEY, null) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,16 @@ import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.packaging.common.PythonOutdatedPackage
|
||||
import com.jetbrains.python.packaging.common.PythonPackage
|
||||
import com.jetbrains.python.packaging.common.PythonRepositoryPackageSpecification
|
||||
import com.jetbrains.python.packaging.dependencies.PythonDependenciesManager
|
||||
import com.jetbrains.python.packaging.common.toPythonPackages
|
||||
|
||||
import com.jetbrains.python.packaging.management.PythonPackageInstallRequest
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManager
|
||||
import com.jetbrains.python.packaging.management.PythonRepositoryManager
|
||||
import com.jetbrains.python.packaging.pip.PipRepositoryManager
|
||||
import com.jetbrains.python.sdk.associatedModulePath
|
||||
import com.jetbrains.python.sdk.pipenv.PipEnvFileHelper
|
||||
import com.jetbrains.python.sdk.pipenv.runPipEnv
|
||||
import com.jetbrains.python.sdk.pipenv.PipEnvParser as SdkPipEnvParser
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.nio.file.Path
|
||||
|
||||
@@ -32,12 +35,6 @@ class PipEnvPackageManager(project: Project, sdk: Sdk) : PythonPackageManager(pr
|
||||
return runPipEnv(modulePath, "lock").mapSuccess { }
|
||||
}
|
||||
|
||||
|
||||
override fun getDependencyManager(): PythonDependenciesManager {
|
||||
return PipEnvDependenciesManager.getInstance(project, sdk)
|
||||
}
|
||||
|
||||
|
||||
override suspend fun installPackageCommand(installRequest: PythonPackageInstallRequest, options: List<String>): PyResult<Unit> {
|
||||
return when (installRequest) {
|
||||
is PythonPackageInstallRequest.ByLocation -> TODO("Not yet implemented")
|
||||
@@ -80,4 +77,13 @@ class PipEnvPackageManager(project: Project, sdk: Sdk) : PythonPackageManager(pr
|
||||
return PyResult.success(emptyList())
|
||||
}
|
||||
|
||||
override suspend fun extractDependencies(): PyResult<List<PythonPackage>>? {
|
||||
val pipFileLock = getDependencyFile() ?: return null
|
||||
val requirements = SdkPipEnvParser.getPipFileLockRequirements(pipFileLock) ?: return null
|
||||
return PyResult.success(requirements.toPythonPackages())
|
||||
}
|
||||
|
||||
override fun getDependencyFile(): com.intellij.openapi.vfs.VirtualFile? {
|
||||
return PipEnvFileHelper.getPipFileLock(sdk)
|
||||
}
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
// 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.requirementsTxt
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.runReadAction
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.FilesModificationTrackerBase
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.getOrCreateUserDataUnsafe
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.jetbrains.python.PythonPluginDisposable
|
||||
import com.jetbrains.python.packaging.PyRequirement
|
||||
import com.jetbrains.python.packaging.PyRequirementParser
|
||||
import com.jetbrains.python.packaging.dependencies.cache.PythonDependenciesManagerCached
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@ApiStatus.Internal
|
||||
class PythonRequirementsTxtManager private constructor(project: Project, val sdk: Sdk) : PythonDependenciesManagerCached(project) {
|
||||
override fun getDependenciesFile(): VirtualFile? = PythonRequirementTxtSdkUtils.findRequirementsTxt(sdk)
|
||||
|
||||
override fun getModificationTracker(): FilesModificationTrackerBase {
|
||||
return RequirementTxtModificationTracker.getInstance(project)
|
||||
}
|
||||
|
||||
override fun parseRequirements(requirementsFile: VirtualFile): List<PyRequirement> {
|
||||
return runReadAction { PyRequirementParser.fromFile(requirementsFile) }
|
||||
}
|
||||
|
||||
override fun addDependency(packageName: String): Boolean {
|
||||
val virtualFile = getDependenciesFile() ?: return false
|
||||
return RequirementsTxtManipulationHelper.addToRequirementsTxt(project, virtualFile, packageName)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val KEY = Key<PythonRequirementsTxtManager>(this::class.java.name)
|
||||
|
||||
@JvmStatic
|
||||
fun getInstance(project: Project, sdk: Sdk): PythonRequirementsTxtManager = sdk.getOrCreateUserDataUnsafe(KEY) {
|
||||
PythonRequirementsTxtManager(project, sdk).also {
|
||||
Disposer.register(PythonPluginDisposable.getInstance(project), it)
|
||||
Disposer.register(it, Disposable { sdk.putUserData(KEY, null) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,9 +30,11 @@ import com.jetbrains.python.psi.PyUtil
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext
|
||||
import com.jetbrains.python.sdk.rootManager
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
|
||||
internal object SetupPyHelpers {
|
||||
@ApiStatus.Internal
|
||||
object SetupPyHelpers {
|
||||
const val SETUP_PY: String = "setup.py"
|
||||
const val REQUIRES: String = "requires"
|
||||
const val INSTALL_REQUIRES: String = "install_requires"
|
||||
@@ -51,6 +53,7 @@ internal object SetupPyHelpers {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun parseSetupPy(file: PyFile): List<PyRequirement>? {
|
||||
val setupCall = findSetupCall(file) ?: return null
|
||||
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
// 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.setupPy
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.runReadAction
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.FilesModificationTrackerBase
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.getOrCreateUserDataUnsafe
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.jetbrains.python.PythonPluginDisposable
|
||||
import com.jetbrains.python.packaging.PyRequirement
|
||||
import com.jetbrains.python.packaging.dependencies.cache.PythonDependenciesManagerCached
|
||||
import com.jetbrains.python.psi.PyFile
|
||||
import com.jetbrains.python.sdk.associatedModuleDir
|
||||
import com.jetbrains.python.sdk.sdkFlavor
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@ApiStatus.Internal
|
||||
class SetupPyManager private constructor(project: Project, val sdk: Sdk) : PythonDependenciesManagerCached(project) {
|
||||
override fun getDependenciesFile(): VirtualFile? {
|
||||
val moduleDir = sdk.associatedModuleDir ?: return null
|
||||
val virtualFile = moduleDir.findFileByRelativePath(SETUP_PY) ?: return null
|
||||
return virtualFile
|
||||
}
|
||||
|
||||
fun getRequirementsPsiFile(): PyFile? {
|
||||
val file = getDependenciesFile() ?: return null
|
||||
return runReadAction {
|
||||
PsiManager.getInstance(project).findFile(file) as? PyFile
|
||||
}
|
||||
}
|
||||
|
||||
override fun getModificationTracker(): FilesModificationTrackerBase {
|
||||
return SetupPyModificationTracker.getInstance(project)
|
||||
}
|
||||
|
||||
override fun parseRequirements(requirementsFile: VirtualFile): List<PyRequirement>? {
|
||||
val psiFile = convertToPsiFile(requirementsFile) ?: return null
|
||||
val parseSetupPy = SetupPyHelpers.parseSetupPy(psiFile) ?: return null
|
||||
return parseSetupPy
|
||||
}
|
||||
|
||||
|
||||
override fun addDependency(packageName: String): Boolean {
|
||||
val file = getRequirementsPsiFile() ?: return false
|
||||
val languageLevel = sdk.sdkFlavor.getLanguageLevel(sdk)
|
||||
return SetupPyHelpers.addRequirementsToSetupPy(file, packageName, languageLevel)
|
||||
}
|
||||
|
||||
|
||||
private fun convertToPsiFile(requirementsFile: VirtualFile): PyFile? = runReadAction {
|
||||
PsiManager.getInstance(project).findFile(requirementsFile) as? PyFile
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val KEY = Key<SetupPyManager>(this::class.java.name)
|
||||
|
||||
@ApiStatus.Internal
|
||||
const val SETUP_PY: String = SetupPyHelpers.SETUP_PY
|
||||
|
||||
@JvmStatic
|
||||
fun getInstance(project: Project, sdk: Sdk): SetupPyManager = sdk.getOrCreateUserDataUnsafe(KEY) {
|
||||
SetupPyManager(project, sdk).also {
|
||||
Disposer.register(PythonPluginDisposable.getInstance(project), it)
|
||||
Disposer.register(it, Disposable { sdk.putUserData(KEY, null) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-4
@@ -371,7 +371,7 @@ class PyPackagingToolWindowService(val project: Project, val serviceScope: Corou
|
||||
suspend fun refreshInstalledPackages() {
|
||||
val context = sdkContext ?: return
|
||||
|
||||
val declaredPackages = context.manager.extractDependencies()?.getOr {
|
||||
val declaredPackages = context.manager.extractDependenciesCached()?.getOr {
|
||||
withContext(Dispatchers.EDT) {
|
||||
val errorMessage = context.manager.syncErrorMessage() ?: return@withContext
|
||||
showErrorNode(errorMessage.descriptionMessage, errorMessage.fixCommandMessage) {
|
||||
@@ -381,8 +381,10 @@ class PyPackagingToolWindowService(val project: Project, val serviceScope: Corou
|
||||
return
|
||||
} ?: emptyList()
|
||||
|
||||
val declaredPackageNames = declaredPackages.map { it.name }.toSet()
|
||||
|
||||
withContext(Dispatchers.Default) {
|
||||
val installedDeclaredPackages = findInstalledDeclaredPackages(context, declaredPackages)
|
||||
val installedDeclaredPackages = findInstalledDeclaredPackages(context, declaredPackageNames)
|
||||
val treeExtractor = PythonPackageRequirementsTreeExtractor.forSdk(context.sdk)
|
||||
|
||||
val packagesWithDependencies = if (treeExtractor != null) {
|
||||
@@ -406,9 +408,9 @@ class PyPackagingToolWindowService(val project: Project, val serviceScope: Corou
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun findInstalledDeclaredPackages(context: SdkContext, declaredPackages: List<PythonPackage>): List<PythonPackage> =
|
||||
private suspend fun findInstalledDeclaredPackages(context: SdkContext, declaredPackageNames: Set<String>): List<PythonPackage> =
|
||||
context.manager.listInstalledPackages().filter {
|
||||
it.name in declaredPackages.map { pkg -> pkg.name }
|
||||
it.name in declaredPackageNames
|
||||
}
|
||||
|
||||
private suspend fun processPackagesWithRequirementsTree(
|
||||
|
||||
@@ -307,7 +307,8 @@ val Sdk.associatedModuleNioPath: Path?
|
||||
|
||||
private val SDK_ERROR_REPORTED = Key.create<Boolean>("pySdkErrorReported")
|
||||
|
||||
internal val Sdk.associatedModuleDir: VirtualFile?
|
||||
@get:Internal
|
||||
val Sdk.associatedModuleDir: VirtualFile?
|
||||
get() {
|
||||
val nioPath = associatedModuleNioPath ?: return null
|
||||
return VirtualFileManager.getInstance().findFileByNioPath(nioPath) ?: TempFileSystem.getInstance().findFileByNioFile(nioPath)
|
||||
|
||||
@@ -3,19 +3,24 @@ package com.jetbrains.python.sdk.poetry
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.packaging.PyPackageName
|
||||
import com.jetbrains.python.packaging.PyRequirement
|
||||
import com.jetbrains.python.packaging.common.PythonOutdatedPackage
|
||||
import com.jetbrains.python.packaging.common.PythonPackage
|
||||
import com.jetbrains.python.packaging.common.PythonRepositoryPackageSpecification
|
||||
import com.jetbrains.python.packaging.management.PythonPackageInstallRequest
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManager
|
||||
import com.jetbrains.python.packaging.management.PythonRepositoryManager
|
||||
import com.jetbrains.python.packaging.management.resolvePyProjectToml
|
||||
import com.jetbrains.python.packaging.pip.PipRepositoryManager
|
||||
import com.jetbrains.python.packaging.pyRequirement
|
||||
import com.jetbrains.python.sdk.associatedModulePath
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import java.nio.file.Path
|
||||
|
||||
@ApiStatus.Internal
|
||||
class PoetryPackageManager(project: Project, sdk: Sdk) : PythonPackageManager(project, sdk) {
|
||||
@@ -166,6 +171,17 @@ class PoetryPackageManager(project: Project, sdk: Sdk) : PythonPackageManager(pr
|
||||
private fun PythonRepositoryPackageSpecification.getPackageWithVersionInPoetryFormat(): String {
|
||||
return versionSpec?.let { "$name@${it.presentableText}" } ?: name
|
||||
}
|
||||
|
||||
override fun getDependencyFile(): VirtualFile? {
|
||||
val projectPathStr = sdk.associatedModulePath ?: return null
|
||||
val projectPath = Path.of(projectPathStr)
|
||||
return resolvePyProjectToml(projectPath)
|
||||
}
|
||||
|
||||
override suspend fun addDependencyImpl(requirement: PyRequirement): Boolean {
|
||||
poetryInstallPackage(sdk, listOf(requirement.presentableText), emptyList()).getOr { return false }
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,10 +4,12 @@ package com.jetbrains.python.sdk.uv
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.util.cancelOnDispose
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.jetbrains.python.PyBundle.message
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.packaging.PyPackageName
|
||||
import com.jetbrains.python.packaging.PyRequirement
|
||||
import com.jetbrains.python.packaging.common.PythonOutdatedPackage
|
||||
import com.jetbrains.python.packaging.common.PythonPackage
|
||||
import com.jetbrains.python.packaging.common.PythonRepositoryPackageSpecification
|
||||
@@ -16,11 +18,14 @@ import com.jetbrains.python.packaging.management.PythonPackageManager
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManager.Companion.PackageManagerErrorMessage
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManagerProvider
|
||||
import com.jetbrains.python.packaging.management.PythonRepositoryManager
|
||||
import com.jetbrains.python.packaging.management.resolvePyProjectToml
|
||||
import com.jetbrains.python.packaging.pip.PipRepositoryManager
|
||||
import com.jetbrains.python.packaging.pyRequirement
|
||||
import com.jetbrains.python.packaging.utils.PyPackageCoroutine
|
||||
import com.jetbrains.python.sdk.uv.impl.createUvCli
|
||||
import com.jetbrains.python.sdk.uv.impl.createUvLowLevel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.async
|
||||
@@ -144,6 +149,23 @@ internal class UvPackageManager(project: Project, sdk: Sdk, uvLowLevelDeferred:
|
||||
reloadPackages().mapSuccess { }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getDependencyFile(): VirtualFile? {
|
||||
val uvWorkingDirectory = (sdk.sdkAdditionalData as? UvSdkAdditionalData)?.uvWorkingDirectory ?: return null
|
||||
return resolvePyProjectToml(uvWorkingDirectory)
|
||||
}
|
||||
|
||||
override suspend fun addDependencyImpl(requirement: PyRequirement): Boolean = withContext(Dispatchers.IO) {
|
||||
val specification = repositoryManager.findPackageSpecification(requirement) ?: return@withContext false
|
||||
|
||||
val request = PythonPackageInstallRequest.ByRepositoryPythonPackageSpecifications(listOf(specification))
|
||||
|
||||
withUv { uv ->
|
||||
uv.addDependency(request, emptyList())
|
||||
}.getOr { return@withContext false }
|
||||
|
||||
return@withContext true
|
||||
}
|
||||
}
|
||||
|
||||
class UvPackageManagerProvider : PythonPackageManagerProvider {
|
||||
|
||||
@@ -12,7 +12,9 @@ import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
|
||||
import com.jetbrains.python.extensions.getSdk
|
||||
import com.jetbrains.python.packaging.PyPIPackageCache
|
||||
import com.jetbrains.python.packaging.PyPackageName
|
||||
import com.jetbrains.python.packaging.common.toRequirements
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManager
|
||||
import com.jetbrains.python.packaging.management.extractDependenciesAsync
|
||||
import com.jetbrains.python.sdk.PythonSdkAdditionalData
|
||||
import com.jetbrains.python.sdk.legacy.PythonSdkUtil
|
||||
|
||||
@@ -48,7 +50,7 @@ internal class PyPackageVersionUsagesCollector : ProjectUsagesCollector() {
|
||||
val sdk = module.getSdk() ?: continue
|
||||
if (!PythonSdkUtil.isPythonSdk(sdk)) continue
|
||||
val usageData = getPythonSpecificInfo(sdk)
|
||||
val requirements = PythonPackageManager.forSdk(project, sdk).getDependencyManager()?.getDependencies().orEmpty()
|
||||
val requirements = PythonPackageManager.forSdk(project, sdk).extractDependenciesAsync()?.toRequirements().orEmpty()
|
||||
requirements
|
||||
.filter { pypiPackages.containsPackage(it.name) }
|
||||
.forEach { req ->
|
||||
|
||||
+9
-6
@@ -12,7 +12,6 @@ import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.packaging.PyPackageUtil
|
||||
import com.jetbrains.python.packaging.management.PythonPackageManager
|
||||
import com.jetbrains.python.packaging.requirementsTxt.PythonRequirementTxtSdkUtils
|
||||
import com.jetbrains.python.packaging.setupPy.SetupPyManager
|
||||
import com.jetbrains.python.projectCreation.createVenvAndSdk
|
||||
import com.jetbrains.python.sdk.ModuleOrProject
|
||||
import com.jetbrains.python.sdk.configuration.CreateSdkInfo
|
||||
@@ -22,6 +21,11 @@ import com.jetbrains.python.sdk.configuration.PyProjectTomlConfigurationExtensio
|
||||
import com.jetbrains.python.sdk.configuration.PySdkConfigurationCollector
|
||||
import com.jetbrains.python.sdk.configuration.PySdkConfigurationCollector.VirtualEnvResult
|
||||
import com.jetbrains.python.sdk.configuration.prepareSdkCreator
|
||||
import com.jetbrains.python.packaging.setupPy.SetupPyHelpers.SETUP_PY
|
||||
import com.jetbrains.python.sdk.configuration.*
|
||||
import com.intellij.openapi.application.readAction
|
||||
|
||||
internal val PY_REQ_TOOL_ID = ToolId("requirements.txt")
|
||||
|
||||
internal class PyRequirementsTxtOrSetupPySdkConfiguration : PyProjectSdkConfigurationExtension {
|
||||
|
||||
@@ -34,8 +38,8 @@ internal class PyRequirementsTxtOrSetupPySdkConfiguration : PyProjectSdkConfigur
|
||||
|
||||
override fun asPyProjectTomlSdkConfigurationExtension(): PyProjectTomlConfigurationExtension? = null
|
||||
|
||||
private fun checkManageableEnv(module: Module): EnvCheckerResult {
|
||||
val configFile = getRequirementsTxtOrSetupPy(module) ?: return EnvCheckerResult.CannotConfigure
|
||||
private suspend fun checkManageableEnv(module: Module): EnvCheckerResult {
|
||||
val configFile = readAction { getRequirementsTxtOrSetupPy(module) } ?: return EnvCheckerResult.CannotConfigure
|
||||
return EnvCheckerResult.EnvNotFound(PyBundle.message("sdk.create.venv.suggestion", configFile.name))
|
||||
}
|
||||
|
||||
@@ -43,14 +47,14 @@ internal class PyRequirementsTxtOrSetupPySdkConfiguration : PyProjectSdkConfigur
|
||||
val sdk = createVenvAndSdk(ModuleOrProject.ModuleAndProject(module)).getOr { return it }
|
||||
PySdkConfigurationCollector.logVirtualEnv(module.project, VirtualEnvResult.CREATED)
|
||||
|
||||
val requirementsTxtOrSetupPyFile = getRequirementsTxtOrSetupPy(module)
|
||||
val requirementsTxtOrSetupPyFile = readAction { getRequirementsTxtOrSetupPy(module) }
|
||||
if (requirementsTxtOrSetupPyFile == null) {
|
||||
PySdkConfigurationCollector.logVirtualEnv(module.project, VirtualEnvResult.DEPS_NOT_FOUND)
|
||||
thisLogger().warn("File with dependencies is not found")
|
||||
return PyResult.success(sdk)
|
||||
}
|
||||
|
||||
val isRequirements = requirementsTxtOrSetupPyFile.name != SetupPyManager.SETUP_PY
|
||||
val isRequirements = requirementsTxtOrSetupPyFile.name != SETUP_PY
|
||||
|
||||
if (isRequirements) {
|
||||
PythonRequirementTxtSdkUtils.saveRequirementsTxtPath(module.project, sdk, requirementsTxtOrSetupPyFile.toNioPath())
|
||||
@@ -61,5 +65,4 @@ internal class PyRequirementsTxtOrSetupPySdkConfiguration : PyProjectSdkConfigur
|
||||
|
||||
private fun getRequirementsTxtOrSetupPy(module: Module) =
|
||||
PyPackageUtil.findRequirementsTxt(module) ?: PyPackageUtil.findSetupPy(module)?.virtualFile
|
||||
|
||||
}
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
// 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.application.readAction
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.packaging.PyRequirementParser
|
||||
import com.jetbrains.python.packaging.common.PythonOutdatedPackage
|
||||
import com.jetbrains.python.packaging.common.PythonPackage
|
||||
import com.jetbrains.python.packaging.common.PythonPackageDetails
|
||||
import com.jetbrains.python.packaging.common.PythonRepositoryPackageSpecification
|
||||
import com.jetbrains.python.packaging.conda.environmentYml.CondaEnvironmentYmlManager
|
||||
import com.jetbrains.python.packaging.dependencies.PythonDependenciesManager
|
||||
import com.jetbrains.python.packaging.requirementsTxt.PythonRequirementsTxtManager
|
||||
import com.jetbrains.python.packaging.setupPy.SetupPyManager
|
||||
import com.jetbrains.python.packaging.common.toPythonPackage
|
||||
import com.jetbrains.python.packaging.common.toPythonPackages
|
||||
import com.jetbrains.python.packaging.conda.environmentYml.format.CondaEnvironmentYmlParser
|
||||
import com.jetbrains.python.packaging.setupPy.SetupPyHelpers
|
||||
import com.jetbrains.python.psi.PyFile
|
||||
import com.jetbrains.python.sdk.associatedModuleDir
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
|
||||
@TestOnly
|
||||
@@ -31,15 +37,6 @@ class TestPythonPackageManager(project: Project, sdk: Sdk) : PythonPackageManage
|
||||
.withPackageDetails(packageDetails)
|
||||
.withRepoPackagesVersions(packageVersions)
|
||||
|
||||
override fun getDependencyManager(): PythonDependenciesManager? {
|
||||
val data = sdk.getUserData(REQUIREMENTS_PROVIDER_KEY) ?: return null
|
||||
return when (data) {
|
||||
RequirementsProviderType.REQUIREMENTS_TXT -> PythonRequirementsTxtManager.getInstance(project, sdk)
|
||||
RequirementsProviderType.SETUP_PY -> SetupPyManager.getInstance(project, sdk)
|
||||
RequirementsProviderType.ENVIRONMENT_YML -> CondaEnvironmentYmlManager.getInstance(project, sdk)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun loadOutdatedPackagesCommand(): PyResult<List<PythonOutdatedPackage>> {
|
||||
return PyResult.success(emptyList())
|
||||
}
|
||||
@@ -82,6 +79,59 @@ class TestPythonPackageManager(project: Project, sdk: Sdk) : PythonPackageManage
|
||||
return PyResult.success(installedPackages)
|
||||
}
|
||||
|
||||
override fun getDependencyFile(): VirtualFile? {
|
||||
val providerType = sdk.getUserData(REQUIREMENTS_PROVIDER_KEY) ?: return null
|
||||
val moduleDir = sdk.associatedModuleDir ?: return null
|
||||
|
||||
return when (providerType) {
|
||||
RequirementsProviderType.REQUIREMENTS_TXT -> moduleDir.findChild("requirements.txt")
|
||||
RequirementsProviderType.SETUP_PY -> moduleDir.findChild("setup.py")
|
||||
RequirementsProviderType.ENVIRONMENT_YML -> moduleDir.findChild("environment.yml")
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun extractDependencies(): PyResult<List<PythonPackage>>? {
|
||||
val providerType = sdk.getUserData(REQUIREMENTS_PROVIDER_KEY) ?: return null
|
||||
val moduleDir = sdk.associatedModuleDir ?: return null
|
||||
|
||||
return when (providerType) {
|
||||
RequirementsProviderType.REQUIREMENTS_TXT -> {
|
||||
val requirementsFile = moduleDir.findChild("requirements.txt") ?: return null
|
||||
extractFromRequirementsTxt(requirementsFile)
|
||||
}
|
||||
RequirementsProviderType.SETUP_PY -> {
|
||||
val setupPyFile = moduleDir.findChild("setup.py") ?: return null
|
||||
extractFromSetupPy(setupPyFile)
|
||||
}
|
||||
RequirementsProviderType.ENVIRONMENT_YML -> {
|
||||
val environmentYmlFile = moduleDir.findChild("environment.yml") ?: return null
|
||||
extractFromEnvironmentYml(environmentYmlFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun extractFromRequirementsTxt(file: VirtualFile): PyResult<List<PythonPackage>> {
|
||||
val requirements = readAction {
|
||||
PyRequirementParser.fromFile(file)
|
||||
}
|
||||
return PyResult.success(requirements.mapNotNull { requirement -> requirement.toPythonPackage() })
|
||||
}
|
||||
|
||||
private suspend fun extractFromSetupPy(file: VirtualFile): PyResult<List<PythonPackage>>? {
|
||||
val requirements = readAction {
|
||||
val pyFile = PsiManager.getInstance(project).findFile(file) as? PyFile ?: return@readAction null
|
||||
SetupPyHelpers.parseSetupPy(pyFile)
|
||||
} ?: return null
|
||||
return PyResult.success(requirements.mapNotNull { requirement -> requirement.toPythonPackage() })
|
||||
}
|
||||
|
||||
private suspend fun extractFromEnvironmentYml(file: VirtualFile): PyResult<List<PythonPackage>>? {
|
||||
val requirements = readAction {
|
||||
CondaEnvironmentYmlParser.fromFile(file)
|
||||
} ?: return null
|
||||
return PyResult.success(requirements.toPythonPackages())
|
||||
}
|
||||
|
||||
private fun findPackageByName(name: String): PythonPackage? {
|
||||
return installedPackages.find { it.name == name }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user