From 003d75a8f8008c25ee3cd9488349a8c5a1abfdcd Mon Sep 17 00:00:00 2001 From: "Ilya.Kazakevich" Date: Sat, 13 Jun 2026 18:14:22 +0200 Subject: [PATCH] cleanup [python], PY-88291 (WIP): Make `ErrorSink` application service. `ErrorSink` is an interface with only one known implementation, and all functions must accept the interface as an argument (to be mocked in tests) but the topmost function near the platform integration can use implementation explicitly. The problem is implementation lives in `community.impl` and isn't aceccisble from other modules, so we made it an application service. From now on, clients depend on interface only, and never touch implementation directly. GitOrigin-RevId: a861d63398e1f4dc31528fab2099b72144e91a1b --- .../ide/impl/miscProject/PyMiscService.kt | 4 ++-- .../python/junit5Tests/framework/fixtures.kt | 11 +++++------ .../python/errorProcessing/ErrorSink.kt | 16 ++++++++++++++-- .../intellij.python.community.impl.xml | 1 + .../featuresTrainer/ift/PythonLangSupport.kt | 5 ++--- .../ShowingMessageErrorSink.kt} | 17 +++++------------ .../com/jetbrains/python/impl/package-info.java | 5 +++++ .../newProject/NewProjectWizardPythonData.kt | 5 ++--- .../newProjectWizard/impl/PyV3UIServicesProd.kt | 3 +-- .../management/PythonPackageManagerAction.kt | 3 +-- .../management/ui/PythonPackageManagerUI.kt | 9 ++++----- .../configuration/PyPoetrySdkConfiguration.kt | 5 +++-- .../python/sdk/AddInterpreterActions.kt | 4 ++-- .../add/v2/PythonAddLocalInterpreterDialog.kt | 5 +++-- .../v2/PythonSdkPanelBuilderAndSdkCreator.kt | 5 +++-- .../com/jetbrains/python/sdk/add/v2/uiUtils.kt | 5 ++--- .../sdk/add/v2/uv/EnvironmentCreatorUv.kt | 6 +++--- .../configuration/PyProjectSdkConfiguration.kt | 6 +++--- .../python/sdk/pipenv/PipEnvPipFileWatcher.kt | 4 ++-- .../python/target/PythonLanguageRuntimeUI.kt | 8 ++++---- .../com/jetbrains/python/util/package-info.java | 5 +++++ .../python/uv/sdk/configuration/uvVenv.kt | 5 +++-- 22 files changed, 75 insertions(+), 62 deletions(-) rename python/src/com/jetbrains/python/{util/ShowingMessageErrorSync.kt => impl/ShowingMessageErrorSink.kt} (78%) create mode 100644 python/src/com/jetbrains/python/impl/package-info.java create mode 100644 python/src/com/jetbrains/python/util/package-info.java diff --git a/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/miscProject/PyMiscService.kt b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/miscProject/PyMiscService.kt index 3816b7b2bd83..b760b7bdbf50 100644 --- a/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/miscProject/PyMiscService.kt +++ b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/miscProject/PyMiscService.kt @@ -10,7 +10,7 @@ import com.intellij.pycharm.community.ide.impl.PyCharmCommunityCustomizationBund import com.intellij.pycharm.community.ide.impl.miscProject.impl.MiscProjectUsageCollector import com.jetbrains.python.Result import com.jetbrains.python.errorProcessing.emit -import com.jetbrains.python.util.ShowingMessageErrorSync +import com.jetbrains.python.errorProcessing.ErrorSink import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -42,7 +42,7 @@ class PyMiscService(private val scope: CoroutineScope) { } is Result.Failure -> { withContext(Dispatchers.EDT) { - ShowingMessageErrorSync.emit(projectCreationResult.error, project) + ErrorSink().emit(projectCreationResult.error, project) } } } diff --git a/python/junit5Tests-framework/src/com/intellij/python/junit5Tests/framework/fixtures.kt b/python/junit5Tests-framework/src/com/intellij/python/junit5Tests/framework/fixtures.kt index 2d3db4120925..48f35aaa161d 100644 --- a/python/junit5Tests-framework/src/com/intellij/python/junit5Tests/framework/fixtures.kt +++ b/python/junit5Tests-framework/src/com/intellij/python/junit5Tests/framework/fixtures.kt @@ -16,7 +16,6 @@ import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel import kotlinx.coroutines.cancelAndJoin -import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.job import org.jetbrains.annotations.TestOnly import java.nio.file.Path @@ -52,16 +51,16 @@ fun applicationScope(name: String = UUID.randomUUID().toString()): TestFixture { - private val _errors = mutableListOf() - val errors: List get() = _errors +class CollectingErrorSink internal constructor() : ErrorSink { + val errors: List + field = mutableListOf() override suspend fun emit(value: PyErrorDetail) { - _errors.add(value) + errors.add(value) } fun clear() { - _errors.clear() + errors.clear() } } diff --git a/python/openapi/src/com/jetbrains/python/errorProcessing/ErrorSink.kt b/python/openapi/src/com/jetbrains/python/errorProcessing/ErrorSink.kt index 11bb24c169cb..c80e43d8ba77 100644 --- a/python/openapi/src/com/jetbrains/python/errorProcessing/ErrorSink.kt +++ b/python/openapi/src/com/jetbrains/python/errorProcessing/ErrorSink.kt @@ -1,6 +1,8 @@ // 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.application.ApplicationManager +import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import kotlinx.coroutines.flow.FlowCollector @@ -11,11 +13,17 @@ import kotlinx.coroutines.flow.FlowCollector * For the most business-logic and backend functions please return [PyResult] or [PyError]. * * There will be a unified sink soon to show and log errors. - * Currently, only [com.jetbrains.python.util.ShowingMessageErrorSync] is a well-known implementation. + * Use function of the same name to get an instance, or accept it as an argument as it can be mocked in tests. * * See [PyError] */ -typealias ErrorSink = FlowCollector +fun interface ErrorSink : FlowCollector + +/** + * Default implementation of [ErrorSink] that shows message to user, but try to accept [ErrorSink] as an argument, use + * this function as a default value as a last resort only. + */ +fun ErrorSink(): ErrorSink = ApplicationManager.getApplication().service() data class PyErrorDetail( val error: PyError, @@ -24,4 +32,8 @@ data class PyErrorDetail( suspend fun ErrorSink.emit(error: PyError, project: Project? = null) { emit(PyErrorDetail(error, project)) +} + +fun ErrorSink.withProject(project: Project): ErrorSink = ErrorSink { + emit(PyErrorDetail(it.error, project)) } \ No newline at end of file diff --git a/python/pluginResources/intellij.python.community.impl.xml b/python/pluginResources/intellij.python.community.impl.xml index d57e7747b45f..615ad848d5d9 100644 --- a/python/pluginResources/intellij.python.community.impl.xml +++ b/python/pluginResources/intellij.python.community.impl.xml @@ -47,6 +47,7 @@ messages.PyBundle + diff --git a/python/python-features-trainer/src/com/intellij/python/featuresTrainer/ift/PythonLangSupport.kt b/python/python-features-trainer/src/com/intellij/python/featuresTrainer/ift/PythonLangSupport.kt index 5b568e209fdd..674b9c816d1c 100644 --- a/python/python-features-trainer/src/com/intellij/python/featuresTrainer/ift/PythonLangSupport.kt +++ b/python/python-features-trainer/src/com/intellij/python/featuresTrainer/ift/PythonLangSupport.kt @@ -10,7 +10,6 @@ import com.intellij.openapi.vfs.VirtualFile import com.intellij.util.concurrency.annotations.RequiresEdt import com.intellij.util.concurrency.annotations.RequiresReadLock import com.jetbrains.python.Result -import com.jetbrains.python.errorProcessing.ErrorSink import com.jetbrains.python.errorProcessing.emit import com.jetbrains.python.inspections.interpreter.InterpreterSettingsQuickFix import com.jetbrains.python.projectCreation.createVenvAndSdk @@ -18,7 +17,7 @@ import com.jetbrains.python.sdk.ModuleOrProject import com.jetbrains.python.sdk.pythonSdk import com.jetbrains.python.sdk.runWithSdkConfigurationLock import com.jetbrains.python.statistics.modules -import com.jetbrains.python.util.ShowingMessageErrorSync +import com.jetbrains.python.errorProcessing.ErrorSink import training.dsl.LessonContext import training.lang.AbstractLangSupport import training.learn.CourseManager @@ -33,7 +32,7 @@ import training.util.getFeedbackLink import training.util.isLearningProject import java.nio.file.Path -internal class PythonLangSupport(private val errorSink: ErrorSink = ShowingMessageErrorSync) : AbstractLangSupport() { +internal class PythonLangSupport(private val errorSink: ErrorSink = ErrorSink()) : AbstractLangSupport() { override val contentRootDirectoryName = "PyCharmLearningProject" diff --git a/python/src/com/jetbrains/python/util/ShowingMessageErrorSync.kt b/python/src/com/jetbrains/python/impl/ShowingMessageErrorSink.kt similarity index 78% rename from python/src/com/jetbrains/python/util/ShowingMessageErrorSync.kt rename to python/src/com/jetbrains/python/impl/ShowingMessageErrorSink.kt index 60fd6380eb76..c9bd05b996d5 100644 --- a/python/src/com/jetbrains/python/util/ShowingMessageErrorSync.kt +++ b/python/src/com/jetbrains/python/impl/ShowingMessageErrorSink.kt @@ -1,8 +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.util +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.jetbrains.python.impl import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.project.Project import com.intellij.openapi.application.EDT import com.intellij.openapi.application.ModalityState import com.intellij.openapi.application.asContextElement @@ -18,13 +17,11 @@ import com.jetbrains.python.packaging.PyExecutionException import com.jetbrains.python.showProcessExecutionErrorDialog import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import org.jetbrains.annotations.ApiStatus /** * Displays the error with a message box and writes it to a log. */ -@ApiStatus.Internal -object ShowingMessageErrorSync : ErrorSink { +internal class ShowingMessageErrorSink : ErrorSink { override suspend fun emit(value: PyErrorDetail) { val (error, project) = value @@ -37,9 +34,9 @@ object ShowingMessageErrorSync : ErrorSink { thisLogger().warn(error.message) // Platform doesn't allow dialogs without a lock for now, fix later writeIntentReadAction { - when (val e = error) { + when (error) { is ExecError -> { - showProcessExecutionErrorDialog(project, e) + showProcessExecutionErrorDialog(project, error) } is MessageError -> { Messages.showErrorDialog(error.message, PyBundle.message("python.error")) @@ -48,8 +45,4 @@ object ShowingMessageErrorSync : ErrorSink { } } } - - fun withProject(project: Project): ErrorSink = ErrorSink { - emit(PyErrorDetail(it.error, project)) - } } diff --git a/python/src/com/jetbrains/python/impl/package-info.java b/python/src/com/jetbrains/python/impl/package-info.java new file mode 100644 index 000000000000..45eebcbc8938 --- /dev/null +++ b/python/src/com/jetbrains/python/impl/package-info.java @@ -0,0 +1,5 @@ +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +@Internal +package com.jetbrains.python.impl; + +import org.jetbrains.annotations.ApiStatus.Internal; diff --git a/python/src/com/jetbrains/python/newProject/NewProjectWizardPythonData.kt b/python/src/com/jetbrains/python/newProject/NewProjectWizardPythonData.kt index 73d245b2f9f4..23c2bf24eac8 100644 --- a/python/src/com/jetbrains/python/newProject/NewProjectWizardPythonData.kt +++ b/python/src/com/jetbrains/python/newProject/NewProjectWizardPythonData.kt @@ -15,7 +15,6 @@ import com.intellij.ui.dsl.builder.Panel import com.jetbrains.python.PyBundle import com.jetbrains.python.sdk.runWithSdkConfigurationLock import com.jetbrains.python.PythonModuleTypeBase -import com.jetbrains.python.errorProcessing.ErrorSink import com.jetbrains.python.errorProcessing.emit import com.jetbrains.python.newProjectWizard.projectPath.ProjectPathFlows import com.jetbrains.python.onFailure @@ -24,7 +23,7 @@ import com.jetbrains.python.sdk.add.v2.PySdkCreator import com.jetbrains.python.sdk.add.v2.PythonSdkPanelBuilderAndSdkCreator import com.jetbrains.python.sdk.configurePythonSdk import com.jetbrains.python.sdk.moduleIfExists -import com.jetbrains.python.util.ShowingMessageErrorSync +import com.jetbrains.python.errorProcessing.ErrorSink import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine @@ -85,7 +84,7 @@ class NewPythonProjectStep(parent: NewProjectWizardStep, val createPythonModuleS private var intellijModule: Module? = null private lateinit var pySdkCreator: PySdkCreator - private val errorSink: ErrorSink = ShowingMessageErrorSync + private val errorSink: ErrorSink = ErrorSink() private val projectPathFlows = ProjectPathFlows.create( pathProperty.toFlow().combine(nameProperty.toFlow()) { dirPath, projectName -> diff --git a/python/src/com/jetbrains/python/newProjectWizard/impl/PyV3UIServicesProd.kt b/python/src/com/jetbrains/python/newProjectWizard/impl/PyV3UIServicesProd.kt index fd61a609033c..a4ce4d040832 100644 --- a/python/src/com/jetbrains/python/newProjectWizard/impl/PyV3UIServicesProd.kt +++ b/python/src/com/jetbrains/python/newProjectWizard/impl/PyV3UIServicesProd.kt @@ -8,7 +8,6 @@ import com.intellij.ui.tree.ui.DefaultTreeUI.AUTO_EXPAND_ALLOWED import com.intellij.util.ui.showingScope import com.jetbrains.python.errorProcessing.ErrorSink import com.jetbrains.python.newProjectWizard.PyV3UIServices -import com.jetbrains.python.util.ShowingMessageErrorSync import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -21,7 +20,7 @@ internal object PyV3UIServicesProd : PyV3UIServices { } } - override val errorSink: ErrorSink = ShowingMessageErrorSync + override val errorSink: ErrorSink get() = ErrorSink() override suspend fun expandProjectTreeView(project: Project): Unit = withContext(Dispatchers.EDT) { // Null means no project pane opened diff --git a/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerAction.kt b/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerAction.kt index d24a4c47b181..d265d896b687 100644 --- a/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerAction.kt +++ b/python/src/com/jetbrains/python/packaging/management/PythonPackageManagerAction.kt @@ -20,7 +20,6 @@ import com.jetbrains.python.packaging.management.ui.PythonPackageManagerUI import com.jetbrains.python.packaging.utils.PyPackageCoroutine import com.jetbrains.python.sdk.associatedModuleDir import com.jetbrains.python.sdk.pythonSdk -import com.jetbrains.python.util.ShowingMessageErrorSync import kotlinx.coroutines.Dispatchers import org.jetbrains.annotations.ApiStatus import kotlin.text.Regex.Companion.escape @@ -34,7 +33,7 @@ import kotlin.text.Regex.Companion.escape */ @ApiStatus.Internal abstract class PythonPackageManagerAction : DumbAwareAction() { - protected val errorSink: ErrorSink = ShowingMessageErrorSync + protected val errorSink: ErrorSink = ErrorSink() /** * The regex pattern that matches the file names that this action is applicable to. diff --git a/python/src/com/jetbrains/python/packaging/management/ui/PythonPackageManagerUI.kt b/python/src/com/jetbrains/python/packaging/management/ui/PythonPackageManagerUI.kt index 766494aa68be..162b161637ec 100644 --- a/python/src/com/jetbrains/python/packaging/management/ui/PythonPackageManagerUI.kt +++ b/python/src/com/jetbrains/python/packaging/management/ui/PythonPackageManagerUI.kt @@ -10,7 +10,6 @@ import com.intellij.platform.ide.progress.runWithModalProgressBlocking import com.intellij.util.concurrency.annotations.RequiresBlockingContext import com.intellij.util.concurrency.annotations.RequiresEdt import com.jetbrains.python.PyBundle -import com.jetbrains.python.errorProcessing.ErrorSink import com.jetbrains.python.errorProcessing.PyResult import com.jetbrains.python.errorProcessing.emit import com.jetbrains.python.getOrNull @@ -24,7 +23,7 @@ import com.jetbrains.python.packaging.management.PythonPackageManager import com.jetbrains.python.packaging.management.findPackageSpecification import com.jetbrains.python.packaging.pyRequirement import com.jetbrains.python.statistics.PyPackagesUsageCollector -import com.jetbrains.python.util.ShowingMessageErrorSync +import com.jetbrains.python.errorProcessing.ErrorSink import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.Nls @@ -46,7 +45,7 @@ import org.jetbrains.annotations.Nls @ApiStatus.Experimental class PythonPackageManagerUI private constructor( @get:ApiStatus.Internal val manager: PythonPackageManager, - @get:ApiStatus.Internal val sink: ErrorSink = ShowingMessageErrorSync, + @get:ApiStatus.Internal val sink: ErrorSink = ErrorSink(), ) { @ApiStatus.Internal val project: Project = manager.project @@ -198,7 +197,7 @@ class PythonPackageManagerUI private constructor( @JvmStatic @JvmOverloads @ApiStatus.Internal - fun forSdk(project: Project, sdk: Sdk, sink: ErrorSink = ShowingMessageErrorSync): PythonPackageManagerUI { + fun forSdk(project: Project, sdk: Sdk, sink: ErrorSink = ErrorSink()): PythonPackageManagerUI { val packageManager = PythonPackageManager.forSdk(project, sdk) return PythonPackageManagerUI(packageManager, sink) } @@ -206,6 +205,6 @@ class PythonPackageManagerUI private constructor( @ApiStatus.Experimental fun forPackageManager( packageManager: PythonPackageManager, - ): PythonPackageManagerUI = PythonPackageManagerUI(packageManager, ShowingMessageErrorSync) + ): PythonPackageManagerUI = PythonPackageManagerUI(packageManager, ErrorSink()) } } diff --git a/python/src/com/jetbrains/python/poetry/sdk/configuration/PyPoetrySdkConfiguration.kt b/python/src/com/jetbrains/python/poetry/sdk/configuration/PyPoetrySdkConfiguration.kt index bc79ec3df923..e097a1451da4 100644 --- a/python/src/com/jetbrains/python/poetry/sdk/configuration/PyPoetrySdkConfiguration.kt +++ b/python/src/com/jetbrains/python/poetry/sdk/configuration/PyPoetrySdkConfiguration.kt @@ -40,7 +40,8 @@ import com.jetbrains.python.sdk.poetry.getPoetryExecutable import com.jetbrains.python.sdk.poetry.runPoetry import com.jetbrains.python.sdk.poetry.setupPoetry import com.jetbrains.python.sdk.poetry.suggestedSdkName -import com.jetbrains.python.util.ShowingMessageErrorSync +import com.jetbrains.python.errorProcessing.ErrorSink +import com.jetbrains.python.errorProcessing.withProject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.nio.file.Path @@ -138,7 +139,7 @@ internal class PyPoetrySdkConfiguration : PyProjectTomlConfigurationExtension { basePythonBinaryPath = baseSystemPython.pythonBinary, installPackages = true, init = tomlFile == null, - errorSink = ShowingMessageErrorSync.withProject(module.project) + errorSink = ErrorSink().withProject(module.project) ).getOr { return@withBackgroundProgress it } val path = poetry.resolvePythonBinary() diff --git a/python/src/com/jetbrains/python/sdk/AddInterpreterActions.kt b/python/src/com/jetbrains/python/sdk/AddInterpreterActions.kt index b3c7f3d7c0a5..bc6323db948a 100644 --- a/python/src/com/jetbrains/python/sdk/AddInterpreterActions.kt +++ b/python/src/com/jetbrains/python/sdk/AddInterpreterActions.kt @@ -43,7 +43,7 @@ import com.jetbrains.python.sdk.add.v2.PythonAddLocalInterpreterDialog import com.jetbrains.python.sdk.add.v2.PythonAddLocalInterpreterPresenter import com.jetbrains.python.sdk.configuration.CreateSdkInfoWithTool import com.jetbrains.python.target.PythonLanguageRuntimeType -import com.jetbrains.python.util.ShowingMessageErrorSync +import com.jetbrains.python.errorProcessing.ErrorSink import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred @@ -121,7 +121,7 @@ internal class AddLocalInterpreterAction( ), DumbAware { override fun createDialog(): PythonAddLocalInterpreterDialog { val dialogPresenter = PythonAddLocalInterpreterPresenter( - moduleOrProject, errorSink = ShowingMessageErrorSync, bestGuessCreateSdkInfo = bestGuessCreateSdkInfo + moduleOrProject, errorSink = ErrorSink(), bestGuessCreateSdkInfo = bestGuessCreateSdkInfo ).apply { // Model provides flow, but we need to call Consumer sdkCreatedFlow.oneShotConsumer(onSdkCreated) diff --git a/python/src/com/jetbrains/python/sdk/add/v2/PythonAddLocalInterpreterDialog.kt b/python/src/com/jetbrains/python/sdk/add/v2/PythonAddLocalInterpreterDialog.kt index 9e17dbabe622..8b45d1c74907 100644 --- a/python/src/com/jetbrains/python/sdk/add/v2/PythonAddLocalInterpreterDialog.kt +++ b/python/src/com/jetbrains/python/sdk/add/v2/PythonAddLocalInterpreterDialog.kt @@ -16,7 +16,8 @@ import com.jetbrains.python.TraceContext import com.jetbrains.python.newProjectWizard.projectPath.ProjectPathFlows import com.jetbrains.python.packaging.utils.PyPackageCoroutine import com.jetbrains.python.sdk.moduleIfExists -import com.jetbrains.python.util.ShowingMessageErrorSync +import com.jetbrains.python.errorProcessing.ErrorSink +import com.jetbrains.python.errorProcessing.withProject import kotlinx.coroutines.supervisorScope import org.jetbrains.annotations.NonNls import javax.swing.JComponent @@ -54,7 +55,7 @@ internal class PythonAddLocalInterpreterDialog(private val dialogPresenter: Pyth } override fun createCenterPanel(): JComponent { - val errorSink = ShowingMessageErrorSync.withProject(dialogPresenter.moduleOrProject.project) + val errorSink = ErrorSink().withProject(dialogPresenter.moduleOrProject.project) val rootPanel = panel { model = PythonLocalAddInterpreterModel(ProjectPathFlows.create(basePath), EelFileSystem(eelApi = localEel)) diff --git a/python/src/com/jetbrains/python/sdk/add/v2/PythonSdkPanelBuilderAndSdkCreator.kt b/python/src/com/jetbrains/python/sdk/add/v2/PythonSdkPanelBuilderAndSdkCreator.kt index be725ff7d819..6deadd37c6ea 100644 --- a/python/src/com/jetbrains/python/sdk/add/v2/PythonSdkPanelBuilderAndSdkCreator.kt +++ b/python/src/com/jetbrains/python/sdk/add/v2/PythonSdkPanelBuilderAndSdkCreator.kt @@ -33,7 +33,8 @@ import com.jetbrains.python.sdk.add.v2.venv.setupVirtualenv import com.jetbrains.python.statistics.InterpreterCreationMode import com.jetbrains.python.statistics.InterpreterTarget import com.jetbrains.python.statistics.InterpreterType -import com.jetbrains.python.util.ShowingMessageErrorSync +import com.jetbrains.python.errorProcessing.ErrorSink +import com.jetbrains.python.errorProcessing.withProject import com.jetbrains.python.venvReader.VirtualEnvReader import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope @@ -108,7 +109,7 @@ internal class PythonSdkPanelBuilderAndSdkCreator( custom = PythonAddCustomInterpreter( model = model, module = module, - errorSink = module?.project?.let { ShowingMessageErrorSync.withProject(it) } ?: ShowingMessageErrorSync, + errorSink = module?.project?.let { ErrorSink().withProject(it) } ?: ErrorSink(), limitExistingEnvironments = limitExistingEnvironments, bestGuessCreateSdkInfo = CompletableDeferred(value = null) ) diff --git a/python/src/com/jetbrains/python/sdk/add/v2/uiUtils.kt b/python/src/com/jetbrains/python/sdk/add/v2/uiUtils.kt index d8c31eaf275a..143e414c5c47 100644 --- a/python/src/com/jetbrains/python/sdk/add/v2/uiUtils.kt +++ b/python/src/com/jetbrains/python/sdk/add/v2/uiUtils.kt @@ -35,7 +35,6 @@ import com.intellij.util.SystemProperties import com.intellij.util.ui.JBUI import com.jetbrains.python.PyBundle import com.jetbrains.python.PyBundle.message -import com.jetbrains.python.errorProcessing.ErrorSink import com.jetbrains.python.errorProcessing.PyResult import com.jetbrains.python.errorProcessing.emit import com.jetbrains.python.onFailure @@ -47,7 +46,7 @@ import com.jetbrains.python.sdk.add.v2.PythonSupportedEnvironmentManagers.VIRTUA import com.jetbrains.python.sdk.flavors.PythonSdkFlavor import com.jetbrains.python.sdk.flavors.conda.PyCondaEnv import com.jetbrains.python.sdk.flavors.conda.PyCondaEnvIdentity -import com.jetbrains.python.util.ShowingMessageErrorSync +import com.jetbrains.python.errorProcessing.ErrorSink import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Dispatchers @@ -316,7 +315,7 @@ internal fun

Panel.pythonInterpreterComboBox( onPathSelected: suspend (P) -> PyResult>, customizer: RowsRange.() -> Unit = {}, ): PythonInterpreterComboBox

{ - val comboBox = PythonInterpreterComboBox(onPathSelected, fileSystem, ShowingMessageErrorSync) + val comboBox = PythonInterpreterComboBox(onPathSelected, fileSystem, ErrorSink()) .apply { setBusy(true) } diff --git a/python/src/com/jetbrains/python/sdk/add/v2/uv/EnvironmentCreatorUv.kt b/python/src/com/jetbrains/python/sdk/add/v2/uv/EnvironmentCreatorUv.kt index 345d99c41734..d648b64a03ee 100644 --- a/python/src/com/jetbrains/python/sdk/add/v2/uv/EnvironmentCreatorUv.kt +++ b/python/src/com/jetbrains/python/sdk/add/v2/uv/EnvironmentCreatorUv.kt @@ -18,7 +18,6 @@ import com.intellij.ui.dsl.gridLayout.UnscaledGaps import com.intellij.ui.dsl.listCellRenderer.textListCellRenderer import com.intellij.util.ui.AsyncProcessIcon import com.jetbrains.python.PyBundle.message -import com.jetbrains.python.errorProcessing.ErrorSink import com.jetbrains.python.errorProcessing.PyResult import com.jetbrains.python.newProjectWizard.collector.PythonNewProjectWizardCollector import com.jetbrains.python.sdk.add.v2.CustomNewEnvironmentCreator @@ -39,7 +38,8 @@ import com.jetbrains.python.sdk.uv.impl.createUvLowLevel import com.jetbrains.python.sdk.uv.impl.setUvExecutableLocal import com.jetbrains.python.sdk.uv.setupNewUvSdkAndEnv import com.jetbrains.python.statistics.InterpreterType -import com.jetbrains.python.util.ShowingMessageErrorSync +import com.jetbrains.python.errorProcessing.ErrorSink +import com.jetbrains.python.errorProcessing.withProject import com.jetbrains.python.venvReader.VirtualEnvReader import io.github.z4kn4fein.semver.Version import kotlinx.coroutines.CoroutineScope @@ -62,7 +62,7 @@ import kotlin.io.path.readText * UV-specific selector if a .venv directory already exists. */ internal fun PythonMutableTargetAddInterpreterModel.uvCreator(module: Module?): EnvironmentCreatorUv { - val errorSink = module?.project?.let { ShowingMessageErrorSync.withProject(it) } ?: ShowingMessageErrorSync + val errorSink = module?.project?.let { ErrorSink().withProject(it) } ?: ErrorSink() return EnvironmentCreatorUv(this, module, errorSink) } diff --git a/python/src/com/jetbrains/python/sdk/configuration/PyProjectSdkConfiguration.kt b/python/src/com/jetbrains/python/sdk/configuration/PyProjectSdkConfiguration.kt index 28514a7bb292..7f28d3e069be 100644 --- a/python/src/com/jetbrains/python/sdk/configuration/PyProjectSdkConfiguration.kt +++ b/python/src/com/jetbrains/python/sdk/configuration/PyProjectSdkConfiguration.kt @@ -17,7 +17,7 @@ import com.jetbrains.python.sdk.configuration.suppressors.PyPackageRequirementsI import com.jetbrains.python.sdk.configuration.suppressors.TipOfTheDaySuppressor import com.jetbrains.python.sdk.configurePythonSdk import com.jetbrains.python.sdk.installExecutableViaPythonScript -import com.jetbrains.python.util.ShowingMessageErrorSync +import com.jetbrains.python.errorProcessing.ErrorSink import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.nio.file.Path @@ -25,7 +25,7 @@ import java.nio.file.Path object PyProjectSdkConfiguration { internal suspend fun installToolAndShowErrorIfNeeded(module: Module, pathPersister: (Path) -> Unit, toolToInstall: String) { performToolInstallation(pathPersister, toolToInstall).errorOrNull?.also { - ShowingMessageErrorSync.emit(it, module.project) + ErrorSink().emit(it, module.project) } } @@ -41,7 +41,7 @@ object PyProjectSdkConfiguration { thisLogger().debug("Configuring sdk using ${createSdkInfoWithTool.toolId}") val sdk = createSdkInfoWithTool.createSdkInfo.getSdkCreator(module).createSdk().getOr { - ShowingMessageErrorSync.emit(it.error, module.project) + ErrorSink().emit(it.error, module.project) return@withContext true } diff --git a/python/src/com/jetbrains/python/sdk/pipenv/PipEnvPipFileWatcher.kt b/python/src/com/jetbrains/python/sdk/pipenv/PipEnvPipFileWatcher.kt index 2ec02d4e7e1f..e51b9248bb47 100644 --- a/python/src/com/jetbrains/python/sdk/pipenv/PipEnvPipFileWatcher.kt +++ b/python/src/com/jetbrains/python/sdk/pipenv/PipEnvPipFileWatcher.kt @@ -30,7 +30,7 @@ import com.jetbrains.python.sdk.findAmongRoots import com.jetbrains.python.sdk.pythonSdk import com.jetbrains.python.sdk.skeleton.PySkeletonUtil import com.jetbrains.python.statistics.PipfileWatcherIdsHolder.Companion.RUN_PIPENV_LOCK_SUGGESTION -import com.jetbrains.python.util.ShowingMessageErrorSync +import com.jetbrains.python.errorProcessing.ErrorSink import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.nio.file.Path @@ -115,7 +115,7 @@ internal class PipEnvPipFileWatcher : EditorFactoryListener { withBackgroundProgress(module.project, description) { val sdk = module.pythonSdk ?: return@withBackgroundProgress runPipEnv(sdk.associatedModulePath?.let { Path.of(it) }, *args.toTypedArray()).onFailure { - ShowingMessageErrorSync.emit(it, module.project) + ErrorSink().emit(it, module.project) } withContext(Dispatchers.IO) { diff --git a/python/src/com/jetbrains/python/target/PythonLanguageRuntimeUI.kt b/python/src/com/jetbrains/python/target/PythonLanguageRuntimeUI.kt index 5f644328545c..98a8352b9dac 100644 --- a/python/src/com/jetbrains/python/target/PythonLanguageRuntimeUI.kt +++ b/python/src/com/jetbrains/python/target/PythonLanguageRuntimeUI.kt @@ -19,7 +19,6 @@ import com.intellij.util.concurrency.annotations.RequiresEdt import com.intellij.util.ui.launchOnShow import com.jetbrains.python.PyBundle.message import com.jetbrains.python.TraceContext -import com.jetbrains.python.errorProcessing.ErrorSink import com.jetbrains.python.errorProcessing.emit import com.jetbrains.python.newProjectWizard.projectPath.ProjectPathFlows import com.jetbrains.python.onFailure @@ -32,7 +31,8 @@ import com.jetbrains.python.sdk.add.v2.PythonLocalAddInterpreterModel import com.jetbrains.python.sdk.add.v2.TargetFileSystem import com.jetbrains.python.sdk.configurePythonSdk import com.jetbrains.python.sdk.runWithSdkConfigurationLock -import com.jetbrains.python.util.ShowingMessageErrorSync +import com.jetbrains.python.errorProcessing.ErrorSink +import com.jetbrains.python.errorProcessing.withProject import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.withContext @@ -51,7 +51,7 @@ internal class PythonLanguageRuntimeUI( private lateinit var mainPanel: PythonAddCustomInterpreter private var validationErrors: Collection = emptyList() - private val errorSink: ErrorSink = ShowingMessageErrorSync + private val errorSink: ErrorSink = ErrorSink() override fun createPanel(): DialogPanel { val targetEnvironmentConfiguration = targetSupplier.get() @@ -67,7 +67,7 @@ internal class PythonLanguageRuntimeUI( mainPanel = PythonAddCustomInterpreter( model = model, module = module, - errorSink = ShowingMessageErrorSync.withProject(project), + errorSink = ErrorSink().withProject(project), limitExistingEnvironments = false, bestGuessCreateSdkInfo = CompletableDeferred(value = null) ) diff --git a/python/src/com/jetbrains/python/util/package-info.java b/python/src/com/jetbrains/python/util/package-info.java new file mode 100644 index 000000000000..7df0bb45d3c2 --- /dev/null +++ b/python/src/com/jetbrains/python/util/package-info.java @@ -0,0 +1,5 @@ +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +@Internal +package com.jetbrains.python.util; + +import org.jetbrains.annotations.ApiStatus.Internal; diff --git a/python/src/com/jetbrains/python/uv/sdk/configuration/uvVenv.kt b/python/src/com/jetbrains/python/uv/sdk/configuration/uvVenv.kt index 8fbc97856819..097812dfd980 100644 --- a/python/src/com/jetbrains/python/uv/sdk/configuration/uvVenv.kt +++ b/python/src/com/jetbrains/python/uv/sdk/configuration/uvVenv.kt @@ -20,7 +20,8 @@ import com.jetbrains.python.sdk.setAssociationToModule import com.jetbrains.python.sdk.uv.impl.getUvExecutableLocal import com.jetbrains.python.sdk.uv.setupExistingEnvAndSdk import com.jetbrains.python.sdk.uv.setupNewUvSdkAndEnv -import com.jetbrains.python.util.ShowingMessageErrorSync +import com.jetbrains.python.errorProcessing.ErrorSink +import com.jetbrains.python.errorProcessing.withProject import com.jetbrains.python.venvReader.tryResolvePath import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -46,7 +47,7 @@ internal suspend fun createUvSdk(module: Module, toolId: ToolId, venvsInModule: throw IllegalStateException("Can't determine working dir for the module") } - val errorSink = ShowingMessageErrorSync.withProject(sdkAssociatedModule.project) + val errorSink = ErrorSink().withProject(sdkAssociatedModule.project) val sdkSetupResult = if (envExists) { getUvEnv(venvsInModule)?.let { setupExistingEnvAndSdk(it, uv, workingDir, false)