From 0cf893efdf0947e5e5ccf787015a8184543a107f Mon Sep 17 00:00:00 2001 From: "Ilya.Kazakevich" Date: Wed, 27 Aug 2025 23:49:44 +0200 Subject: [PATCH] Python: support getting version for python on target. See test for usage example GitOrigin-RevId: aa65f15d294585040c3cdadc2ef3057c85136a19 --- .../src/advancedApi/ExecutablePython.kt | 11 +++++++--- .../src/advancedApi/advancedApi.kt | 2 +- .../execService.python/src/api.kt | 21 +++++++++++-------- .../execService.python/src/impl/impl.kt | 12 ++++++----- .../python/community/execService/api.kt | 2 ++ 5 files changed, 30 insertions(+), 18 deletions(-) diff --git a/python/python-exec-service/execService.python/src/advancedApi/ExecutablePython.kt b/python/python-exec-service/execService.python/src/advancedApi/ExecutablePython.kt index 73e996cf25e6..c09a7ea041d6 100644 --- a/python/python-exec-service/execService.python/src/advancedApi/ExecutablePython.kt +++ b/python/python-exec-service/execService.python/src/advancedApi/ExecutablePython.kt @@ -1,9 +1,11 @@ // 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.execService.python.advancedApi +import com.intellij.python.community.execService.BinaryToExec +import com.intellij.python.community.execService.asBinToExec +import com.intellij.python.community.execService.python.PythonBinaryOnEelOrTarget import com.intellij.python.community.execService.python.advancedApi.ExecutablePython.Companion.vanillaExecutablePython import com.jetbrains.python.PythonBinary -import java.nio.file.Path /** * Something that can execute python code (vanilla cpython, conda). @@ -12,7 +14,7 @@ import java.nio.file.Path * For [vanillaExecutablePython] it is `python` without arguments, but for conda it might be `conda run` etc */ data class ExecutablePython( - val binary: Path, + val binary: BinaryToExec, val args: List, val env: Map, ) { @@ -21,7 +23,10 @@ data class ExecutablePython( /** * Plain python that doesn't have args nor envs */ - fun vanillaExecutablePython(binary: PythonBinary): ExecutablePython = + fun vanillaExecutablePython(binary: PythonBinaryOnEelOrTarget): ExecutablePython = ExecutablePython(binary, emptyList(), emptyMap()) + + fun vanillaExecutablePython(binary: PythonBinary): ExecutablePython = vanillaExecutablePython(binary.asBinToExec()) } + } diff --git a/python/python-exec-service/execService.python/src/advancedApi/advancedApi.kt b/python/python-exec-service/execService.python/src/advancedApi/advancedApi.kt index 6245009b9bbb..3716d17b8b50 100644 --- a/python/python-exec-service/execService.python/src/advancedApi/advancedApi.kt +++ b/python/python-exec-service/execService.python/src/advancedApi/advancedApi.kt @@ -22,7 +22,7 @@ suspend fun ExecService.executePythonAdvanced( processInteractiveHandler: ProcessInteractiveHandler, ): PyResult = executeAdvanced( - binary = BinOnEel(python.binary), + binary = python.binary, args = Args(*python.args.toTypedArray()).add(args), // TODO: Merge PATH options = options.copy(env = options.env + python.env), processInteractiveHandler) diff --git a/python/python-exec-service/execService.python/src/api.kt b/python/python-exec-service/execService.python/src/api.kt index 63b55c718028..071c58bc79d0 100644 --- a/python/python-exec-service/execService.python/src/api.kt +++ b/python/python-exec-service/execService.python/src/api.kt @@ -1,11 +1,7 @@ // 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.execService.python -import com.intellij.python.community.execService.Args -import com.intellij.python.community.execService.ExecOptions -import com.intellij.python.community.execService.ExecService -import com.intellij.python.community.execService.PyProcessListener -import com.intellij.python.community.execService.ZeroCodeStdoutTransformer +import com.intellij.python.community.execService.* import com.intellij.python.community.execService.python.advancedApi.ExecutablePython import com.intellij.python.community.execService.python.advancedApi.executeHelperAdvanced import com.intellij.python.community.execService.python.advancedApi.validatePythonAndGetVersion @@ -15,6 +11,12 @@ import com.jetbrains.python.errorProcessing.PyResult import com.jetbrains.python.psi.LanguageLevel import org.jetbrains.annotations.ApiStatus +/** + * Python binary itself (i.e python.exe) + */ +typealias PythonBinaryOnEelOrTarget = BinaryToExec + + /** * Execute [helper] on [python]. For remote eels, [helper] is copied (but only one file!). * Returns `stdout` @@ -26,7 +28,7 @@ suspend fun ExecService.executeHelper( options: ExecOptions = ExecOptions(), procListener: PyProcessListener? = null, ): PyResult = - executeHelperAdvanced(ExecutablePython.vanillaExecutablePython(python), helper, args, options, procListener, ZeroCodeStdoutTransformer) + executeHelperAdvanced(ExecutablePython.vanillaExecutablePython(python.asBinToExec()), helper, args, options, procListener, ZeroCodeStdoutTransformer) /** * Ensures that this python is executable and returns its version. Error if python is broken. @@ -34,11 +36,12 @@ suspend fun ExecService.executeHelper( * Some pythons might be broken: they may be executable, even return a version, but still fail to execute it. * As we need workable pythons, we validate it by executing */ -@ApiStatus.Internal -suspend fun ExecService.validatePythonAndGetVersion(python: PythonBinary): PyResult = +suspend fun ExecService.validatePythonAndGetVersion(python: PythonBinaryOnEelOrTarget): PyResult = validatePythonAndGetVersion(ExecutablePython.vanillaExecutablePython(python)) -suspend fun PythonBinary.validatePythonAndGetVersion(): PyResult = ExecService().validatePythonAndGetVersion(this) +suspend fun PythonBinaryOnEelOrTarget.validatePythonAndGetVersion(): PyResult = ExecService().validatePythonAndGetVersion(this) +suspend fun ExecService.validatePythonAndGetVersion(python: PythonBinary): PyResult = validatePythonAndGetVersion(python.asBinToExec()) +suspend fun PythonBinary.validatePythonAndGetVersion(): PyResult = asBinToExec().validatePythonAndGetVersion() /** diff --git a/python/python-exec-service/execService.python/src/impl/impl.kt b/python/python-exec-service/execService.python/src/impl/impl.kt index cc7fea9d2e32..6a59dccde036 100644 --- a/python/python-exec-service/execService.python/src/impl/impl.kt +++ b/python/python-exec-service/execService.python/src/impl/impl.kt @@ -5,10 +5,7 @@ import com.intellij.openapi.util.NlsSafe import com.intellij.platform.eel.provider.utils.EelProcessExecutionResult import com.intellij.platform.eel.provider.utils.stderrString import com.intellij.platform.eel.provider.utils.stdoutString -import com.intellij.python.community.execService.Args -import com.intellij.python.community.execService.ExecOptions -import com.intellij.python.community.execService.ExecService -import com.intellij.python.community.execService.ZeroCodeStdoutTransformer +import com.intellij.python.community.execService.* import com.intellij.python.community.execService.impl.transformerToHandler import com.intellij.python.community.execService.python.advancedApi.ExecutablePython import com.intellij.python.community.execService.python.advancedApi.executePythonAdvanced @@ -46,4 +43,9 @@ internal suspend fun ExecService.validatePythonAndGetVersionImpl(python: Executa return@withContext Result.success(languageLevel) } -private val ExecutablePython.userReadableName: @NlsSafe String get() = (listOf(binary.pathString) + args).joinToString(" ") +private val ExecutablePython.userReadableName: @NlsSafe String + get() = + (listOf(when (binary) { + is BinOnEel -> binary.path.pathString + is BinOnTarget -> binary + }) + args).joinToString(" ") diff --git a/python/python-exec-service/src/com/intellij/python/community/execService/api.kt b/python/python-exec-service/src/com/intellij/python/community/execService/api.kt index 2f46b7cd42c5..a7ade91de516 100644 --- a/python/python-exec-service/src/com/intellij/python/community/execService/api.kt +++ b/python/python-exec-service/src/com/intellij/python/community/execService/api.kt @@ -17,6 +17,7 @@ import com.intellij.python.community.execService.impl.Arg import com.intellij.python.community.execService.impl.ExecServiceImpl import com.intellij.python.community.execService.impl.PyExecBundle import com.intellij.python.community.execService.impl.transformerToHandler +import com.jetbrains.python.PythonBinary import com.jetbrains.python.Result import com.jetbrains.python.errorProcessing.ExecError import com.jetbrains.python.errorProcessing.PyResult @@ -54,6 +55,7 @@ data class BinOnTarget(internal val configureTargetCmdLine: (TargetedCommandLine constructor(exePath: FullPathOnTarget, target: TargetEnvironmentConfiguration?) : this({ it.setExePath(exePath) }, target) } +fun PythonBinary.asBinToExec(): BinaryToExec = BinOnEel(this) /** * Execute [binary] right directly on the eel it resides on.