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
This commit is contained in:
Ilya.Kazakevich
2026-06-13 21:17:39 +00:00
committed by intellij-monorepo-bot
parent d22724fdf2
commit 003d75a8f8
22 changed files with 75 additions and 62 deletions
@@ -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)
}
}
}
@@ -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<C
* Use [errors] to inspect collected errors and [clear] to acknowledge them.
*/
@TestOnly
class CollectingErrorSink internal constructor() : FlowCollector<PyErrorDetail> {
private val _errors = mutableListOf<PyErrorDetail>()
val errors: List<PyErrorDetail> get() = _errors
class CollectingErrorSink internal constructor() : ErrorSink {
val errors: List<PyErrorDetail>
field = mutableListOf()
override suspend fun emit(value: PyErrorDetail) {
_errors.add(value)
errors.add(value)
}
fun clear() {
_errors.clear()
errors.clear()
}
}
@@ -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<PyErrorDetail>
fun interface ErrorSink : FlowCollector<PyErrorDetail>
/**
* 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<ErrorSink>()
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))
}
@@ -47,6 +47,7 @@
<resource-bundle>messages.PyBundle</resource-bundle>
<extensions defaultExtensionNs="com.intellij">
<applicationService serviceInterface="com.jetbrains.python.errorProcessing.ErrorSink" serviceImplementation="com.jetbrains.python.impl.ShowingMessageErrorSink"/>
<statusBarWidgetFactory id="pythonDependencyFileWidget"
implementation="com.jetbrains.python.requirements.widget.PyDependenciesFileStatusBarWidgetFactory"
order="after pythonInterpreterWidget, before git, before hg, before Notifications"/>
@@ -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"
@@ -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))
}
}
@@ -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;
@@ -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 ->
@@ -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
@@ -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<T : PythonPackageManager, V> : 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.
@@ -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())
}
}
@@ -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()
@@ -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)
@@ -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))
@@ -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)
)
@@ -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 <P : PathHolder> Panel.pythonInterpreterComboBox(
onPathSelected: suspend (P) -> PyResult<PythonSelectableInterpreter<P>>,
customizer: RowsRange.() -> Unit = {},
): PythonInterpreterComboBox<P> {
val comboBox = PythonInterpreterComboBox(onPathSelected, fileSystem, ShowingMessageErrorSync)
val comboBox = PythonInterpreterComboBox(onPathSelected, fileSystem, ErrorSink())
.apply {
setBusy(true)
}
@@ -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<PathHolder.Eel>.uvCreator(module: Module?): EnvironmentCreatorUv<PathHolder.Eel> {
val errorSink = module?.project?.let { ShowingMessageErrorSync.withProject(it) } ?: ShowingMessageErrorSync
val errorSink = module?.project?.let { ErrorSink().withProject(it) } ?: ErrorSink()
return EnvironmentCreatorUv(this, module, errorSink)
}
@@ -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
}
@@ -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) {
@@ -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<PathHolder.Target>
private var validationErrors: Collection<ValidationInfo> = 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)
)
@@ -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;
@@ -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)