mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-26492 Install, uninstall, show list of packages via pipenv
This commit is contained in:
@@ -21,6 +21,7 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.jetbrains.python.packaging.ui.PyCondaManagementService;
|
||||
import com.jetbrains.python.packaging.ui.PyPackageManagementService;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import com.jetbrains.python.sdk.flavors.PipenvKt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -30,17 +31,20 @@ import java.util.Map;
|
||||
* @author yole
|
||||
*/
|
||||
public class PyPackageManagersImpl extends PyPackageManagers {
|
||||
private final Map<String, PyPackageManagerImpl> myInstances = new HashMap<>();
|
||||
private final Map<String, PyPackageManager> myInstances = new HashMap<>();
|
||||
|
||||
@NotNull
|
||||
public synchronized PyPackageManager forSdk(@NotNull final Sdk sdk) {
|
||||
final String key = PythonSdkType.getSdkKey(sdk);
|
||||
PyPackageManagerImpl manager = myInstances.get(key);
|
||||
PyPackageManager manager = myInstances.get(key);
|
||||
if (manager == null) {
|
||||
final VirtualFile homeDirectory = sdk.getHomeDirectory();
|
||||
if (PythonSdkType.isRemote(sdk)) {
|
||||
manager = new PyRemotePackageManagerImpl(sdk);
|
||||
}
|
||||
else if (PipenvKt.isPipEnv(sdk)) {
|
||||
manager = new PyPipEnvPackageManager(sdk);
|
||||
}
|
||||
else if (PyCondaPackageManagerImpl.isConda(sdk) &&
|
||||
homeDirectory != null &&
|
||||
PyCondaPackageService.getCondaExecutable(homeDirectory) != null) {
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.python.packaging
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonSyntaxException
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.intellij.execution.ExecutionException
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.roots.OrderRootType
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.jetbrains.python.sdk.PythonSdkType
|
||||
import com.jetbrains.python.sdk.flavors.runPipEnv
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
class PyPipEnvPackageManager(val sdk: Sdk) : PyPackageManager() {
|
||||
@Volatile
|
||||
private var packages: List<PyPackage>? = null
|
||||
|
||||
override fun installManagement() {}
|
||||
|
||||
override fun hasManagement() = true
|
||||
|
||||
override fun install(requirementString: String) {
|
||||
install(parseRequirements(requirementString), emptyList())
|
||||
}
|
||||
|
||||
override fun install(requirements: List<PyRequirement>, extraArgs: List<String>) {
|
||||
val args = listOf("install") +
|
||||
requirements.flatMap { it.installOptions } +
|
||||
extraArgs
|
||||
try {
|
||||
runPipEnv(sdk, *args.toTypedArray())
|
||||
}
|
||||
finally {
|
||||
refreshAndGetPackages(true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun uninstall(packages: List<PyPackage>) {
|
||||
val args = listOf("uninstall") +
|
||||
packages.map { it.name }
|
||||
try {
|
||||
runPipEnv(sdk, *args.toTypedArray())
|
||||
}
|
||||
finally {
|
||||
refreshAndGetPackages(true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
with(ApplicationManager.getApplication()) {
|
||||
invokeLater {
|
||||
runWriteAction {
|
||||
val files = sdk.rootProvider.getFiles(OrderRootType.CLASSES)
|
||||
VfsUtil.markDirtyAndRefresh(true, true, true, *files)
|
||||
}
|
||||
PythonSdkType.getInstance().setupSdkPaths(sdk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun createVirtualEnv(destinationDir: String, useGlobalSite: Boolean): String {
|
||||
throw ExecutionException("Creating virtual environments based on Pipenv environments is not supported")
|
||||
}
|
||||
|
||||
override fun getPackages() = packages
|
||||
|
||||
override fun refreshAndGetPackages(alwaysRefresh: Boolean): List<PyPackage> {
|
||||
if (alwaysRefresh || packages == null) {
|
||||
val output = try {
|
||||
runPipEnv(sdk, "graph", "--json")
|
||||
}
|
||||
catch (e: ExecutionException) {
|
||||
packages = emptyList()
|
||||
throw e
|
||||
}
|
||||
packages = parsePipEnvGraph(output)
|
||||
}
|
||||
return packages ?: emptyList()
|
||||
}
|
||||
|
||||
override fun getRequirements(module: Module): List<PyRequirement>? = null
|
||||
|
||||
override fun parseRequirements(text: String): List<PyRequirement> =
|
||||
PyPackageUtil.fix(PyRequirement.fromText(text))
|
||||
|
||||
override fun getDependents(pkg: PyPackage): Set<PyPackage> {
|
||||
// TODO: Parse the dependency information from `pipenv graph`
|
||||
return emptySet()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private data class GraphPackage(@SerializedName("key") var key: String,
|
||||
@SerializedName("package_name") var packageName: String,
|
||||
@SerializedName("installed_version") var installedVersion: String,
|
||||
@SerializedName("required_version") var requiredVersion: String?)
|
||||
|
||||
private data class GraphEntry(@SerializedName("package") var pkg: GraphPackage,
|
||||
@SerializedName("dependencies") var dependencies: List<GraphPackage>)
|
||||
|
||||
/**
|
||||
* Parses the output of `pipenv graph --json` into a list of packages.
|
||||
*/
|
||||
private fun parsePipEnvGraph(input: String): List<PyPackage> {
|
||||
val entries = try {
|
||||
Gson().fromJson(input, Array<GraphEntry>::class.java)
|
||||
}
|
||||
catch (e: JsonSyntaxException) {
|
||||
// TODO: Log errors
|
||||
return emptyList()
|
||||
}
|
||||
return entries
|
||||
.asSequence()
|
||||
.filterNotNull()
|
||||
.map { PyPackage(it.pkg.packageName, it.pkg.installedVersion, null, emptyList()) }
|
||||
.toList()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,6 +121,16 @@ fun setupPipEnv(projectPath: @SystemDependent String, python: String?, installPa
|
||||
return runPipEnv(projectPath, "--venv").trim()
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the configured pipenv for the specified Pipenv SDK with the associated project path.
|
||||
*/
|
||||
fun runPipEnv(sdk: Sdk, vararg args: String): String {
|
||||
val projectPath = sdk.associatedModulePath ?:
|
||||
throw PyExecutionException("Cannot find the project associated with this Pipenv environment",
|
||||
"Pipenv", emptyList(), ProcessOutput())
|
||||
return runPipEnv(projectPath, *args)
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the configured pipenv for the specified project path.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user