mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
support for listing uv provided pythons PY-77537
GitOrigin-RevId: 7003a66ba729d7565c1cf876198d85d45d025659
This commit is contained in:
committed by
intellij-monorepo-bot
parent
ade80998a2
commit
e0e9e611a6
@@ -166,6 +166,8 @@
|
||||
<orderEntry type="library" name="ap-validation" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.python.community.impl.poetry" />
|
||||
<orderEntry type="module" module-name="intellij.python.community.impl.installer" />
|
||||
<orderEntry type="module" module-name="intellij.platform.eel" />
|
||||
<orderEntry type="module" module-name="intellij.platform.eel.provider" />
|
||||
<orderEntry type="module" module-name="intellij.python.community.impl.venv" />
|
||||
<orderEntry type="module" module-name="intellij.python.community.execService" />
|
||||
</component>
|
||||
|
||||
@@ -14,5 +14,5 @@ interface SystemPythonProvider {
|
||||
val EP: ExtensionPointName<SystemPythonProvider> = ExtensionPointName<SystemPythonProvider>("Pythonid.systemPythonProvider")
|
||||
}
|
||||
|
||||
suspend fun findSystemPythons(eelApi: EelApi): Set<PythonBinary>
|
||||
suspend fun findSystemPythons(eelApi: EelApi): Result<Set<PythonBinary>>
|
||||
}
|
||||
@@ -872,6 +872,8 @@ The Python plug-in provides smart editing for Python scripts. The feature set of
|
||||
<pythonPackageManagerProvider implementation="com.jetbrains.python.packaging.pip.PipPackageManagerProvider" order="last"/>
|
||||
<pythonPackageManagerProvider implementation="com.jetbrains.python.packaging.conda.CondaPackageManagerProvider"/>
|
||||
|
||||
<systemPythonProvider implementation="com.jetbrains.python.sdk.uv.UvSystemPythonProvider"/>
|
||||
|
||||
<inspectionExtension implementation="com.jetbrains.python.sdk.configuration.PyInterpreterInspectionSuppressor"/>
|
||||
<inspectionExtension implementation="com.jetbrains.python.inspections.PyUnresolvedReferenceDefaultInspectionExtension" order="last"/>
|
||||
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ private class SystemPythonServiceImpl : SystemPythonService, SimplePersistentSta
|
||||
|
||||
val pythonsFromExtensions = SystemPythonProvider.EP
|
||||
.extensionList
|
||||
.flatMap { it.findSystemPythons(eelApi) }.filter { it.getEelDescriptor().upgrade() == eelApi }
|
||||
.flatMap { it.findSystemPythons(eelApi).getOrNull() ?: emptyList() }.filter { it.getEelDescriptor().upgrade() == eelApi }
|
||||
|
||||
val badPythons = mutableSetOf<PythonBinary>()
|
||||
val pythons = corePythons + pythonsFromExtensions + state.userProvidedPythons.filter { it.getEelDescriptor() == eelApi.descriptor }
|
||||
|
||||
+15
-11
@@ -15,16 +15,20 @@ import kotlinx.coroutines.flow.toSet
|
||||
* Register tests pythons as system pythons
|
||||
*/
|
||||
internal class EnvTestPythonProvider : SystemPythonProvider {
|
||||
override suspend fun findSystemPythons(eelApi: EelApi): Set<PythonBinary> {
|
||||
if (eelApi != localEel) return emptySet()
|
||||
return TypeVanillaPython3
|
||||
.getTestEnvironments()
|
||||
.map { (python, closeable) ->
|
||||
Disposer.register(ApplicationManager.getApplication()) {
|
||||
closeable.close()
|
||||
}
|
||||
python
|
||||
}
|
||||
.toSet()
|
||||
override suspend fun findSystemPythons(eelApi: EelApi): Result<Set<PythonBinary>> {
|
||||
var pythons = emptySet<PythonBinary>()
|
||||
if (eelApi == localEel) {
|
||||
pythons = TypeVanillaPython3
|
||||
.getTestEnvironments()
|
||||
.map { (python, closeable) ->
|
||||
Disposer.register(ApplicationManager.getApplication()) {
|
||||
closeable.close()
|
||||
}
|
||||
|
||||
python
|
||||
}.toSet()
|
||||
}
|
||||
|
||||
return Result.success(pythons)
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,7 @@ interface UvCli {
|
||||
interface UvLowLevel {
|
||||
suspend fun initializeEnvironment(init: Boolean, python: Path?): Result<Path>
|
||||
|
||||
suspend fun listPackages(): Result<List<PythonPackage>>
|
||||
suspend fun listOutdatedPackages(): Result<List<PythonOutdatedPackage>>
|
||||
suspend fun discoverUvInstalledPythons(): Result<Set<Path>>
|
||||
|
||||
/**
|
||||
* Manage project dependencies by adding/removing them to the project along side installation
|
||||
@@ -27,4 +26,7 @@ interface UvLowLevel {
|
||||
*/
|
||||
suspend fun installPackage(name: PythonPackageSpecification, options: List<String>): Result<Unit>
|
||||
suspend fun uninstallPackage(name: PythonPackage): Result<Unit>
|
||||
|
||||
suspend fun listPackages(): Result<List<PythonPackage>>
|
||||
suspend fun listOutdatedPackages(): Result<List<PythonOutdatedPackage>>
|
||||
}
|
||||
@@ -13,11 +13,11 @@ import com.jetbrains.python.icons.PythonIcons
|
||||
import com.jetbrains.python.sdk.*
|
||||
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 java.nio.file.Path
|
||||
import javax.swing.Icon
|
||||
import kotlin.io.path.pathString
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
internal val Sdk.isUv: Boolean
|
||||
get() = sdkAdditionalData is UvSdkAdditionalData
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// 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.sdk.uv
|
||||
|
||||
import com.intellij.platform.eel.EelApi
|
||||
import com.intellij.platform.eel.provider.localEel
|
||||
import com.jetbrains.python.PythonBinary
|
||||
import com.jetbrains.python.sdk.uv.impl.createUvLowLevel
|
||||
import com.jetbrains.python.sdk.uv.impl.hasUvExecutable
|
||||
import com.jetbrains.python.systemPythonSpi.SystemPythonProvider
|
||||
import java.nio.file.Path
|
||||
|
||||
class UvSystemPythonProvider : SystemPythonProvider {
|
||||
override suspend fun findSystemPythons(eelApi: EelApi): Result<Set<PythonBinary>> {
|
||||
if (eelApi != localEel || !hasUvExecutable()) {
|
||||
// TODO: support for remote execution
|
||||
return Result.success(emptySet())
|
||||
}
|
||||
|
||||
val uv = createUvLowLevel(Path.of("."))
|
||||
return uv.discoverUvInstalledPythons()
|
||||
}
|
||||
}
|
||||
@@ -93,6 +93,10 @@ fun setUvExecutable(path: Path) {
|
||||
PropertiesComponent.getInstance().uvPath = path
|
||||
}
|
||||
|
||||
fun createUvCli(dispatcher: CoroutineDispatcher = Dispatchers.IO, uv: Path? = null): UvCli {
|
||||
fun hasUvExecutable(): Boolean {
|
||||
return getUvExecutable() != null
|
||||
}
|
||||
|
||||
fun createUvCli(uv: Path? = null, dispatcher: CoroutineDispatcher = Dispatchers.IO): UvCli {
|
||||
return UvCliImpl(dispatcher, uv)
|
||||
}
|
||||
@@ -8,14 +8,15 @@ import com.intellij.util.io.delete
|
||||
import com.jetbrains.python.packaging.common.PythonOutdatedPackage
|
||||
import com.jetbrains.python.packaging.common.PythonPackage
|
||||
import com.jetbrains.python.packaging.common.PythonPackageSpecification
|
||||
import com.jetbrains.python.venvReader.VirtualEnvReader
|
||||
import com.jetbrains.python.sdk.uv.UvCli
|
||||
import com.jetbrains.python.sdk.uv.UvLowLevel
|
||||
import com.jetbrains.python.venvReader.VirtualEnvReader
|
||||
import com.jetbrains.python.venvReader.tryResolvePath
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.pathString
|
||||
|
||||
internal class UvLowLevelImpl(val cwd: Path, private val uvCli: UvCli) : UvLowLevel {
|
||||
private class UvLowLevelImpl(val cwd: Path, private val uvCli: UvCli) : UvLowLevel {
|
||||
override suspend fun initializeEnvironment(init: Boolean, python: Path?): Result<Path> {
|
||||
val addPythonArg: (MutableList<String>) -> Unit = { args ->
|
||||
python?.let {
|
||||
@@ -32,16 +33,14 @@ internal class UvLowLevelImpl(val cwd: Path, private val uvCli: UvCli) : UvLowLe
|
||||
initArgs.add("--vcs")
|
||||
initArgs.add("none")
|
||||
|
||||
uvCli.runUv(cwd, *initArgs.toTypedArray()).getOrElse {
|
||||
return Result.failure(it)
|
||||
}
|
||||
uvCli.runUv(cwd, *initArgs.toTypedArray())
|
||||
.onFailure { return Result.failure(it) }
|
||||
}
|
||||
|
||||
val venvArgs = mutableListOf("venv");
|
||||
val venvArgs = mutableListOf("venv")
|
||||
addPythonArg(venvArgs)
|
||||
uvCli.runUv(cwd, *venvArgs.toTypedArray()).getOrElse {
|
||||
return Result.failure(it)
|
||||
}
|
||||
uvCli.runUv(cwd, *venvArgs.toTypedArray())
|
||||
.onFailure { return Result.failure(it) }
|
||||
|
||||
// TODO: ask for an uv option not to create
|
||||
val hello = cwd.resolve("hello.py").takeIf { it.exists() }
|
||||
@@ -55,11 +54,27 @@ internal class UvLowLevelImpl(val cwd: Path, private val uvCli: UvCli) : UvLowLe
|
||||
return Result.success(path)
|
||||
}
|
||||
|
||||
override suspend fun listPackages(): Result<List<PythonPackage>> {
|
||||
val out = uvCli.runUv(cwd, "pip", "list", "--format", "json").getOrElse {
|
||||
return Result.failure(it)
|
||||
override suspend fun discoverUvInstalledPythons(): Result<Set<Path>> {
|
||||
var out = uvCli.runUv(cwd, "python", "dir")
|
||||
.getOrElse { return Result.failure(it) }
|
||||
|
||||
val uvDir = tryResolvePath(out)
|
||||
if (uvDir == null) {
|
||||
return Result.failure(RuntimeException("failed to detect uv python directory"))
|
||||
}
|
||||
|
||||
// TODO: ask for json output format
|
||||
out = uvCli.runUv(cwd, "python", "list", "--only-installed")
|
||||
.getOrElse { return Result.failure(it) }
|
||||
|
||||
val pythons = parseUvPythonList(uvDir, out)
|
||||
return Result.success(pythons)
|
||||
}
|
||||
|
||||
override suspend fun listPackages(): Result<List<PythonPackage>> {
|
||||
val out = uvCli.runUv(cwd, "pip", "list", "--format", "json")
|
||||
.getOrElse { return Result.failure(it) }
|
||||
|
||||
data class PackageInfo(val name: String, val version: String)
|
||||
|
||||
val mapper = jacksonObjectMapper()
|
||||
@@ -73,9 +88,8 @@ internal class UvLowLevelImpl(val cwd: Path, private val uvCli: UvCli) : UvLowLe
|
||||
|
||||
override suspend fun listOutdatedPackages(): Result<List<PythonOutdatedPackage>> {
|
||||
|
||||
val out = uvCli.runUv(cwd, "pip", "list", "--outdated", "--format", "json").getOrElse {
|
||||
return Result.failure(it)
|
||||
}
|
||||
val out = uvCli.runUv(cwd, "pip", "list", "--outdated", "--format", "json")
|
||||
.getOrElse { return Result.failure(it) }
|
||||
|
||||
data class OutdatedPackageInfo(val name: String, val version: String, val latest_version: String)
|
||||
|
||||
@@ -94,9 +108,8 @@ internal class UvLowLevelImpl(val cwd: Path, private val uvCli: UvCli) : UvLowLe
|
||||
}
|
||||
|
||||
override suspend fun installPackage(name: PythonPackageSpecification, options: List<String>): Result<Unit> {
|
||||
uvCli.runUv(cwd, "pip", "install", formatPackageName(name), *options.toTypedArray()).getOrElse {
|
||||
return Result.failure(it)
|
||||
}
|
||||
uvCli.runUv(cwd, "pip", "install", formatPackageName(name), *options.toTypedArray())
|
||||
.onFailure { return Result.failure(it) }
|
||||
|
||||
return Result.success(Unit)
|
||||
}
|
||||
@@ -110,9 +123,8 @@ internal class UvLowLevelImpl(val cwd: Path, private val uvCli: UvCli) : UvLowLe
|
||||
}
|
||||
|
||||
override suspend fun addDependency(name: PythonPackageSpecification, options: List<String>): Result<Unit> {
|
||||
uvCli.runUv(cwd, "add", formatPackageName(name), *options.toTypedArray()).getOrElse {
|
||||
return Result.failure(it)
|
||||
}
|
||||
uvCli.runUv(cwd, "add", formatPackageName(name), *options.toTypedArray())
|
||||
.onFailure { return Result.failure(it) }
|
||||
|
||||
return Result.success(Unit)
|
||||
}
|
||||
@@ -127,6 +139,24 @@ internal class UvLowLevelImpl(val cwd: Path, private val uvCli: UvCli) : UvLowLe
|
||||
fun formatPackageName(name: PythonPackageSpecification): String {
|
||||
return if (name.versionSpecs.isNullOrBlank()) name.name else "${name.name}${name.versionSpecs}"
|
||||
}
|
||||
|
||||
fun parseUvPythonList(uvDir: Path, out: String): Set<Path> {
|
||||
val lines = out.lines()
|
||||
val pythons = lines.mapNotNull { line ->
|
||||
var arrow = line.indexOf("->").takeIf { it >= 0 } ?: line.length
|
||||
val pythonAndPath = line.substring(0, arrow).trim().split(" ")
|
||||
if (pythonAndPath.size != 2) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
|
||||
val python = tryResolvePath(pythonAndPath[1])
|
||||
?.takeIf { it.exists() && it.startsWith(uvDir) }
|
||||
|
||||
python
|
||||
}.toSet()
|
||||
|
||||
return pythons
|
||||
}
|
||||
}
|
||||
|
||||
fun createUvLowLevel(cwd: Path, uvCli: UvCli = createUvCli()): UvLowLevel {
|
||||
|
||||
Reference in New Issue
Block a user