mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Python: refactor PyError hierarchy, migrate to PyResult.
DO: For upper-level (public) API use `PyResult`. (Optionally) for low-level APIs inside your modules use python `Result<S, E>`. Represent errors as `PyError` whenever possible. Report `PyError` to `ErrorSink` at the top of your code. DON'T: Use `kotlin.Result` Use `PyExecutionException` Use any exception to represent user errors. GitOrigin-RevId: 4ecf69e1fae8be9192cd33b90e0147c725a98964
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d366245171
commit
803e270d45
+3
-4
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// 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.pycharm.community.ide.impl.configuration
|
||||
|
||||
import com.intellij.CommonBundle
|
||||
@@ -32,7 +32,6 @@ import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
|
||||
import com.intellij.util.ui.JBUI
|
||||
import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.PySdkBundle
|
||||
import com.jetbrains.python.failure
|
||||
import com.jetbrains.python.packaging.PyPackageManager
|
||||
import com.jetbrains.python.packaging.PyPackageUtil
|
||||
import com.jetbrains.python.packaging.PyTargetEnvironmentPackageManager
|
||||
@@ -67,7 +66,7 @@ class PyRequirementsTxtOrSetupPySdkConfiguration : PyProjectSdkConfigurationExte
|
||||
|
||||
val data = askForEnvData(module, existingSdks, source)
|
||||
if (data == null) {
|
||||
return failure("askForEnvData is null")
|
||||
return com.jetbrains.python.failure("askForEnvData is null")
|
||||
}
|
||||
|
||||
val (location, chosenBaseSdk, requirementsTxtOrSetupPy) = data
|
||||
@@ -181,7 +180,7 @@ class PyRequirementsTxtOrSetupPySdkConfiguration : PyProjectSdkConfigurationExte
|
||||
|
||||
override fun createCenterPanel(): JComponent {
|
||||
return JPanel(BorderLayout()).apply {
|
||||
val border = IdeBorderFactory.createEmptyBorder(Insets(4, 0, 6, 0))
|
||||
val border = IdeBorderFactory.createEmptyBorder(JBUI.insets(4, 0, 6, 0))
|
||||
val message = PyCharmCommunityCustomizationBundle.message("sdk.create.venv.permission", requirementsTxtOrSetupPy.name)
|
||||
|
||||
add(
|
||||
|
||||
@@ -25,7 +25,8 @@ import com.intellij.python.community.services.systemPython.SystemPythonService
|
||||
import com.intellij.util.SystemProperties
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.MessageError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.errorProcessing.failure
|
||||
import com.jetbrains.python.mapResult
|
||||
import com.jetbrains.python.projectCreation.createVenvAndSdk
|
||||
@@ -53,7 +54,7 @@ fun createMiscProject(
|
||||
confirmInstallation: suspend () -> Boolean,
|
||||
projectPath: Path = miscProjectDefaultPath.value,
|
||||
systemPythonService: SystemPythonService = SystemPythonService(),
|
||||
): Result<Job, PyError> =
|
||||
): PyResult<Job> =
|
||||
runWithModalProgressBlocking(ModalTaskOwner.guess(),
|
||||
PyCharmCommunityCustomizationBundle.message("misc.project.generating.env"),
|
||||
TaskCancellation.cancellable()) {
|
||||
@@ -130,7 +131,7 @@ private suspend fun createProjectAndSdk(
|
||||
projectPath: Path,
|
||||
confirmInstallation: suspend () -> Boolean,
|
||||
systemPythonService: SystemPythonService,
|
||||
): Result<Pair<Project, Sdk>, PyError> {
|
||||
): PyResult<Pair<Project, Sdk>> {
|
||||
val vfsProjectPath = createProjectDir(projectPath).getOr { return it }
|
||||
val project = openProject(projectPath)
|
||||
val sdk = createVenvAndSdk(project, confirmInstallation, systemPythonService, vfsProjectPath).getOr { return it }
|
||||
@@ -155,7 +156,7 @@ private suspend fun openProject(projectPath: Path): Project {
|
||||
/**
|
||||
* Creating a project != creating a directory for it, but we need a directory to create a template file
|
||||
*/
|
||||
private suspend fun createProjectDir(projectPath: Path): Result<VirtualFile, PyError.Message> = withContext(Dispatchers.IO) {
|
||||
private suspend fun createProjectDir(projectPath: Path): Result<VirtualFile, MessageError> = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
projectPath.createDirectories()
|
||||
}
|
||||
|
||||
+2
-2
@@ -10,14 +10,14 @@ import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.pycharm.community.ide.impl.newProjectWizard.welcome.PyWelcome
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.newProjectWizard.PyV3ProjectTypeSpecificSettings
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class PyV3EmptyProjectSettings(var generateWelcomeScript: Boolean = false) : PyV3ProjectTypeSpecificSettings {
|
||||
|
||||
override suspend fun generateProject(module: Module, baseDir: VirtualFile, sdk: Sdk): Result<Unit, PyError> {
|
||||
override suspend fun generateProject(module: Module, baseDir: VirtualFile, sdk: Sdk): PyResult<Unit> {
|
||||
if (!generateWelcomeScript) return Result.success(Unit)
|
||||
|
||||
val sourceRoot = module.rootManager.sourceRoots.firstOrNull() ?: baseDir
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
python.execution.error={0}\nThe following command finished with error: {1}\nOutput: {2}\nError: {3}\Exit code: {4}
|
||||
python.execution.cant.start.error={0}\nThe following command could not be started: {1}
|
||||
python.execution.cant.start.error={0}\nThe following command could not be started: {1}. Error {2} code: {3}
|
||||
python.execution.timeout={0}\nThe following command stopped due to timeout: {1}.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
// 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
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
@@ -6,6 +6,9 @@ import com.jetbrains.python.Result.Failure
|
||||
import com.jetbrains.python.Result.Success
|
||||
|
||||
/**
|
||||
* TL;TR: This class is kinda low-level. Use [com.jetbrains.python.errorProcessing.PyResult] in upper-level signatures,
|
||||
* but use this class as low-level api (preferably internal) inside your modules.
|
||||
*
|
||||
* Operation result to be used as `Maybe` instead of checked exceptions.
|
||||
* Unlike Kotlin `Result`, [ERR] could be anything (i.e [String]).
|
||||
*
|
||||
|
||||
@@ -4,38 +4,14 @@ package com.jetbrains.python.errorProcessing
|
||||
import kotlinx.coroutines.flow.FlowCollector
|
||||
|
||||
/**
|
||||
* [emit] user-readable errors here.
|
||||
* [emit] user-readable [PyError] 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.
|
||||
* For the most business-logic and backend functions please return [PyResult] or [PyError].
|
||||
*
|
||||
* 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 a unified sink soon to show and log errors.
|
||||
* Currently, only [com.jetbrains.python.util.ShowingMessageErrorSync] is a well-known implementation.
|
||||
*
|
||||
* 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)
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* See [PyError]
|
||||
*/
|
||||
typealias ErrorSink = FlowCollector<PyError>
|
||||
@@ -0,0 +1,85 @@
|
||||
// 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.execution.process.ProcessOutput
|
||||
import com.intellij.openapi.util.NlsContexts
|
||||
import com.jetbrains.python.PyCommunityBundle
|
||||
import org.jetbrains.annotations.Nls
|
||||
|
||||
/**
|
||||
* External process error.
|
||||
*/
|
||||
class ExecError(
|
||||
/**
|
||||
* I.e ['python', '-v']
|
||||
*/
|
||||
val command: Array<String>,
|
||||
|
||||
val errorReason: ExecErrorReason,
|
||||
/**
|
||||
* optional message to be displayed to the user: Why did we run this process. I.e "running pip to install package".
|
||||
*/
|
||||
val additionalMessageToUser: @NlsContexts.DialogTitle String? = null,
|
||||
) : PyError(getExecErrorMessage(command, additionalMessageToUser, errorReason)) {
|
||||
val exeAndArgs: Pair<String, Array<String>> = Pair(command[0], command.drop(1).toTypedArray())
|
||||
|
||||
init {
|
||||
assert(command.isNotEmpty()) { "Command can't be empty" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sealed interface ExecErrorReason {
|
||||
/**
|
||||
* A process failed to start.
|
||||
* That means the process hasn't been even created.
|
||||
* Os might return [errNo] (not always possible to fetchit) and [cantExecProcessError]
|
||||
*/
|
||||
data class CantStart(val errNo: Int?, val cantExecProcessError: String) : ExecErrorReason
|
||||
|
||||
/**
|
||||
* A process started but failed with an error.
|
||||
*/
|
||||
data class UnexpectedProcessTermination(val exitCode: Int, val stdout: String, val stderr: String) : ExecErrorReason
|
||||
|
||||
/**
|
||||
* Process started, but killed due to timeout without returning any useful data
|
||||
*/
|
||||
data object Timeout : ExecErrorReason
|
||||
}
|
||||
|
||||
fun ProcessOutput.asExecutionFailed(): ExecErrorReason.UnexpectedProcessTermination =
|
||||
ExecErrorReason.UnexpectedProcessTermination(exitCode, stdout, stderr)
|
||||
|
||||
internal fun getExecErrorMessage(
|
||||
command: Array<String>,
|
||||
additionalMessage: @NlsContexts.DialogTitle String?,
|
||||
execErrorReason: ExecErrorReason,
|
||||
): @Nls String {
|
||||
val commandLine = command.joinToString(" ")
|
||||
return when (val r = execErrorReason) {
|
||||
is ExecErrorReason.CantStart -> {
|
||||
PyCommunityBundle.message("python.execution.cant.start.error",
|
||||
additionalMessage ?: "",
|
||||
commandLine,
|
||||
r.cantExecProcessError,
|
||||
r.errNo ?: "unknown")
|
||||
}
|
||||
is ExecErrorReason.UnexpectedProcessTermination -> {
|
||||
PyCommunityBundle.message("python.execution.error",
|
||||
additionalMessage ?: "",
|
||||
commandLine,
|
||||
r.stdout,
|
||||
r.stderr,
|
||||
r.exitCode)
|
||||
}
|
||||
|
||||
ExecErrorReason.Timeout -> {
|
||||
PyCommunityBundle.message("python.execution.timeout",
|
||||
additionalMessage ?: "",
|
||||
commandLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// 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.Result
|
||||
import org.jetbrains.annotations.Nls
|
||||
|
||||
/**
|
||||
* Some "business" error: just a message to be displayed to a user
|
||||
*/
|
||||
open class MessageError(message: @NlsSafe String) : PyError(message)
|
||||
|
||||
fun failure(message: @Nls String): Result<Nothing, MessageError> = failure(MessageError(message))
|
||||
|
||||
suspend fun ErrorSink.emit(message: @Nls String) {
|
||||
emit(MessageError(message))
|
||||
}
|
||||
@@ -2,57 +2,16 @@
|
||||
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
|
||||
|
||||
/**
|
||||
* Error that is interested to user.
|
||||
* Such errors usually stem from user errors or external process errors (i.e., permissions, network connections).
|
||||
* Those are *not* NPEs nor OOBs nor various assertions.
|
||||
* Do *not* use `catch(Exception)` or `runCatching` with this class.
|
||||
*
|
||||
* Most probably you will send this error to [ErrorSink].
|
||||
*/
|
||||
sealed class PyError(val message: @NlsSafe String) {
|
||||
/**
|
||||
* Some "business" error: just a message to be displayed to a user
|
||||
*/
|
||||
open class Message(message: @NlsSafe String) : PyError(message)
|
||||
|
||||
/**
|
||||
* Some process can't be executed. To be displayed specially.
|
||||
*/
|
||||
open 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))
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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.jetbrains.python.packaging.PyExecutionException
|
||||
|
||||
/**
|
||||
* This class is expected to be used as a return value of most PyCharm APIs.
|
||||
* Use it instead of exceptions and Kotlin Result.
|
||||
*/
|
||||
typealias PyResult<T> = com.jetbrains.python.Result<T, PyError>
|
||||
|
||||
inline fun <reified T : PyError> failure(pyError: T): com.jetbrains.python.Result.Failure<T> = com.jetbrains.python.Result.Companion.failure(pyError)
|
||||
|
||||
|
||||
@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.pyError)
|
||||
}
|
||||
else {
|
||||
failure(MessageError(it.localizedMessage))
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -1,4 +1,7 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
/**
|
||||
* See {@link com.jetbrains.python.errorProcessing.PyResultKt} as an entry point.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
package com.jetbrains.python.errorProcessing;
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
// 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.execution
|
||||
|
||||
import com.intellij.execution.process.ProcessOutput
|
||||
import com.jetbrains.python.packaging.PyExecutionException
|
||||
|
||||
/**
|
||||
* Types of execution error for [PyExecutionFailure]
|
||||
*/
|
||||
sealed interface FailureReason {
|
||||
/**
|
||||
* A process failed to start, or the code that ought to start it decided not to run it.
|
||||
* That means the process hasn't been even created.
|
||||
*/
|
||||
data object CantStart : FailureReason
|
||||
|
||||
/**
|
||||
* A process started but failed with an error. See [output] for the result
|
||||
*/
|
||||
data class ExecutionFailed(val output: ProcessOutput) : FailureReason
|
||||
}
|
||||
|
||||
internal fun copyWith(ex: PyExecutionException, newCommand: String, newArgs: List<String>): PyExecutionException =
|
||||
when (val err = ex.failureReason) {
|
||||
FailureReason.CantStart -> {
|
||||
PyExecutionException(ex.additionalMessage, newCommand, newArgs, ex.fixes)
|
||||
}
|
||||
is FailureReason.ExecutionFailed -> {
|
||||
PyExecutionException(ex.additionalMessage, newCommand, newArgs, err.output, ex.fixes)
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
// 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.execution
|
||||
|
||||
import com.intellij.openapi.util.NlsContexts
|
||||
import com.jetbrains.python.PyCommunityBundle
|
||||
import org.jetbrains.annotations.Nls
|
||||
|
||||
/**
|
||||
* Some command can't be executed
|
||||
*/
|
||||
interface PyExecutionFailure {
|
||||
val command: String
|
||||
|
||||
val args: List<String>
|
||||
|
||||
/**
|
||||
* optional message to be displayed to the user
|
||||
*/
|
||||
val additionalMessage: @NlsContexts.DialogTitle String?
|
||||
|
||||
|
||||
val failureReason: FailureReason
|
||||
}
|
||||
|
||||
/**
|
||||
* User-readable message about this problem
|
||||
*/
|
||||
val PyExecutionFailure.userMessage: @Nls String get() = getUserMessage(command, args, additionalMessage, failureReason)
|
||||
|
||||
internal fun getUserMessage(
|
||||
command: String,
|
||||
args: List<String>,
|
||||
additionalMessage: @NlsContexts.DialogTitle String?,
|
||||
failureReason: FailureReason,
|
||||
): @Nls String = when (val r = failureReason) {
|
||||
FailureReason.CantStart -> {
|
||||
PyCommunityBundle.message("python.execution.cant.start.error",
|
||||
additionalMessage ?: "",
|
||||
(listOf(command) + args).joinToString(" "))
|
||||
}
|
||||
is FailureReason.ExecutionFailed -> {
|
||||
|
||||
PyCommunityBundle.message("python.execution.error",
|
||||
additionalMessage ?: "",
|
||||
(listOf(command) + args).joinToString(" "),
|
||||
r.output.stdout,
|
||||
r.output.stderr,
|
||||
r.output.getExitCode())
|
||||
}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.jetbrains.python.packaging;
|
||||
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.process.ProcessOutput;
|
||||
import com.intellij.openapi.util.NlsContexts.DialogMessage;
|
||||
import com.jetbrains.python.execution.FailureReason;
|
||||
import com.jetbrains.python.execution.FailureReasonKt;
|
||||
import com.jetbrains.python.execution.PyExecutionFailure;
|
||||
import com.jetbrains.python.execution.PyExecutionFailureKt;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Process execution failed.
|
||||
* There are two cases, see {@link FailureReason}.
|
||||
* Each constructor represents one or another.
|
||||
*
|
||||
* @see FailureReason
|
||||
*/
|
||||
public final class PyExecutionException extends ExecutionException implements PyExecutionFailure {
|
||||
private final @NotNull String myCommand;
|
||||
private final @NotNull List<String> myArgs;
|
||||
private final @NotNull List<? extends PyExecutionFix> myFixes;
|
||||
private final @DialogMessage @Nullable String myAdditionalMessage;
|
||||
private final @NotNull FailureReason myError;
|
||||
|
||||
/**
|
||||
* A process failed to start, {@link FailureReason.CantStart}
|
||||
*
|
||||
* @param additionalMessage a process start reason for a user
|
||||
*/
|
||||
public PyExecutionException(@DialogMessage @Nullable String additionalMessage,
|
||||
@NotNull String command,
|
||||
@NotNull List<String> args) {
|
||||
this(additionalMessage, command, args, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* A process failed to start, {@link FailureReason.CantStart}
|
||||
*
|
||||
* @param additionalMessage a process start reason for a user
|
||||
*/
|
||||
public PyExecutionException(@DialogMessage @Nullable String additionalMessage,
|
||||
@NotNull String command,
|
||||
@NotNull List<String> args,
|
||||
@NotNull List<? extends PyExecutionFix> fixes) {
|
||||
super(PyExecutionFailureKt.getUserMessage(command, args, additionalMessage, FailureReason.CantStart.INSTANCE));
|
||||
myAdditionalMessage = additionalMessage;
|
||||
myCommand = command;
|
||||
myArgs = args;
|
||||
myFixes = fixes;
|
||||
myError = FailureReason.CantStart.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* A process started, but failed {@link FailureReason.ExecutionFailed}
|
||||
*
|
||||
* @param additionalMessage a process start reason for a user
|
||||
* @param output execution output
|
||||
*/
|
||||
public PyExecutionException(@DialogMessage @Nullable String additionalMessage,
|
||||
@NotNull String command,
|
||||
@NotNull List<String> args,
|
||||
@NotNull ProcessOutput output) {
|
||||
this(additionalMessage, command, args, output, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* A process started, but failed {@link FailureReason.ExecutionFailed}
|
||||
*
|
||||
* @param additionalMessage a process start reason for a user
|
||||
* @param output execution output
|
||||
*/
|
||||
public PyExecutionException(@DialogMessage @Nullable String additionalMessage,
|
||||
@NotNull String command,
|
||||
@NotNull List<String> args,
|
||||
@NotNull ProcessOutput output,
|
||||
@NotNull List<? extends PyExecutionFix> fixes) {
|
||||
super(PyExecutionFailureKt.getUserMessage(command, args, additionalMessage, new FailureReason.ExecutionFailed(output)));
|
||||
myAdditionalMessage = additionalMessage;
|
||||
myCommand = command;
|
||||
myArgs = args;
|
||||
myFixes = fixes;
|
||||
myError = new FailureReason.ExecutionFailed(output);
|
||||
}
|
||||
|
||||
/**
|
||||
* A process started, but failed {@link FailureReason.ExecutionFailed}
|
||||
*
|
||||
* @param additionalMessage a process start reason for a user
|
||||
*/
|
||||
public PyExecutionException(@DialogMessage @Nullable String additionalMessage,
|
||||
@NotNull String command,
|
||||
@NotNull List<String> args,
|
||||
@NotNull String stdout,
|
||||
@NotNull String stderr,
|
||||
int exitCode,
|
||||
@NotNull List<? extends PyExecutionFix> fixes) {
|
||||
this(additionalMessage, command, args, new ProcessOutput(stdout, stderr, exitCode, false, false), fixes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getCommand() {
|
||||
return myCommand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<String> getArgs() {
|
||||
return myArgs;
|
||||
}
|
||||
|
||||
public @NotNull List<? extends PyExecutionFix> getFixes() {
|
||||
return myFixes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getAdditionalMessage() {
|
||||
return myAdditionalMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull FailureReason getFailureReason() {
|
||||
return myError;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #getFailureReason()} and match it as when process failed to start there is no exit code
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public int getExitCode() {
|
||||
if (getFailureReason() instanceof FailureReason.ExecutionFailed executionFailed) {
|
||||
return executionFailed.getOutput().getExitCode();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@NotNull
|
||||
public PyExecutionException copyWith(@NotNull String newCommand, @NotNull List<@NotNull String> newArgs) {
|
||||
return FailureReasonKt.copyWith(this, newCommand, newArgs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// 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.packaging
|
||||
|
||||
import com.intellij.execution.ExecutionException
|
||||
import com.intellij.execution.process.ProcessOutput
|
||||
import com.intellij.openapi.util.NlsContexts
|
||||
import com.jetbrains.python.errorProcessing.*
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* Wraps [PyError] for cases where [ExecutionException] is used.
|
||||
* As [PyResult] should be used instead of exceptions, new code should use [PyError]
|
||||
*
|
||||
* Migrate to [PyError], please.
|
||||
*/
|
||||
@ApiStatus.Obsolete
|
||||
class PyExecutionException private constructor(
|
||||
val pyError: PyError,
|
||||
val fixes: List<PyExecutionFix>,
|
||||
ioException: IOException? = null,
|
||||
) : ExecutionException(pyError.message, ioException) {
|
||||
companion object {
|
||||
/**
|
||||
* [com.jetbrains.python.packaging.PyExecutionException] for process died with timeout
|
||||
*/
|
||||
@JvmStatic
|
||||
fun createForTimeout(
|
||||
additionalMessageToUser: @NlsContexts.DialogMessage String?,
|
||||
command: String,
|
||||
args: List<String>,
|
||||
): PyExecutionException = PyExecutionException(ExecError(arrayOf(command) + args.toTypedArray(), ExecErrorReason.Timeout, additionalMessageToUser))
|
||||
}
|
||||
|
||||
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
pyError: PyError,
|
||||
fixes: List<PyExecutionFix> = listOf<PyExecutionFix>(),
|
||||
) : this(pyError, fixes, null)
|
||||
|
||||
/**
|
||||
* System decided not to start a process at all due to [messageToUser]
|
||||
*/
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
messageToUser: @NlsContexts.DialogMessage String,
|
||||
fixes: List<PyExecutionFix> = listOf<PyExecutionFix>(),
|
||||
) : this(
|
||||
pyError = MessageError(messageToUser),
|
||||
fixes = fixes)
|
||||
|
||||
/**
|
||||
* A process failed to start, [ExecErrorReason.CantStart]
|
||||
*
|
||||
* @param additionalMessage a process start reason for a user
|
||||
*/
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
startException: IOException,
|
||||
additionalMessage: @NlsContexts.DialogMessage String?,
|
||||
command: String,
|
||||
args: List<String>,
|
||||
fixes: List<PyExecutionFix> = listOf<PyExecutionFix>(),
|
||||
) : this(
|
||||
pyError = ExecError(arrayOf(command) + args.toTypedArray(), ExecErrorReason.CantStart(null, startException.localizedMessage), additionalMessage),
|
||||
fixes = fixes,
|
||||
ioException = startException)
|
||||
|
||||
|
||||
/**
|
||||
* A process started, but failed [com.jetbrains.python.errorProcessing.ExecErrorReason.UnexpectedProcessTermination]
|
||||
*
|
||||
* @param additionalMessage a process start reason for a user
|
||||
* @param output execution output
|
||||
*/
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
additionalMessage: @NlsContexts.DialogMessage String?,
|
||||
command: String,
|
||||
args: List<String>,
|
||||
output: ProcessOutput,
|
||||
fixes: List<PyExecutionFix> = listOf<PyExecutionFix>(),
|
||||
) : this(
|
||||
pyError = ExecError(arrayOf(command) + args.toTypedArray(), output.asExecutionFailed()),
|
||||
fixes = fixes)
|
||||
|
||||
/**
|
||||
* A process started, but failed [ExecErrorReason.UnexpectedProcessTermination]
|
||||
*
|
||||
* @param additionalMessage a process start reason for a user
|
||||
*/
|
||||
constructor(
|
||||
additionalMessage: @NlsContexts.DialogMessage String?,
|
||||
command: String,
|
||||
args: List<String>,
|
||||
stdout: String,
|
||||
stderr: String,
|
||||
exitCode: Int,
|
||||
fixes: List<PyExecutionFix>,
|
||||
) : this(additionalMessage, command, args, ProcessOutput(stdout, stderr, exitCode, false, false), fixes)
|
||||
}
|
||||
@@ -558,7 +558,7 @@ sdk.create.custom.hatch.environment=Environment:
|
||||
sdk.create.custom.hatch.environment.loading=Loading environments\u2026
|
||||
sdk.create.custom.hatch.environment.exists=Environment already exists
|
||||
sdk.create.custom.hatch.error.no.environments.to.select=Hatch didn't provide any environment to select
|
||||
sdk.create.custom.hatch.error.execution.failed=Please verify Hatch tool, executed with error: {0} {1}
|
||||
sdk.create.custom.hatch.error.execution.failed=Please verify Hatch tool, executed with error: {0}
|
||||
sdk.create.custom.hatch.error.module.is.not.selected=Module is not selected
|
||||
sdk.create.custom.hatch.error.project.is.not.selected=Project is not selected
|
||||
sdk.create.custom.hatch.error.environment.is.not.selected=Hatch environment is not selected
|
||||
|
||||
@@ -8,7 +8,7 @@ import com.intellij.platform.eel.EelProcess
|
||||
import com.intellij.python.community.execService.impl.ExecServiceImpl
|
||||
import com.jetbrains.python.PythonBinary
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyError.ExecException
|
||||
import com.jetbrains.python.errorProcessing.ExecError
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.CheckReturnValue
|
||||
import org.jetbrains.annotations.Nls
|
||||
@@ -17,7 +17,7 @@ import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
|
||||
/**
|
||||
* Error is an optional additionalMessage, that will be used instead of a default one for the [ExecException] in the [com.jetbrains.python.execution.PyExecutionFailure].
|
||||
* Error is an optional additionalMessage, that will be used instead of a default one for the [ExecError] in the [com.jetbrains.python.execution.PyExecutionFailure].
|
||||
*/
|
||||
typealias ProcessOutputTransformer<T> = (ProcessOutput) -> Result<T, @NlsSafe String?>
|
||||
|
||||
@@ -41,7 +41,7 @@ interface ExecService {
|
||||
args: List<String> = emptyList(),
|
||||
options: ExecOptions = ExecOptions(),
|
||||
eelProcessInteractiveHandler: EelProcessInteractiveHandler<T>,
|
||||
): Result<T, ExecException>
|
||||
): Result<T, ExecError>
|
||||
|
||||
/**
|
||||
* Execute [whatToExec] with [args] and get both stdout/stderr outputs if `errorCode != 0`, gets error otherwise.
|
||||
@@ -57,14 +57,14 @@ interface ExecService {
|
||||
args: List<String> = emptyList(),
|
||||
options: ExecOptions = ExecOptions(),
|
||||
processOutputTransformer: ProcessOutputTransformer<T>,
|
||||
): Result<T, ExecException>
|
||||
): Result<T, ExecError>
|
||||
|
||||
@CheckReturnValue
|
||||
suspend fun execGetStdout(
|
||||
whatToExec: WhatToExec,
|
||||
args: List<String> = emptyList(),
|
||||
options: ExecOptions = ExecOptions(),
|
||||
): Result<String, ExecException> = execute(
|
||||
): Result<String, ExecError> = execute(
|
||||
whatToExec = whatToExec,
|
||||
args = args,
|
||||
options = options,
|
||||
|
||||
+26
-42
@@ -3,31 +3,18 @@ package com.intellij.python.community.execService.impl
|
||||
|
||||
import com.intellij.execution.process.ProcessOutput
|
||||
import com.intellij.openapi.diagnostic.fileLogger
|
||||
import com.intellij.platform.eel.EelApi
|
||||
import com.intellij.platform.eel.EelExecApi
|
||||
import com.intellij.platform.eel.EelProcess
|
||||
import com.intellij.platform.eel.execute
|
||||
import com.intellij.platform.eel.getOr
|
||||
import com.intellij.platform.eel.*
|
||||
import com.intellij.platform.eel.path.EelPath
|
||||
import com.intellij.platform.eel.provider.asEelPath
|
||||
import com.intellij.platform.eel.provider.getEelDescriptor
|
||||
import com.intellij.platform.eel.provider.utils.EelPathUtils
|
||||
import com.intellij.platform.eel.provider.utils.EelProcessExecutionResult
|
||||
import com.intellij.platform.eel.provider.utils.awaitProcessResult
|
||||
import com.intellij.platform.eel.provider.utils.stderrString
|
||||
import com.intellij.platform.eel.provider.utils.stdoutString
|
||||
import com.intellij.python.community.execService.EelProcessInteractiveHandler
|
||||
import com.intellij.python.community.execService.ExecOptions
|
||||
import com.intellij.python.community.execService.ExecService
|
||||
import com.intellij.python.community.execService.ProcessOutputTransformer
|
||||
import com.intellij.python.community.execService.WhatToExec
|
||||
import com.intellij.platform.eel.provider.utils.*
|
||||
import com.intellij.python.community.execService.*
|
||||
import com.jetbrains.python.PythonHelpersLocator
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyError.ExecException
|
||||
import com.jetbrains.python.errorProcessing.ExecError
|
||||
import com.jetbrains.python.errorProcessing.ExecErrorReason
|
||||
import com.jetbrains.python.errorProcessing.asExecutionFailed
|
||||
import com.jetbrains.python.errorProcessing.failure
|
||||
import com.jetbrains.python.execution.FailureReason
|
||||
import com.jetbrains.python.execution.PyExecutionFailure
|
||||
import com.jetbrains.python.execution.userMessage
|
||||
import kotlinx.coroutines.TimeoutCancellationException
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import org.jetbrains.annotations.CheckReturnValue
|
||||
@@ -43,7 +30,7 @@ internal object ExecServiceImpl : ExecService {
|
||||
args: List<String>,
|
||||
options: ExecOptions,
|
||||
eelProcessInteractiveHandler: EelProcessInteractiveHandler<T>,
|
||||
): Result<T, ExecException> {
|
||||
): Result<T, ExecError> {
|
||||
val executableProcess = whatToExec.buildExecutableProcess(args, options)
|
||||
val eelProcess = executableProcess.run().getOr { return it }
|
||||
|
||||
@@ -70,7 +57,7 @@ internal object ExecServiceImpl : ExecService {
|
||||
args: List<String>,
|
||||
options: ExecOptions,
|
||||
processOutputTransformer: ProcessOutputTransformer<T>,
|
||||
): Result<T, ExecException> {
|
||||
): Result<T, ExecError> {
|
||||
val executableProcess = whatToExec.buildExecutableProcess(args, options)
|
||||
val eelProcess = executableProcess.run().getOr { return it }
|
||||
|
||||
@@ -124,7 +111,7 @@ private suspend fun WhatToExec.buildExecutableProcess(args: List<String>, option
|
||||
}
|
||||
|
||||
@CheckReturnValue
|
||||
private suspend fun EelExecutableProcess.run(): Result<EelProcess, ExecException> {
|
||||
private suspend fun EelExecutableProcess.run(): Result<EelProcess, ExecError> {
|
||||
val workDirectoryEelPath = workingDirectory?.let { EelPath.parse(it.toString(), eel.descriptor) }
|
||||
val executionResult = eel.exec.execute(exe)
|
||||
.args(args)
|
||||
@@ -137,41 +124,38 @@ private suspend fun EelExecutableProcess.run(): Result<EelProcess, ExecException
|
||||
return Result.success(process)
|
||||
}
|
||||
|
||||
private fun EelExecutableProcess.failAsCantStart(executeProcessError: EelExecApi.ExecuteProcessError): Result.Failure<ExecException> {
|
||||
return PyExecFailureImpl(
|
||||
command = exe,
|
||||
args = args,
|
||||
additionalMessage = PyExecBundle.message("py.exec.start.error", description, executeProcessError.message, executeProcessError.errno),
|
||||
failureReason = FailureReason.CantStart
|
||||
private fun EelExecutableProcess.failAsCantStart(executeProcessError: EelExecApi.ExecuteProcessError): Result.Failure<ExecError> {
|
||||
return ExecError(
|
||||
command = arrayOf(exe) + args.toTypedArray(),
|
||||
additionalMessageToUser = PyExecBundle.message("py.exec.start.error", description, executeProcessError.message, executeProcessError.errno),
|
||||
errorReason = ExecErrorReason.CantStart(executeProcessError.errno, executeProcessError.message)
|
||||
).logAndFail()
|
||||
}
|
||||
|
||||
private suspend fun EelExecutableProcess.killProcessAndFailAsTimeout(eelProcess: EelProcess, timeout: Duration): Result.Failure<ExecException> {
|
||||
private suspend fun EelExecutableProcess.killProcessAndFailAsTimeout(eelProcess: EelProcess, timeout: Duration): Result.Failure<ExecError> {
|
||||
eelProcess.kill()
|
||||
|
||||
return PyExecFailureImpl(
|
||||
command = exe,
|
||||
args = args,
|
||||
additionalMessage = PyExecBundle.message("py.exec.timeout.error", description, timeout),
|
||||
failureReason = FailureReason.CantStart
|
||||
return ExecError(
|
||||
command = arrayOf(exe) + args.toTypedArray(),
|
||||
additionalMessageToUser = PyExecBundle.message("py.exec.timeout.error", description, timeout),
|
||||
errorReason = ExecErrorReason.Timeout
|
||||
).logAndFail()
|
||||
}
|
||||
|
||||
private fun EelExecutableProcess.failAsExecutionFailed(processOutput: ProcessOutput, customMessage: @Nls String?): Result.Failure<ExecException> {
|
||||
private fun EelExecutableProcess.failAsExecutionFailed(processOutput: ProcessOutput, customMessage: @Nls String?): Result.Failure<ExecError> {
|
||||
val additionalMessage = customMessage ?: run {
|
||||
PyExecBundle.message("py.exec.exitCode.error", description, processOutput.exitCode)
|
||||
}
|
||||
|
||||
return PyExecFailureImpl(
|
||||
command = exe,
|
||||
args = args,
|
||||
additionalMessage = additionalMessage,
|
||||
failureReason = FailureReason.ExecutionFailed(processOutput)
|
||||
return ExecError(
|
||||
command = arrayOf(exe) + args.toTypedArray(),
|
||||
additionalMessageToUser = additionalMessage,
|
||||
errorReason = processOutput.asExecutionFailed()
|
||||
).logAndFail()
|
||||
}
|
||||
|
||||
private fun PyExecutionFailure.logAndFail(): Result.Failure<ExecException> {
|
||||
fileLogger().warn(userMessage)
|
||||
private fun ExecError.logAndFail(): Result.Failure<ExecError> {
|
||||
fileLogger().warn(message)
|
||||
return failure(this)
|
||||
}
|
||||
|
||||
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
// 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.util.NlsContexts
|
||||
import com.jetbrains.python.execution.FailureReason
|
||||
import com.jetbrains.python.execution.PyExecutionFailure
|
||||
import com.jetbrains.python.execution.userMessage
|
||||
|
||||
internal data class PyExecFailureImpl(
|
||||
override val command: String,
|
||||
override val args: List<String>,
|
||||
override val additionalMessage: @NlsContexts.DialogTitle String? = null,
|
||||
override val failureReason: FailureReason,
|
||||
) : PyExecutionFailure {
|
||||
override fun toString(): String = userMessage
|
||||
}
|
||||
+2
-3
@@ -94,9 +94,8 @@ class ExecServiceShowCaseTest {
|
||||
is Result.Success -> fail("Execution of bad command should lead to an error")
|
||||
is Result.Failure -> {
|
||||
val err = output.error
|
||||
val failure = err.execFailure
|
||||
assertEquals(command.command, failure.command, "Wrong command reported")
|
||||
assertEquals("foo", failure.args[0], "Wrong args reported")
|
||||
assertEquals(command.command, err.command[0], "Wrong command reported")
|
||||
assertEquals("foo", err.command[1], "Wrong args reported")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ 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.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyError.ExecException
|
||||
import com.jetbrains.python.errorProcessing.ExecError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import io.github.z4kn4fein.semver.Version
|
||||
import io.github.z4kn4fein.semver.VersionFormatException
|
||||
import java.nio.file.Path
|
||||
@@ -18,7 +18,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, ExecException> {
|
||||
private suspend fun <T> HatchRuntime.executeAndHandleErrors(vararg arguments: String, transformer: ProcessOutputTransformer<T>): Result<T, ExecError> {
|
||||
val errorHandlerTransformer: ProcessOutputTransformer<T> = { output ->
|
||||
when {
|
||||
output.exitCode !in 0..1 -> Result.failure(null)
|
||||
@@ -38,7 +38,7 @@ private suspend fun <T> HatchRuntime.executeAndMatch(
|
||||
expectedOutput: Regex,
|
||||
outputContentSupplier: (ProcessOutput) -> String = ProcessOutput::getStdout,
|
||||
transformer: (MatchResult) -> Result<T, @NlsSafe String?>,
|
||||
): Result<T, ExecException> {
|
||||
): Result<T, ExecError> {
|
||||
return this.executeAndHandleErrors(*arguments) { processOutput ->
|
||||
if (processOutput.exitCode != 0) return@executeAndHandleErrors Result.failure(null)
|
||||
|
||||
@@ -57,11 +57,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, ExecException> {
|
||||
protected suspend fun <T> executeAndHandleErrors(vararg arguments: String, transformer: ProcessOutputTransformer<T>): Result<T, ExecError> {
|
||||
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, ExecException> {
|
||||
protected suspend fun <T> executeAndMatch(vararg arguments: String, expectedOutput: Regex, transformer: (MatchResult) -> Result<T, @NlsSafe String?>): Result<T, ExecError> {
|
||||
return runtime.executeAndMatch(*command, *arguments, expectedOutput = expectedOutput, transformer = transformer)
|
||||
}
|
||||
}
|
||||
@@ -70,12 +70,12 @@ class HatchCli(private val runtime: HatchRuntime) {
|
||||
/**
|
||||
* Build a project
|
||||
*/
|
||||
fun build(): Result<Unit, ExecException> = TODO()
|
||||
fun build(): Result<Unit, ExecError> = TODO()
|
||||
|
||||
/**
|
||||
* Remove build artifacts
|
||||
*/
|
||||
fun clean(): Result<Unit, ExecException> = TODO()
|
||||
fun clean(): Result<Unit, ExecError> = TODO()
|
||||
|
||||
/**
|
||||
* Manage the config file
|
||||
@@ -95,7 +95,7 @@ class HatchCli(private val runtime: HatchRuntime) {
|
||||
/**
|
||||
* Format and lint source code
|
||||
*/
|
||||
fun fmt(): Result<Unit, ExecException> = TODO()
|
||||
fun fmt(): Result<Unit, ExecError> = TODO()
|
||||
|
||||
/**
|
||||
* Create or initialize a project.
|
||||
@@ -114,7 +114,7 @@ class HatchCli(private val runtime: HatchRuntime) {
|
||||
*
|
||||
* @param[initExistingProject] Initialize an existing project
|
||||
*/
|
||||
suspend fun new(projectName: String, location: Path? = null, initExistingProject: Boolean = false): Result<String, PyError> {
|
||||
suspend fun new(projectName: String, location: Path? = null, initExistingProject: Boolean = false): PyResult<String> {
|
||||
val options = listOf(
|
||||
initExistingProject to "--init",
|
||||
true to projectName,
|
||||
@@ -136,7 +136,7 @@ class HatchCli(private val runtime: HatchRuntime) {
|
||||
/**
|
||||
* Publish build artifacts
|
||||
*/
|
||||
fun publish(): Result<Unit, ExecException> = TODO()
|
||||
fun publish(): Result<Unit, ExecError> = TODO()
|
||||
|
||||
/**
|
||||
* Manage Python installations
|
||||
@@ -146,7 +146,7 @@ class HatchCli(private val runtime: HatchRuntime) {
|
||||
/**
|
||||
* Run commands within project environments
|
||||
*/
|
||||
suspend fun run(envName: String, vararg command: String): Result<String, ExecException> {
|
||||
suspend fun run(envName: String, vararg command: String): Result<String, ExecError> {
|
||||
val envRuntime = runtime.withEnv(HatchConstants.AppEnvVars.ENV to envName)
|
||||
return envRuntime.executeAndHandleErrors("run", *command) { output ->
|
||||
val scenario = output.stderr.trim()
|
||||
@@ -170,14 +170,14 @@ class HatchCli(private val runtime: HatchRuntime) {
|
||||
/**
|
||||
* Enter a shell within a project's environment
|
||||
*/
|
||||
fun shell(): Result<Unit, ExecException> = TODO()
|
||||
fun shell(): Result<Unit, ExecError> = TODO()
|
||||
|
||||
data class HatchStatus(val project: String, val location: Path, val config: Path)
|
||||
|
||||
/**
|
||||
* Show information about the current environment
|
||||
*/
|
||||
suspend fun status(): Result<HatchStatus, ExecException> {
|
||||
suspend fun status(): Result<HatchStatus, ExecError> {
|
||||
val expectedOutput = """^\[Project] - (.*)\n\[Location] - (.*)\n\[Config] - (.*)\n$""".toRegex()
|
||||
|
||||
return runtime.executeAndMatch("status", expectedOutput = expectedOutput, outputContentSupplier = { it.stderr }) { matchResult ->
|
||||
@@ -194,14 +194,14 @@ class HatchCli(private val runtime: HatchRuntime) {
|
||||
/**
|
||||
* Run tests
|
||||
*/
|
||||
fun test(): Result<Unit, ExecException> = TODO()
|
||||
fun test(): Result<Unit, ExecError> = TODO()
|
||||
|
||||
/**
|
||||
* View a project's version.
|
||||
*
|
||||
* @return Project Version
|
||||
*/
|
||||
suspend fun getVersion(): Result<Version, ExecException> {
|
||||
suspend fun getVersion(): Result<Version, ExecError> {
|
||||
return runtime.executeAndHandleErrors("version") { processOutput ->
|
||||
val output = processOutput.takeIf { it.exitCode == 0 }?.stdout?.trim()
|
||||
?: return@executeAndHandleErrors Result.failure(null)
|
||||
@@ -219,7 +219,7 @@ class HatchCli(private val runtime: HatchRuntime) {
|
||||
*
|
||||
* @return OldVersion to NewVersion as Pair
|
||||
*/
|
||||
suspend fun setVersion(desiredVersion: String): Result<Pair<Version, Version>, PyError> {
|
||||
suspend fun setVersion(desiredVersion: String): PyResult<Pair<Version, Version>> {
|
||||
val expectedOutput = """^Old: (.*)\nNew: (.*)\n$""".toRegex()
|
||||
|
||||
return runtime.executeAndMatch("version", desiredVersion, expectedOutput = expectedOutput, outputContentSupplier = { it.stderr }) { matchResult ->
|
||||
|
||||
@@ -4,7 +4,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.PyError.ExecException
|
||||
import com.jetbrains.python.errorProcessing.ExecError
|
||||
|
||||
/**
|
||||
* Manage environment dependencies
|
||||
@@ -13,28 +13,28 @@ class HatchConfig(runtime: HatchRuntime) : HatchCommand("config", runtime) {
|
||||
/**
|
||||
* Open the config location in your file manager
|
||||
*/
|
||||
suspend fun explore(): Result<String, ExecException> {
|
||||
suspend fun explore(): Result<String, ExecError> {
|
||||
return executeAndHandleErrors("explore", transformer = ZeroCodeStdoutTransformer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the location of the config file
|
||||
*/
|
||||
suspend fun find(): Result<String, ExecException> {
|
||||
suspend fun find(): Result<String, ExecError> {
|
||||
return executeAndHandleErrors("find", transformer = ZeroCodeStdoutTransformer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the config file to default settings
|
||||
*/
|
||||
suspend fun restore(): Result<String, ExecException> {
|
||||
suspend fun restore(): Result<String, ExecError> {
|
||||
return executeAndHandleErrors("restore", transformer = ZeroCodeStdoutTransformer)
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign values to config file entries
|
||||
*/
|
||||
suspend fun set(key: String, value: String): Result<String, ExecException> {
|
||||
suspend fun set(key: String, value: String): Result<String, ExecError> {
|
||||
return executeAndHandleErrors("set", key, value, transformer = ZeroCodeStdoutTransformer)
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ class HatchConfig(runtime: HatchRuntime) : HatchCommand("config", runtime) {
|
||||
*
|
||||
* @param all Do not scrub secret fields
|
||||
*/
|
||||
suspend fun show(all: Boolean? = null): Result<String, ExecException> {
|
||||
suspend fun show(all: Boolean? = null): Result<String, ExecError> {
|
||||
val options = listOf(all to "--all").makeOptions()
|
||||
return executeAndHandleErrors("show", *options, transformer = ZeroCodeStdoutTransformer)
|
||||
}
|
||||
@@ -51,7 +51,7 @@ class HatchConfig(runtime: HatchRuntime) : HatchCommand("config", runtime) {
|
||||
/**
|
||||
* Update the config file with any new fields
|
||||
*/
|
||||
suspend fun update(): Result<String, ExecException> {
|
||||
suspend fun update(): Result<String, ExecError> {
|
||||
return executeAndHandleErrors("update", transformer = ZeroCodeStdoutTransformer)
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,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.PyError.ExecException
|
||||
import com.jetbrains.python.errorProcessing.ExecError
|
||||
|
||||
enum class Scope(val options: Array<String>) {
|
||||
All(emptyArray()),
|
||||
@@ -19,7 +19,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, ExecException> {
|
||||
suspend fun hash(scope: Scope = Scope.All): Result<String, ExecError> {
|
||||
return executeAndHandleErrors("hash", *scope.options, transformer = ZeroCodeStdoutTransformer)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,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, ExecException> {
|
||||
suspend fun requirements(scope: Scope = Scope.All, features: List<String>? = null): Result<String, ExecError> {
|
||||
val options = features?.flatMap { listOf("--feature", it) }?.toTypedArray() ?: arrayOf("--all")
|
||||
return executeAndHandleErrors("requirements", *scope.options, *options, transformer = ZeroCodeStdoutTransformer)
|
||||
}
|
||||
@@ -46,7 +46,7 @@ class HatchDepShow(runtime: HatchRuntime) : HatchCommand(arrayOf("dep", "show"),
|
||||
/**
|
||||
* Enumerate dependencies in a tabular format.
|
||||
*/
|
||||
suspend fun table(scope: Scope = Scope.All): Result<String, ExecException> {
|
||||
suspend fun table(scope: Scope = Scope.All): Result<String, ExecError> {
|
||||
val options = listOf(null to "--lines", true to "--ascii").makeOptions()
|
||||
return executeAndHandleErrors("table", *scope.options, *options, transformer = ZeroCodeStdoutTransformer)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.python.hatch.runtime.HatchRuntime
|
||||
import com.jetbrains.python.PythonHomePath
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyError.ExecException
|
||||
import com.jetbrains.python.errorProcessing.ExecError
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
@@ -127,7 +127,7 @@ class HatchEnv(runtime: HatchRuntime) : HatchCommand("env", runtime) {
|
||||
*
|
||||
* @return true if created, false if already exists
|
||||
*/
|
||||
suspend fun create(envName: String? = null): Result<CreateResult, ExecException> {
|
||||
suspend fun create(envName: String? = null): Result<CreateResult, ExecError> {
|
||||
val arguments = if (envName == null) emptyArray() else arrayOf(envName)
|
||||
return executeAndHandleErrors("create", *arguments) {
|
||||
val actualEnvName = envName ?: DEFAULT_ENV_NAME
|
||||
@@ -145,7 +145,7 @@ class HatchEnv(runtime: HatchRuntime) : HatchCommand("env", runtime) {
|
||||
*
|
||||
* @return path to environment
|
||||
*/
|
||||
suspend fun find(envName: String? = null): Result<PythonHomePath?, ExecException> {
|
||||
suspend fun find(envName: String? = null): Result<PythonHomePath?, ExecError> {
|
||||
val arguments = if (envName == null) emptyArray() else arrayOf(envName)
|
||||
return executeAndHandleErrors("find", *arguments) {
|
||||
when (it.exitCode) {
|
||||
@@ -178,9 +178,9 @@ class HatchEnv(runtime: HatchRuntime) : HatchCommand("env", runtime) {
|
||||
* - [RemoveResult.NotExists] if the environment does not exist.
|
||||
* - [RemoveResult.NotDefinedInConfig] if the environment is not defined in the project configuration.
|
||||
* - [RemoveResult.CantRemoveActiveEnvironment] if the environment cannot be removed because it is currently active.
|
||||
* - An error wrapped in [ExecException] in case of execution failure.
|
||||
* - An error wrapped in [ExecError] in case of execution failure.
|
||||
*/
|
||||
suspend fun remove(envName: String? = null): Result<RemoveResult, ExecException> {
|
||||
suspend fun remove(envName: String? = null): Result<RemoveResult, ExecError> {
|
||||
val arguments = if (envName == null) emptyArray() else arrayOf(envName)
|
||||
return executeAndHandleErrors("remove", *arguments) {
|
||||
val actualEnvName = envName ?: DEFAULT_ENV_NAME
|
||||
@@ -200,9 +200,9 @@ class HatchEnv(runtime: HatchRuntime) : HatchCommand("env", runtime) {
|
||||
* @param envs A vararg parameter specifying the environment names to be displayed. If not provided, information for all environments is shown.
|
||||
* @return A [Result] containing:
|
||||
* - [HatchDetailedEnvironments] if operation is successful.
|
||||
* - An error wrapped in [ExecException] if an execution failure occurs.
|
||||
* - An error wrapped in [ExecError] if an execution failure occurs.
|
||||
*/
|
||||
suspend fun showWithDetails(vararg envs: String): Result<HatchDetailedEnvironments, ExecException> {
|
||||
suspend fun showWithDetails(vararg envs: String): Result<HatchDetailedEnvironments, ExecError> {
|
||||
return executeAndHandleErrors("show", "--json", *envs) { processOutput ->
|
||||
val output = processOutput.takeIf { it.exitCode == 0 }?.stdout
|
||||
?: return@executeAndHandleErrors Result.failure(null)
|
||||
@@ -227,9 +227,9 @@ class HatchEnv(runtime: HatchRuntime) : HatchCommand("env", runtime) {
|
||||
* @param internal Optional parameter indicating whether to include internal environments. Defaults to false.
|
||||
* @return A [Result] containing:
|
||||
* - [HatchDetailedEnvironments] if operation is successful.
|
||||
* - An error wrapped in [ExecException] if an execution failure occurs.
|
||||
* - An error wrapped in [ExecError] if an execution failure occurs.
|
||||
*/
|
||||
suspend fun show(vararg envs: String, internal: Boolean = false): Result<HatchEnvironments, ExecException> {
|
||||
suspend fun show(vararg envs: String, internal: Boolean = false): Result<HatchEnvironments, ExecError> {
|
||||
val options = listOf(internal to "--internal").makeOptions()
|
||||
|
||||
return executeAndMatch("show", "--ascii", *options, *envs, expectedOutput = SHOW_RESPONSE_REGEX) { matchResult ->
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.intellij.python.hatch.cli
|
||||
|
||||
import com.intellij.python.hatch.runtime.HatchRuntime
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyError.ExecException
|
||||
import com.jetbrains.python.errorProcessing.ExecError
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
@@ -61,7 +61,7 @@ class HatchProject(runtime: HatchRuntime) : HatchCommand("project", runtime) {
|
||||
/**
|
||||
* Display project metadata
|
||||
*/
|
||||
suspend fun metadata(): Result<Metadata, ExecException> {
|
||||
suspend fun metadata(): Result<Metadata, ExecError> {
|
||||
return executeAndHandleErrors("metadata") { processOutput ->
|
||||
val output = processOutput.takeIf { it.exitCode == 0 }?.stdout
|
||||
?: return@executeAndHandleErrors Result.failure(null)
|
||||
|
||||
@@ -3,10 +3,10 @@ package com.intellij.python.hatch.cli
|
||||
|
||||
import com.intellij.execution.process.ProcessOutput
|
||||
import com.intellij.openapi.util.io.NioFiles
|
||||
import com.intellij.python.hatch.runtime.HatchRuntime
|
||||
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.PyError.ExecException
|
||||
import com.jetbrains.python.errorProcessing.ExecError
|
||||
import java.nio.file.Path
|
||||
|
||||
/**
|
||||
@@ -24,7 +24,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?, ExecException> {
|
||||
suspend fun find(name: String, parent: Boolean? = null, dir: String? = null): Result<Path?, ExecError> {
|
||||
val options = listOf(parent to "--parent").makeOptions() + buildDirOption(dir)
|
||||
|
||||
return executeAndHandleErrors("find", *options, name) { output ->
|
||||
@@ -127,7 +127,7 @@ class HatchPython(runtime: HatchRuntime) : HatchCommand("python", runtime) {
|
||||
private: Boolean? = null,
|
||||
update: Boolean? = null,
|
||||
dir: String? = null,
|
||||
): Result<PythonInstallResponse, ExecException> {
|
||||
): Result<PythonInstallResponse, ExecError> {
|
||||
val options = listOf(update to "--update", private to "--private").makeOptions() + buildDirOption(dir)
|
||||
return executeAndHandleErrors("install", *options, *names) { output ->
|
||||
Result.success(parsePythonInstallCommandOutput(output))
|
||||
@@ -142,7 +142,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, ExecException> {
|
||||
suspend fun remove(vararg names: String = ALL_NAMES, dir: String? = null): Result<PythonRemoveResponse, ExecError> {
|
||||
return executeAndHandleErrors("remove", *buildDirOption(dir), *names) { processOutput ->
|
||||
val output = processOutput.stderr
|
||||
val notInstalledRegex = Regex("""^Distribution is not installed: (.*)$""", RegexOption.MULTILINE)
|
||||
@@ -163,7 +163,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, ExecException> {
|
||||
suspend fun show(dir: String? = null): Result<ShowResponse, ExecError> {
|
||||
val nameToVersionRegex = """\|\s+([^|\s]+)\s+\|\s+([^|\s]+)\s+\|""".toRegex()
|
||||
fun parseNameToVersions(payload: String) = nameToVersionRegex.findAll(payload).associate {
|
||||
val (name, version) = it.destructured
|
||||
@@ -198,7 +198,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, ExecException> {
|
||||
suspend fun update(vararg names: String = ALL_NAMES, dir: String? = null): Result<PythonInstallResponse, ExecError> {
|
||||
return executeAndHandleErrors("update", *buildDirOption(dir), *names) { output ->
|
||||
Result.success(parsePythonInstallCommandOutput(output))
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// 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.hatch.cli
|
||||
|
||||
import com.intellij.python.hatch.runtime.HatchRuntime
|
||||
import com.intellij.python.hatch.PyHatchBundle
|
||||
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.PyError.ExecException
|
||||
import com.jetbrains.python.errorProcessing.ExecError
|
||||
|
||||
/**
|
||||
* Manage environment dependencies
|
||||
@@ -16,7 +16,7 @@ class HatchSelf(runtime: HatchRuntime) : HatchCommand("self", runtime) {
|
||||
/**
|
||||
* Generate a pre-populated GitHub issue.
|
||||
*/
|
||||
suspend fun report(): Result<Url, ExecException> {
|
||||
suspend fun report(): Result<Url, ExecError> {
|
||||
return executeAndHandleErrors("report", "--no-open") { processOutput ->
|
||||
val output = processOutput.takeIf { it.exitCode == 0 }?.stdout?.trim()
|
||||
?: return@executeAndHandleErrors Result.failure(null)
|
||||
@@ -33,10 +33,10 @@ class HatchSelf(runtime: HatchRuntime) : HatchCommand("self", runtime) {
|
||||
/**
|
||||
* Restore the installation
|
||||
*/
|
||||
fun restore(): Result<String, ExecException> = TODO()
|
||||
fun restore(): Result<String, ExecError> = TODO()
|
||||
|
||||
/**
|
||||
* Install the latest version
|
||||
*/
|
||||
fun update(): Result<String, ExecException> = TODO()
|
||||
fun update(): Result<String, ExecError> = TODO()
|
||||
}
|
||||
@@ -10,11 +10,12 @@ import com.intellij.python.hatch.service.CliBasedHatchService
|
||||
import com.jetbrains.python.PythonBinary
|
||||
import com.jetbrains.python.PythonHomePath
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.MessageError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.sdk.basePath
|
||||
import java.nio.file.Path
|
||||
|
||||
sealed class HatchError(message: @NlsSafe String) : PyError.Message(message)
|
||||
sealed class HatchError(message: @NlsSafe String) : MessageError(message)
|
||||
|
||||
class HatchExecutableNotFoundHatchError(path: Path?) : HatchError(
|
||||
PyHatchBundle.message("python.hatch.error.executable.is.not.found", path.toString())
|
||||
@@ -79,25 +80,25 @@ data class ProjectStructure(
|
||||
interface HatchService {
|
||||
fun getWorkingDirectoryPath(): Path
|
||||
|
||||
suspend fun syncDependencies(envName: String): Result<String, PyError>
|
||||
suspend fun syncDependencies(envName: String): PyResult<String>
|
||||
|
||||
suspend fun isHatchManagedProject(): Result<Boolean, PyError>
|
||||
suspend fun isHatchManagedProject(): PyResult<Boolean>
|
||||
|
||||
suspend fun createNewProject(projectName: String): Result<ProjectStructure, PyError>
|
||||
suspend fun createNewProject(projectName: String): PyResult<ProjectStructure>
|
||||
|
||||
/**
|
||||
* param[basePythonBinaryPath] base python for environment, the one on the PATH should be used if null.
|
||||
* param[envName] environment name to create, 'default' should be used if null.
|
||||
*/
|
||||
suspend fun createVirtualEnvironment(basePythonBinaryPath: PythonBinary? = null, envName: String? = null): Result<PythonVirtualEnvironment.Existing, PyError>
|
||||
suspend fun createVirtualEnvironment(basePythonBinaryPath: PythonBinary? = null, envName: String? = null): PyResult<PythonVirtualEnvironment.Existing>
|
||||
|
||||
suspend fun findVirtualEnvironments(): Result<List<HatchVirtualEnvironment>, PyError>
|
||||
suspend fun findVirtualEnvironments(): PyResult<List<HatchVirtualEnvironment>>
|
||||
}
|
||||
|
||||
/**
|
||||
* Hatch Service for working directory (where hatch.toml / pyproject.toml is usually placed)
|
||||
*/
|
||||
suspend fun Path.getHatchService(hatchExecutablePath: Path? = null): Result<HatchService, PyError> {
|
||||
suspend fun Path.getHatchService(hatchExecutablePath: Path? = null): PyResult<HatchService> {
|
||||
return CliBasedHatchService(hatchExecutablePath = hatchExecutablePath, workingDirectoryPath = this)
|
||||
}
|
||||
|
||||
@@ -105,7 +106,7 @@ suspend fun Path.getHatchService(hatchExecutablePath: Path? = null): Result<Hatc
|
||||
* Hatch Service for Module.
|
||||
* Working directory considered as the module base path.
|
||||
*/
|
||||
suspend fun Module.getHatchService(hatchExecutablePath: Path? = null): Result<HatchService, PyError> {
|
||||
suspend fun Module.getHatchService(hatchExecutablePath: Path? = null): PyResult<HatchService> {
|
||||
val workingDirectoryPath = resolveHatchWorkingDirectory(this.project, this).getOr { return it }
|
||||
return workingDirectoryPath.getHatchService(hatchExecutablePath = hatchExecutablePath)
|
||||
}
|
||||
@@ -115,7 +116,7 @@ suspend fun Module.getHatchService(hatchExecutablePath: Path? = null): Result<Ha
|
||||
*/
|
||||
fun PythonHomePath.getHatchEnvVirtualProjectPath(): Path = this.parent.parent
|
||||
|
||||
fun resolveHatchWorkingDirectory(project: Project, module: Module?): Result<Path, PyError> {
|
||||
fun resolveHatchWorkingDirectory(project: Project, module: Module?): PyResult<Path> {
|
||||
val pathString = module?.basePath ?: project.basePath
|
||||
|
||||
return when (val path = pathString?.let { Path.of(it) }) {
|
||||
|
||||
@@ -12,7 +12,8 @@ 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.PyError
|
||||
import com.jetbrains.python.errorProcessing.ExecError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.resolvePythonBinary
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.isDirectory
|
||||
@@ -57,15 +58,15 @@ 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, PyError.ExecException> {
|
||||
internal suspend fun <T> execute(vararg arguments: String, processOutputTransformer: ProcessOutputTransformer<T>): Result<T, ExecError> {
|
||||
return execService.execute(hatchBinary, arguments.toList(), execOptions, processOutputTransformer)
|
||||
}
|
||||
|
||||
internal suspend fun <T> executeInteractive(vararg arguments: String, eelProcessInteractiveHandler: EelProcessInteractiveHandler<T>): Result<T, PyError.ExecException> {
|
||||
internal suspend fun <T> executeInteractive(vararg arguments: String, eelProcessInteractiveHandler: EelProcessInteractiveHandler<T>): Result<T, ExecError> {
|
||||
return execService.executeInteractive(hatchBinary, arguments.toList(), execOptions, eelProcessInteractiveHandler)
|
||||
}
|
||||
|
||||
internal suspend fun resolvePythonVirtualEnvironment(pythonHomePath: PythonHomePath): Result<PythonVirtualEnvironment, PyError> {
|
||||
internal suspend fun resolvePythonVirtualEnvironment(pythonHomePath: PythonHomePath): PyResult<PythonVirtualEnvironment> {
|
||||
val pythonVersion = pythonHomePath.takeIf { it.isDirectory() }?.resolvePythonBinary()?.let { pythonBinaryPath ->
|
||||
execService.execGetStdout(Binary(pythonBinaryPath), listOf("--version")).getOr { return it }.trim()
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import com.intellij.python.hatch.runtime.HatchRuntime
|
||||
import com.intellij.python.hatch.runtime.createHatchRuntime
|
||||
import com.jetbrains.python.PythonBinary
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.sync.Semaphore
|
||||
import kotlinx.coroutines.sync.withPermit
|
||||
@@ -30,7 +30,7 @@ internal class CliBasedHatchService private constructor(
|
||||
private val hatchRuntime: HatchRuntime,
|
||||
) : HatchService {
|
||||
companion object {
|
||||
suspend operator fun invoke(workingDirectoryPath: Path, hatchExecutablePath: Path?): Result<CliBasedHatchService, PyError> {
|
||||
suspend operator fun invoke(workingDirectoryPath: Path, hatchExecutablePath: Path?): PyResult<CliBasedHatchService> {
|
||||
val hatchRuntime = createHatchRuntime(
|
||||
hatchExecutablePath = hatchExecutablePath,
|
||||
workingDirectoryPath = workingDirectoryPath,
|
||||
@@ -51,13 +51,13 @@ internal class CliBasedHatchService private constructor(
|
||||
|
||||
override fun getWorkingDirectoryPath(): Path = workingDirectoryPath
|
||||
|
||||
override suspend fun syncDependencies(envName: String): Result<String, PyError> {
|
||||
override suspend fun syncDependencies(envName: String): PyResult<String> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
hatchRuntime.hatchCli().run(envName, "python", "--version")
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun isHatchManagedProject(): Result<Boolean, PyError> {
|
||||
override suspend fun isHatchManagedProject(): PyResult<Boolean> {
|
||||
val isHatchManaged = withContext(Dispatchers.IO) {
|
||||
when {
|
||||
workingDirectoryPath.resolve("hatch.toml").exists() -> true
|
||||
@@ -72,7 +72,7 @@ internal class CliBasedHatchService private constructor(
|
||||
}
|
||||
|
||||
|
||||
override suspend fun findVirtualEnvironments(): Result<List<HatchVirtualEnvironment>, PyError> {
|
||||
override suspend fun findVirtualEnvironments(): PyResult<List<HatchVirtualEnvironment>> {
|
||||
val hatchEnv = hatchRuntime.hatchCli().env()
|
||||
val environments: HatchEnvironments = hatchEnv.show().getOr { return it }
|
||||
val virtualEnvironments = environments.getAvailableVirtualHatchEnvironments()
|
||||
@@ -90,7 +90,7 @@ internal class CliBasedHatchService private constructor(
|
||||
}
|
||||
|
||||
|
||||
override suspend fun createNewProject(projectName: String): Result<ProjectStructure, PyError> {
|
||||
override suspend fun createNewProject(projectName: String): PyResult<ProjectStructure> {
|
||||
val eelApi = workingDirectoryPath.getEelDescriptor().upgrade()
|
||||
val tempDir = eelApi.fs.createTemporaryDirectory(EelFileSystemApi.CreateTemporaryEntryOptions.Builder().build()).getOr { failure ->
|
||||
return Result.failure(FileSystemOperationHatchError(failure.error))
|
||||
@@ -108,7 +108,7 @@ internal class CliBasedHatchService private constructor(
|
||||
))
|
||||
}
|
||||
|
||||
override suspend fun createVirtualEnvironment(basePythonBinaryPath: PythonBinary?, envName: String?): Result<PythonVirtualEnvironment.Existing, PyError> {
|
||||
override suspend fun createVirtualEnvironment(basePythonBinaryPath: PythonBinary?, envName: String?): PyResult<PythonVirtualEnvironment.Existing> {
|
||||
val pythonBasedRuntime = basePythonBinaryPath?.let { path ->
|
||||
hatchRuntime.withBasePythonBinaryPath(path).getOr { return it }
|
||||
} ?: hatchRuntime
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package com.jetbrains.python
|
||||
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import kotlin.Result
|
||||
|
||||
/**
|
||||
* Please use [com.jetbrains.python.Result] (low level) or ([com.jetbrains.python.errorProcessing.PyResult] high level)
|
||||
* [kotlin.Result] failure with user-readable error
|
||||
*/
|
||||
@ApiStatus.Obsolete
|
||||
fun <T> failure(error: @NlsSafe String): Result<T> = Result.Companion.failure(Throwable(error))
|
||||
@@ -9,10 +9,10 @@ import com.jetbrains.python.mapResult
|
||||
import org.apache.tuweni.toml.TomlArray
|
||||
import org.apache.tuweni.toml.TomlTable
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
import org.toml.lang.psi.TomlKeyValue as PsiTomlKeyValue
|
||||
import org.toml.lang.psi.TomlTable as PsiTomlTable
|
||||
import org.toml.lang.psi.TomlLiteral as PsiTomlLiteral
|
||||
import kotlin.reflect.KClass
|
||||
import org.toml.lang.psi.TomlKeyValue as PsiTomlKeyValue
|
||||
import org.toml.lang.psi.TomlLiteral as PsiTomlLiteral
|
||||
import org.toml.lang.psi.TomlTable as PsiTomlTable
|
||||
|
||||
/**
|
||||
* The error union used by [TomlTable.safeGet], [TomlTable.safeGetRequired] and [TomlTable.safeGetArr].
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
// 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.pyproject
|
||||
|
||||
import com.jetbrains.python.Result.Companion.failure
|
||||
import com.jetbrains.python.Result.Companion.success
|
||||
import com.jetbrains.python.Result
|
||||
import org.apache.tuweni.toml.Toml
|
||||
import org.apache.tuweni.toml.TomlParseError
|
||||
import org.apache.tuweni.toml.TomlTable
|
||||
import java.io.InputStream
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.toNioPathOrNull
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.Result.Companion.success
|
||||
import com.jetbrains.python.sdk.basePath
|
||||
import com.jetbrains.python.sdk.findAmongRoots
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.apache.tuweni.toml.Toml
|
||||
import org.apache.tuweni.toml.TomlParseError
|
||||
import org.apache.tuweni.toml.TomlTable
|
||||
import org.jetbrains.annotations.ApiStatus.Internal
|
||||
import java.io.InputStream
|
||||
import java.nio.file.Path
|
||||
|
||||
/**
|
||||
@@ -112,7 +111,7 @@ data class PyProjectToml(
|
||||
val toml = Toml.parse(inputStream)
|
||||
|
||||
if (toml.hasErrors()) {
|
||||
return failure(toml.errors())
|
||||
return Result.failure(toml.errors())
|
||||
}
|
||||
|
||||
val projectTable = toml.safeGet<TomlTable>("project").getOrIssue(issues)
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
package com.intellij.python.pyproject
|
||||
|
||||
import com.intellij.python.pyproject.PyProjectIssue.InvalidContact
|
||||
import com.intellij.python.pyproject.PyProjectIssue.MissingName
|
||||
import com.intellij.python.pyproject.PyProjectIssue.MissingVersion
|
||||
import com.intellij.python.pyproject.PyProjectIssue.SafeGetError
|
||||
import com.intellij.python.pyproject.PyProjectIssue.*
|
||||
import com.intellij.python.pyproject.TomlTableSafeGetError.RequiredValueMissing
|
||||
import com.intellij.python.pyproject.TomlTableSafeGetError.UnexpectedType
|
||||
import com.jetbrains.python.Result.Failure
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.getOrThrow
|
||||
import com.jetbrains.python.isFailure
|
||||
import org.apache.tuweni.toml.TomlArray
|
||||
@@ -29,7 +26,7 @@ class PyProjectTomlTest {
|
||||
|
||||
// THEN
|
||||
assert(result.isFailure)
|
||||
assert((result as Failure).error.isNotEmpty())
|
||||
assert((result as Result.Failure).error.isNotEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.jetbrains.python
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.platform.eel.EelPlatform
|
||||
import com.intellij.platform.eel.getOr
|
||||
import com.intellij.platform.eel.path.EelPath
|
||||
import com.intellij.platform.eel.provider.getEelDescriptor
|
||||
import com.intellij.platform.eel.provider.utils.EelProcessExecutionResult
|
||||
import com.intellij.platform.eel.provider.utils.exec
|
||||
|
||||
@@ -9,7 +9,7 @@ import com.intellij.python.community.execService.HelperName
|
||||
import com.intellij.python.community.execService.WhatToExec
|
||||
import com.jetbrains.python.PythonBinary
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.errorProcessing.failure
|
||||
import com.jetbrains.python.sdk.PySdkSettings
|
||||
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor
|
||||
@@ -36,7 +36,7 @@ suspend fun createVenv(
|
||||
venvDir: Directory,
|
||||
inheritSitePackages: Boolean = false,
|
||||
envReader: VirtualEnvReader = VirtualEnvReader.Instance,
|
||||
): Result<PythonBinary, PyError> {
|
||||
): PyResult<PythonBinary> {
|
||||
val execService = ExecService()
|
||||
val args = buildList {
|
||||
if (inheritSitePackages) {
|
||||
|
||||
@@ -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.jetbrains.python
|
||||
|
||||
import com.intellij.CommonBundle
|
||||
import com.intellij.ide.IdeBundle
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.ui.DialogWrapper
|
||||
@@ -9,23 +10,48 @@ import com.intellij.ui.IdeBorderFactory
|
||||
import com.intellij.ui.JBColor
|
||||
import com.intellij.ui.components.JBLabel
|
||||
import com.intellij.ui.components.JBScrollPane
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||
import com.intellij.util.ui.FormBuilder
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import com.intellij.util.ui.components.BorderLayoutPanel
|
||||
import com.jetbrains.python.execution.FailureReason
|
||||
import com.jetbrains.python.execution.PyExecutionFailure
|
||||
import com.jetbrains.python.execution.userMessage
|
||||
import com.jetbrains.python.errorProcessing.ExecError
|
||||
import com.jetbrains.python.errorProcessing.ExecErrorReason
|
||||
import com.jetbrains.python.errorProcessing.MessageError
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.awt.Dimension
|
||||
import java.awt.Font
|
||||
import javax.swing.*
|
||||
import javax.swing.text.StyleConstants
|
||||
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if [project] is not `null` and it is disposed
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
@RequiresEdt
|
||||
fun showErrorDialog(
|
||||
project: Project?,
|
||||
execError: PyError,
|
||||
) {
|
||||
when (execError) {
|
||||
is ExecError -> {
|
||||
showProcessExecutionErrorDialog(project, execError)
|
||||
}
|
||||
is MessageError -> {
|
||||
Messages.showErrorDialog(execError.message, CommonBundle.message("title.error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if [project] is not `null` and it is disposed
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
@RequiresEdt
|
||||
fun showProcessExecutionErrorDialog(
|
||||
project: Project?,
|
||||
exception: PyExecutionFailure,
|
||||
execError: ExecError,
|
||||
) {
|
||||
check(project == null || !project.isDisposed)
|
||||
|
||||
@@ -34,15 +60,17 @@ fun showProcessExecutionErrorDialog(
|
||||
val errorMessageLabel = JBLabel(UIUtil.toHtml(errorMessageText), Messages.getErrorIcon(), SwingConstants.LEFT)
|
||||
|
||||
val commandOutputTextPane = JTextPane().apply {
|
||||
val command = (listOf(exception.command) + exception.args).joinToString(" ")
|
||||
when (val err = exception.failureReason) {
|
||||
FailureReason.CantStart -> {
|
||||
appendProcessOutput(command, "\n", exception.userMessage, null)
|
||||
val command = execError.command.joinToString(" ")
|
||||
when (val err = execError.errorReason) {
|
||||
is ExecErrorReason.CantStart -> {
|
||||
appendProcessOutput(command, err.cantExecProcessError, execError.message, null)
|
||||
|
||||
}
|
||||
is FailureReason.ExecutionFailed -> {
|
||||
val output = err.output
|
||||
appendProcessOutput(command, output.stdout, output.stderr, output.exitCode)
|
||||
is ExecErrorReason.UnexpectedProcessTermination -> {
|
||||
appendProcessOutput(command, err.stdout, err.stderr, err.exitCode)
|
||||
}
|
||||
ExecErrorReason.Timeout -> {
|
||||
appendProcessOutput(command, "Timeout", "\n", null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +92,7 @@ fun showProcessExecutionErrorDialog(
|
||||
object : DialogWrapper(project) {
|
||||
init {
|
||||
init()
|
||||
title = exception.additionalMessage ?: errorMessageText
|
||||
title = execError.additionalMessageToUser ?: errorMessageText
|
||||
}
|
||||
|
||||
override fun createActions(): Array<Action> = arrayOf(okAction)
|
||||
|
||||
@@ -8,7 +8,7 @@ import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.python.hatch.*
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.resolvePythonBinary
|
||||
import com.jetbrains.python.sdk.createSdk
|
||||
import com.jetbrains.python.sdk.persist
|
||||
@@ -19,7 +19,7 @@ import java.nio.file.Path
|
||||
import kotlin.io.path.name
|
||||
|
||||
@ApiStatus.Internal
|
||||
suspend fun HatchVirtualEnvironment.createSdk(workingDirectoryPath: Path, module: Module?): Result<Sdk, PyError> {
|
||||
suspend fun HatchVirtualEnvironment.createSdk(workingDirectoryPath: Path, module: Module?): PyResult<Sdk> {
|
||||
val existingPythonEnvironment = pythonVirtualEnvironment as? PythonVirtualEnvironment.Existing
|
||||
?: return Result.failure(BasePythonExecutableNotFoundHatchError(null as String?))
|
||||
val pythonHomePath = pythonVirtualEnvironment?.pythonHomePath
|
||||
|
||||
@@ -8,7 +8,7 @@ import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.platform.ide.progress.withBackgroundProgress
|
||||
import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo
|
||||
import com.jetbrains.python.sdk.ModuleOrProject
|
||||
import com.jetbrains.python.sdk.add.v2.PySdkCreator
|
||||
@@ -24,7 +24,7 @@ import kotlinx.coroutines.launch
|
||||
class PyV3BaseProjectSettings(var createGitRepository: Boolean = false) {
|
||||
lateinit var sdkCreator: PySdkCreator
|
||||
|
||||
suspend fun generateAndGetSdk(module: Module, baseDir: VirtualFile, supportsNotEmptyModuleStructure: Boolean = false): Result<Pair<Sdk, InterpreterStatisticsInfo>, PyError> = coroutineScope {
|
||||
suspend fun generateAndGetSdk(module: Module, baseDir: VirtualFile, supportsNotEmptyModuleStructure: Boolean = false): PyResult<Pair<Sdk, InterpreterStatisticsInfo>> = coroutineScope {
|
||||
val project = module.project
|
||||
if (createGitRepository) {
|
||||
launch(CoroutineName("Generating git") + Dispatchers.IO) {
|
||||
@@ -43,7 +43,7 @@ class PyV3BaseProjectSettings(var createGitRepository: Boolean = false) {
|
||||
return@coroutineScope Result.success(Pair(sdk, interpreterStatistics))
|
||||
}
|
||||
|
||||
private suspend fun getSdkAndInterpreter(module: Module): Result<Pair<Sdk, InterpreterStatisticsInfo>, PyError> =
|
||||
private suspend fun getSdkAndInterpreter(module: Module): PyResult<Pair<Sdk, InterpreterStatisticsInfo>> =
|
||||
sdkCreator.getSdk(ModuleOrProject.ModuleAndProject(module))
|
||||
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ package com.jetbrains.python.newProjectWizard
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import org.jetbrains.annotations.CheckReturnValue
|
||||
|
||||
/**
|
||||
@@ -26,5 +25,5 @@ fun interface PyV3ProjectTypeSpecificSettings {
|
||||
* Returns error if generation failed due to execution error
|
||||
*/
|
||||
@CheckReturnValue
|
||||
suspend fun generateProject(module: Module, baseDir: VirtualFile, sdk: Sdk): Result<Unit, PyError>
|
||||
suspend fun generateProject(module: Module, baseDir: VirtualFile, sdk: Sdk): PyResult<Unit>
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// 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.packaging;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
@@ -9,8 +9,8 @@ import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.jetbrains.python.PySdkBundle;
|
||||
import com.jetbrains.python.sdk.PythonSdkUtil;
|
||||
import com.jetbrains.python.venvReader.VirtualEnvReader;
|
||||
import com.jetbrains.python.sdk.flavors.PyCondaRunKt;
|
||||
import com.jetbrains.python.venvReader.VirtualEnvReader;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -51,7 +51,7 @@ public class PyCondaPackageManagerImpl extends PyPackageManagerImpl {
|
||||
final Sdk sdk = getSdk();
|
||||
|
||||
final String path = getCondaDirectory();
|
||||
if (path == null) throw new PyExecutionException(PySdkBundle.message("python.sdk.conda.dialog.empty.conda.name", sdk.getHomePath()), command, arguments);
|
||||
if (path == null) throw new PyExecutionException(PySdkBundle.message("python.sdk.conda.dialog.empty.conda.name", sdk.getHomePath()));
|
||||
|
||||
final ArrayList<String> parameters = Lists.newArrayList(command, "-p", path);
|
||||
parameters.addAll(arguments);
|
||||
@@ -123,8 +123,8 @@ public class PyCondaPackageManagerImpl extends PyPackageManagerImpl {
|
||||
PyPackage pkg = parsePackaging(line,
|
||||
"=",
|
||||
false,
|
||||
PySdkBundle.message("python.sdk.conda.dialog.invalid.conda.output.format"),
|
||||
"conda");
|
||||
PySdkBundle.message("python.sdk.conda.dialog.invalid.conda.output.format")
|
||||
);
|
||||
if (pkg != null) {
|
||||
packages.add(pkg);
|
||||
}
|
||||
@@ -135,14 +135,13 @@ public class PyCondaPackageManagerImpl extends PyPackageManagerImpl {
|
||||
public static @NotNull String createVirtualEnv(@Nullable String condaExecutable, @NotNull String destinationDir,
|
||||
@NotNull String version) throws ExecutionException {
|
||||
if (condaExecutable == null) {
|
||||
throw new PyExecutionException(PySdkBundle.message("python.sdk.conda.dialog.cannot.find.conda"), "Conda", Collections.emptyList(),
|
||||
new ProcessOutput());
|
||||
throw new PyExecutionException(PySdkBundle.message("python.sdk.conda.dialog.cannot.find.conda"));
|
||||
}
|
||||
|
||||
final ArrayList<String> parameters = Lists.newArrayList("create", "-p", destinationDir, "-y", "python=" + version);
|
||||
|
||||
PyCondaRunKt.runConda(condaExecutable, parameters);
|
||||
final Path binary = VirtualEnvReader.getInstance().findPythonInPythonRoot(Path.of(destinationDir));
|
||||
final Path binary = VirtualEnvReader.getInstance().findPythonInPythonRoot(Path.of(destinationDir));
|
||||
final String binaryFallback = destinationDir + File.separator + "bin" + File.separator + "python";
|
||||
return (binary != null) ? binary.toString() : binaryFallback;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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.packaging
|
||||
|
||||
import com.intellij.execution.process.ProcessOutput
|
||||
import com.jetbrains.python.errorProcessing.ExecError
|
||||
import com.jetbrains.python.errorProcessing.ExecErrorReason
|
||||
import com.jetbrains.python.errorProcessing.MessageError
|
||||
import java.io.IOException
|
||||
|
||||
/**
|
||||
* A temporary hack for some outdated code (see usages), do not use in a new code. Stay away from [PyExecutionException]
|
||||
*/
|
||||
internal fun PyExecutionException.copyWith(newCommand: String, newArgs: List<String>): PyExecutionException =
|
||||
when (val err = pyError) {
|
||||
is ExecError -> {
|
||||
when (val reason = err.errorReason) {
|
||||
is ExecErrorReason.CantStart -> {
|
||||
PyExecutionException(IOException(reason.cantExecProcessError), err.additionalMessageToUser, newCommand, newArgs, fixes)
|
||||
}
|
||||
ExecErrorReason.Timeout -> {
|
||||
val command = arrayOf(newCommand) + newArgs.toTypedArray()
|
||||
PyExecutionException(ExecError(command, ExecErrorReason.Timeout, err.additionalMessageToUser))
|
||||
}
|
||||
is ExecErrorReason.UnexpectedProcessTermination -> {
|
||||
val output = ProcessOutput(reason.stdout, reason.stderr, reason.exitCode, false, false)
|
||||
PyExecutionException(err.additionalMessageToUser, newCommand, newArgs, output, fixes)
|
||||
}
|
||||
}
|
||||
}
|
||||
is MessageError -> error("Error ${err.message} has no command, command can't be changed")
|
||||
}
|
||||
@@ -42,7 +42,8 @@ import static com.jetbrains.python.sdk.PySdkExtKt.showSdkExecutionException;
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public class PyPackageManagerImpl extends PyPackageManagerImplBase {
|
||||
private static final String LEGACY_VIRTUALENV_ZIPAPP_NAME = "virtualenv-20.13.0.pyz"; // virtualenv used to create virtual environments for python 2.7 & 3.6
|
||||
private static final String LEGACY_VIRTUALENV_ZIPAPP_NAME = "virtualenv-20.13.0.pyz";
|
||||
// virtualenv used to create virtual environments for python 2.7 & 3.6
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(PyPackageManagerImpl.class);
|
||||
|
||||
@@ -108,7 +109,7 @@ public class PyPackageManagerImpl extends PyPackageManagerImplBase {
|
||||
for (PyRequirement req : requirements) {
|
||||
simplifiedArgs.addAll(req.getInstallOptions());
|
||||
}
|
||||
throw e.copyWith("pip", makeSafeToDisplayCommand(simplifiedArgs));
|
||||
throw PyExecutionExceptionExtKt.copyWith(e, "pip", makeSafeToDisplayCommand(simplifiedArgs));
|
||||
}
|
||||
finally {
|
||||
LOG.debug("Packages cache is about to be refreshed because these requirements were installed: " + requirements);
|
||||
@@ -134,7 +135,7 @@ public class PyPackageManagerImpl extends PyPackageManagerImplBase {
|
||||
getHelperResult(args, !canModify, true);
|
||||
}
|
||||
catch (PyExecutionException e) {
|
||||
throw e.copyWith("pip", args);
|
||||
throw PyExecutionExceptionExtKt.copyWith(e, "pip", args);
|
||||
}
|
||||
finally {
|
||||
LOG.debug("Packages cache is about to be refreshed because these packages were uninstalled: " + packages);
|
||||
@@ -197,7 +198,7 @@ public class PyPackageManagerImpl extends PyPackageManagerImplBase {
|
||||
showSdkExecutionException(sdk, e, PySdkBundle.message("python.creating.venv.failed.title"));
|
||||
}
|
||||
|
||||
final Path binary = VirtualEnvReader.getInstance().findPythonInPythonRoot(Paths.get(destinationDir));
|
||||
final Path binary = VirtualEnvReader.getInstance().findPythonInPythonRoot(Paths.get(destinationDir));
|
||||
final String binaryFallback = destinationDir + mySeparator + "bin" + mySeparator + "python";
|
||||
|
||||
return (binary != null) ? binary.toString() : binaryFallback;
|
||||
@@ -240,7 +241,7 @@ public class PyPackageManagerImpl extends PyPackageManagerImplBase {
|
||||
final ProcessOutput output = getPythonProcessOutput(path, args, askForSudo, showProgress, workingDir, pyArgs);
|
||||
final int exitCode = output.getExitCode();
|
||||
if (output.isTimeout()) {
|
||||
throw new PyExecutionException(PySdkBundle.message("python.sdk.packaging.timed.out"), path, args, output);
|
||||
throw PyExecutionException.createForTimeout(PySdkBundle.message("python.sdk.packaging.timed.out"), path, args);
|
||||
}
|
||||
else if (exitCode != 0) {
|
||||
throw new PyExecutionException(PySdkBundle.message("python.sdk.packaging.non.zero.exit.code", exitCode), path, args, output);
|
||||
@@ -309,7 +310,7 @@ public class PyPackageManagerImpl extends PyPackageManagerImplBase {
|
||||
return result;
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new PyExecutionException(e.getMessage(), helperPath, args);
|
||||
throw new PyExecutionException(e, null, helperPath, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// 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.packaging;
|
||||
|
||||
import com.intellij.execution.ExecutionException;
|
||||
@@ -15,7 +15,8 @@ import com.intellij.util.net.HttpConfigurable;
|
||||
import com.jetbrains.python.PyPsiPackageUtil;
|
||||
import com.jetbrains.python.PySdkBundle;
|
||||
import com.jetbrains.python.PythonHelpersLocator;
|
||||
import com.jetbrains.python.execution.FailureReason;
|
||||
import com.jetbrains.python.errorProcessing.ExecError;
|
||||
import com.jetbrains.python.errorProcessing.ExecErrorReason;
|
||||
import com.jetbrains.python.packaging.repository.PyPackageRepositoryUtil;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import com.jetbrains.python.sdk.PyDetectedSdk;
|
||||
@@ -35,6 +36,10 @@ import java.util.regex.Pattern;
|
||||
|
||||
import static com.intellij.webcore.packaging.PackageVersionComparator.VERSION_COMPARATOR;
|
||||
|
||||
/**
|
||||
* @deprecated TODO: explain
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public abstract class PyPackageManagerImplBase extends PyPackageManager {
|
||||
protected static final String SETUPTOOLS_VERSION = "44.1.1";
|
||||
protected static final String PIP_VERSION = "24.3.1";
|
||||
@@ -125,11 +130,14 @@ public abstract class PyPackageManagerImplBase extends PyPackageManager {
|
||||
return setuptoolsPackage != null ? setuptoolsPackage : PyPsiPackageUtil.findPackage(packages, PyPackageUtil.DISTRIBUTE);
|
||||
}
|
||||
catch (PyExecutionException e) {
|
||||
var error = e.getFailureReason();
|
||||
if (error instanceof FailureReason.ExecutionFailed executionFailed) {
|
||||
int exitCode = executionFailed.getOutput().getExitCode();
|
||||
if (exitCode == ERROR_NO_SETUPTOOLS) {
|
||||
return null;
|
||||
var pyError = e.getPyError();
|
||||
if (pyError instanceof ExecError error) {
|
||||
var errorReason = error.getErrorReason();
|
||||
if (errorReason instanceof ExecErrorReason.UnexpectedProcessTermination unexpectedProcessTermination) {
|
||||
int exitCode = unexpectedProcessTermination.getExitCode();
|
||||
if (exitCode == ERROR_NO_SETUPTOOLS) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
@@ -283,8 +291,8 @@ public abstract class PyPackageManagerImplBase extends PyPackageManager {
|
||||
PyPackage pkg = parsePackaging(line,
|
||||
"\t",
|
||||
true,
|
||||
PySdkBundle.message("python.sdk.packaging.invalid.output.format"),
|
||||
PACKAGING_TOOL);
|
||||
PySdkBundle.message("python.sdk.packaging.invalid.output.format")
|
||||
);
|
||||
|
||||
if (pkg != null) {
|
||||
packages.add(pkg);
|
||||
@@ -296,11 +304,10 @@ public abstract class PyPackageManagerImplBase extends PyPackageManager {
|
||||
protected final @Nullable PyPackage parsePackaging(@NotNull @NonNls String line,
|
||||
@NotNull @NonNls String separator,
|
||||
boolean useLocation,
|
||||
@NotNull @Nls String errorMessage,
|
||||
@NotNull @NonNls String command) throws PyExecutionException {
|
||||
@NotNull @Nls String errorMessage) throws PyExecutionException {
|
||||
List<String> fields = StringUtil.split(line, separator);
|
||||
if (fields.size() < 3) {
|
||||
throw new PyExecutionException(errorMessage, command, List.of());
|
||||
throw new PyExecutionException(errorMessage);
|
||||
}
|
||||
|
||||
final String name = fields.get(0);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// 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.packaging;
|
||||
|
||||
import com.intellij.execution.ExecutionException;
|
||||
@@ -23,6 +23,8 @@ import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.errorProcessing.ExecError;
|
||||
import com.jetbrains.python.errorProcessing.PyError;
|
||||
import com.jetbrains.python.packaging.management.PythonPackagesInstaller;
|
||||
import com.jetbrains.python.packaging.ui.PyPackageManagementService;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
@@ -184,7 +186,15 @@ public final class PyPackageManagerUI {
|
||||
((InstallTask)this).myRequirements,
|
||||
req -> ContainerUtil.map(req.getInstallOptions(), option -> Pair.create(option, req.getName()))) : null;
|
||||
final List<String> packageManagerArguments = exceptions.stream()
|
||||
.flatMap(e -> (e instanceof PyExecutionException) ? ((PyExecutionException)e).getArgs().stream() : null)
|
||||
.flatMap(e -> {
|
||||
if (e instanceof PyExecutionException pyExecutionException) {
|
||||
PyError pyError = pyExecutionException.getPyError();
|
||||
if (pyError instanceof ExecError execError) {
|
||||
return Arrays.stream(execError.getExeAndArgs().getSecond());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.toList();
|
||||
final String packageNames = requirements != null ? requirements.stream()
|
||||
.filter(req -> packageManagerArguments.contains(req.first))
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// 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.packaging;
|
||||
|
||||
import com.intellij.execution.ExecutionException;
|
||||
@@ -45,7 +45,11 @@ import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
|
||||
public class PyTargetEnvironmentPackageManager extends PyPackageManagerImplBase {
|
||||
/**
|
||||
* @deprecated TODO: explain
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public final class PyTargetEnvironmentPackageManager extends PyPackageManagerImplBase {
|
||||
private static final Logger LOG = Logger.getInstance(PyTargetEnvironmentPackageManager.class);
|
||||
|
||||
@Override
|
||||
@@ -113,7 +117,7 @@ public class PyTargetEnvironmentPackageManager extends PyPackageManagerImplBase
|
||||
for (PyRequirement req : requirements) {
|
||||
simplifiedArgs.addAll(req.getInstallOptions());
|
||||
}
|
||||
throw e.copyWith("pip", makeSafeToDisplayCommand(simplifiedArgs));
|
||||
throw PyExecutionExceptionExtKt.copyWith(e, "pip", makeSafeToDisplayCommand(simplifiedArgs));
|
||||
}
|
||||
finally {
|
||||
LOG.debug("Packages cache is about to be refreshed because these requirements were installed: " + requirements);
|
||||
@@ -158,7 +162,7 @@ public class PyTargetEnvironmentPackageManager extends PyPackageManagerImplBase
|
||||
getPythonProcessResult(pythonExecution, !canModify, true, targetEnvironmentRequest);
|
||||
}
|
||||
catch (PyExecutionException e) {
|
||||
throw e.copyWith("pip", args);
|
||||
throw PyExecutionExceptionExtKt.copyWith(e, "pip", args);
|
||||
}
|
||||
finally {
|
||||
LOG.debug("Packages cache is about to be refreshed because these packages were uninstalled: " + packages);
|
||||
@@ -251,7 +255,7 @@ public class PyTargetEnvironmentPackageManager extends PyPackageManagerImplBase
|
||||
int exitCode = processOutput.getExitCode();
|
||||
if (processOutput.isTimeout()) {
|
||||
// TODO [targets] Make cancellable right away?
|
||||
throw new PyExecutionException(PySdkBundle.message("python.sdk.packaging.timed.out"), path, args, processOutput);
|
||||
throw PyExecutionException.createForTimeout(PySdkBundle.message("python.sdk.packaging.timed.out"), path, args);
|
||||
}
|
||||
else if (exitCode != 0) {
|
||||
throw new PyExecutionException(PySdkBundle.message("python.sdk.packaging.non.zero.exit.code", exitCode), path, args, processOutput);
|
||||
@@ -347,7 +351,7 @@ public class PyTargetEnvironmentPackageManager extends PyPackageManagerImplBase
|
||||
catch (IOException e) {
|
||||
String exePath = localCommandLine.getExePath();
|
||||
List<String> args = localCommandLine.getCommandLineList(exePath);
|
||||
throw new PyExecutionException(e.getMessage(), exePath, args);
|
||||
throw new PyExecutionException(e, null, exePath, args);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
@file:JvmName("PythonPackageManagerExt")
|
||||
|
||||
package com.jetbrains.python.packaging.management
|
||||
@@ -113,7 +113,7 @@ suspend fun PythonPackageManager.runPackagingTool(
|
||||
}
|
||||
|
||||
if (result.isTimeout) {
|
||||
throw PyExecutionException(PySdkBundle.message("python.sdk.packaging.timed.out"), helperPath, args, result)
|
||||
throw PyExecutionException.createForTimeout(PySdkBundle.message("python.sdk.packaging.timed.out"), helperPath, args)
|
||||
}
|
||||
|
||||
return result.stdout
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// 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.packaging.ui;
|
||||
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.RunCanceledByUserException;
|
||||
import com.intellij.execution.process.ProcessOutput;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.util.NlsContexts;
|
||||
@@ -20,7 +19,8 @@ import com.intellij.webcore.packaging.PackageManagementServiceEx;
|
||||
import com.intellij.webcore.packaging.RepoPackage;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.PySdkBundle;
|
||||
import com.jetbrains.python.execution.FailureReason;
|
||||
import com.jetbrains.python.errorProcessing.ExecError;
|
||||
import com.jetbrains.python.errorProcessing.ExecErrorReason;
|
||||
import com.jetbrains.python.packaging.*;
|
||||
import com.jetbrains.python.packaging.PyPIPackageUtil.PackageDetails;
|
||||
import com.jetbrains.python.packaging.requirement.PyRequirementRelation;
|
||||
@@ -33,7 +33,10 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -318,15 +321,17 @@ public class PyPackageManagementService extends PackageManagementServiceEx {
|
||||
private static @NotNull PyPackageInstallationErrorDescription createDescription(@NotNull ExecutionException e,
|
||||
@Nullable Sdk sdk,
|
||||
@Nullable String packageName) {
|
||||
if (e instanceof PyExecutionException pyExecEx && pyExecEx.getFailureReason() instanceof FailureReason.ExecutionFailed execFailed) {
|
||||
var ee = execFailed.getOutput();
|
||||
final String stdout = ee.getStdout();
|
||||
if (e instanceof PyExecutionException pyExecEx &&
|
||||
pyExecEx.getPyError() instanceof ExecError execError &&
|
||||
execError.getErrorReason() instanceof ExecErrorReason.UnexpectedProcessTermination execFailed) {
|
||||
final String stdout = execFailed.getStdout();
|
||||
final String stdoutCause = findErrorCause(stdout);
|
||||
final String stderrCause = findErrorCause(ee.getStderr());
|
||||
final String stderrCause = findErrorCause(execFailed.getStderr());
|
||||
final String cause = stdoutCause != null ? stdoutCause : stderrCause;
|
||||
final String message = cause != null ? cause : pyExecEx.getMessage();
|
||||
final String command = pyExecEx.getCommand() + " " + StringUtil.join(pyExecEx.getArgs(), " ");
|
||||
return new PyPackageInstallationErrorDescription(message, command, stdout.isEmpty() ? ee.getStderr() : stdout + "\n" + ee.getStderr(),
|
||||
final String command = StringUtil.join(execError.getCommand(), " ");
|
||||
return new PyPackageInstallationErrorDescription(message, command,
|
||||
stdout.isEmpty() ? execFailed.getStderr() : stdout + "\n" + execFailed.getStderr(),
|
||||
findErrorSolution(pyExecEx, cause, sdk), packageName, sdk);
|
||||
}
|
||||
else {
|
||||
@@ -334,30 +339,33 @@ public class PyPackageManagementService extends PackageManagementServiceEx {
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable @DetailedDescription String findErrorSolution(@NotNull PyExecutionException e,
|
||||
private static @Nullable @DetailedDescription String findErrorSolution(@NotNull PyExecutionException executionException,
|
||||
@Nullable String cause,
|
||||
@Nullable Sdk sdk) {
|
||||
if (cause != null) {
|
||||
if (StringUtil.containsIgnoreCase(cause, "SyntaxError")) {
|
||||
final LanguageLevel languageLevel = PythonSdkType.getLanguageLevelForSdk(sdk);
|
||||
return PySdkBundle.message("python.sdk.use.python.version.supported.by.this.package", languageLevel);
|
||||
if (executionException.getPyError() instanceof ExecError e) {
|
||||
|
||||
if (cause != null) {
|
||||
if (StringUtil.containsIgnoreCase(cause, "SyntaxError")) {
|
||||
final LanguageLevel languageLevel = PythonSdkType.getLanguageLevelForSdk(sdk);
|
||||
return PySdkBundle.message("python.sdk.use.python.version.supported.by.this.package", languageLevel);
|
||||
}
|
||||
}
|
||||
|
||||
if (e.getErrorReason() instanceof ExecErrorReason.UnexpectedProcessTermination unexpectedProcessTermination) {
|
||||
if (SystemInfo.isLinux && (containsInOutput(unexpectedProcessTermination, "pyconfig.h") || containsInOutput(
|
||||
unexpectedProcessTermination, "Python.h"))) {
|
||||
return PySdkBundle.message("python.sdk.check.python.development.packages.installed");
|
||||
}
|
||||
}
|
||||
|
||||
if ("pip".equals(e.getCommand()[0]) && sdk != null) {
|
||||
return PySdkBundle.message("python.sdk.try.to.run.command.from.system.terminal", sdk.getHomePath());
|
||||
}
|
||||
}
|
||||
|
||||
if (e.getFailureReason() instanceof FailureReason.ExecutionFailed executionFailed) {
|
||||
if (SystemInfo.isLinux && (containsInOutput(executionFailed.getOutput(), "pyconfig.h") || containsInOutput(executionFailed.getOutput(), "Python.h"))) {
|
||||
return PySdkBundle.message("python.sdk.check.python.development.packages.installed");
|
||||
}
|
||||
}
|
||||
|
||||
if ("pip".equals(e.getCommand()) && sdk != null) {
|
||||
return PySdkBundle.message("python.sdk.try.to.run.command.from.system.terminal", sdk.getHomePath());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean containsInOutput(@NotNull ProcessOutput e, @NotNull String text) {
|
||||
private static boolean containsInOutput(@NotNull ExecErrorReason.UnexpectedProcessTermination e, @NotNull String text) {
|
||||
return StringUtil.containsIgnoreCase(e.getStdout(), text) || StringUtil.containsIgnoreCase(e.getStderr(), text);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ import com.intellij.platform.util.progress.withProgressText
|
||||
import com.intellij.python.community.impl.venv.createVenv
|
||||
import com.intellij.python.community.services.systemPython.SystemPythonService
|
||||
import com.jetbrains.python.*
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.MessageError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.errorProcessing.failure
|
||||
import com.jetbrains.python.sdk.configurePythonSdk
|
||||
import com.jetbrains.python.sdk.createSdk
|
||||
@@ -44,7 +45,7 @@ suspend fun createVenvAndSdk(
|
||||
confirmInstallation: suspend () -> Boolean = { true },
|
||||
systemPythonService: SystemPythonService = SystemPythonService(),
|
||||
explicitProjectPath: VirtualFile? = null,
|
||||
): Result<Sdk, PyError> {
|
||||
): PyResult<Sdk> {
|
||||
val vfsProjectPath = withContext(Dispatchers.IO) {
|
||||
explicitProjectPath
|
||||
?: (project.modules.firstOrNull()?.let { module -> ModuleRootManager.getInstance(module).contentRoots.firstOrNull() }
|
||||
@@ -114,7 +115,7 @@ private suspend fun findExistingVenv(
|
||||
private suspend fun getSystemPython(
|
||||
confirmInstallation: suspend () -> Boolean,
|
||||
pythonService: SystemPythonService,
|
||||
): Result<PythonBinary, PyError.Message> {
|
||||
): Result<PythonBinary, MessageError> {
|
||||
|
||||
|
||||
// First, find the latest python according to strategy
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// 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.sdk
|
||||
|
||||
import com.intellij.execution.ExecutionException
|
||||
@@ -52,7 +52,12 @@ internal suspend fun runCommandLine(commandLine: GeneralCommandLine): Result<Str
|
||||
)
|
||||
}
|
||||
catch (e: ExecutionException) {
|
||||
return Result.failure(PyExecutionException(e.localizedMessage, commandLine.exePath, commandLine.parametersList.array.toList()))
|
||||
return Result.failure(PyExecutionException(
|
||||
startException = e.toIOException(),
|
||||
additionalMessage = null,
|
||||
command = commandLine.exePath,
|
||||
args = commandLine.parametersList.list
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// 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.sdk.add
|
||||
|
||||
import com.intellij.CommonBundle
|
||||
@@ -25,21 +25,11 @@ import com.intellij.util.ui.JBUI
|
||||
import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.icons.PythonIcons
|
||||
import com.jetbrains.python.packaging.PyExecutionException
|
||||
import com.jetbrains.python.sdk.PreferredSdkComparator
|
||||
import com.jetbrains.python.sdk.PythonSdkType
|
||||
import com.jetbrains.python.sdk.add.v1.CreateSdkInterrupted
|
||||
import com.jetbrains.python.sdk.add.v1.PyAddExistingCondaEnvPanel
|
||||
import com.jetbrains.python.sdk.add.v1.PyAddExistingVirtualEnvPanel
|
||||
import com.jetbrains.python.sdk.add.v1.PyAddNewCondaEnvPanel
|
||||
import com.jetbrains.python.sdk.add.v1.PyAddNewVirtualEnvPanel
|
||||
import com.jetbrains.python.sdk.add.v1.PyAddSystemWideInterpreterPanel
|
||||
import com.jetbrains.python.sdk.add.v1.doCreateSouthPanel
|
||||
import com.jetbrains.python.showProcessExecutionErrorDialog
|
||||
import com.jetbrains.python.sdk.add.v1.swipe
|
||||
import com.jetbrains.python.sdk.*
|
||||
import com.jetbrains.python.sdk.add.PyAddSdkDialog.Companion.show
|
||||
import com.jetbrains.python.sdk.add.v1.*
|
||||
import com.jetbrains.python.sdk.conda.PyCondaSdkCustomizer
|
||||
import com.jetbrains.python.sdk.detectVirtualEnvs
|
||||
import com.jetbrains.python.sdk.isAssociatedWithModule
|
||||
import com.jetbrains.python.sdk.sdkSeemsValid
|
||||
import com.jetbrains.python.showErrorDialog
|
||||
import java.awt.CardLayout
|
||||
import java.awt.event.ActionEvent
|
||||
import java.io.IOException
|
||||
@@ -169,7 +159,7 @@ class PyAddSdkDialog private constructor(
|
||||
|
||||
panel.addStateListener(object : PyAddSdkStateListener {
|
||||
override fun onComponentChanged() {
|
||||
com.jetbrains.python.sdk.add.v1.show(mainPanel, panel.component)
|
||||
show(mainPanel, panel.component)
|
||||
|
||||
selectedPanel?.let { updateWizardActionButtons(it) }
|
||||
}
|
||||
@@ -311,7 +301,7 @@ class PyAddSdkDialog private constructor(
|
||||
catch (e: Exception) {
|
||||
val cause = ExceptionUtil.findCause(e, PyExecutionException::class.java)
|
||||
if (cause != null) {
|
||||
showProcessExecutionErrorDialog(project, cause)
|
||||
showErrorDialog(project, cause.pyError)
|
||||
return
|
||||
}
|
||||
throw e
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// 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.sdk.add.v1
|
||||
|
||||
import com.intellij.CommonBundle
|
||||
@@ -33,7 +33,7 @@ import com.jetbrains.python.sdk.conda.PyCondaSdkCustomizer
|
||||
import com.jetbrains.python.sdk.pipenv.ui.PyAddPipEnvPanel
|
||||
import com.jetbrains.python.sdk.poetry.ui.createPoetryPanel
|
||||
import com.jetbrains.python.sdk.sdkSeemsValid
|
||||
import com.jetbrains.python.showProcessExecutionErrorDialog
|
||||
import com.jetbrains.python.showErrorDialog
|
||||
import com.jetbrains.python.target.PythonLanguageRuntimeConfiguration
|
||||
import java.awt.CardLayout
|
||||
import java.awt.Component
|
||||
@@ -44,12 +44,14 @@ import javax.swing.JPanel
|
||||
/**
|
||||
* The panel that is supposed to be used both for local and non-local target-based versions of "New Interpreter" dialog.
|
||||
*/
|
||||
internal class PyAddTargetBasedSdkPanel(private val project: Project?,
|
||||
private val module: Module?,
|
||||
private val existingSdks: List<Sdk>,
|
||||
private val targetSupplier: Supplier<TargetEnvironmentConfiguration>?,
|
||||
private val config: PythonLanguageRuntimeConfiguration,
|
||||
private val introspectable: LanguageRuntimeType.Introspectable?) : Disposable {
|
||||
internal class PyAddTargetBasedSdkPanel(
|
||||
private val project: Project?,
|
||||
private val module: Module?,
|
||||
private val existingSdks: List<Sdk>,
|
||||
private val targetSupplier: Supplier<TargetEnvironmentConfiguration>?,
|
||||
private val config: PythonLanguageRuntimeConfiguration,
|
||||
private val introspectable: LanguageRuntimeType.Introspectable?,
|
||||
) : Disposable {
|
||||
private val mainPanel: JPanel = JPanel(JBCardLayout())
|
||||
|
||||
private var selectedPanel: PyAddSdkView? = null
|
||||
@@ -177,7 +179,7 @@ internal class PyAddTargetBasedSdkPanel(private val project: Project?,
|
||||
Messages.showErrorDialog(e.localizedMessage, CommonBundle.message("title.error"))
|
||||
}
|
||||
else {
|
||||
showProcessExecutionErrorDialog(project, cause)
|
||||
showErrorDialog(project, cause.pyError)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import com.jetbrains.python.PyBundle.message
|
||||
import com.jetbrains.python.PythonHelpersLocator
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.ErrorSink
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.errorProcessing.emit
|
||||
import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo
|
||||
import com.jetbrains.python.sdk.*
|
||||
@@ -75,7 +75,7 @@ internal abstract class CustomNewEnvironmentCreator(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): Result<Sdk, PyError> {
|
||||
override suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): PyResult<Sdk> {
|
||||
savePathToExecutableToProperties(null)
|
||||
|
||||
// todo think about better error handling
|
||||
@@ -201,7 +201,7 @@ internal abstract class CustomNewEnvironmentCreator(
|
||||
*/
|
||||
internal abstract fun savePathToExecutableToProperties(path: Path?)
|
||||
|
||||
protected abstract suspend fun setupEnvSdk(project: Project, module: Module?, baseSdks: List<Sdk>, projectPath: String, homePath: String?, installPackages: Boolean): Result<Sdk, PyError>
|
||||
protected abstract suspend fun setupEnvSdk(project: Project, module: Module?, baseSdks: List<Sdk>, projectPath: String, homePath: String?, installPackages: Boolean): PyResult<Sdk>
|
||||
|
||||
internal abstract suspend fun detectExecutable()
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// 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.sdk.add.v2
|
||||
|
||||
import com.intellij.ide.util.PropertiesComponent
|
||||
@@ -7,14 +7,13 @@ import com.intellij.openapi.observable.properties.ObservableMutableProperty
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.util.text.nullize
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.errorProcessing.asPythonResult
|
||||
import com.jetbrains.python.sdk.pipenv.pipEnvPath
|
||||
import com.jetbrains.python.sdk.pipenv.setupPipEnvSdkUnderProgress
|
||||
import com.jetbrains.python.statistics.InterpreterType
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.pathString
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.asPythonResult
|
||||
|
||||
internal class EnvironmentCreatorPip(model: PythonMutableTargetAddInterpreterModel) : CustomNewEnvironmentCreator("pipenv", model) {
|
||||
override val interpreterType: InterpreterType = InterpreterType.PIPENV
|
||||
@@ -26,7 +25,7 @@ internal class EnvironmentCreatorPip(model: PythonMutableTargetAddInterpreterMod
|
||||
PropertiesComponent.getInstance().pipEnvPath = savingPath
|
||||
}
|
||||
|
||||
override suspend fun setupEnvSdk(project: Project, module: Module?, baseSdks: List<Sdk>, projectPath: String, homePath: String?, installPackages: Boolean): Result<Sdk, PyError> =
|
||||
override suspend fun setupEnvSdk(project: Project, module: Module?, baseSdks: List<Sdk>, projectPath: String, homePath: String?, installPackages: Boolean): PyResult<Sdk> =
|
||||
setupPipEnvSdkUnderProgress(project, module, baseSdks, projectPath, homePath, installPackages).asPythonResult()
|
||||
|
||||
override suspend fun detectExecutable() {
|
||||
|
||||
@@ -15,7 +15,7 @@ import com.intellij.ui.dsl.builder.components.validationTooltip
|
||||
import com.intellij.util.ui.showingScope
|
||||
import com.jetbrains.python.PyBundle.message
|
||||
import com.jetbrains.python.errorProcessing.ErrorSink
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.errorProcessing.failure
|
||||
import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo
|
||||
import com.jetbrains.python.newProjectWizard.collector.PythonNewProjectWizardCollector
|
||||
@@ -155,7 +155,7 @@ class EnvironmentCreatorVenv(model: PythonMutableTargetAddInterpreterModel) : Py
|
||||
return currentName.removeSuffix(digitSuffix) + newSuffix
|
||||
}
|
||||
|
||||
override suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): com.jetbrains.python.Result<Sdk, PyError> =
|
||||
override suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): PyResult<Sdk> =
|
||||
// todo remove project path, or move to controller
|
||||
try {
|
||||
val venvPath = Path.of(model.state.venvPath.get())
|
||||
|
||||
@@ -4,7 +4,7 @@ package com.jetbrains.python.sdk.add.v2
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo
|
||||
import com.jetbrains.python.sdk.ModuleOrProject
|
||||
|
||||
@@ -12,7 +12,7 @@ interface PySdkCreator {
|
||||
/**
|
||||
* Error is shown to user. Do not catch all exceptions, only return exceptions valuable to user
|
||||
*/
|
||||
suspend fun getSdk(moduleOrProject: ModuleOrProject): Result<Pair<Sdk, InterpreterStatisticsInfo>, PyError>
|
||||
suspend fun getSdk(moduleOrProject: ModuleOrProject): PyResult<Pair<Sdk, InterpreterStatisticsInfo>>
|
||||
|
||||
/**
|
||||
* Creates the Python module structure using tools (uv, poetry, hatch, etc) within the given project module.
|
||||
@@ -46,6 +46,6 @@ interface PySdkCreator {
|
||||
* ├── README.md
|
||||
* └── pyproject.toml
|
||||
*/
|
||||
suspend fun createPythonModuleStructure(module: Module): Result<Unit, PyError> = Result.success(Unit)
|
||||
suspend fun createPythonModuleStructure(module: Module): PyResult<Unit> = Result.success(Unit)
|
||||
|
||||
}
|
||||
@@ -4,11 +4,11 @@ package com.jetbrains.python.sdk.add.v2
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.util.io.toNioPathOrNull
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.ErrorSink
|
||||
import com.jetbrains.python.sdk.ModuleOrProject
|
||||
import com.jetbrains.python.venvReader.VirtualEnvReader
|
||||
import com.jetbrains.python.sdk.rootManager
|
||||
import com.jetbrains.python.sdk.service.PySdkService.Companion.pySdkService
|
||||
import com.jetbrains.python.errorProcessing.ErrorSink
|
||||
import com.jetbrains.python.venvReader.VirtualEnvReader
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
|
||||
@@ -25,7 +25,7 @@ import com.intellij.util.ui.showingScope
|
||||
import com.jetbrains.python.PyBundle.message
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.ErrorSink
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.errorProcessing.asPythonResult
|
||||
import com.jetbrains.python.getOrThrow
|
||||
import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo
|
||||
@@ -206,14 +206,14 @@ internal class PythonAddNewEnvironmentPanel(
|
||||
}.getOrThrow().first
|
||||
}
|
||||
|
||||
override suspend fun createPythonModuleStructure(module: Module): Result<Unit, PyError> {
|
||||
override suspend fun createPythonModuleStructure(module: Module): PyResult<Unit> {
|
||||
return when (selectedMode.get()) {
|
||||
CUSTOM -> custom.currentSdkManager.createPythonModuleStructure(module)
|
||||
else -> Result.success(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getSdk(moduleOrProject: ModuleOrProject): Result<Pair<Sdk, InterpreterStatisticsInfo>, PyError> {
|
||||
override suspend fun getSdk(moduleOrProject: ModuleOrProject): PyResult<Pair<Sdk, InterpreterStatisticsInfo>> {
|
||||
model.navigator.saveLastState()
|
||||
val sdk = when (selectedMode.get()) {
|
||||
PROJECT_VENV -> {
|
||||
|
||||
@@ -8,7 +8,7 @@ import com.intellij.ui.dsl.builder.Panel
|
||||
import com.jetbrains.python.PyBundle.message
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.ErrorSink
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo
|
||||
import com.jetbrains.python.sdk.ModuleOrProject
|
||||
import com.jetbrains.python.sdk.moduleIfExists
|
||||
@@ -37,7 +37,7 @@ class PythonExistingEnvironmentSelector(model: PythonAddInterpreterModel, privat
|
||||
comboBox.setItems(model.allInterpreters.map { sortForExistingEnvironment(it, moduleOrProject?.moduleIfExists) })
|
||||
}
|
||||
|
||||
override suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): Result<Sdk, PyError> {
|
||||
override suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): PyResult<Sdk> {
|
||||
// todo error handling, nullability issues
|
||||
return Result.success(setupSdkIfDetected(model.state.selectedInterpreter.get()!!, model.existingSdks)!!)
|
||||
}
|
||||
|
||||
@@ -18,23 +18,23 @@ import com.intellij.openapi.ui.validation.DialogValidationRequestor
|
||||
import com.intellij.openapi.ui.validation.WHEN_PROPERTY_CHANGED
|
||||
import com.intellij.openapi.ui.validation.and
|
||||
import com.intellij.openapi.wm.IdeFocusManager
|
||||
import com.intellij.python.hatch.icons.PythonHatchIcons
|
||||
import com.intellij.ui.dsl.builder.Align
|
||||
import com.intellij.ui.dsl.builder.Panel
|
||||
import com.intellij.ui.dsl.builder.Row
|
||||
import com.jetbrains.python.PyBundle.message
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.ErrorSink
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.icons.PythonIcons
|
||||
import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo
|
||||
import com.jetbrains.python.psi.icons.PythonPsiApiIcons
|
||||
import com.jetbrains.python.sdk.*
|
||||
import com.jetbrains.python.sdk.pipenv.PIPENV_ICON
|
||||
import com.jetbrains.python.sdk.poetry.POETRY_ICON
|
||||
import com.jetbrains.python.sdk.uv.UV_ICON
|
||||
import com.jetbrains.python.statistics.InterpreterTarget
|
||||
import com.jetbrains.python.errorProcessing.ErrorSink
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import javax.swing.Icon
|
||||
import com.intellij.python.hatch.icons.PythonHatchIcons
|
||||
import com.intellij.ui.dsl.builder.Align
|
||||
import com.intellij.ui.dsl.builder.Row
|
||||
import com.jetbrains.python.psi.icons.PythonPsiApiIcons
|
||||
|
||||
abstract class PythonAddEnvironment(open val model: PythonAddInterpreterModel) {
|
||||
|
||||
@@ -52,9 +52,9 @@ abstract class PythonAddEnvironment(open val model: PythonAddInterpreterModel) {
|
||||
*
|
||||
* Error is shown to user. Do not catch all exceptions, only return exceptions valuable to user
|
||||
*/
|
||||
abstract suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): Result<Sdk, PyError>
|
||||
abstract suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): PyResult<Sdk>
|
||||
|
||||
open suspend fun createPythonModuleStructure(module: Module): Result<Unit, PyError> = Result.success(Unit)
|
||||
open suspend fun createPythonModuleStructure(module: Module): PyResult<Unit> = Result.success(Unit)
|
||||
|
||||
abstract fun createStatisticsInfo(target: PythonInterpreterCreationTargets): InterpreterStatisticsInfo
|
||||
}
|
||||
|
||||
+5
-16
@@ -15,27 +15,16 @@ import com.intellij.ui.dsl.builder.Panel
|
||||
import com.intellij.ui.dsl.builder.bindItem
|
||||
import com.intellij.ui.layout.predicate
|
||||
import com.jetbrains.python.PyBundle.message
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.ErrorSink
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.errorProcessing.asPythonResult
|
||||
import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo
|
||||
import com.jetbrains.python.sdk.ModuleOrProject
|
||||
import com.jetbrains.python.sdk.add.v2.*
|
||||
import com.jetbrains.python.sdk.flavors.conda.PyCondaEnv
|
||||
import com.jetbrains.python.sdk.flavors.conda.PyCondaEnvIdentity
|
||||
import com.jetbrains.python.statistics.InterpreterCreationMode
|
||||
import com.jetbrains.python.statistics.InterpreterType
|
||||
import com.jetbrains.python.errorProcessing.ErrorSink
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.asPythonResult
|
||||
import com.jetbrains.python.sdk.add.v2.CondaEnvComboBoxListCellRenderer
|
||||
import com.jetbrains.python.sdk.add.v2.PythonAddInterpreterModel
|
||||
import com.jetbrains.python.sdk.add.v2.PythonExistingEnvironmentConfigurator
|
||||
import com.jetbrains.python.sdk.add.v2.PythonInterpreterCreationTargets
|
||||
import com.jetbrains.python.sdk.add.v2.UNKNOWN_EXECUTABLE
|
||||
import com.jetbrains.python.sdk.add.v2.createInstallCondaFix
|
||||
import com.jetbrains.python.sdk.add.v2.detectCondaEnvironmentsOrError
|
||||
import com.jetbrains.python.sdk.add.v2.displayLoaderWhen
|
||||
import com.jetbrains.python.sdk.add.v2.executableSelector
|
||||
import com.jetbrains.python.sdk.add.v2.selectCondaEnvironment
|
||||
import com.jetbrains.python.sdk.add.v2.toStatisticsField
|
||||
import kotlinx.coroutines.CoroutineStart
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
@@ -120,7 +109,7 @@ internal class CondaExistingEnvironmentSelector(model: PythonAddInterpreterModel
|
||||
//}
|
||||
}
|
||||
|
||||
override suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): Result<Sdk, PyError> =
|
||||
override suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): PyResult<Sdk> =
|
||||
model.selectCondaEnvironment(base = false).asPythonResult()
|
||||
|
||||
override fun createStatisticsInfo(target: PythonInterpreterCreationTargets): InterpreterStatisticsInfo {
|
||||
|
||||
@@ -10,25 +10,18 @@ import com.intellij.ui.dsl.builder.bindItem
|
||||
import com.intellij.ui.dsl.builder.bindText
|
||||
import com.intellij.ui.dsl.listCellRenderer.textListCellRenderer
|
||||
import com.jetbrains.python.PyBundle.message
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.ErrorSink
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.errorProcessing.asPythonResult
|
||||
import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo
|
||||
import com.jetbrains.python.psi.LanguageLevel
|
||||
import com.jetbrains.python.sdk.ModuleOrProject
|
||||
import com.jetbrains.python.sdk.add.v2.*
|
||||
import com.jetbrains.python.sdk.conda.condaSupportedLanguages
|
||||
import com.jetbrains.python.sdk.flavors.conda.NewCondaEnvRequest
|
||||
import com.jetbrains.python.statistics.InterpreterCreationMode
|
||||
import com.jetbrains.python.statistics.InterpreterType
|
||||
import com.jetbrains.python.ui.flow.bindText
|
||||
import com.jetbrains.python.errorProcessing.ErrorSink
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.asPythonResult
|
||||
import com.jetbrains.python.sdk.add.v2.PythonInterpreterCreationTargets
|
||||
import com.jetbrains.python.sdk.add.v2.PythonMutableTargetAddInterpreterModel
|
||||
import com.jetbrains.python.sdk.add.v2.PythonNewEnvironmentCreator
|
||||
import com.jetbrains.python.sdk.add.v2.createInstallCondaFix
|
||||
import com.jetbrains.python.sdk.add.v2.displayLoaderWhen
|
||||
import com.jetbrains.python.sdk.add.v2.executableSelector
|
||||
import com.jetbrains.python.sdk.add.v2.toStatisticsField
|
||||
|
||||
internal class CondaNewEnvironmentCreator(model: PythonMutableTargetAddInterpreterModel) : PythonNewEnvironmentCreator(model) {
|
||||
|
||||
@@ -61,7 +54,7 @@ internal class CondaNewEnvironmentCreator(model: PythonMutableTargetAddInterpret
|
||||
|
||||
override fun onShown() = Unit
|
||||
|
||||
override suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): Result<Sdk, PyError> {
|
||||
override suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): PyResult<Sdk> {
|
||||
return model.createCondaEnvironment(NewCondaEnvRequest.EmptyNamedEnv(pythonVersion.get(), model.state.newCondaEnvName.get())).asPythonResult()
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ import com.intellij.python.hatch.resolveHatchWorkingDirectory
|
||||
import com.intellij.ui.dsl.builder.Panel
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.ErrorSink
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.hatch.sdk.createSdk
|
||||
import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo
|
||||
import com.jetbrains.python.onSuccess
|
||||
@@ -52,7 +52,7 @@ internal class HatchExistingEnvironmentSelector(
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): Result<Sdk, PyError> {
|
||||
override suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): PyResult<Sdk> {
|
||||
val environment = state.selectedHatchEnv.get()
|
||||
val existingHatchVenv = environment?.pythonVirtualEnvironment as? PythonVirtualEnvironment.Existing
|
||||
?: return Result.failure(HatchUIError.HatchEnvironmentIsNotSelected())
|
||||
|
||||
@@ -16,7 +16,7 @@ import com.intellij.ui.dsl.builder.Panel
|
||||
import com.intellij.util.text.nullize
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.ErrorSink
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.hatch.sdk.createSdk
|
||||
import com.jetbrains.python.onSuccess
|
||||
import com.jetbrains.python.sdk.add.v2.CustomNewEnvironmentCreator
|
||||
@@ -58,7 +58,7 @@ internal class HatchNewEnvironmentCreator(
|
||||
HatchConfiguration.persistPathForTarget(hatchExecutablePath = savingPath)
|
||||
}
|
||||
|
||||
override suspend fun createPythonModuleStructure(module: Module): Result<Unit, PyError> {
|
||||
override suspend fun createPythonModuleStructure(module: Module): PyResult<Unit> {
|
||||
val hatchExecutablePath = executable.get().toPath().getOr { return it }
|
||||
val hatchService = module.getHatchService(hatchExecutablePath = hatchExecutablePath).getOr { return it }
|
||||
|
||||
@@ -76,7 +76,7 @@ internal class HatchNewEnvironmentCreator(
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun setupEnvSdk(project: Project, module: Module?, baseSdks: List<Sdk>, projectPath: String, homePath: String?, installPackages: Boolean): Result<Sdk, PyError> {
|
||||
override suspend fun setupEnvSdk(project: Project, module: Module?, baseSdks: List<Sdk>, projectPath: String, homePath: String?, installPackages: Boolean): PyResult<Sdk> {
|
||||
val hatchEnv = hatchEnvironmentProperty.get()?.hatchEnvironment
|
||||
?: return Result.failure(HatchUIError.HatchEnvironmentIsNotSelected())
|
||||
|
||||
|
||||
@@ -23,7 +23,10 @@ import com.intellij.ui.dsl.builder.components.validationTooltip
|
||||
import com.intellij.ui.layout.ComponentPredicate
|
||||
import com.jetbrains.python.PyBundle.message
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.ExecError
|
||||
import com.jetbrains.python.errorProcessing.MessageError
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.icons.PythonIcons
|
||||
import com.jetbrains.python.isFailure
|
||||
import com.jetbrains.python.newProjectWizard.collector.PythonNewProjectWizardCollector
|
||||
@@ -35,7 +38,7 @@ import org.jetbrains.annotations.Nls
|
||||
import java.nio.file.Path
|
||||
import javax.swing.JList
|
||||
|
||||
internal sealed class HatchUIError(message: String) : PyError.Message(message) {
|
||||
internal sealed class HatchUIError(message: String) : MessageError(message) {
|
||||
class ProjectIsNotSelected : HatchUIError(
|
||||
message("sdk.create.custom.hatch.error.project.is.not.selected")
|
||||
)
|
||||
@@ -53,14 +56,14 @@ internal sealed class HatchUIError(message: String) : PyError.Message(message) {
|
||||
hatchExecutablePath)
|
||||
)
|
||||
|
||||
class HatchExecutionFailure(execException: ExecException) : HatchUIError(
|
||||
class HatchExecutionFailure(execError: ExecError) : HatchUIError(
|
||||
message("sdk.create.custom.hatch.error.execution.failed",
|
||||
execException.execFailure.command, execException.execFailure.args.joinToString(" ")
|
||||
execError.command.joinToString(" ")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
internal fun String.toPath(): Result<Path, PyError> {
|
||||
internal fun String.toPath(): PyResult<Path> {
|
||||
return when (val selectedPath = Path.of(this)) {
|
||||
null -> Result.failure(HatchUIError.HatchExecutablePathIsNotValid(this))
|
||||
else -> Result.success(selectedPath)
|
||||
@@ -148,8 +151,8 @@ private fun Panel.addExecutableSelector(
|
||||
propertyGraph.dependsOn(hatchErrorMessage, hatchErrorProperty, deleteWhenChildModified = false) {
|
||||
when (val error = hatchErrorProperty.get()) {
|
||||
null -> ""
|
||||
is PyError.Message -> error.message
|
||||
is PyError.ExecException -> HatchUIError.HatchExecutionFailure(error).message
|
||||
is MessageError -> error.message
|
||||
is ExecError -> HatchUIError.HatchExecutionFailure(error).message
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +227,7 @@ internal fun Panel.buildHatchFormFields(
|
||||
|
||||
@Synchronized
|
||||
private fun ComboBox<HatchVirtualEnvironment>.syncWithEnvs(
|
||||
environmentsResult: Result<List<HatchVirtualEnvironment>, PyError>,
|
||||
environmentsResult: PyResult<List<HatchVirtualEnvironment>>,
|
||||
isFilterOnlyExisting: Boolean = false,
|
||||
) {
|
||||
removeAllItems()
|
||||
|
||||
@@ -24,9 +24,8 @@ import com.intellij.python.hatch.getHatchService
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||
import com.jetbrains.python.PyBundle.message
|
||||
import com.jetbrains.python.errorProcessing.ErrorSink
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.errorProcessing.emit
|
||||
import com.jetbrains.python.failure
|
||||
import com.jetbrains.python.getOrNull
|
||||
import com.jetbrains.python.isFailure
|
||||
import com.jetbrains.python.newProjectWizard.projectPath.ProjectPathFlows
|
||||
@@ -75,7 +74,7 @@ abstract class PythonAddInterpreterModel(
|
||||
val manuallyAddedInterpreters: MutableStateFlow<List<PythonSelectableInterpreter>> = MutableStateFlow(emptyList())
|
||||
private var installable: List<PythonSelectableInterpreter> = emptyList()
|
||||
val condaEnvironments: MutableStateFlow<List<PyCondaEnv>> = MutableStateFlow(emptyList())
|
||||
val hatchEnvironmentsResult: MutableStateFlow<com.jetbrains.python.Result<List<HatchVirtualEnvironment>, PyError>?> = MutableStateFlow(
|
||||
val hatchEnvironmentsResult: MutableStateFlow<PyResult<List<HatchVirtualEnvironment>>?> = MutableStateFlow(
|
||||
null)
|
||||
|
||||
var allInterpreters: StateFlow<List<PythonSelectableInterpreter>> = combine(knownInterpreters, detectedInterpreters,
|
||||
@@ -145,7 +144,7 @@ abstract class PythonAddInterpreterModel(
|
||||
|
||||
suspend fun detectHatchEnvironments(
|
||||
hatchExecutablePathString: String,
|
||||
): com.jetbrains.python.Result<List<HatchVirtualEnvironment>, PyError> {
|
||||
): PyResult<List<HatchVirtualEnvironment>> {
|
||||
val environmentsResult = withContext(Dispatchers.IO) {
|
||||
val projectPath = myProjectPathFlows.projectPathWithDefault.first()
|
||||
val hatchExecutablePath = NioFiles.toPath(hatchExecutablePathString)
|
||||
@@ -481,7 +480,7 @@ internal suspend fun PythonAddInterpreterModel.detectCondaEnvironmentsOrError(er
|
||||
internal suspend fun PythonAddInterpreterModel.getBaseCondaOrError(): Result<PyCondaEnv> {
|
||||
var baseConda = state.baseCondaEnv.get()
|
||||
if (baseConda != null) return Result.success(baseConda)
|
||||
detectCondaEnvironments()?.let { return failure(it) }
|
||||
detectCondaEnvironments()?.let { return com.jetbrains.python.failure(it) }
|
||||
baseConda = state.baseCondaEnv.get()
|
||||
return if (baseConda != null) Result.success(baseConda) else failure(message("python.sdk.conda.no.base.env.error"))
|
||||
return if (baseConda != null) Result.success(baseConda) else com.jetbrains.python.failure(message("python.sdk.conda.no.base.env.error"))
|
||||
}
|
||||
@@ -14,33 +14,32 @@ import com.intellij.ui.dsl.builder.bindSelected
|
||||
import com.intellij.util.text.nullize
|
||||
import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.errorProcessing.ErrorSink
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.errorProcessing.asPythonResult
|
||||
import com.jetbrains.python.newProjectWizard.collector.PythonNewProjectWizardCollector
|
||||
import com.jetbrains.python.sdk.ModuleOrProject
|
||||
import com.jetbrains.python.sdk.add.v2.CustomNewEnvironmentCreator
|
||||
import com.jetbrains.python.sdk.add.v2.PythonInterpreterSelectionMethod.SELECT_EXISTING
|
||||
import com.jetbrains.python.sdk.add.v2.PythonMutableTargetAddInterpreterModel
|
||||
import com.jetbrains.python.sdk.add.v2.PythonSelectableInterpreter
|
||||
import com.jetbrains.python.sdk.add.v2.PythonSupportedEnvironmentManagers.POETRY
|
||||
import com.jetbrains.python.sdk.add.v2.PythonSupportedEnvironmentManagers.PYTHON
|
||||
import com.jetbrains.python.sdk.add.v2.VenvExistenceValidationState.Error
|
||||
import com.jetbrains.python.sdk.add.v2.VenvExistenceValidationState.Invisible
|
||||
import com.jetbrains.python.sdk.baseDir
|
||||
import com.jetbrains.python.sdk.basePath
|
||||
import com.jetbrains.python.sdk.poetry.*
|
||||
import com.jetbrains.python.statistics.InterpreterType
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.pathString
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.asPythonResult
|
||||
import com.jetbrains.python.newProjectWizard.collector.PythonNewProjectWizardCollector
|
||||
import com.jetbrains.python.sdk.add.v2.CustomNewEnvironmentCreator
|
||||
import com.jetbrains.python.sdk.add.v2.PythonInterpreterSelectionMethod
|
||||
import com.jetbrains.python.sdk.add.v2.PythonInterpreterSelectionMethod.*
|
||||
import com.jetbrains.python.sdk.add.v2.PythonMutableTargetAddInterpreterModel
|
||||
import com.jetbrains.python.sdk.add.v2.PythonSelectableInterpreter
|
||||
import com.jetbrains.python.sdk.add.v2.PythonSupportedEnvironmentManagers
|
||||
import com.jetbrains.python.sdk.add.v2.PythonSupportedEnvironmentManagers.*
|
||||
import com.jetbrains.python.sdk.add.v2.VenvExistenceValidationState.*
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.pathString
|
||||
|
||||
internal class EnvironmentCreatorPoetry(model: PythonMutableTargetAddInterpreterModel, private val moduleOrProject: ModuleOrProject?) : CustomNewEnvironmentCreator("poetry", model) {
|
||||
override val interpreterType: InterpreterType = InterpreterType.POETRY
|
||||
@@ -108,7 +107,7 @@ internal class EnvironmentCreatorPoetry(model: PythonMutableTargetAddInterpreter
|
||||
PropertiesComponent.getInstance().poetryPath = savingPath
|
||||
}
|
||||
|
||||
override suspend fun setupEnvSdk(project: Project, module: Module?, baseSdks: List<Sdk>, projectPath: String, homePath: String?, installPackages: Boolean): Result<Sdk, PyError> {
|
||||
override suspend fun setupEnvSdk(project: Project, module: Module?, baseSdks: List<Sdk>, projectPath: String, homePath: String?, installPackages: Boolean): PyResult<Sdk> {
|
||||
module?.let { service<PoetryConfigService>().setInProjectEnv(it) }
|
||||
return setupPoetrySdkUnderProgress(project, module, baseSdks, projectPath, homePath, installPackages).asPythonResult()
|
||||
}
|
||||
@@ -141,7 +140,7 @@ internal class EnvironmentCreatorPoetry(model: PythonMutableTargetAddInterpreter
|
||||
@Service(Service.Level.APP)
|
||||
@State(name = "PyPoetrySettings", storages = [Storage("pyPoetrySettings.xml")])
|
||||
private class PoetryConfigService : SerializablePersistentStateComponent<PoetryConfigService.PyPoetrySettingsState>(PyPoetrySettingsState()) {
|
||||
class PyPoetrySettingsState: BaseState() {
|
||||
class PyPoetrySettingsState : BaseState() {
|
||||
var isInProjectEnv = false
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ import com.intellij.openapi.projectRoots.ProjectJdkTable
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.vfs.toNioPathOrNull
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.errorProcessing.asPythonResult
|
||||
import com.jetbrains.python.sdk.ModuleOrProject
|
||||
import com.jetbrains.python.sdk.add.v2.CustomExistingEnvironmentSelector
|
||||
@@ -26,7 +26,7 @@ internal class PoetryExistingEnvironmentSelector(model: PythonMutableTargetAddIn
|
||||
override val executable: ObservableMutableProperty<String> = model.state.poetryExecutable
|
||||
override val interpreterType: InterpreterType = InterpreterType.POETRY
|
||||
|
||||
override suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): Result<Sdk, PyError> {
|
||||
override suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): PyResult<Sdk> {
|
||||
val selectedInterpreter = selectedEnv.get()
|
||||
ProjectJdkTable.getInstance().allJdks.find { sdk -> sdk.isPoetry && sdk.homePath == selectedInterpreter?.homePath }?.let { return Result.success(it) }
|
||||
val module = when (moduleOrProject) {
|
||||
|
||||
@@ -12,8 +12,7 @@ import com.intellij.platform.ide.progress.TaskCancellation
|
||||
import com.intellij.platform.ide.progress.withModalProgress
|
||||
import com.intellij.python.community.impl.venv.createVenv
|
||||
import com.jetbrains.python.PyBundle.message
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.failure
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.sdk.*
|
||||
import com.jetbrains.python.sdk.conda.createCondaSdkFromExistingEnv
|
||||
import com.jetbrains.python.sdk.conda.isConda
|
||||
@@ -24,7 +23,7 @@ import java.nio.file.Path
|
||||
|
||||
|
||||
// todo should it be overriden for targets?
|
||||
suspend fun PythonMutableTargetAddInterpreterModel.setupVirtualenv(venvPath: Path, projectPath: Path, moduleOrProject: ModuleOrProject?): com.jetbrains.python.Result<Sdk, PyError> {
|
||||
suspend fun PythonMutableTargetAddInterpreterModel.setupVirtualenv(venvPath: Path, projectPath: Path, moduleOrProject: ModuleOrProject?): PyResult<Sdk> {
|
||||
val baseSdk = state.baseInterpreter.get()!!
|
||||
|
||||
|
||||
@@ -84,7 +83,8 @@ suspend fun PythonAddInterpreterModel.selectCondaEnvironment(base: Boolean): Res
|
||||
getBaseCondaOrError()
|
||||
}
|
||||
else {
|
||||
state.selectedCondaEnv.get()?.let { Result.success(it) } ?: failure(message("python.sdk.conda.no.env.selected.error"))
|
||||
state.selectedCondaEnv.get()?.let { Result.success(it) }
|
||||
?: com.jetbrains.python.failure(message("python.sdk.conda.no.env.selected.error"))
|
||||
}
|
||||
.getOrElse { return Result.failure(it) }
|
||||
.envIdentity
|
||||
|
||||
@@ -6,24 +6,24 @@ import com.intellij.openapi.observable.properties.ObservableMutableProperty
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.util.text.nullize
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.errorProcessing.asPythonResult
|
||||
import com.jetbrains.python.newProjectWizard.collector.PythonNewProjectWizardCollector
|
||||
import com.jetbrains.python.sdk.ModuleOrProject
|
||||
import com.jetbrains.python.sdk.add.v2.CustomNewEnvironmentCreator
|
||||
import com.jetbrains.python.sdk.add.v2.PythonInterpreterSelectionMethod.SELECT_EXISTING
|
||||
import com.jetbrains.python.sdk.add.v2.PythonMutableTargetAddInterpreterModel
|
||||
import com.jetbrains.python.sdk.add.v2.PythonSupportedEnvironmentManagers.PYTHON
|
||||
import com.jetbrains.python.sdk.add.v2.PythonSupportedEnvironmentManagers.UV
|
||||
import com.jetbrains.python.sdk.add.v2.VenvExistenceValidationState
|
||||
import com.jetbrains.python.sdk.basePath
|
||||
import com.jetbrains.python.sdk.uv.impl.setUvExecutable
|
||||
import com.jetbrains.python.sdk.uv.setupNewUvSdkAndEnvUnderProgress
|
||||
import com.jetbrains.python.statistics.InterpreterType
|
||||
import com.jetbrains.python.venvReader.tryResolvePath
|
||||
import java.nio.file.Path
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.asPythonResult
|
||||
import com.jetbrains.python.newProjectWizard.collector.PythonNewProjectWizardCollector
|
||||
import com.jetbrains.python.sdk.ModuleOrProject
|
||||
import com.jetbrains.python.sdk.add.v2.CustomNewEnvironmentCreator
|
||||
import com.jetbrains.python.sdk.add.v2.PythonInterpreterSelectionMethod.*
|
||||
import com.jetbrains.python.sdk.add.v2.VenvExistenceValidationState
|
||||
import com.jetbrains.python.sdk.add.v2.PythonMutableTargetAddInterpreterModel
|
||||
import com.jetbrains.python.sdk.add.v2.PythonSupportedEnvironmentManagers.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import kotlin.io.path.exists
|
||||
|
||||
@@ -70,10 +70,10 @@ internal class EnvironmentCreatorUv(
|
||||
setUvExecutable(savingPath)
|
||||
}
|
||||
|
||||
override suspend fun setupEnvSdk(project: Project, module: Module?, baseSdks: List<Sdk>, projectPath: String, homePath: String?, installPackages: Boolean): Result<Sdk, PyError> {
|
||||
override suspend fun setupEnvSdk(project: Project, module: Module?, baseSdks: List<Sdk>, projectPath: String, homePath: String?, installPackages: Boolean): PyResult<Sdk> {
|
||||
val workingDir = module?.basePath?.let { tryResolvePath(it) } ?: project.basePath?.let { tryResolvePath(it) }
|
||||
if (workingDir == null) {
|
||||
return kotlin.Result.failure<Sdk>(Exception("working dir is not specified for uv environment setup")).asPythonResult()
|
||||
return Result.failure<Sdk>(Exception("working dir is not specified for uv environment setup")).asPythonResult()
|
||||
}
|
||||
|
||||
val python = homePath?.let { Path.of(it) }
|
||||
|
||||
@@ -8,7 +8,7 @@ import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.vfs.toNioPathOrNull
|
||||
import com.intellij.python.pyproject.PyProjectToml
|
||||
import com.jetbrains.python.Result
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.errorProcessing.PyResult
|
||||
import com.jetbrains.python.errorProcessing.asPythonResult
|
||||
import com.jetbrains.python.errorProcessing.failure
|
||||
import com.jetbrains.python.sdk.ModuleOrProject
|
||||
@@ -31,7 +31,7 @@ internal class UvExistingEnvironmentSelector(model: PythonMutableTargetAddInterp
|
||||
override val executable: ObservableMutableProperty<String> = model.state.uvExecutable
|
||||
override val interpreterType: InterpreterType = InterpreterType.UV
|
||||
|
||||
override suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): Result<Sdk, PyError> {
|
||||
override suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): PyResult<Sdk> {
|
||||
val selectedInterpreterPath = tryResolvePath(selectedEnv.get()?.homePath) ?: return failure("No selected interpreter")
|
||||
val allSdk = ProjectJdkTable.getInstance().allJdks
|
||||
val existingSdk = allSdk.find { it.homePath == selectedInterpreterPath.pathString }
|
||||
|
||||
@@ -18,7 +18,6 @@ import com.intellij.openapi.projectRoots.ProjectJdkTable
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil
|
||||
import com.intellij.platform.util.progress.RawProgressReporter
|
||||
import com.jetbrains.python.failure
|
||||
import com.jetbrains.python.psi.LanguageLevel
|
||||
import com.jetbrains.python.sdk.PythonSdkAdditionalData
|
||||
import com.jetbrains.python.sdk.PythonSdkType
|
||||
@@ -129,7 +128,7 @@ suspend fun PyCondaCommand.createCondaSdkAlongWithNewEnv(
|
||||
val process = PyCondaEnv.createEnv(this, newCondaEnvInfo).getOrElse { return Result.failure(it) }
|
||||
val error = ProcessHandlerReader(process).runProcessAndGetError(uiContext, reporter)
|
||||
|
||||
return error?.let { failure(it) }
|
||||
return error?.let { com.jetbrains.python.failure(it) }
|
||||
?: Result.success(
|
||||
createCondaSdkFromExistingEnv(newCondaEnvInfo.toIdentity(), existingSdks, project)).apply {
|
||||
onSuccess {
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// 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.sdk.flavors.conda
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
|
||||
import com.google.gson.Gson
|
||||
import com.intellij.execution.target.*
|
||||
import com.intellij.execution.target.FullPathOnTarget
|
||||
import com.intellij.execution.target.TargetEnvironmentConfiguration
|
||||
import com.intellij.execution.target.TargetedCommandLineBuilder
|
||||
import com.intellij.execution.target.createProcessWithResult
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.jetbrains.python.failure
|
||||
import com.jetbrains.python.psi.LanguageLevel
|
||||
import com.jetbrains.python.sdk.conda.TargetCommandExecutor
|
||||
import com.jetbrains.python.sdk.conda.createCondaSdkFromExistingEnv
|
||||
@@ -39,7 +41,7 @@ data class PyCondaEnv(
|
||||
*/
|
||||
private suspend fun getEnvsInfo(command: TargetCommandExecutor, fullCondaPathOnTarget: FullPathOnTarget): Result<String> {
|
||||
val output = command.execute(listOf(fullCondaPathOnTarget, "info", "--envs", "--json")).await()
|
||||
return if (output.exitCode == 0) Result.success(output.stdout) else failure(output.stderr)
|
||||
return if (output.exitCode == 0) Result.success(output.stdout) else com.jetbrains.python.failure(output.stderr)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,6 +9,8 @@ import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.errorProcessing.ErrorSink
|
||||
import com.jetbrains.python.errorProcessing.ExecError
|
||||
import com.jetbrains.python.errorProcessing.MessageError
|
||||
import com.jetbrains.python.errorProcessing.PyError
|
||||
import com.jetbrains.python.showProcessExecutionErrorDialog
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -26,10 +28,10 @@ object ShowingMessageErrorSync : ErrorSink {
|
||||
// Platform doesn't allow dialogs without a lock for now, fix later
|
||||
writeIntentReadAction {
|
||||
when (val e = error) {
|
||||
is PyError.ExecException -> {
|
||||
showProcessExecutionErrorDialog(null, e.execFailure)
|
||||
is ExecError -> {
|
||||
showProcessExecutionErrorDialog(null, e)
|
||||
}
|
||||
is PyError.Message -> {
|
||||
is MessageError -> {
|
||||
Messages.showErrorDialog(error.message, PyBundle.message("python.error"))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user