mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[python]: PY-85585 : Do not display vens as system pythons in "Add new interpreter" window.
Instead of old `addManuallyAddedInterpreter` we now have two functions: one that requires system python and one that doesn't. Both functions register system python if provided, but the latter one accepts any python (venv included). Various "selectors" use these functions. We also make sure no non-system python is set to `baseInterpreters`: base are always system! As a bonus, we show "system" or "virtual env" title near interpreter. It now checks that python is system (see `ensureSystemPython`). Non-system pythons are never reported, and `registerSystemPython` also returns an error for non-system pythons We need `execGetBoolFromStdout` for the further changes Merge-request: IJ-MR-182415 Merged-by: Ilya Kazakevich <ilya.kazakevich@jetbrains.com> (cherry picked from commit 2950f5f0cd2745c12987a92e40774d366568f312) GitOrigin-RevId: f30e9a6cd7b5d103454d66f26a0c2282d7c587fc
This commit is contained in:
committed by
intellij-monorepo-bot
parent
ed0fda649a
commit
3388536d2c
@@ -35,6 +35,7 @@ jvm_library(
|
||||
"//python/installer",
|
||||
"//python/python-venv:community-impl-venv",
|
||||
"//python/python-psi-impl:psi-impl",
|
||||
"//python/python-exec-service/execService.python",
|
||||
],
|
||||
exports = ["//python/services/shared"]
|
||||
)
|
||||
@@ -75,6 +76,8 @@ jvm_library(
|
||||
"//platform/testFramework",
|
||||
"//platform/testFramework:testFramework_test_lib",
|
||||
"//python/python-psi-impl:psi-impl",
|
||||
"//python/python-exec-service/execService.python",
|
||||
"//python/python-exec-service/execService.python:execService.python_test_lib",
|
||||
],
|
||||
exports = [
|
||||
"//python/services/shared",
|
||||
|
||||
@@ -33,5 +33,6 @@
|
||||
<orderEntry type="module" module-name="intellij.python.community.testFramework.testEnv" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.platform.testFramework" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.python.psi.impl" />
|
||||
<orderEntry type="module" module-name="intellij.python.community.execService.python" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -1 +1,2 @@
|
||||
py.system.python.service.python.is.broken=Python {0} is broken
|
||||
py.system.python.service.python.is.broken=Python {0} is broken
|
||||
py.system.python.service.python.is.not.system={0} is not a system python
|
||||
|
||||
+48
-4
@@ -9,10 +9,16 @@ import com.intellij.python.community.impl.venv.createVenv
|
||||
import com.intellij.python.community.services.shared.PythonInfoWithUiComparator
|
||||
import com.intellij.python.community.services.shared.PythonWithUi
|
||||
import com.intellij.python.community.services.shared.VanillaPythonWithPythonInfo
|
||||
import com.intellij.python.community.services.systemPython.impl.PySystemPythonBundle
|
||||
import com.intellij.python.community.services.systemPython.impl.asSysPythonRegisterError
|
||||
import com.intellij.python.community.services.systemPython.impl.ensureSystemPython
|
||||
import com.jetbrains.python.PyToolUIInfo
|
||||
import com.jetbrains.python.PythonBinary
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.MessageError
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.mapError
|
||||
import com.jetbrains.python.venvReader.Directory
|
||||
import com.jetbrains.python.venvReader.VirtualEnvReader
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
@@ -32,9 +38,9 @@ interface SystemPythonService {
|
||||
|
||||
/**
|
||||
* When user provides a path to the python binary, use this method to the [SystemPython].
|
||||
* @return either [SystemPython] or an error if python is broken.
|
||||
* @return either [SystemPython] or an error if python is broken or not a system python.
|
||||
*/
|
||||
suspend fun registerSystemPython(pythonPath: PythonBinary): PyResult<SystemPython>
|
||||
suspend fun registerSystemPython(pythonPath: PythonBinary): Result<SystemPython, SysPythonRegisterError>
|
||||
|
||||
/**
|
||||
* @return tool to install python on OS If [eelApi] supports python installation
|
||||
@@ -42,6 +48,35 @@ interface SystemPythonService {
|
||||
fun getInstaller(eelApi: EelApi = localEel): PythonInstallerService?
|
||||
}
|
||||
|
||||
/**
|
||||
* System python has an error.
|
||||
* It is either [NotASystemPython] (think: virtual env) or [PythonIsBroken] (and completely unusable)
|
||||
*/
|
||||
sealed interface SysPythonRegisterError {
|
||||
val asPyError: PyError
|
||||
|
||||
/**
|
||||
* Virtual env, not a system python
|
||||
*/
|
||||
class NotASystemPython private constructor(val notSystemPython: VanillaPythonWithPythonInfo, override val asPyError: PyError) : SysPythonRegisterError {
|
||||
companion object : suspend (VanillaPythonWithPythonInfo) -> NotASystemPython {
|
||||
override suspend fun invoke(notSystemPython: VanillaPythonWithPythonInfo): NotASystemPython = NotASystemPython(
|
||||
notSystemPython = notSystemPython,
|
||||
asPyError = MessageError(PySystemPythonBundle.message("py.system.python.service.python.is.not.system", notSystemPython.getReadableName()))
|
||||
)
|
||||
}
|
||||
|
||||
override fun toString(): String = "NotASystemPython(notSystemPython=$notSystemPython, asPyError=$asPyError)"
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Python failed during execution
|
||||
*/
|
||||
data class PythonIsBroken(override val asPyError: PyError) : SysPythonRegisterError
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an instance of this service
|
||||
*/
|
||||
@@ -56,10 +91,19 @@ fun SystemPythonService(): SystemPythonService = ApplicationManager.getApplicati
|
||||
*
|
||||
* Instances could be obtained with [SystemPythonService]
|
||||
*/
|
||||
class SystemPython internal constructor(private val delegate: VanillaPythonWithPythonInfo, override val ui: PyToolUIInfo?) : VanillaPythonWithPythonInfo by delegate, PythonWithUi, Comparable<SystemPython> {
|
||||
class SystemPython private constructor(private val delegate: VanillaPythonWithPythonInfo, override val ui: PyToolUIInfo?) : VanillaPythonWithPythonInfo by delegate, PythonWithUi, Comparable<SystemPython> {
|
||||
|
||||
private companion object {
|
||||
internal companion object {
|
||||
val comparator = PythonInfoWithUiComparator<SystemPython>()
|
||||
internal suspend fun create(delegate: VanillaPythonWithPythonInfo, ui: PyToolUIInfo?): Result<SystemPython, SysPythonRegisterError> {
|
||||
val isSystemPython = ensureSystemPython(delegate).mapError { it.asSysPythonRegisterError() }.getOr { return it }
|
||||
return if (isSystemPython) {
|
||||
Result.success(SystemPython(delegate, ui))
|
||||
}
|
||||
else {
|
||||
Result.failure(SysPythonRegisterError.NotASystemPython(delegate))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
|
||||
+24
@@ -0,0 +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.intellij.python.community.services.systemPython.impl
|
||||
|
||||
import com.intellij.python.community.execService.python.execGetBoolFromStdout
|
||||
import com.intellij.python.community.services.shared.VanillaPythonWithPythonInfo
|
||||
import com.intellij.python.community.services.systemPython.SysPythonRegisterError
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import org.intellij.lang.annotations.Language
|
||||
|
||||
internal fun PyError.asSysPythonRegisterError(): SysPythonRegisterError.PythonIsBroken = SysPythonRegisterError.PythonIsBroken(this)
|
||||
|
||||
@Language("Python")
|
||||
internal const val ENSURE_SYSTEM_PYTHON_CMD = "import sys; print(sys.prefix == sys.base_prefix)"
|
||||
|
||||
internal suspend fun ensureSystemPython(python: VanillaPythonWithPythonInfo): PyResult<Boolean> {
|
||||
if (python.pythonInfo.languageLevel.isPython2) {
|
||||
// there is no obvious check for venv in py2.7. Nobody uses it, anyway. Let it be.
|
||||
return Result.success(true)
|
||||
}
|
||||
val systemPython = python.asExecutablePython.execGetBoolFromStdout(ENSURE_SYSTEM_PYTHON_CMD).getOr { return it }
|
||||
return Result.success(systemPython)
|
||||
}
|
||||
+12
-12
@@ -15,13 +15,9 @@ import com.intellij.python.community.services.internal.impl.VanillaPythonWithPyt
|
||||
import com.intellij.python.community.services.systemPython.SystemPythonServiceImpl.MyServiceState
|
||||
import com.intellij.python.community.services.systemPython.impl.Cache
|
||||
import com.intellij.python.community.services.systemPython.impl.PySystemPythonBundle
|
||||
import com.jetbrains.python.NON_INTERACTIVE_ROOT_TRACE_CONTEXT
|
||||
import com.jetbrains.python.PyToolUIInfo
|
||||
import com.jetbrains.python.PythonBinary
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.intellij.python.community.services.systemPython.impl.asSysPythonRegisterError
|
||||
import com.jetbrains.python.*
|
||||
import com.jetbrains.python.errorProcessing.getOr
|
||||
import com.jetbrains.python.getOrNull
|
||||
import com.jetbrains.python.sdk.installer.installBinary
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
@@ -65,10 +61,10 @@ internal class SystemPythonServiceImpl(scope: CoroutineScope) : SystemPythonServ
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun registerSystemPython(pythonPath: PythonBinary): PyResult<SystemPython> {
|
||||
override suspend fun registerSystemPython(pythonPath: PythonBinary): Result<SystemPython, SysPythonRegisterError> {
|
||||
val pythonWithLangLevel = VanillaPythonWithPythonInfoImpl.createByPythonBinary(pythonPath)
|
||||
.getOr(PySystemPythonBundle.message("py.system.python.service.python.is.broken", pythonPath)) { return it }
|
||||
val systemPython = SystemPython(pythonWithLangLevel, null)
|
||||
.getOr(PySystemPythonBundle.message("py.system.python.service.python.is.broken", pythonPath)) { return Result.failure(it.error.asSysPythonRegisterError()) }
|
||||
val systemPython = SystemPython.create(pythonWithLangLevel, null).getOr { return it }
|
||||
state.userProvidedPythons.add(pythonPath.pathString)
|
||||
cache()?.get(pythonPath.getEelDescriptor())?.add(systemPython)
|
||||
return Result.success(systemPython)
|
||||
@@ -126,10 +122,14 @@ internal class SystemPythonServiceImpl(scope: CoroutineScope) : SystemPythonServ
|
||||
|
||||
val result = VanillaPythonWithPythonInfoImpl.createByPythonBinaries(pythons.toSet())
|
||||
.mapNotNull { (python, r) ->
|
||||
when (r) {
|
||||
is Result.Success -> SystemPython(r.result, pythonsUi[r.result.pythonBinary])
|
||||
val sysPython = r.mapSuccessError(
|
||||
onSuccess = { r -> SystemPython.create(r, pythonsUi[r.pythonBinary]) },
|
||||
onErr = { it.asSysPythonRegisterError() }
|
||||
)
|
||||
when (sysPython) {
|
||||
is Result.Success -> sysPython.result
|
||||
is Result.Failure -> {
|
||||
fileLogger().warn("Skipping $python : ${r.error}")
|
||||
fileLogger().warn("Skipping $python : ${sysPython.error.asPyError}")
|
||||
badPythons.add(python)
|
||||
null
|
||||
}
|
||||
|
||||
-22
@@ -68,28 +68,6 @@ class SystemPythonServiceShowCaseTest {
|
||||
SystemPythonService().registerSystemPython(randomBinary).assertFail()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCustomPythonSunnyDay(@PythonBinaryPath python: Path, @TempDir venvPath: Path): Unit = timeoutRunBlocking(10.minutes) {
|
||||
createVenv(python, venvPath).getOrThrow()
|
||||
val python = VirtualEnvReader.Instance.findPythonInPythonRoot(venvPath) ?: error("no python in $venvPath")
|
||||
val newPython = SystemPythonService().registerSystemPython(python).orThrow()
|
||||
var allPythons = SystemPythonService().findSystemPythons()
|
||||
assertThat("No newly registered python returned", allPythons, hasItem(newPython))
|
||||
if (SystemInfo.isWindows) {
|
||||
deleteCheckLocking(python)
|
||||
}
|
||||
else {
|
||||
python.deleteExisting()
|
||||
}
|
||||
|
||||
allPythons = SystemPythonService().findSystemPythons(forceRefresh = true)
|
||||
assertThat("Broken python returned", allPythons, not(hasItem(newPython)))
|
||||
|
||||
if (SystemInfo.isWindows) {
|
||||
deleteCheckLocking(venvPath)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRefresh(@TestDisposable disposable: Disposable): Unit = timeoutRunBlocking(10.minutes) {
|
||||
val provider = CountingTestProvider(Result.failure(MessageError("...")))
|
||||
|
||||
Reference in New Issue
Block a user