Python: move PyError and ErrorSink into api to reuse it in modules

GitOrigin-RevId: 44eeac6d4a44a4ce095a1b4aed1cccca61a4efbb
This commit is contained in:
Ilya.Kazakevich
2025-01-29 01:00:19 +01:00
committed by intellij-monorepo-bot
parent cd5f782c15
commit 4678462b27
28 changed files with 202 additions and 197 deletions

View File

@@ -0,0 +1,41 @@
// 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.errorProcessing
import kotlinx.coroutines.flow.FlowCollector
/**
* [emit] user-readable errors here.
*
* This class should be used by the topmost classes, tightly coupled to the UI.
* For the most business-logic and backend functions please return [com.jetbrains.python.Result] or error.
*
* Please do not report *all* exceptions here: This is *not* the class for NPEs and AOOBs:
* do not pass exceptions caught by `catch(e: Exception)` or `runCatching`: only report exceptions user interested in.
* `IOException` or `ExecutionException` are generally ok.
*
* There will be unified sink soon to show and log errors.
* Currently, only [com.jetbrains.python.util.ShowingMessageErrorSync] is a well-known implementation
*
* Example:
* ```kotlin
* suspend fun someLogic(): Result<@NlsSafe String, IOException> = withContext(Dispatchers.IO) {
* try {
* Result.success(Path.of("1.txt").readText())
* }
* catch (e: IOException) {
* Result.failure(e)
* }
* }
*
* suspend fun ui(errorSink: ErrorSink) {
* someLogic()
* .onSuccess {
* Messages.showInfoMessage("..", it)
* }
* .onFailure {
* errorSink.emit(it.localizedMessage)
* }
* }
* ```
*/
typealias ErrorSink = FlowCollector<PyError>

View File

@@ -0,0 +1,58 @@
// 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.errorProcessing
import com.intellij.openapi.util.NlsSafe
import com.jetbrains.python.execution.PyExecutionFailure
import com.jetbrains.python.execution.userMessage
import com.jetbrains.python.packaging.PyExecutionException
import org.jetbrains.annotations.Nls
sealed class PyError(val message: @NlsSafe String) {
/**
* Some "business" error: just a message to be displayed to a user
*/
class Message(message: @NlsSafe String) : PyError(message)
/**
* Some process can't be executed. To be displayed specially.
*/
data class ExecException(val execFailure: PyExecutionFailure) : PyError(execFailure.userMessage)
override fun toString(): String = message
}
suspend fun ErrorSink.emit(@NlsSafe message: String) {
emit(PyError.Message(message))
}
suspend fun ErrorSink.emit(e: PyExecutionException) {
emit(PyError.ExecException(e))
}
@Deprecated("Migrate to native python result")
fun <T> Result<T>.asPythonResult(): com.jetbrains.python.Result<T, PyError> =
com.jetbrains.python.Result.Companion.success(getOrElse {
return if (it is PyExecutionException) {
failure(it)
}
else {
failure(it.localizedMessage)
}
}
)
@Deprecated("Use python result, not kotlin result")
fun <S, E> com.jetbrains.python.Result<S, E>.asKotlinResult(): Result<S> = when (this) {
is com.jetbrains.python.Result.Failure -> Result.failure(
when (val r = error) {
is Throwable -> r
is PyError.Message -> Exception(r.message)
is PyError.ExecException -> Exception(r.execFailure.userMessage)
else -> Exception(r.toString())
}
)
is com.jetbrains.python.Result.Success -> Result.success(result)
}
fun failure(message: @Nls String): com.jetbrains.python.Result.Failure<PyError.Message> = com.jetbrains.python.Result.Companion.failure(PyError.Message(message))
fun failure(failure: PyExecutionFailure): com.jetbrains.python.Result.Failure<PyError.ExecException> = com.jetbrains.python.Result.failure(PyError.ExecException(failure))

View File

@@ -0,0 +1,5 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
@ApiStatus.Experimental
package com.jetbrains.python.errorProcessing;
import org.jetbrains.annotations.ApiStatus;