Various Python refactorings as prerequirements for PY-65425 and PY-78035.

1. Make SDK->Module association function `suspend` to make it testable (one can't test something that runs code by `invokeLater`)
2. Add SDK->Module associations checks into `ensureSdkIsUsable` test tool.
3. Rename fields in v2 widgets to emphasize their purpose.
4. Make this association default for `pyVenv` fixture.

GitOrigin-RevId: 58267750b6dda0b596183c8bd335ce75b00fd41d
This commit is contained in:
Ilya.Kazakevich
2025-04-22 23:35:48 +02:00
committed by intellij-monorepo-bot
parent 1e7ecbbd9c
commit 36d87867b7
21 changed files with 111 additions and 84 deletions

View File

@@ -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.codeInspection.util.IntentionName
@@ -45,7 +45,6 @@ import com.jetbrains.python.sdk.showSdkExecutionException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import java.awt.BorderLayout
import java.awt.Insets
import java.nio.file.Path
import javax.swing.JComponent
import javax.swing.JPanel
@@ -200,7 +199,7 @@ internal class PyEnvironmentYmlSdkConfiguration : PyProjectSdkConfigurationExten
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.condaenv.permission")
add(

View File

@@ -29,12 +29,11 @@ import com.jetbrains.python.sdk.configuration.PyProjectSdkConfigurationExtension
import com.jetbrains.python.sdk.findAmongRoots
import com.jetbrains.python.sdk.pipenv.*
import com.jetbrains.python.sdk.pipenv.ui.PyAddNewPipEnvFromFilePanel
import com.jetbrains.python.sdk.setAssociationToModule
import com.jetbrains.python.sdk.setAssociationToModuleAsync
import com.jetbrains.python.venvReader.VirtualEnvReader
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.awt.BorderLayout
import java.awt.Insets
import java.io.FileNotFoundException
import java.nio.file.Path
import javax.swing.JComponent
@@ -124,7 +123,7 @@ internal class PyPipfileSdkConfiguration : PyProjectSdkConfigurationExtension {
withContext(Dispatchers.EDT) {
LOGGER.debug("Adding associated pipenv environment: $path, $basePath")
sdk.setAssociationToModule(module)
sdk.setAssociationToModuleAsync(module)
SdkConfigurationUtil.addSdk(sdk)
}
@@ -146,7 +145,7 @@ internal class PyPipfileSdkConfiguration : PyProjectSdkConfigurationExtension {
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.pipenv.permission")
add(

View File

@@ -27,7 +27,7 @@ import com.jetbrains.python.sdk.configuration.PyProjectSdkConfigurationExtension
import com.jetbrains.python.sdk.findAmongRoots
import com.jetbrains.python.sdk.poetry.*
import com.jetbrains.python.sdk.poetry.ui.PyAddNewPoetryFromFilePanel
import com.jetbrains.python.sdk.setAssociationToModule
import com.jetbrains.python.sdk.setAssociationToModuleAsync
import com.jetbrains.python.util.runWithModalBlockingOrInBackground
import com.jetbrains.python.venvReader.VirtualEnvReader
import kotlinx.coroutines.Dispatchers
@@ -131,7 +131,7 @@ internal class PyPoetrySdkConfiguration : PyProjectSdkConfigurationExtension {
withContext(Dispatchers.EDT) {
LOGGER.debug("Adding associated poetry environment: ${path}, $basePath")
sdk.setAssociationToModule(module)
sdk.setAssociationToModuleAsync(module)
SdkConfigurationUtil.addSdk(sdk)
}

View File

@@ -4,12 +4,18 @@ package com.jetbrains.python.sdk
import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.JsonSerializer
import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.google.common.io.Resources
import com.intellij.openapi.application.writeAction
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.module.Module
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.util.Version
import com.intellij.util.PathUtilRt
import com.intellij.util.Url
@@ -17,6 +23,7 @@ import com.intellij.util.Urls
import com.intellij.util.system.CpuArch
import com.intellij.util.system.OS
import com.jetbrains.python.psi.LanguageLevel
import org.jetbrains.annotations.ApiStatus
import java.net.URL
import java.nio.charset.StandardCharsets
@@ -171,3 +178,27 @@ object SdksKeeper {
private fun load() = configUrl?.let { Resources.toString(it, StandardCharsets.UTF_8) }
}
@ApiStatus.Internal
suspend fun Sdk.setAssociationToModuleAsync(module: Module) {
val path = module.basePath
assert(path != null) { "Module $module has not paths, and can't be associated" }
setAssociationToPath(path)
}
@ApiStatus.Internal
suspend fun Sdk.setAssociationToPath(path: String?) {
val data = getOrCreateAdditionalData().also {
when {
path != null -> it.associateWithModulePath(path)
else -> it.associatedModulePath = null
}
}
val modificator = sdkModificator
modificator.sdkAdditionalData = data
writeAction {
modificator.commitChanges()
}
}

View File

@@ -15,6 +15,7 @@ import com.intellij.testFramework.junit5.fixture.testFixture
import com.jetbrains.python.PythonBinary
import com.jetbrains.python.getOrThrow
import com.jetbrains.python.sdk.persist
import com.jetbrains.python.sdk.setAssociationToModuleAsync
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.nio.file.Path
@@ -38,6 +39,7 @@ fun TestFixture<SdkFixture<PythonBinary>>.pyVenvFixture(
venvSdk.persist()
if (module != null) {
ModuleRootModificationUtil.setModuleSdk(module, venvSdk)
venvSdk.setAssociationToModuleAsync(module)
}
}
initialized(venvSdk) {

View File

@@ -12,7 +12,6 @@ import com.jetbrains.python.errorProcessing.PyError
import com.jetbrains.python.resolvePythonBinary
import com.jetbrains.python.sdk.createSdk
import com.jetbrains.python.sdk.persist
import com.jetbrains.python.sdk.setAssociationToModule
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jetbrains.annotations.ApiStatus

View File

@@ -8,12 +8,10 @@ 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.newProject.collector.InterpreterStatisticsInfo
import com.jetbrains.python.sdk.ModuleOrProject
import com.jetbrains.python.sdk.add.v2.PySdkCreator
import com.jetbrains.python.sdk.pythonSdk
import com.jetbrains.python.sdk.setAssociationToModule
import com.jetbrains.python.errorProcessing.PyError
import com.jetbrains.python.sdk.configurePythonSdk
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.Dispatchers
@@ -42,7 +40,7 @@ class PyV3BaseProjectSettings(var createGitRepository: Boolean = false) {
.getOr { return@coroutineScope it }
configurePythonSdk(project, module, sdk)
return@coroutineScope com.jetbrains.python.Result.success(Pair(sdk, interpreterStatistics))
return@coroutineScope Result.success(Pair(sdk, interpreterStatistics))
}
private suspend fun getSdkAndInterpreter(module: Module): Result<Pair<Sdk, InterpreterStatisticsInfo>, PyError> =

View File

@@ -43,9 +43,9 @@ import com.jetbrains.python.sdk.flavors.PythonSdkFlavor
import com.jetbrains.python.sdk.flavors.VirtualEnvSdkFlavor
import com.jetbrains.python.sdk.flavors.conda.CondaEnvSdkFlavor
import com.jetbrains.python.target.PyTargetAwareAdditionalData
import com.jetbrains.python.ui.pyMayBeModalBlocking
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.ApiStatus.Internal
import java.nio.file.Files
import java.nio.file.Path
@@ -86,7 +86,7 @@ fun filterSystemWideSdks(existingSdks: List<Sdk>): List<Sdk> {
return existingSdks.filter { it.sdkType is PythonSdkType && it.isSystemWide }
}
@ApiStatus.Internal
@Internal
fun configurePythonSdk(project: Project, module: Module, sdk: Sdk) {
// in case module contains root of the project we consider it as a project wide interpreter
if (project.basePath == module.basePath) {
@@ -239,26 +239,9 @@ fun showSdkExecutionException(sdk: Sdk?, e: ExecutionException, @NlsContexts.Dia
}
}
@Deprecated(replaceWith = ReplaceWith("setAssociationToModule"), message = "Use suspend version")
fun Sdk.setAssociationToModule(module: Module) {
setAssociationToPath(module.basePath)
}
fun Sdk.setAssociationToPath(path: String?) {
val data = getOrCreateAdditionalData().also {
when {
path != null -> it.associateWithModulePath(path)
else -> it.associatedModulePath = null
}
}
val modificator = sdkModificator
modificator.sdkAdditionalData = data
runInEdt {
ApplicationManager.getApplication().runWriteAction {
modificator.commitChanges()
}
}
pyMayBeModalBlocking { setAssociationToModuleAsync(module) }
}
fun Sdk.isAssociatedWithModule(module: Module?): Boolean {

View File

@@ -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.execution.ExecutionException
@@ -25,11 +25,12 @@ import com.jetbrains.python.packaging.PyCondaPackageManagerImpl
import com.jetbrains.python.packaging.PyCondaPackageService
import com.jetbrains.python.sdk.*
import com.jetbrains.python.sdk.add.PyAddNewEnvPanel
import com.jetbrains.python.sdk.conda.condaSupportedLanguages
import com.jetbrains.python.sdk.add.v2.PythonInterpreterSelectionMode
import com.jetbrains.python.sdk.conda.PyCondaSdkCustomizer
import com.jetbrains.python.sdk.conda.condaSupportedLanguages
import com.jetbrains.python.statistics.InterpreterTarget
import com.jetbrains.python.statistics.InterpreterType
import com.jetbrains.python.ui.pyMayBeModalBlocking
import org.jetbrains.annotations.SystemIndependent
import java.awt.BorderLayout
import javax.swing.Icon
@@ -121,7 +122,7 @@ open class PyAddNewCondaEnvPanel(
val sdk = createSdkByGenerateTask(task, existingSdks, null, associatedPath, null)
if (!shared) {
when {
newProjectPath != null -> sdk.setAssociationToPath(newProjectPath)
newProjectPath != null -> pyMayBeModalBlocking { sdk.setAssociationToPath(newProjectPath) }
module != null -> sdk.setAssociationToModule(module)
}
}

View File

@@ -99,9 +99,11 @@ internal abstract class CustomNewEnvironmentCreator(
}.getOr { return it }
newSdk.persist()
module?.excludeInnerVirtualEnv(newSdk)
if (!model.state.makeAvailable.get()) {
module?.let { newSdk.setAssociationToModule(it) }
if (module != null) {
module.excludeInnerVirtualEnv(newSdk)
if (!model.state.makeAvailableForAllProjects.get()) {
newSdk.setAssociationToModuleAsync(module)
}
}
model.addInterpreter(newSdk)
@@ -113,7 +115,7 @@ internal abstract class CustomNewEnvironmentCreator(
InterpreterStatisticsInfo(interpreterType,
target.toStatisticsField(),
false,
model.state.makeAvailable.get(),
model.state.makeAvailableForAllProjects.get(),
false,
false, // todo fix for wsl
InterpreterCreationMode.CUSTOM)

View File

@@ -1,9 +1,6 @@
// 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.openapi.application.EDT
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.application.asContextElement
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.ui.ValidationInfo
@@ -17,22 +14,20 @@ import com.intellij.ui.dsl.builder.*
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.failure
import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo
import com.jetbrains.python.newProjectWizard.collector.PythonNewProjectWizardCollector
import com.jetbrains.python.newProjectWizard.projectPath.ProjectPathFlows.Companion.validatePath
import com.jetbrains.python.sdk.ModuleOrProject
import com.jetbrains.python.sdk.PySdkSettings
import com.jetbrains.python.venvReader.VirtualEnvReader
import com.jetbrains.python.sdk.add.v2.PythonInterpreterSelectionMethod.SELECT_EXISTING
import com.jetbrains.python.sdk.add.v2.PythonSupportedEnvironmentManagers.PYTHON
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.failure
import kotlinx.coroutines.Dispatchers
import com.jetbrains.python.venvReader.VirtualEnvReader
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import java.nio.file.InvalidPathException
import java.nio.file.Path
import java.nio.file.Paths
@@ -135,7 +130,7 @@ class EnvironmentCreatorVenv(model: PythonMutableTargetAddInterpreterModel) : Py
}
row("") {
checkBox(message("available.to.all.projects"))
.bindSelected(model.state.makeAvailable)
.bindSelected(model.state.makeAvailableForAllProjects)
}
}
@@ -164,7 +159,7 @@ class EnvironmentCreatorVenv(model: PythonMutableTargetAddInterpreterModel) : Py
// todo remove project path, or move to controller
try {
val venvPath = Path.of(model.state.venvPath.get())
model.setupVirtualenv(venvPath, model.myProjectPathFlows.projectPathWithDefault.first())
model.setupVirtualenv(venvPath, model.myProjectPathFlows.projectPathWithDefault.first(), moduleOrProject)
}
catch (e: InvalidPathException) {
failure(e.localizedMessage)
@@ -176,7 +171,7 @@ class EnvironmentCreatorVenv(model: PythonMutableTargetAddInterpreterModel) : Py
return InterpreterStatisticsInfo(InterpreterType.VIRTUALENV,
statisticsTarget,
model.state.inheritSitePackages.get(),
model.state.makeAvailable.get(),
model.state.makeAvailableForAllProjects.get(),
false,
//presenter.projectLocationContext is WslContext,
false, // todo fix for wsl

View File

@@ -219,7 +219,7 @@ internal class PythonAddNewEnvironmentPanel(
PROJECT_VENV -> {
val projectPath = projectPathFlows.projectPathWithDefault.first()
// todo just keep venv path, all the rest is in the model
model.setupVirtualenv(projectPath.resolve(VirtualEnvReader.DEFAULT_VIRTUALENV_DIRNAME), projectPath)
model.setupVirtualenv(projectPath.resolve(VirtualEnvReader.DEFAULT_VIRTUALENV_DIRNAME), projectPath, moduleOrProject)
}
BASE_CONDA -> model.selectCondaEnvironment(base = true).asPythonResult()
CUSTOM -> custom.currentSdkManager.getOrCreateSdk(moduleOrProject)

View File

@@ -2,7 +2,6 @@
package com.jetbrains.python.sdk.add.v2.hatch
import com.intellij.openapi.observable.properties.ObservableMutableProperty
import com.intellij.openapi.projectRoots.ProjectJdkTable
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.ui.validation.DialogValidationRequestor
import com.intellij.python.hatch.HatchConfiguration
@@ -23,7 +22,7 @@ import com.jetbrains.python.sdk.add.v2.PythonInterpreterCreationTargets
import com.jetbrains.python.sdk.add.v2.PythonMutableTargetAddInterpreterModel
import com.jetbrains.python.sdk.add.v2.toStatisticsField
import com.jetbrains.python.sdk.destructured
import com.jetbrains.python.sdk.setAssociationToModule
import com.jetbrains.python.sdk.setAssociationToModuleAsync
import com.jetbrains.python.statistics.InterpreterCreationMode
import com.jetbrains.python.statistics.InterpreterType
import kotlinx.coroutines.Dispatchers
@@ -69,7 +68,7 @@ internal class HatchExistingEnvironmentSelector(
val (project, module) = moduleOrProject.destructured
val workingDirectory = resolveHatchWorkingDirectory(project, module).getOr { return it }
environment.createSdk(workingDirectory, module).onSuccess { sdk ->
module?.let { module -> sdk.setAssociationToModule(module) }
module?.let { module -> sdk.setAssociationToModuleAsync(module) }
}
}
}.onSuccess {

View File

@@ -6,6 +6,7 @@ import com.intellij.openapi.application.EDT
import com.intellij.openapi.diagnostic.fileLogger
import com.intellij.openapi.diagnostic.getOrLogException
import com.intellij.openapi.fileChooser.FileChooser
import com.intellij.openapi.observable.properties.GraphProperty
import com.intellij.openapi.observable.properties.ObservableMutableProperty
import com.intellij.openapi.observable.properties.PropertyGraph
import com.intellij.openapi.project.Project
@@ -438,8 +439,12 @@ class MutableTargetState(propertyGraph: PropertyGraph) : AddInterpreterState(pro
val hatchExecutable: ObservableMutableProperty<String> = propertyGraph.property("")
val pipenvExecutable: ObservableMutableProperty<String> = propertyGraph.property("")
val venvPath: ObservableMutableProperty<String> = propertyGraph.property("")
val inheritSitePackages = propertyGraph.property(false)
val makeAvailable = propertyGraph.property(false)
val inheritSitePackages: GraphProperty<Boolean> = propertyGraph.property(false)
/**
* Associate SDK with particular module (if true)
*/
val makeAvailableForAllProjects: GraphProperty<Boolean> = propertyGraph.property(false)
}

View File

@@ -14,21 +14,17 @@ 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.sdk.PythonSdkType
import com.jetbrains.python.sdk.*
import com.jetbrains.python.sdk.conda.createCondaSdkFromExistingEnv
import com.jetbrains.python.sdk.conda.isConda
import com.jetbrains.python.sdk.createSdk
import com.jetbrains.python.sdk.excludeInnerVirtualEnv
import com.jetbrains.python.sdk.flavors.conda.PyCondaCommand
import com.jetbrains.python.sdk.persist
import com.jetbrains.python.sdk.setAssociationToModule
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.nio.file.Path
// todo should it be overriden for targets?
suspend fun PythonMutableTargetAddInterpreterModel.setupVirtualenv(venvPath: Path, projectPath: Path): com.jetbrains.python.Result<Sdk, PyError> {
suspend fun PythonMutableTargetAddInterpreterModel.setupVirtualenv(venvPath: Path, projectPath: Path, moduleOrProject: ModuleOrProject?): com.jetbrains.python.Result<Sdk, PyError> {
val baseSdk = state.baseInterpreter.get()!!
@@ -57,16 +53,22 @@ suspend fun PythonMutableTargetAddInterpreterModel.setupVirtualenv(venvPath: Pat
val newSdk = createSdk(homeFile, projectPath, existingSdks.toTypedArray())
// todo check exclude
val module = ProjectManager.getInstance().openProjects
.firstNotNullOfOrNull {
val module = when (moduleOrProject) {
is ModuleOrProject.ModuleAndProject -> moduleOrProject.module
is ModuleOrProject.ProjectOnly -> {
withContext(Dispatchers.IO) {
ModuleUtil.findModuleForFile(homeFile, it)
ModuleUtil.findModuleForFile(homeFile, moduleOrProject.project)
}
}
null -> null
}
module?.excludeInnerVirtualEnv(newSdk)
if (!this.state.makeAvailable.get()) {
module?.let { newSdk.setAssociationToModule(module) }
if (module != null) {
module.excludeInnerVirtualEnv(newSdk)
if (!this.state.makeAvailableForAllProjects.get()) {
newSdk.setAssociationToModuleAsync(module)
}
}
return com.jetbrains.python.Result.success(newSdk)

View File

@@ -1,4 +1,4 @@
// Copyright 2000-2020 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.
@file:JvmName("PyProjectVirtualEnvConfiguration")
package com.jetbrains.python.sdk.configuration
@@ -33,6 +33,7 @@ import com.jetbrains.python.sdk.flavors.PyFlavorAndData
import com.jetbrains.python.sdk.flavors.PyFlavorData
import com.jetbrains.python.target.PyTargetAwareAdditionalData
import com.jetbrains.python.target.getInterpreterVersion
import com.jetbrains.python.ui.pyModalBlocking
import org.jetbrains.annotations.ApiStatus
/**
@@ -96,8 +97,8 @@ fun createVirtualEnvAndSdkSynchronously(
if (!makeShared) {
when {
module != null -> venvSdk.setAssociationToModule(module)
projectPath != null -> venvSdk.setAssociationToPath(projectPath)
module != null -> pyModalBlocking { venvSdk.setAssociationToModuleAsync(module) }
projectPath != null -> pyModalBlocking { venvSdk.setAssociationToPath(projectPath) }
}
}

View File

@@ -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.pipenv
import com.intellij.execution.configurations.PathEnvironmentVariableUtil
@@ -10,12 +10,10 @@ import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.util.SystemInfo
import com.intellij.platform.ide.progress.withBackgroundProgress
import com.jetbrains.python.PyBundle
import com.jetbrains.python.venvReader.VirtualEnvReader
import com.jetbrains.python.sdk.basePath
import com.jetbrains.python.sdk.createSdk
import com.jetbrains.python.sdk.runExecutable
import com.jetbrains.python.sdk.setAssociationToModule
import com.jetbrains.python.sdk.setAssociationToPath
import com.jetbrains.python.venvReader.VirtualEnvReader
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jetbrains.annotations.ApiStatus.Internal

View File

@@ -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.pipenv.quickFixes
import com.intellij.codeInspection.LocalQuickFix

View File

@@ -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.poetry.quickFixes
import com.intellij.codeInspection.LocalQuickFix

View File

@@ -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.uv
import com.intellij.codeInspection.LocalQuickFix

View File

@@ -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.ui
import com.intellij.platform.ide.progress.ModalTaskOwner
@@ -7,6 +7,8 @@ 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.PySdkBundle
import kotlinx.coroutines.runBlocking
import javax.swing.SwingUtilities
/**
* Runs [code] in background under the modal dialog
@@ -18,3 +20,14 @@ fun <T> pyModalBlocking(modalTaskOwner: ModalTaskOwner = ModalTaskOwner.guess(),
code.invoke()
}
/**
* It is *not* recommended to use this function. Prefer suspend functions.
*/
internal fun <T> pyMayBeModalBlocking(modalTaskOwner: ModalTaskOwner = ModalTaskOwner.guess(), code: suspend () -> T): T =
if (SwingUtilities.isEventDispatchThread()) {
pyModalBlocking(modalTaskOwner, code)
}
else {
runBlocking { code() }
}