From ec7562bf0716930d977d8a0e3b3c393fa44c4382 Mon Sep 17 00:00:00 2001 From: Ilya Kazakevich Date: Fri, 8 Aug 2025 19:08:05 +0000 Subject: [PATCH] Python: Introduce API to get process from SDK via `ExecService` Look `src/com/jetbrains/python/sdk/sdkExecApi.kt` for method that executes SDK and returns a process. See `PySdkOnTargetTest` as a usage example. Merge-request: IJ-MR-171740 Merged-by: Ilya Kazakevich GitOrigin-RevId: a7e9b6995eba7ae1fa71019b370acca2d98738d2 --- .../python/errorProcessing/ExecError.kt | 6 +- .../python/packaging/PyExecutionException.kt | 6 +- .../community/execService/advancedApi.kt | 29 +++++++++- .../python/community/execService/api.kt | 21 ++++++- .../execService/impl/ExecServiceImpl.kt | 56 ++++++++++++++----- .../impl/processLaunchers/ProcessLauncher.kt | 4 +- .../execService/impl/processLaunchers/eel.kt | 6 +- .../impl/processLaunchers/targets.kt | 28 +++++++--- .../community/execService/implInterfaces.kt | 10 ++++ .../packaging/PyExecutionExceptionExt.kt | 7 +-- .../packaging/PyPackageManagerImplBase.java | 4 +- .../ui/PyPackageManagementService.java | 6 +- .../com/jetbrains/python/sdk/sdkExecApi.kt | 13 +++++ 13 files changed, 154 insertions(+), 42 deletions(-) create mode 100644 python/python-exec-service/src/com/intellij/python/community/execService/implInterfaces.kt diff --git a/python/openapi/src/com/jetbrains/python/errorProcessing/ExecError.kt b/python/openapi/src/com/jetbrains/python/errorProcessing/ExecError.kt index 6be3d0302723..d16e3bc1c90c 100644 --- a/python/openapi/src/com/jetbrains/python/errorProcessing/ExecError.kt +++ b/python/openapi/src/com/jetbrains/python/errorProcessing/ExecError.kt @@ -38,17 +38,19 @@ sealed interface Exe { } } +typealias ExecError = ExecErrorImpl<*> + /** * External process error. */ -class ExecError( +class ExecErrorImpl( val exe: Exe, /** * I.e ['-v'] */ val args: Array, - val errorReason: ExecErrorReason, + val errorReason: T, /** * optional message to be displayed to the user: Why did we run this process. I.e "running pip to install package". */ diff --git a/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.kt b/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.kt index 9c647b04e392..a20c81be0c90 100644 --- a/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.kt +++ b/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.kt @@ -29,7 +29,7 @@ class PyExecutionException private constructor( additionalMessageToUser: @NlsContexts.DialogMessage String?, command: String, args: List, - ): PyExecutionException = PyExecutionException(ExecError(Exe.fromString(command), args.toTypedArray(), ExecErrorReason.Timeout, additionalMessageToUser)) + ): PyExecutionException = PyExecutionException(ExecErrorImpl(Exe.fromString(command), args.toTypedArray(), ExecErrorReason.Timeout, additionalMessageToUser)) } @@ -63,7 +63,7 @@ class PyExecutionException private constructor( args: List, fixes: List = listOf(), ) : this( - pyError = ExecError(Exe.fromString(command), args.toTypedArray(), ExecErrorReason.CantStart(null, startException.localizedMessage), additionalMessage), + pyError = ExecErrorImpl(Exe.fromString(command), args.toTypedArray(), ExecErrorReason.CantStart(null, startException.localizedMessage), additionalMessage), fixes = fixes, ioException = startException) @@ -82,7 +82,7 @@ class PyExecutionException private constructor( output: ProcessOutput, fixes: List = listOf(), ) : this( - pyError = ExecError(Exe.fromString(command), args.toTypedArray(), output.asExecutionFailed()), + pyError = ExecErrorImpl(Exe.fromString(command), args.toTypedArray(), output.asExecutionFailed()), fixes = fixes) /** diff --git a/python/python-exec-service/src/com/intellij/python/community/execService/advancedApi.kt b/python/python-exec-service/src/com/intellij/python/community/execService/advancedApi.kt index 19a509c39323..7ea08ac3d7b1 100644 --- a/python/python-exec-service/src/com/intellij/python/community/execService/advancedApi.kt +++ b/python/python-exec-service/src/com/intellij/python/community/execService/advancedApi.kt @@ -5,7 +5,8 @@ import com.intellij.platform.eel.channels.EelSendChannel import com.intellij.platform.eel.provider.utils.EelProcessExecutionResult import com.intellij.python.community.execService.impl.ProcessSemiInteractiveHandlerImpl import com.jetbrains.python.Result -import com.jetbrains.python.errorProcessing.PyResult +import com.jetbrains.python.errorProcessing.* +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.CheckReturnValue @@ -40,6 +41,18 @@ interface ExecService { options: ExecOptions = ExecOptions(), processInteractiveHandler: ProcessInteractiveHandler, ): PyResult + + /** + * Execute process from [binary] with [args]. It is up to you to manage its lifecycle. + * When bound to [scopeToBind] process will be destroyed once [scopeToBind] gets cancelled. + */ + @CheckReturnValue + suspend fun executeGetProcess( + binary: BinaryToExec, + args: Args = Args(), + scopeToBind: CoroutineScope? = null, + options: ExecGetProcessOptions = ExecGetProcessOptions(), + ): Result> } /** @@ -72,3 +85,17 @@ typealias ProcessSemiInteractiveFun = suspend (EelSendChannel, Deferred processSemiInteractiveHandler(pyProcessListener: PyProcessListener? = null, code: ProcessSemiInteractiveFun): ProcessInteractiveHandler = ProcessSemiInteractiveHandlerImpl(pyProcessListener, code) + + +sealed class ExecuteGetProcessError(val pyError: T) { + /** + * Failed to create environment for process (i.e. failed to start docker) + */ + class EnvironmentError internal constructor(pyError: MessageError) : ExecuteGetProcessError(pyError) + + /** + * Process couldn't be started + */ + class CanStart internal constructor(pyError: ExecErrorImpl) : ExecuteGetProcessError>(pyError) +} + 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 c7fc60982670..e3bd53697007 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 @@ -169,12 +169,27 @@ object ZeroCodeStdoutTransformer : ProcessOutputTransformer { * @property[env] Environment variables to be applied with the process run * @property[timeout] Process gets killed after this timeout * @property[processDescription] optional description to be displayed to user + * [tty] Much like [com.intellij.platform.eel.EelExecApi.Pty] */ data class ExecOptions( - val env: Map = emptyMap(), - val processDescription: @Nls String? = null, + override val env: Map = emptyMap(), + override val processDescription: @Nls String? = null, val timeout: Duration = 5.minutes, -) + override val tty: TtySize? = null, +) : ExecOptionsBase + + +/** + * Options for [ExecService.executeGetProcess] + * See [ExecOptions] + */ +data class ExecGetProcessOptions( + override val env: Map = emptyMap(), + override val processDescription: @Nls String? = null, + override val tty: TtySize? = null, +) : ExecOptionsBase + +data class TtySize(val rows: UShort, val cols: UShort) /** * See [Args.addLocalFile] diff --git a/python/python-exec-service/src/com/intellij/python/community/execService/impl/ExecServiceImpl.kt b/python/python-exec-service/src/com/intellij/python/community/execService/impl/ExecServiceImpl.kt index 3c60a1394431..ef1fc43d516f 100644 --- a/python/python-exec-service/src/com/intellij/python/community/execService/impl/ExecServiceImpl.kt +++ b/python/python-exec-service/src/com/intellij/python/community/execService/impl/ExecServiceImpl.kt @@ -1,14 +1,18 @@ // 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.impl +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.components.Service +import com.intellij.openapi.components.service import com.intellij.openapi.diagnostic.fileLogger import com.intellij.python.community.execService.* -import com.intellij.python.community.execService.impl.processLaunchers.* +import com.intellij.python.community.execService.impl.processLaunchers.LaunchRequest +import com.intellij.python.community.execService.impl.processLaunchers.ProcessLauncher +import com.intellij.python.community.execService.impl.processLaunchers.createProcessLauncherOnEel +import com.intellij.python.community.execService.impl.processLaunchers.createProcessLauncherOnTarget import com.jetbrains.python.Result -import com.jetbrains.python.errorProcessing.ExecError -import com.jetbrains.python.errorProcessing.ExecErrorReason -import com.jetbrains.python.errorProcessing.PyResult -import com.jetbrains.python.errorProcessing.failure +import com.jetbrains.python.errorProcessing.* +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.withTimeout @@ -17,13 +21,34 @@ import org.jetbrains.annotations.Nls internal object ExecServiceImpl : ExecService { - override suspend fun executeAdvanced(binary: BinaryToExec, args: Args, options: ExecOptions, processInteractiveHandler: ProcessInteractiveHandler): PyResult { + override suspend fun executeGetProcess(binary: BinaryToExec, args: Args, scopeToBind: CoroutineScope?, options: ExecGetProcessOptions): Result> { + val launcher = create(binary, args, options, scopeToBind).getOr { return it } + val process = launcher.start().getOr { + val createExecError = launcher.createExecError(options.processDescription ?: "", it.error).error + return Result.failure(ExecuteGetProcessError.CanStart(createExecError)) + } + return Result.success(process) + } - return coroutineScope { - val request = LaunchRequest(this, args, options.env) - val processLauncher: ProcessLauncher = when (binary) { + private suspend fun create(binary: BinaryToExec, args: Args, options: ExecOptionsBase, scopeToBind: CoroutineScope? = null): Result { + val scope = scopeToBind ?: ApplicationManager.getApplication().service().scope + val request = LaunchRequest(scope, args, options.env, options.tty) + return Result.success( + when (binary) { is BinOnEel -> createProcessLauncherOnEel(binary, request) - is BinOnTarget -> createProcessLauncherOnTarget(binary, request).getOr { return@coroutineScope it } + is BinOnTarget -> createProcessLauncherOnTarget(binary, request).getOr { + options.processDescription?.let { message -> + it.error.pyError.addMessage(message) // TODO: 18n + } + return it + } + }) + } + + override suspend fun executeAdvanced(binary: BinaryToExec, args: Args, options: ExecOptions, processInteractiveHandler: ProcessInteractiveHandler): PyResult { + return coroutineScope { + val processLauncher = create(binary, args, options, this).getOr { + return@coroutineScope it.asPyError() } val description = options.processDescription @@ -66,8 +91,8 @@ internal object ExecServiceImpl : ExecService { } } -private fun ProcessLauncher.createExecError(messageToUser: @Nls String, errorReason: ExecErrorReason): Result.Failure = - ExecError( +private fun ProcessLauncher.createExecError(messageToUser: @Nls String, errorReason: T): Result.Failure> = + ExecErrorImpl( exe = exeForError, args = args.toTypedArray(), additionalMessageToUser = messageToUser, @@ -75,7 +100,12 @@ private fun ProcessLauncher.createExecError(messageToUser: @Nls String, errorRea ).logAndFail() -private fun ExecError.logAndFail(): Result.Failure { +private fun ExecErrorImpl.logAndFail(): Result.Failure> { fileLogger().warn(message) return failure(this) } + +@Service +private class MyService(val scope: CoroutineScope) + +private fun Result.Failure>.asPyError(): Result.Failure = PyResult.failure(this.error.pyError) \ No newline at end of file diff --git a/python/python-exec-service/src/com/intellij/python/community/execService/impl/processLaunchers/ProcessLauncher.kt b/python/python-exec-service/src/com/intellij/python/community/execService/impl/processLaunchers/ProcessLauncher.kt index 11cd55113a2f..898208b3fc6d 100644 --- a/python/python-exec-service/src/com/intellij/python/community/execService/impl/processLaunchers/ProcessLauncher.kt +++ b/python/python-exec-service/src/com/intellij/python/community/execService/impl/processLaunchers/ProcessLauncher.kt @@ -4,6 +4,7 @@ package com.intellij.python.community.execService.impl.processLaunchers import com.intellij.openapi.diagnostic.fileLogger import com.intellij.platform.eel.provider.utils.ProcessFunctions import com.intellij.python.community.execService.Args +import com.intellij.python.community.execService.TtySize import com.jetbrains.python.Result import com.jetbrains.python.errorProcessing.Exe import com.jetbrains.python.errorProcessing.ExecErrorReason @@ -31,4 +32,5 @@ internal data class LaunchRequest( val scopeToBind: CoroutineScope, val args: Args, val env: Map, -) \ No newline at end of file + val usePty: TtySize? +) diff --git a/python/python-exec-service/src/com/intellij/python/community/execService/impl/processLaunchers/eel.kt b/python/python-exec-service/src/com/intellij/python/community/execService/impl/processLaunchers/eel.kt index b3f8533fce80..6b2aa9ce3076 100644 --- a/python/python-exec-service/src/com/intellij/python/community/execService/impl/processLaunchers/eel.kt +++ b/python/python-exec-service/src/com/intellij/python/community/execService/impl/processLaunchers/eel.kt @@ -1,6 +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.impl.processLaunchers +import com.intellij.platform.eel.EelExecApi import com.intellij.platform.eel.EelProcess import com.intellij.platform.eel.ExecuteProcessException import com.intellij.platform.eel.path.EelPath @@ -9,6 +10,7 @@ import com.intellij.platform.eel.provider.utils.EelPathUtils import com.intellij.platform.eel.provider.utils.ProcessFunctions import com.intellij.platform.eel.spawnProcess import com.intellij.python.community.execService.BinOnEel +import com.intellij.python.community.execService.TtySize import com.jetbrains.python.Result import com.jetbrains.python.errorProcessing.Exe import com.jetbrains.python.errorProcessing.ExecErrorReason @@ -28,7 +30,7 @@ internal suspend fun createProcessLauncherOnEel(binOnEel: BinOnEel, launchReques return ProcessLauncher( exeForError = Exe.OnEel(exePath), args = args, - processCommands = EelProcessCommands(launchRequest.scopeToBind, binOnEel, exePath, args, launchRequest.env) + processCommands = EelProcessCommands(launchRequest.scopeToBind, binOnEel, exePath, args, launchRequest.env, launchRequest.usePty) ) } @@ -38,6 +40,7 @@ private class EelProcessCommands( private val path: EelPath, private val args: List, private val env: Map, + private val tty: TtySize?, ) : ProcessCommands { private var eelProcess: EelProcess? = null @@ -56,6 +59,7 @@ private class EelProcessCommands( .args(args) .env(env) .workingDirectory(workDir?.asEelPath()) + .interactionOptions(if (tty != null) EelExecApi.Pty(tty.cols.toInt(), tty.rows.toInt()) else null) .eelIt() this.eelProcess = eelProcess return Result.success(eelProcess.convertToJavaProcess()) diff --git a/python/python-exec-service/src/com/intellij/python/community/execService/impl/processLaunchers/targets.kt b/python/python-exec-service/src/com/intellij/python/community/execService/impl/processLaunchers/targets.kt index 5e269756ea3e..1604b4ed0dca 100644 --- a/python/python-exec-service/src/com/intellij/python/community/execService/impl/processLaunchers/targets.kt +++ b/python/python-exec-service/src/com/intellij/python/community/execService/impl/processLaunchers/targets.kt @@ -4,18 +4,20 @@ package com.intellij.python.community.execService.impl.processLaunchers import com.intellij.execution.ExecutionException +import com.intellij.execution.process.LocalPtyOptions import com.intellij.execution.target.* import com.intellij.execution.target.local.LocalTargetEnvironmentRequest +import com.intellij.execution.target.local.LocalTargetPtyOptions import com.intellij.openapi.diagnostic.fileLogger import com.intellij.openapi.project.ProjectManager import com.intellij.platform.eel.provider.utils.ProcessFunctions import com.intellij.platform.eel.provider.utils.bindProcessToScopeImpl import com.intellij.python.community.execService.BinOnTarget +import com.intellij.python.community.execService.ExecuteGetProcessError import com.jetbrains.python.Result import com.jetbrains.python.errorProcessing.Exe import com.jetbrains.python.errorProcessing.ExecErrorReason import com.jetbrains.python.errorProcessing.MessageError -import com.jetbrains.python.errorProcessing.PyResult import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -25,7 +27,7 @@ import kotlin.time.Duration.Companion.milliseconds private val logger = fileLogger() -internal suspend fun createProcessLauncherOnTarget(binOnTarget: BinOnTarget, launchRequest: LaunchRequest): PyResult = withContext(Dispatchers.IO) { +internal suspend fun createProcessLauncherOnTarget(binOnTarget: BinOnTarget, launchRequest: LaunchRequest): Result = withContext(Dispatchers.IO) { val target = binOnTarget.target val request = if (target != null) { @@ -44,19 +46,29 @@ internal suspend fun createProcessLauncherOnTarget(binOnTarget: BinOnTarget, lau } catch (e: ExecutionException) { fileLogger().warn("Failed to start $target", e) // TODO: i18n - return@withContext Result.failure(MessageError("Failed to start environment due to ${e.localizedMessage}")) + return@withContext Result.failure(ExecuteGetProcessError.EnvironmentError(MessageError("Failed to start environment due to ${e.localizedMessage}"))) } val args = launchRequest.args.getArgs { localFile -> targetEnv.getTargetPaths(localFile.pathString).first() } val exePath: FullPathOnTarget - val cmdLine = TargetedCommandLineBuilder(request).also { - binOnTarget.configureTargetCmdLine(it) + val cmdLine = TargetedCommandLineBuilder(request).also {commandLineBuilder -> + binOnTarget.configureTargetCmdLine(commandLineBuilder) // exe path is always fixed (pre-presolved) promise. It can't be obtained directly because of Targets API limitation - exePath = it.exePath.localValue.blockingGet(1000) ?: error("Exe path not set: $binOnTarget is broken") - it.addParameters(args) + exePath = commandLineBuilder.exePath.localValue.blockingGet(1000) ?: error("Exe path not set: $binOnTarget is broken") + launchRequest.usePty?.let { + val ptyOptions = LocalPtyOptions + .defaults() + .builder() + .initialRows(it.rows.toInt()) + .initialColumns(it.cols.toInt()) + .build() + commandLineBuilder.ptyOptions = LocalTargetPtyOptions(ptyOptions) + } + + commandLineBuilder.addParameters(args) for ((k, v) in launchRequest.env) { - it.addEnvironmentVariable(k, v) + commandLineBuilder.addEnvironmentVariable(k, v) } }.build() return@withContext Result.success(ProcessLauncher(exeForError = Exe.OnTarget(exePath), args = args, processCommands = TargetProcessCommands(launchRequest.scopeToBind, exePath, targetEnv, cmdLine))) diff --git a/python/python-exec-service/src/com/intellij/python/community/execService/implInterfaces.kt b/python/python-exec-service/src/com/intellij/python/community/execService/implInterfaces.kt new file mode 100644 index 000000000000..e16c59fd5899 --- /dev/null +++ b/python/python-exec-service/src/com/intellij/python/community/execService/implInterfaces.kt @@ -0,0 +1,10 @@ +// 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 + +import org.jetbrains.annotations.Nls + +internal sealed interface ExecOptionsBase { + val env: Map + val processDescription: @Nls String? + val tty: TtySize? +} \ No newline at end of file diff --git a/python/src/com/jetbrains/python/packaging/PyExecutionExceptionExt.kt b/python/src/com/jetbrains/python/packaging/PyExecutionExceptionExt.kt index 107358911962..ed3f61a65085 100644 --- a/python/src/com/jetbrains/python/packaging/PyExecutionExceptionExt.kt +++ b/python/src/com/jetbrains/python/packaging/PyExecutionExceptionExt.kt @@ -4,10 +4,7 @@ package com.jetbrains.python.packaging import com.intellij.execution.process.ProcessOutput import com.intellij.platform.eel.provider.utils.stderrString import com.intellij.platform.eel.provider.utils.stdoutString -import com.jetbrains.python.errorProcessing.Exe -import com.jetbrains.python.errorProcessing.ExecError -import com.jetbrains.python.errorProcessing.ExecErrorReason -import com.jetbrains.python.errorProcessing.MessageError +import com.jetbrains.python.errorProcessing.* import java.io.IOException /** @@ -21,7 +18,7 @@ internal fun PyExecutionException.copyWith(newCommand: String, newArgs: List { - PyExecutionException(ExecError(Exe.fromString(newCommand), newArgs.toTypedArray(), ExecErrorReason.Timeout, err.message)) + PyExecutionException(ExecErrorImpl(Exe.fromString(newCommand), newArgs.toTypedArray(), ExecErrorReason.Timeout, err.message)) } is ExecErrorReason.UnexpectedProcessTermination -> { val output = ProcessOutput(reason.stdoutString, reason.stderrString, reason.exitCode, false, false) diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImplBase.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImplBase.java index 3152d7fdd823..c9e6fd6149b9 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImplBase.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImplBase.java @@ -15,7 +15,7 @@ import com.intellij.util.concurrency.annotations.RequiresReadLock; import com.intellij.util.net.HttpConfigurable; import com.jetbrains.python.PyPsiPackageUtil; import com.jetbrains.python.PySdkBundle; -import com.jetbrains.python.errorProcessing.ExecError; +import com.jetbrains.python.errorProcessing.ExecErrorImpl; import com.jetbrains.python.errorProcessing.ExecErrorReason; import com.jetbrains.python.packaging.common.PythonPackage; import com.jetbrains.python.packaging.pip.PipParseUtils; @@ -134,7 +134,7 @@ public abstract class PyPackageManagerImplBase extends PyPackageManager { } catch (PyExecutionException e) { var pyError = e.getPyError(); - if (pyError instanceof ExecError error) { + if (pyError instanceof ExecErrorImpl error) { var errorReason = error.getErrorReason(); if (errorReason instanceof ExecErrorReason.UnexpectedProcessTermination unexpectedProcessTermination) { int exitCode = unexpectedProcessTermination.getExitCode(); diff --git a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java index 704109a6b914..5c9dc28082d5 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java @@ -20,7 +20,7 @@ import com.intellij.webcore.packaging.RepoPackage; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PySdkBundle; import com.jetbrains.python.errorProcessing.Exe; -import com.jetbrains.python.errorProcessing.ExecError; +import com.jetbrains.python.errorProcessing.ExecErrorImpl; import com.jetbrains.python.errorProcessing.ExecErrorReason; import com.jetbrains.python.packaging.*; import com.jetbrains.python.packaging.PyPIPackageUtil.PackageDetails; @@ -320,7 +320,7 @@ public class PyPackageManagementService extends PackageManagementServiceEx { @Nullable Sdk sdk, @Nullable String packageName) { if (e instanceof PyExecutionException pyExecEx && - pyExecEx.getPyError() instanceof ExecError execError && + pyExecEx.getPyError() instanceof ExecErrorImpl execError && execError.getErrorReason() instanceof ExecErrorReason.UnexpectedProcessTermination execFailed) { var stdout = getStdoutString(execFailed); var stderr = getStderrString(execFailed); @@ -343,7 +343,7 @@ public class PyPackageManagementService extends PackageManagementServiceEx { private static @Nullable @DetailedDescription String findErrorSolution(@NotNull PyExecutionException executionException, @Nullable String cause, @Nullable Sdk sdk) { - if (executionException.getPyError() instanceof ExecError e) { + if (executionException.getPyError() instanceof ExecErrorImpl e) { if (cause != null) { if (StringUtil.containsIgnoreCase(cause, "SyntaxError")) { diff --git a/python/src/com/jetbrains/python/sdk/sdkExecApi.kt b/python/src/com/jetbrains/python/sdk/sdkExecApi.kt index 5cfd737dd645..bf9285df6fb0 100644 --- a/python/src/com/jetbrains/python/sdk/sdkExecApi.kt +++ b/python/src/com/jetbrains/python/sdk/sdkExecApi.kt @@ -5,8 +5,10 @@ import com.intellij.openapi.projectRoots.Sdk import com.intellij.python.community.execService.* import com.intellij.python.community.execService.python.HelperName import com.intellij.python.community.execService.python.addHelper +import com.jetbrains.python.Result import com.jetbrains.python.errorProcessing.PyResult import com.jetbrains.python.target.PyTargetAwareAdditionalData +import kotlinx.coroutines.CoroutineScope import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.CheckReturnValue @@ -48,6 +50,17 @@ suspend fun ExecService.executeHelper( ): PyResult = execGetStdout(sdk, Args().addHelper(helper).addArgs(helperArgs), options, procListener) + +// See function it calls for more info +@ApiStatus.Internal +@CheckReturnValue +suspend fun ExecService.executeGetProcess( + sdk: Sdk, + args: Args = Args(), + scopeToBind: CoroutineScope? = null, + options: ExecGetProcessOptions = ExecGetProcessOptions(), +): Result> = executeGetProcess(sdk.asBinToExecute(), args, scopeToBind, options) + /** * Converts SDK to [BinOnTarget] to be used by [ExecService] */