Python: introduce PyExecResult as an alias for Result<T, ExecError>.

It is just more convenient to right less generic params.

GitOrigin-RevId: cd33be23da4bb3cb09658aa6564e4d298a3ba72d
This commit is contained in:
Ilya.Kazakevich
2025-05-14 20:47:57 +00:00
committed by intellij-monorepo-bot
parent f1f77c0af7
commit 6bf17f551a
22 changed files with 109 additions and 116 deletions
@@ -12,7 +12,7 @@ import com.intellij.python.hatch.PyHatchBundle
import com.intellij.python.hatch.runtime.HatchConstants
import com.intellij.python.hatch.runtime.HatchRuntime
import com.jetbrains.python.Result
import com.jetbrains.python.errorProcessing.ExecError
import com.jetbrains.python.errorProcessing.PyExecResult
import com.jetbrains.python.errorProcessing.PyResult
import io.github.z4kn4fein.semver.Version
import io.github.z4kn4fein.semver.VersionFormatException
@@ -21,7 +21,7 @@ import java.nio.file.Path
/**
* Handles hatch-specific errors, runs [transformer] only on outputs with codes 0 or 1 without tracebacks.
*/
private suspend fun <T> HatchRuntime.executeAndHandleErrors(vararg arguments: String, transformer: ProcessOutputTransformer<T>): Result<T, ExecError> {
private suspend fun <T> HatchRuntime.executeAndHandleErrors(vararg arguments: String, transformer: ProcessOutputTransformer<T>): PyExecResult<T> {
val errorHandlerTransformer: ProcessOutputTransformer<T> = { output ->
when {
output.exitCode !in 0..1 -> Result.failure(null)
@@ -41,7 +41,7 @@ private suspend fun <T> HatchRuntime.executeAndMatch(
expectedOutput: Regex,
outputContentSupplier: (EelProcessExecutionResultInfo) -> String = { it.stdoutString },
transformer: (MatchResult) -> Result<T, @NlsSafe String?>,
): Result<T, ExecError> {
): PyExecResult<T> {
return this.executeAndHandleErrors(*arguments) { processOutput ->
if (processOutput.exitCode != 0) return@executeAndHandleErrors Result.failure(null)
@@ -60,11 +60,11 @@ sealed class HatchCommand(private val command: Array<String>, protected val runt
@Suppress("unused")
constructor(command: String, runtime: HatchRuntime) : this(arrayOf(command), runtime)
protected suspend fun <T> executeAndHandleErrors(vararg arguments: String, transformer: ProcessOutputTransformer<T>): Result<T, ExecError> {
protected suspend fun <T> executeAndHandleErrors(vararg arguments: String, transformer: ProcessOutputTransformer<T>): PyExecResult<T> {
return runtime.executeAndHandleErrors(*command, *arguments, transformer = transformer)
}
protected suspend fun <T> executeAndMatch(vararg arguments: String, expectedOutput: Regex, transformer: (MatchResult) -> Result<T, @NlsSafe String?>): Result<T, ExecError> {
protected suspend fun <T> executeAndMatch(vararg arguments: String, expectedOutput: Regex, transformer: (MatchResult) -> Result<T, @NlsSafe String?>): PyExecResult<T> {
return runtime.executeAndMatch(*command, *arguments, expectedOutput = expectedOutput, transformer = transformer)
}
}
@@ -73,12 +73,12 @@ class HatchCli(private val runtime: HatchRuntime) {
/**
* Build a project
*/
fun build(): Result<Unit, ExecError> = TODO()
fun build(): PyExecResult<Unit> = TODO()
/**
* Remove build artifacts
*/
fun clean(): Result<Unit, ExecError> = TODO()
fun clean(): PyExecResult<Unit> = TODO()
/**
* Manage the config file
@@ -98,7 +98,7 @@ class HatchCli(private val runtime: HatchRuntime) {
/**
* Format and lint source code
*/
fun fmt(): Result<Unit, ExecError> = TODO()
fun fmt(): PyExecResult<Unit> = TODO()
/**
* Create or initialize a project.
@@ -139,7 +139,7 @@ class HatchCli(private val runtime: HatchRuntime) {
/**
* Publish build artifacts
*/
fun publish(): Result<Unit, ExecError> = TODO()
fun publish(): PyExecResult<Unit> = TODO()
/**
* Manage Python installations
@@ -149,7 +149,7 @@ class HatchCli(private val runtime: HatchRuntime) {
/**
* Run commands within project environments
*/
suspend fun run(envName: String? = null, vararg command: String): Result<String, ExecError> {
suspend fun run(envName: String? = null, vararg command: String): PyExecResult<String> {
val envRuntime = envName?.let { runtime.withEnv(HatchConstants.AppEnvVars.ENV to it) } ?: runtime
return envRuntime.executeAndHandleErrors("run", *command) { output ->
if (output.exitCode != 0) return@executeAndHandleErrors Result.failure(null)
@@ -170,14 +170,14 @@ class HatchCli(private val runtime: HatchRuntime) {
/**
* Enter a shell within a project's environment
*/
fun shell(): Result<Unit, ExecError> = TODO()
fun shell(): PyExecResult<Unit> = TODO()
data class HatchStatus(val project: String, val location: Path, val config: Path)
/**
* Show information about the current environment
*/
suspend fun status(): Result<HatchStatus, ExecError> {
suspend fun status(): PyExecResult<HatchStatus> {
val expectedOutput = """^\[Project] - (.*)\n\[Location] - (.*)\n\[Config] - (.*)\n$""".toRegex()
return runtime.executeAndMatch("status", expectedOutput = expectedOutput, outputContentSupplier = { it.stderrString }) { matchResult ->
@@ -194,14 +194,14 @@ class HatchCli(private val runtime: HatchRuntime) {
/**
* Run tests
*/
fun test(): Result<Unit, ExecError> = TODO()
fun test(): PyExecResult<Unit> = TODO()
/**
* View a project's version.
*
* @return Project Version
*/
suspend fun getVersion(): Result<Version, ExecError> {
suspend fun getVersion(): PyExecResult<Version> {
return runtime.executeAndHandleErrors("version") { processOutput ->
val output = processOutput.takeIf { it.exitCode == 0 }?.stdoutString?.trim()
?: return@executeAndHandleErrors Result.failure(null)
@@ -3,8 +3,7 @@ package com.intellij.python.hatch.cli
import com.intellij.python.community.execService.ZeroCodeStdoutTransformer
import com.intellij.python.hatch.runtime.HatchRuntime
import com.jetbrains.python.Result
import com.jetbrains.python.errorProcessing.ExecError
import com.jetbrains.python.errorProcessing.PyExecResult
/**
* Manage environment dependencies
@@ -13,28 +12,28 @@ class HatchConfig(runtime: HatchRuntime) : HatchCommand("config", runtime) {
/**
* Open the config location in your file manager
*/
suspend fun explore(): Result<String, ExecError> {
suspend fun explore(): PyExecResult<String> {
return executeAndHandleErrors("explore", transformer = ZeroCodeStdoutTransformer)
}
/**
* Show the location of the config file
*/
suspend fun find(): Result<String, ExecError> {
suspend fun find(): PyExecResult<String> {
return executeAndHandleErrors("find", transformer = ZeroCodeStdoutTransformer)
}
/**
* Restore the config file to default settings
*/
suspend fun restore(): Result<String, ExecError> {
suspend fun restore(): PyExecResult<String> {
return executeAndHandleErrors("restore", transformer = ZeroCodeStdoutTransformer)
}
/**
* Assign values to config file entries
*/
suspend fun set(key: String, value: String): Result<String, ExecError> {
suspend fun set(key: String, value: String): PyExecResult<String> {
return executeAndHandleErrors("set", key, value, transformer = ZeroCodeStdoutTransformer)
}
@@ -43,7 +42,7 @@ class HatchConfig(runtime: HatchRuntime) : HatchCommand("config", runtime) {
*
* @param all Do not scrub secret fields
*/
suspend fun show(all: Boolean? = null): Result<String, ExecError> {
suspend fun show(all: Boolean? = null): PyExecResult<String> {
val options = listOf(all to "--all").makeOptions()
return executeAndHandleErrors("show", *options, transformer = ZeroCodeStdoutTransformer)
}
@@ -51,7 +50,7 @@ class HatchConfig(runtime: HatchRuntime) : HatchCommand("config", runtime) {
/**
* Update the config file with any new fields
*/
suspend fun update(): Result<String, ExecError> {
suspend fun update(): PyExecResult<String> {
return executeAndHandleErrors("update", transformer = ZeroCodeStdoutTransformer)
}
}
@@ -3,8 +3,7 @@ package com.intellij.python.hatch.cli
import com.intellij.python.community.execService.ZeroCodeStdoutTransformer
import com.intellij.python.hatch.runtime.HatchRuntime
import com.jetbrains.python.Result
import com.jetbrains.python.errorProcessing.ExecError
import com.jetbrains.python.errorProcessing.PyExecResult
enum class Scope(val options: Array<String>) {
All(emptyArray()),
@@ -19,7 +18,7 @@ class HatchDep(runtime: HatchRuntime) : HatchCommand("dep", runtime) {
/**
* Output a hash of the currently defined dependencies
**/
suspend fun hash(scope: Scope = Scope.All): Result<String, ExecError> {
suspend fun hash(scope: Scope = Scope.All): PyExecResult<String> {
return executeAndHandleErrors("hash", *scope.options, transformer = ZeroCodeStdoutTransformer)
}
@@ -38,7 +37,7 @@ class HatchDepShow(runtime: HatchRuntime) : HatchCommand(arrayOf("dep", "show"),
*
* @param features only show the dependencies of the specified features
*/
suspend fun requirements(scope: Scope = Scope.All, features: List<String>? = null): Result<String, ExecError> {
suspend fun requirements(scope: Scope = Scope.All, features: List<String>? = null): PyExecResult<String> {
val options = features?.flatMap { listOf("--feature", it) }?.toTypedArray() ?: arrayOf("--all")
return executeAndHandleErrors("requirements", *scope.options, *options, transformer = ZeroCodeStdoutTransformer)
}
@@ -46,7 +45,7 @@ class HatchDepShow(runtime: HatchRuntime) : HatchCommand(arrayOf("dep", "show"),
/**
* Enumerate dependencies in a tabular format.
*/
suspend fun table(scope: Scope = Scope.All): Result<String, ExecError> {
suspend fun table(scope: Scope = Scope.All): PyExecResult<String> {
val options = listOf(null to "--lines", true to "--ascii").makeOptions()
return executeAndHandleErrors("table", *scope.options, *options, transformer = ZeroCodeStdoutTransformer)
}
@@ -8,6 +8,7 @@ import com.intellij.python.hatch.runtime.HatchRuntime
import com.jetbrains.python.PythonHomePath
import com.jetbrains.python.Result
import com.jetbrains.python.errorProcessing.ExecError
import com.jetbrains.python.errorProcessing.PyExecResult
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
@@ -129,7 +130,7 @@ class HatchEnv(runtime: HatchRuntime) : HatchCommand("env", runtime) {
*
* @return true if created, false if already exists
*/
suspend fun create(envName: String? = null): Result<CreateResult, ExecError> {
suspend fun create(envName: String? = null): PyExecResult<CreateResult> {
val arguments = if (envName == null) emptyArray() else arrayOf(envName)
return executeAndHandleErrors("create", *arguments) {
val actualEnvName = envName ?: DEFAULT_ENV_NAME
@@ -147,7 +148,7 @@ class HatchEnv(runtime: HatchRuntime) : HatchCommand("env", runtime) {
*
* @return path to environment
*/
suspend fun find(envName: String? = null): Result<PythonHomePath?, ExecError> {
suspend fun find(envName: String? = null): PyExecResult<PythonHomePath?> {
val arguments = if (envName == null) emptyArray() else arrayOf(envName)
return executeAndHandleErrors("find", *arguments) {
when (it.exitCode) {
@@ -182,7 +183,7 @@ class HatchEnv(runtime: HatchRuntime) : HatchCommand("env", runtime) {
* - [RemoveResult.CantRemoveActiveEnvironment] if the environment cannot be removed because it is currently active.
* - An error wrapped in [ExecError] in case of execution failure.
*/
suspend fun remove(envName: String? = null): Result<RemoveResult, ExecError> {
suspend fun remove(envName: String? = null): PyExecResult<RemoveResult> {
val arguments = if (envName == null) emptyArray() else arrayOf(envName)
return executeAndHandleErrors("remove", *arguments) {
val actualEnvName = envName ?: DEFAULT_ENV_NAME
@@ -204,7 +205,7 @@ class HatchEnv(runtime: HatchRuntime) : HatchCommand("env", runtime) {
* - [HatchDetailedEnvironments] if operation is successful.
* - An error wrapped in [ExecError] if an execution failure occurs.
*/
suspend fun showWithDetails(vararg envs: String): Result<HatchDetailedEnvironments, ExecError> {
suspend fun showWithDetails(vararg envs: String): PyExecResult<HatchDetailedEnvironments> {
return executeAndHandleErrors("show", "--json", *envs) { processOutput ->
val output = processOutput.takeIf { it.exitCode == 0 }?.stdoutString
?: return@executeAndHandleErrors Result.failure(null)
@@ -231,7 +232,7 @@ class HatchEnv(runtime: HatchRuntime) : HatchCommand("env", runtime) {
* - [HatchDetailedEnvironments] if operation is successful.
* - An error wrapped in [ExecError] if an execution failure occurs.
*/
suspend fun show(vararg envs: String, internal: Boolean = false): Result<HatchEnvironments, ExecError> {
suspend fun show(vararg envs: String, internal: Boolean = false): PyExecResult<HatchEnvironments> {
val options = listOf(internal to "--internal").makeOptions()
return executeAndMatch("show", "--ascii", *options, *envs, expectedOutput = SHOW_RESPONSE_REGEX) { matchResult ->
@@ -4,7 +4,7 @@ package com.intellij.python.hatch.cli
import com.intellij.platform.eel.provider.utils.stdoutString
import com.intellij.python.hatch.runtime.HatchRuntime
import com.jetbrains.python.Result
import com.jetbrains.python.errorProcessing.ExecError
import com.jetbrains.python.errorProcessing.PyExecResult
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
@@ -62,7 +62,7 @@ class HatchProject(runtime: HatchRuntime) : HatchCommand("project", runtime) {
/**
* Display project metadata
*/
suspend fun metadata(): Result<Metadata, ExecError> {
suspend fun metadata(): PyExecResult<Metadata> {
return executeAndHandleErrors("metadata") { processOutput ->
val output = processOutput.takeIf { it.exitCode == 0 }?.stdoutString
?: return@executeAndHandleErrors Result.failure(null)
@@ -8,7 +8,7 @@ import com.intellij.platform.eel.provider.utils.stdoutString
import com.intellij.python.hatch.cli.HatchPython.PythonInstallResponse.AbortReason
import com.intellij.python.hatch.runtime.HatchRuntime
import com.jetbrains.python.Result
import com.jetbrains.python.errorProcessing.ExecError
import com.jetbrains.python.errorProcessing.PyExecResult
import java.nio.file.Path
/**
@@ -26,7 +26,7 @@ class HatchPython(runtime: HatchRuntime) : HatchCommand("python", runtime) {
* @param parent Show the parent directory of the Python binary
* @param dir The directory in which distributions reside
*/
suspend fun find(name: String, parent: Boolean? = null, dir: String? = null): Result<Path?, ExecError> {
suspend fun find(name: String, parent: Boolean? = null, dir: String? = null): PyExecResult<Path?> {
val options = listOf(parent to "--parent").makeOptions() + buildDirOption(dir)
return executeAndHandleErrors("find", *options, name) { output ->
@@ -129,7 +129,7 @@ class HatchPython(runtime: HatchRuntime) : HatchCommand("python", runtime) {
private: Boolean? = null,
update: Boolean? = null,
dir: String? = null,
): Result<PythonInstallResponse, ExecError> {
): PyExecResult<PythonInstallResponse> {
val options = listOf(update to "--update", private to "--private").makeOptions() + buildDirOption(dir)
return executeAndHandleErrors("install", *options, *names) { output ->
Result.success(parsePythonInstallCommandOutput(output))
@@ -144,7 +144,7 @@ class HatchPython(runtime: HatchRuntime) : HatchCommand("python", runtime) {
* @param names Distributions to remove, you may select `all` to install all compatible distributions
* @param dir The directory in which distributions reside
*/
suspend fun remove(vararg names: String = ALL_NAMES, dir: String? = null): Result<PythonRemoveResponse, ExecError> {
suspend fun remove(vararg names: String = ALL_NAMES, dir: String? = null): PyExecResult<PythonRemoveResponse> {
return executeAndHandleErrors("remove", *buildDirOption(dir), *names) { processOutput ->
val output = processOutput.stderrString
val notInstalledRegex = Regex("""^Distribution is not installed: (.*)$""", RegexOption.MULTILINE)
@@ -165,7 +165,7 @@ class HatchPython(runtime: HatchRuntime) : HatchCommand("python", runtime) {
* @param dir The directory in which distributions reside
* @return Name to Version as a map
*/
suspend fun show(dir: String? = null): Result<ShowResponse, ExecError> {
suspend fun show(dir: String? = null): PyExecResult<ShowResponse> {
val nameToVersionRegex = """\|\s+([^|\s]+)\s+\|\s+([^|\s]+)\s+\|""".toRegex()
fun parseNameToVersions(payload: String) = nameToVersionRegex.findAll(payload).associate {
val (name, version) = it.destructured
@@ -200,7 +200,7 @@ class HatchPython(runtime: HatchRuntime) : HatchCommand("python", runtime) {
* @param names Distributions to update, you may select `all` to install all compatible distributions
* @param dir The directory in which distributions reside
*/
suspend fun update(vararg names: String = ALL_NAMES, dir: String? = null): Result<PythonInstallResponse, ExecError> {
suspend fun update(vararg names: String = ALL_NAMES, dir: String? = null): PyExecResult<PythonInstallResponse> {
return executeAndHandleErrors("update", *buildDirOption(dir), *names) { output ->
Result.success(parsePythonInstallCommandOutput(output))
}
@@ -7,7 +7,7 @@ import com.intellij.python.hatch.runtime.HatchRuntime
import com.intellij.util.Url
import com.intellij.util.Urls
import com.jetbrains.python.Result
import com.jetbrains.python.errorProcessing.ExecError
import com.jetbrains.python.errorProcessing.PyExecResult
/**
* Manage environment dependencies
@@ -17,7 +17,7 @@ class HatchSelf(runtime: HatchRuntime) : HatchCommand("self", runtime) {
/**
* Generate a pre-populated GitHub issue.
*/
suspend fun report(): Result<Url, ExecError> {
suspend fun report(): PyExecResult<Url> {
return executeAndHandleErrors("report", "--no-open") { processOutput ->
val output = processOutput.takeIf { it.exitCode == 0 }?.stdoutString?.trim()
?: return@executeAndHandleErrors Result.failure(null)
@@ -34,10 +34,10 @@ class HatchSelf(runtime: HatchRuntime) : HatchCommand("self", runtime) {
/**
* Restore the installation
*/
fun restore(): Result<String, ExecError> = TODO()
fun restore(): PyExecResult<String> = TODO()
/**
* Install the latest version
*/
fun update(): Result<String, ExecError> = TODO()
fun update(): PyExecResult<String> = TODO()
}
@@ -9,7 +9,7 @@ import com.intellij.python.hatch.cli.HatchCli
import com.jetbrains.python.PythonBinary
import com.jetbrains.python.PythonHomePath
import com.jetbrains.python.Result
import com.jetbrains.python.errorProcessing.ExecError
import com.jetbrains.python.errorProcessing.PyExecResult
import com.jetbrains.python.errorProcessing.PyResult
import com.jetbrains.python.resolvePythonBinary
import java.nio.file.Path
@@ -55,11 +55,11 @@ class HatchRuntime(
* Pure execution of [hatchBinary] with command line [arguments] and [execOptions] by [execService]
* Doesn't make any validation of stdout/stderr content.
*/
internal suspend fun <T> execute(vararg arguments: String, processOutputTransformer: ProcessOutputTransformer<T>): Result<T, ExecError> {
internal suspend fun <T> execute(vararg arguments: String, processOutputTransformer: ProcessOutputTransformer<T>): PyExecResult<T> {
return execService.execute(hatchBinary, arguments.toList(), execOptions, processOutputTransformer = processOutputTransformer)
}
internal suspend fun <T> executeInteractive(vararg arguments: String, processSemiInteractiveFun: ProcessSemiInteractiveFun<T>): Result<T, ExecError> {
internal suspend fun <T> executeInteractive(vararg arguments: String, processSemiInteractiveFun: ProcessSemiInteractiveFun<T>): PyExecResult<T> {
return execService.executeInteractive(hatchBinary, arguments.toList(), execOptions, processSemiInteractiveHandler(code = processSemiInteractiveFun))
}