Python: Replace localized string class with string with annotation.

No need to have a separate class

Signed-off-by: Ilya.Kazakevich <ilya.kazakevich@jetbrains.com>

GitOrigin-RevId: 9af33f30bbab9a85609c0c6536cb3120347fa20b
This commit is contained in:
Ilya.Kazakevich
2025-01-23 21:43:20 +01:00
committed by intellij-monorepo-bot
parent 1cc82efbbe
commit d1b441d0bb
8 changed files with 37 additions and 27 deletions

View File

@@ -44,7 +44,7 @@ internal class PyMiscFileAction(private val miscFileType: MiscFileType) : AnActi
scopeProvider = { it.service<MyService>().scope })) {
is Result.Success -> Unit
is Result.Failure -> {
Messages.showErrorDialog(null as Project?, r.error.text, PyCharmCommunityCustomizationBundle.message("misc.project.error.title"))
Messages.showErrorDialog(null as Project?, r.error, PyCharmCommunityCustomizationBundle.message("misc.project.error.title"))
}
}
}

View File

@@ -40,6 +40,7 @@ import com.jetbrains.python.sdk.add.v2.createSdk
import com.jetbrains.python.sdk.add.v2.createVirtualenv
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor
import kotlinx.coroutines.*
import org.jetbrains.annotations.Nls
import java.io.IOException
import java.nio.file.FileAlreadyExistsException
import java.nio.file.Path
@@ -66,7 +67,7 @@ fun createMiscProject(
confirmInstallation: suspend () -> Boolean,
projectPath: Path = miscProjectDefaultPath.value,
systemPythonService: SystemPythonService = SystemPythonService(),
): Result<Job, LocalizedErrorString> =
): Result<Job, @Nls String> =
runWithModalProgressBlocking(ModalTaskOwner.guess(),
PyCharmCommunityCustomizationBundle.message("misc.project.generating.env"),
TaskCancellation.cancellable()) {
@@ -142,7 +143,7 @@ private suspend fun createProjectAndSdk(
projectPath: Path,
confirmInstallation: suspend () -> Boolean,
systemPythonService: SystemPythonService,
): Result<Pair<Project, Sdk>, LocalizedErrorString> {
): Result<Pair<Project, Sdk>, @Nls String> {
val projectPathVfs = createProjectDir(projectPath).getOr { return it }
val venvDirPath = projectPath.resolve(VirtualEnvReader.DEFAULT_VIRTUALENV_DIRNAME)
@@ -159,7 +160,7 @@ private suspend fun createProjectAndSdk(
venvPython = findExistingVenv(venvDirPath)
if (venvPython == null) {
// No venv even after venv installation
return Result.failure(LocalizedErrorString(PyCharmCommunityCustomizationBundle.message("misc.project.error.create.venv", "", venvDirPath)))
return Result.failure(PyCharmCommunityCustomizationBundle.message("misc.project.error.create.venv", "", venvDirPath))
}
}
@@ -194,16 +195,16 @@ private suspend fun findExistingVenv(
}
}
private suspend fun createVenv(systemPython: PythonBinary, venvDirPath: Path, projectPath: Path): Result<Unit, LocalizedErrorString> =
private suspend fun createVenv(systemPython: PythonBinary, venvDirPath: Path, projectPath: Path): Result<Unit, @Nls String> =
try {
createVirtualenv(systemPython, venvDirPath, projectPath)
Result.success(Unit)
}
catch (e: ExecutionException) {
Result.failure(LocalizedErrorString(PyCharmCommunityCustomizationBundle.message("misc.project.error.create.venv", e.toString(), venvDirPath)))
Result.failure(PyCharmCommunityCustomizationBundle.message("misc.project.error.create.venv", e.toString(), venvDirPath))
}
private suspend fun getSystemPython(confirmInstallation: suspend () -> Boolean, pythonService: SystemPythonService): Result<PythonBinary, LocalizedErrorString> {
private suspend fun getSystemPython(confirmInstallation: suspend () -> Boolean, pythonService: SystemPythonService): Result<PythonBinary, @Nls String> {
// First, find the latest python according to strategy
@@ -213,15 +214,15 @@ private suspend fun getSystemPython(confirmInstallation: suspend () -> Boolean,
if (systemPythonBinary == null) {
// Install it
val installer = pythonService.getInstaller()
?: return Result.failure(LocalizedErrorString(PyCharmCommunityCustomizationBundle.message("misc.project.error.install.not.supported")))
?: return Result.failure(PyCharmCommunityCustomizationBundle.message("misc.project.error.install.not.supported"))
if (confirmInstallation()) {
// Install
when (val r = installer.installLatestPython()) {
is Result.Failure -> {
val error = r.error
logger.warn("Python installation failed $error")
return Result.Failure(LocalizedErrorString(
PyCharmCommunityCustomizationBundle.message("misc.project.error.install.python", error)))
return Result.Failure(
PyCharmCommunityCustomizationBundle.message("misc.project.error.install.python", error))
}
is Result.Success -> {
// Find the latest python again, after installation
@@ -232,7 +233,7 @@ private suspend fun getSystemPython(confirmInstallation: suspend () -> Boolean,
}
return if (systemPythonBinary == null) {
Result.Failure(LocalizedErrorString(PyCharmCommunityCustomizationBundle.message("misc.project.error.all.pythons.bad")))
Result.Failure(PyCharmCommunityCustomizationBundle.message("misc.project.error.all.pythons.bad"))
}
else {
Result.Success(systemPythonBinary.pythonBinary)
@@ -267,14 +268,14 @@ private suspend fun getSdk(pythonPath: PythonBinary, project: Project): Sdk =
/**
* 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, LocalizedErrorString> = withContext(Dispatchers.IO) {
private suspend fun createProjectDir(projectPath: Path): Result<VirtualFile, @Nls String> = withContext(Dispatchers.IO) {
try {
projectPath.createDirectories()
}
catch (e: IOException) {
thisLogger().warn("Couldn't create $projectPath", e)
return@withContext Result.Failure(LocalizedErrorString(
PyCharmCommunityCustomizationBundle.message("misc.project.error.create.dir", projectPath, e.localizedMessage)))
return@withContext Result.Failure(
PyCharmCommunityCustomizationBundle.message("misc.project.error.create.dir", projectPath, e.localizedMessage))
}
val projectPathVfs = VfsUtil.findFile(projectPath, true)
?: error("Can't find VFS $projectPath")

View File

@@ -8,7 +8,7 @@ import java.lang.Exception
/**
* Operation result to be used as `Maybe` instead of checked exceptions.
* Unlike Kotlin `Result`, [ERR] could be anything (See [LocalizedErrorString]).
* Unlike Kotlin `Result`, [ERR] could be anything (i.e [String]).
*
* Typical usages:
*

View File

@@ -4,6 +4,7 @@ package com.intellij.python.junit5Tests.unit
import com.jetbrains.python.LocalizedErrorString
import com.jetbrains.python.Result
import com.jetbrains.python.mapResult
import org.jetbrains.annotations.Nls
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.fail
@@ -25,7 +26,7 @@ class ResultShowCaseTest {
fun openFile(): Result<ByteBuffer, IOException> = Result.Success(ByteBuffer.allocate(1))
fun readData(bytes: ByteBuffer): Result<Data, IOException> = Result.Success(Data(bytes[0]))
fun businessLogic(data: Data): Result<Money, LocalizedErrorString> = Result.Success(Money(data.b))
fun businessLogic(data: Data): Result<Money, @Nls String> = Result.Success(Money(data.b))
}
@Test
@@ -40,7 +41,7 @@ class ResultShowCaseTest {
)
when (result) { // Classic matching
is Result.Success -> println("Money: ${result.result.dollars}")
is Result.Failure -> fail("Too bad: ${result.error.text}")
is Result.Failure -> fail("Too bad: ${result.error}")
}
}
@@ -69,7 +70,7 @@ class ResultShowCaseTest {
is Result.Success -> r.result
}
when (val r = businessLogic(data)) {
is Result.Failure -> fail(r.error.text)
is Result.Failure -> fail(r.error)
is Result.Success -> Unit
}
}

View File

@@ -23,10 +23,10 @@ import kotlin.time.Duration.Companion.seconds
* As we need workable pythons, we validate it by executing
*/
@ApiStatus.Internal
suspend fun PythonBinary.validatePythonAndGetVersion(): Result<LanguageLevel, LocalizedErrorString> = withContext(Dispatchers.IO) {
suspend fun PythonBinary.validatePythonAndGetVersion(): Result<LanguageLevel, @NlsSafe String> = withContext(Dispatchers.IO) {
val smokeTestOutput = executeWithResult("-c", "print(1)").getOr { return@withContext it }.trim()
if (smokeTestOutput != "1") {
return@withContext failure(LocalizedErrorString(message("python.get.version.error", pathString, smokeTestOutput)))
return@withContext failure(message("python.get.version.error", pathString, smokeTestOutput))
}
val versionString = executeWithResult(PYTHON_VERSION_ARG).getOr { return@withContext it }
@@ -34,7 +34,7 @@ suspend fun PythonBinary.validatePythonAndGetVersion(): Result<LanguageLevel, Lo
LanguageLevel.fromPythonVersion(it)
}
if (languageLevel == null) {
return@withContext failure(LocalizedErrorString(message("python.get.version.wrong.version", pathString, versionString)))
return@withContext failure(message("python.get.version.wrong.version", pathString, versionString))
}
return@withContext Result.success(languageLevel)
}
@@ -43,13 +43,13 @@ suspend fun PythonBinary.validatePythonAndGetVersion(): Result<LanguageLevel, Lo
* Executes [this] with [args], returns either stdout or error (if execution failed or exit code != 0)
*/
@ApiStatus.Internal
suspend fun PythonBinary.executeWithResult(vararg args: String): Result<@NlsSafe String, LocalizedErrorString> {
suspend fun PythonBinary.executeWithResult(vararg args: String): Result<@NlsSafe String, @NlsSafe String> {
val output = exec(*args, timeout = 5.seconds).getOr {
val text = it.error?.message ?: message("python.get.version.too.long", pathString)
return failure(LocalizedErrorString(text))
return failure(text)
}
return if (output.exitCode != 0) {
failure(LocalizedErrorString(message("python.get.version.error", pathString, "code ${output.exitCode}, {output.stderr}")))
failure(message("python.get.version.error", pathString, "code ${output.exitCode}, {output.stderr}"))
}
else {
Result.success(output.stdout)

View File

@@ -22,7 +22,7 @@ class PythonWithLanguageLevelImpl internal constructor(
override val languageLevel: LanguageLevel,
) : PythonWithLanguageLevel, Comparable<PythonWithLanguageLevelImpl> {
companion object {
suspend fun createByPythonBinary(pythonBinary: PythonBinary): Result<PythonWithLanguageLevelImpl, LocalizedErrorString> {
suspend fun createByPythonBinary(pythonBinary: PythonBinary): Result<PythonWithLanguageLevelImpl, @Nls String> {
val languageLevel = pythonBinary.validatePythonAndGetVersion().getOr { return it }
return Result.success(PythonWithLanguageLevelImpl(pythonBinary, languageLevel))
}

View File

@@ -21,6 +21,7 @@ import com.jetbrains.python.sdk.installer.installBinary
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.Nls
/**
* Service to register and obtain [SystemPython]s
@@ -37,7 +38,7 @@ sealed interface SystemPythonService {
* When user provides a path to the python binary, use this method to the [SystemPython].
* @return either [SystemPython] or an error if python is broken.
*/
suspend fun registerSystemPython(pythonPath: PythonBinary): Result<SystemPython, LocalizedErrorString>
suspend fun registerSystemPython(pythonPath: PythonBinary): Result<SystemPython, @Nls String>
/**
* @return tool to install python on OS If [eelApi] supports python installation
@@ -56,7 +57,7 @@ fun SystemPythonService(): SystemPythonService = ApplicationManager.getApplicati
@State(name = "SystemPythonService", storages = [Storage("SystemPythonService.xml")], allowLoadInTests = true)
private class SystemPythonServiceImpl : SystemPythonService, SimplePersistentStateComponent<MyServiceState>(MyServiceState()) {
override suspend fun registerSystemPython(pythonPath: PythonBinary): Result<SystemPython, LocalizedErrorString> {
override suspend fun registerSystemPython(pythonPath: PythonBinary): Result<SystemPython, @Nls String> {
val impl = PythonWithLanguageLevelImpl.createByPythonBinary(pythonPath).getOr { return it }
state.userProvidedPythons.add(pythonPath)
return Result.success(SystemPython(impl))

View File

@@ -20,6 +20,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.withContext
import org.jetbrains.annotations.Nls
import kotlin.reflect.KClass
/**
* [FlowCollector.emit] user-readable errors here.
@@ -130,3 +131,9 @@ fun <S, E> Result<S, E>.asKotlinResult(): kotlin.Result<S> = when (this) {
)
is Success -> kotlin.Result.success(result)
}
interface CustomErrorRendererEP<T : PyError.Message> {
val clazz: KClass<T>
suspend fun renderMe(error: T)
}