From 3d6abaf160fc1842a497c3eded6d9ade3983ee1f Mon Sep 17 00:00:00 2001 From: "Ilya.Kazakevich" Date: Mon, 15 Sep 2025 22:30:59 +0200 Subject: [PATCH] PY-79486: Cleanup (WIP) We now have only one (`python.pyproject.model`) registry key enabled by default. Number of boilerplate was a little bit decreased too. GitOrigin-RevId: 0f7ecf324be038430230d9865688ce22ee922de6 --- .../configuration/PyUvSdkConfiguration.kt | 3 +- .../intellij.python.community.impl.xml | 6 +-- .../python/PyDirectoryIconProvider.java | 3 +- .../inspections/PyInterpreterInspection.java | 3 +- .../PyProjectTomlOpenProcessorBase.kt | 43 ++++++++++++++++++ .../projectModel/poetry/PoetryLinkAction.kt | 4 +- .../projectModel/poetry/PoetryProjectAware.kt | 18 ++++---- .../poetry/PoetryProjectOpenProcessor.kt | 45 +++---------------- .../projectModel/poetry/PoetrySyncAction.kt | 4 +- .../poetry/PoetryUnlinkedProjectAware.kt | 4 +- .../python/projectModel/projectModel.kt | 7 +++ .../python/projectModel/uv/UvLinkAction.kt | 3 +- .../python/projectModel/uv/UvProjectAware.kt | 20 ++++----- .../projectModel/uv/UvProjectOpenProcessor.kt | 44 ++---------------- .../python/projectModel/uv/UvSyncAction.kt | 4 +- .../projectModel/uv/UvUnlinkedProjectAware.kt | 6 +-- .../PyProjectSdkConfiguration.kt | 3 +- .../PoetryProjectOpenIntegrationTest.kt | 6 +-- .../PoetryProjectSyncIntegrationTest.kt | 4 +- .../uv/UvProjectOpenIntegrationTest.kt | 2 +- .../uv/UvProjectSyncIntegrationTest.kt | 2 +- 21 files changed, 108 insertions(+), 126 deletions(-) create mode 100644 python/src/com/jetbrains/python/projectModel/PyProjectTomlOpenProcessorBase.kt diff --git a/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/configuration/PyUvSdkConfiguration.kt b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/configuration/PyUvSdkConfiguration.kt index b0117a0f598a..9cf7e571f2f7 100644 --- a/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/configuration/PyUvSdkConfiguration.kt +++ b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/configuration/PyUvSdkConfiguration.kt @@ -12,6 +12,7 @@ import com.jetbrains.python.errorProcessing.MessageError import com.jetbrains.python.errorProcessing.PyResult import com.jetbrains.python.getOrNull import com.jetbrains.python.onSuccess +import com.jetbrains.python.projectModel.enablePyProjectToml import com.jetbrains.python.projectModel.uv.UvProjectModelService import com.jetbrains.python.sdk.* import com.jetbrains.python.sdk.configuration.PyProjectSdkConfigurationExtension @@ -45,7 +46,7 @@ class PyUvSdkConfiguration : PyProjectSdkConfigurationExtension { private suspend fun createUv(module: Module): PyResult { val sdkAssociatedModule: Module - if (Registry.`is`("python.project.model.uv", false)) { + if (enablePyProjectToml) { val uvWorkspace = UvProjectModelService.findWorkspace(module) sdkAssociatedModule = uvWorkspace?.root ?: module } diff --git a/python/pluginResources/intellij.python.community.impl.xml b/python/pluginResources/intellij.python.community.impl.xml index 5fe68177f341..0f88000c641f 100644 --- a/python/pluginResources/intellij.python.community.impl.xml +++ b/python/pluginResources/intellij.python.community.impl.xml @@ -659,10 +659,8 @@ - - + diff --git a/python/src/com/jetbrains/python/PyDirectoryIconProvider.java b/python/src/com/jetbrains/python/PyDirectoryIconProvider.java index 1e0b73769bf5..c7fa9ee9edc6 100644 --- a/python/src/com/jetbrains/python/PyDirectoryIconProvider.java +++ b/python/src/com/jetbrains/python/PyDirectoryIconProvider.java @@ -11,6 +11,7 @@ import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; +import com.jetbrains.python.projectModel.ProjectModelKt; import com.jetbrains.python.psi.PyUtil; import org.jetbrains.annotations.NotNull; @@ -36,7 +37,7 @@ public final class PyDirectoryIconProvider extends IconProvider { } private static boolean isMultimoduleProjectDetectionEnabled() { - return Registry.is("python.project.model.uv") || Registry.is("python.project.model.poetry"); + return ProjectModelKt.getEnablePyProjectToml(); } private static boolean isSpecialDirectory(@NotNull PsiDirectory directory) { diff --git a/python/src/com/jetbrains/python/inspections/PyInterpreterInspection.java b/python/src/com/jetbrains/python/inspections/PyInterpreterInspection.java index 89d67bced8f8..c1b39292420e 100644 --- a/python/src/com/jetbrains/python/inspections/PyInterpreterInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyInterpreterInspection.java @@ -43,6 +43,7 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.workspaceModel.ide.impl.legacyBridge.module.ModuleEntityUtils; import com.jetbrains.python.PyPsiBundle; import com.jetbrains.python.PythonIdeLanguageCustomization; +import com.jetbrains.python.projectModel.ProjectModelKt; import com.jetbrains.python.projectModel.uv.UvProjectModelService; import com.jetbrains.python.projectModel.uv.UvProjectModelService.UvWorkspace; import com.jetbrains.python.psi.LanguageLevel; @@ -131,7 +132,7 @@ public final class PyInterpreterInspection extends PyInspection { boolean isAlreadyUsedByModule = (PySdkExtKt.getPythonSdk(module) == sdk); boolean isAssociatedWithThisModule = associatedModulePath != null && associatedModulePath.equals(BasePySdkExtKt.getBasePath(module)); // TODO: this logic should be generalized via the workspace manager - boolean isAssociatedWithUvRoot = associatedModulePath != null && Registry.is("python.project.model.uv", false) && + boolean isAssociatedWithUvRoot = associatedModulePath != null && ProjectModelKt.getEnablePyProjectToml() && isAssociatedWithUvWorkspaceRootModule(associatedModulePath, module); if (!isAlreadyUsedByModule && !isAssociatedWithThisModule && !isAssociatedWithUvRoot && diff --git a/python/src/com/jetbrains/python/projectModel/PyProjectTomlOpenProcessorBase.kt b/python/src/com/jetbrains/python/projectModel/PyProjectTomlOpenProcessorBase.kt new file mode 100644 index 000000000000..97c1184f293c --- /dev/null +++ b/python/src/com/jetbrains/python/projectModel/PyProjectTomlOpenProcessorBase.kt @@ -0,0 +1,43 @@ +// 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.projectModel + +import com.intellij.ide.impl.runUnderModalProgressIfIsEdt +import com.intellij.openapi.externalSystem.importing.AbstractOpenProjectProvider +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.projectImport.ProjectOpenProcessor + +/** + * Automatically configures a new project without `.idea/` as a project managed by Poetry if there is + * a top-level pyproject.toml at the project root. + * The user will be asked if + * - There are several possible build systems for the project. + * - The top-level pyproject.toml is added afterward in a project with existing `.idea/`. + * - pyproject.toml files are found in non-top-level directories (requires IJPL-180733). + */ +internal abstract class PyProjectTomlOpenProcessorBase : ProjectOpenProcessor() { + abstract val importProvider: AbstractOpenProjectProvider + + final override fun canOpenProject(file: VirtualFile): Boolean = enablePyProjectToml && importProvider.canOpenProject(file) + + final override fun doOpenProject(virtualFile: VirtualFile, projectToClose: Project?, forceOpenInNewFrame: Boolean): Project? { + @Suppress("DEPRECATION") // Can't get rid of it because the platform doesn't provide neither suspend API nor thread guarantee + return runUnderModalProgressIfIsEdt { importProvider.openProject(virtualFile, projectToClose, forceOpenInNewFrame) } + } + + final override suspend fun openProjectAsync( + virtualFile: VirtualFile, + projectToClose: Project?, + forceOpenInNewFrame: Boolean, + ): Project? { + return importProvider.openProject(virtualFile, projectToClose, forceOpenInNewFrame) + } + + final override fun canImportProjectAfterwards(): Boolean = true + + // TODO Requires IJPL-180733 + + final override suspend fun importProjectAfterwardsAsync(project: Project, file: VirtualFile) { + importProvider.linkToExistingProjectAsync(file, project) + } +} \ No newline at end of file diff --git a/python/src/com/jetbrains/python/projectModel/poetry/PoetryLinkAction.kt b/python/src/com/jetbrains/python/projectModel/poetry/PoetryLinkAction.kt index 3b57dd0825a5..cd3392600da2 100644 --- a/python/src/com/jetbrains/python/projectModel/poetry/PoetryLinkAction.kt +++ b/python/src/com/jetbrains/python/projectModel/poetry/PoetryLinkAction.kt @@ -7,11 +7,11 @@ import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.components.Service import com.intellij.openapi.components.service import com.intellij.openapi.project.Project -import com.intellij.openapi.util.registry.Registry import com.intellij.platform.backend.observation.ActivityKey import com.intellij.platform.backend.observation.launchTracked import com.intellij.platform.backend.observation.trackActivityBlocking import com.jetbrains.python.PyBundle +import com.jetbrains.python.projectModel.enablePyProjectToml import com.jetbrains.python.projectModel.poetry.PoetryLinkAction.CoroutineScopeService.Companion.coroutineScope import kotlinx.coroutines.CoroutineScope import org.jetbrains.annotations.Nls @@ -32,7 +32,7 @@ internal class PoetryLinkAction : AnAction() { } override fun update(e: AnActionEvent) { - e.presentation.isEnabledAndVisible = Registry.`is`("python.project.model.poetry") + e.presentation.isEnabledAndVisible = enablePyProjectToml } override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT diff --git a/python/src/com/jetbrains/python/projectModel/poetry/PoetryProjectAware.kt b/python/src/com/jetbrains/python/projectModel/poetry/PoetryProjectAware.kt index 9d6b6de9a0e5..4a24c6cd74a3 100644 --- a/python/src/com/jetbrains/python/projectModel/poetry/PoetryProjectAware.kt +++ b/python/src/com/jetbrains/python/projectModel/poetry/PoetryProjectAware.kt @@ -9,7 +9,6 @@ import com.intellij.openapi.externalSystem.autoimport.* import com.intellij.openapi.project.Project import com.intellij.openapi.startup.ProjectActivity import com.intellij.openapi.util.io.toCanonicalPath -import com.intellij.openapi.util.registry.Registry import com.intellij.platform.backend.observation.launchTracked import com.intellij.platform.backend.workspace.workspaceModel import com.intellij.platform.workspace.jps.entities.ContentRootEntity @@ -18,6 +17,7 @@ import com.intellij.platform.workspace.storage.impl.url.toVirtualFileUrl import com.intellij.platform.workspace.storage.url.VirtualFileUrl import com.intellij.python.pyproject.PY_PROJECT_TOML import com.intellij.workspaceModel.ide.toPath +import com.jetbrains.python.projectModel.enablePyProjectToml import com.jetbrains.python.projectModel.poetry.PoetryProjectAware.CoroutineScopeService.Companion.coroutineScope import kotlinx.coroutines.CoroutineScope import java.nio.file.Path @@ -42,7 +42,7 @@ internal class PoetryProjectAware( } override fun reloadProject(context: ExternalSystemProjectReloadContext) { - project.coroutineScope.launchTracked { + project.coroutineScope.launchTracked { PoetryProjectModelService.syncProjectModelRoot(project, Path.of(projectId.externalProjectPath)) } } @@ -70,14 +70,14 @@ internal class PoetryProjectAware( get() = service().coroutineScope } } - - internal class PoetrySyncStartupActivity: ProjectActivity { + + internal class PoetrySyncStartupActivity : ProjectActivity { init { - if (!Registry.`is`("python.project.model.poetry")) { + if (!enablePyProjectToml) { throw ExtensionNotApplicableException.create() } } - + override suspend fun execute(project: Project) { val projectTracker = ExternalSystemProjectTracker.getInstance(project) project.service().getLinkedProjects().forEach { projectRoot -> @@ -89,13 +89,13 @@ internal class PoetryProjectAware( } } - internal class PoetryListener(private val project: Project): PoetrySettingsListener { + internal class PoetryListener(private val project: Project) : PoetrySettingsListener { init { - if (!Registry.`is`("python.project.model.poetry")) { + if (!enablePyProjectToml) { throw ExtensionNotApplicableException.create() } } - + override fun onLinkedProjectAdded(projectRoot: Path) { val projectTracker = ExternalSystemProjectTracker.getInstance(project) val projectId = ExternalSystemProjectId(PoetryConstants.SYSTEM_ID, projectRoot.toCanonicalPath()) diff --git a/python/src/com/jetbrains/python/projectModel/poetry/PoetryProjectOpenProcessor.kt b/python/src/com/jetbrains/python/projectModel/poetry/PoetryProjectOpenProcessor.kt index f9baf356e5f0..2dfabe6a5987 100644 --- a/python/src/com/jetbrains/python/projectModel/poetry/PoetryProjectOpenProcessor.kt +++ b/python/src/com/jetbrains/python/projectModel/poetry/PoetryProjectOpenProcessor.kt @@ -1,50 +1,15 @@ // 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.projectModel.poetry -import com.intellij.ide.impl.runUnderModalProgressIfIsEdt -import com.intellij.openapi.project.Project -import com.intellij.openapi.util.registry.Registry -import com.intellij.openapi.vfs.VirtualFile -import com.intellij.projectImport.ProjectOpenProcessor import com.jetbrains.python.PyBundle import com.jetbrains.python.icons.PythonIcons +import com.jetbrains.python.projectModel.PyProjectTomlOpenProcessorBase import org.jetbrains.annotations.Nls import javax.swing.Icon -/** - * Automatically configures a new project without `.idea/` as a project managed by Poetry if there is - * a top-level pyproject.toml at the project root. - * The user will be asked if - * - There are several possible build systems for the project. - * - The top-level pyproject.toml is added afterward in a project with existing `.idea/`. - * - pyproject.toml files are found in non-top-level directories (requires IJPL-180733). - */ -internal class PoetryProjectOpenProcessor: ProjectOpenProcessor() { - private val importProvider = PoetryProjectOpenProvider() - + +internal class PoetryProjectOpenProcessor : PyProjectTomlOpenProcessorBase() { + override val importProvider = PoetryProjectOpenProvider() override val name: @Nls String = PyBundle.message("python.project.model.poetry") - - override val icon: Icon? - get() = PythonIcons.Python.Origami - - override fun canOpenProject(file: VirtualFile): Boolean { - return Registry.`is`("python.project.model.poetry") && importProvider.canOpenProject(file) - } - - override fun doOpenProject(virtualFile: VirtualFile, projectToClose: Project?, forceOpenInNewFrame: Boolean): Project? { - return runUnderModalProgressIfIsEdt { importProvider.openProject(virtualFile, projectToClose, forceOpenInNewFrame) } - } - - override suspend fun openProjectAsync(virtualFile: VirtualFile, - projectToClose: Project?, - forceOpenInNewFrame: Boolean): Project? { - return importProvider.openProject(virtualFile, projectToClose, forceOpenInNewFrame) - } - - override fun canImportProjectAfterwards(): Boolean = true - - // TODO Requires IJPL-180733 - override suspend fun importProjectAfterwardsAsync(project: Project, file: VirtualFile) { - importProvider.linkToExistingProjectAsync(file, project) - } + override val icon: Icon = PythonIcons.Python.Origami } \ No newline at end of file diff --git a/python/src/com/jetbrains/python/projectModel/poetry/PoetrySyncAction.kt b/python/src/com/jetbrains/python/projectModel/poetry/PoetrySyncAction.kt index 642a21965eb3..6ba592eb52f3 100644 --- a/python/src/com/jetbrains/python/projectModel/poetry/PoetrySyncAction.kt +++ b/python/src/com/jetbrains/python/projectModel/poetry/PoetrySyncAction.kt @@ -7,11 +7,11 @@ import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.components.Service import com.intellij.openapi.components.service import com.intellij.openapi.project.Project -import com.intellij.openapi.util.registry.Registry import com.intellij.platform.backend.observation.ActivityKey import com.intellij.platform.backend.observation.launchTracked import com.intellij.platform.backend.observation.trackActivityBlocking import com.jetbrains.python.PyBundle +import com.jetbrains.python.projectModel.enablePyProjectToml import com.jetbrains.python.projectModel.poetry.PoetrySyncAction.CoroutineScopeService.Companion.coroutineScope import kotlinx.coroutines.CoroutineScope import org.jetbrains.annotations.Nls @@ -30,7 +30,7 @@ internal class PoetrySyncAction : AnAction() { } override fun update(e: AnActionEvent) { - e.presentation.isEnabledAndVisible = Registry.`is`("python.project.model.poetry") + e.presentation.isEnabledAndVisible = enablePyProjectToml } override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT diff --git a/python/src/com/jetbrains/python/projectModel/poetry/PoetryUnlinkedProjectAware.kt b/python/src/com/jetbrains/python/projectModel/poetry/PoetryUnlinkedProjectAware.kt index 6e7e8e1aff65..4f50697928a8 100644 --- a/python/src/com/jetbrains/python/projectModel/poetry/PoetryUnlinkedProjectAware.kt +++ b/python/src/com/jetbrains/python/projectModel/poetry/PoetryUnlinkedProjectAware.kt @@ -8,8 +8,8 @@ import com.intellij.openapi.externalSystem.autolink.ExternalSystemUnlinkedProjec import com.intellij.openapi.externalSystem.model.ProjectSystemId import com.intellij.openapi.project.Project import com.intellij.openapi.util.io.toCanonicalPath -import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.vfs.VirtualFile +import com.jetbrains.python.projectModel.enablePyProjectToml import java.nio.file.Path internal class PoetryUnlinkedProjectAware : ExternalSystemUnlinkedProjectAware { @@ -18,7 +18,7 @@ internal class PoetryUnlinkedProjectAware : ExternalSystemUnlinkedProjectAware { override val systemId: ProjectSystemId = PoetryConstants.SYSTEM_ID override fun isBuildFile(project: Project, buildFile: VirtualFile): Boolean { - return Registry.`is`("python.project.model.poetry") && openProvider.canOpenProject(buildFile) + return enablePyProjectToml && openProvider.canOpenProject(buildFile) } override fun isLinkedProject(project: Project, externalProjectPath: String): Boolean { diff --git a/python/src/com/jetbrains/python/projectModel/projectModel.kt b/python/src/com/jetbrains/python/projectModel/projectModel.kt index 06aacc81e7a5..6cf3ab890f28 100644 --- a/python/src/com/jetbrains/python/projectModel/projectModel.kt +++ b/python/src/com/jetbrains/python/projectModel/projectModel.kt @@ -2,6 +2,7 @@ package com.jetbrains.python.projectModel import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.util.registry.Registry import com.intellij.util.concurrency.annotations.RequiresBackgroundThread import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -10,6 +11,12 @@ import java.nio.file.Path import kotlin.io.path.ExperimentalPathApi import kotlin.io.path.visitFileTree + +/** + * Convert `pyproject.toml` to modules + */ +val enablePyProjectToml: Boolean get() = Registry.`is`("python.pyproject.model") + /** * Represents a graph of modules residing under a common root directory. * These modules might depend on each other, but it's not a requirement. diff --git a/python/src/com/jetbrains/python/projectModel/uv/UvLinkAction.kt b/python/src/com/jetbrains/python/projectModel/uv/UvLinkAction.kt index 8dddfb8e51db..a6eed5b7c3e0 100644 --- a/python/src/com/jetbrains/python/projectModel/uv/UvLinkAction.kt +++ b/python/src/com/jetbrains/python/projectModel/uv/UvLinkAction.kt @@ -12,6 +12,7 @@ import com.intellij.platform.backend.observation.ActivityKey import com.intellij.platform.backend.observation.launchTracked import com.intellij.platform.backend.observation.trackActivityBlocking import com.jetbrains.python.PyBundle +import com.jetbrains.python.projectModel.enablePyProjectToml import com.jetbrains.python.projectModel.uv.UvLinkAction.CoroutineScopeService.Companion.coroutineScope import kotlinx.coroutines.CoroutineScope import org.jetbrains.annotations.Nls @@ -32,7 +33,7 @@ internal class UvLinkAction : AnAction() { } override fun update(e: AnActionEvent) { - e.presentation.isEnabledAndVisible = Registry.`is`("python.project.model.uv") + e.presentation.isEnabledAndVisible = enablePyProjectToml } override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT diff --git a/python/src/com/jetbrains/python/projectModel/uv/UvProjectAware.kt b/python/src/com/jetbrains/python/projectModel/uv/UvProjectAware.kt index 2f61a656e2af..28e69a992ebf 100644 --- a/python/src/com/jetbrains/python/projectModel/uv/UvProjectAware.kt +++ b/python/src/com/jetbrains/python/projectModel/uv/UvProjectAware.kt @@ -9,7 +9,6 @@ import com.intellij.openapi.externalSystem.autoimport.* import com.intellij.openapi.project.Project import com.intellij.openapi.startup.ProjectActivity import com.intellij.openapi.util.io.toCanonicalPath -import com.intellij.openapi.util.registry.Registry import com.intellij.platform.backend.observation.launchTracked import com.intellij.platform.backend.workspace.workspaceModel import com.intellij.platform.workspace.jps.entities.ContentRootEntity @@ -18,6 +17,7 @@ import com.intellij.platform.workspace.storage.impl.url.toVirtualFileUrl import com.intellij.platform.workspace.storage.url.VirtualFileUrl import com.intellij.python.pyproject.PY_PROJECT_TOML import com.intellij.workspaceModel.ide.toPath +import com.jetbrains.python.projectModel.enablePyProjectToml import com.jetbrains.python.projectModel.uv.UvProjectAware.CoroutineScopeService.Companion.coroutineScope import kotlinx.coroutines.CoroutineScope import java.nio.file.Path @@ -42,7 +42,7 @@ internal class UvProjectAware( } override fun reloadProject(context: ExternalSystemProjectReloadContext) { - project.coroutineScope.launchTracked { + project.coroutineScope.launchTracked { UvProjectModelService.syncProjectModelRoot(project, Path.of(projectId.externalProjectPath)) } } @@ -70,14 +70,14 @@ internal class UvProjectAware( get() = service().coroutineScope } } - - private class UvSyncStartupActivity: ProjectActivity { + + internal class UvSyncStartupActivity : ProjectActivity { init { - if (!Registry.`is`("python.project.model.uv")) { + if (!enablePyProjectToml) { throw ExtensionNotApplicableException.create() } } - + override suspend fun execute(project: Project) { val projectTracker = ExternalSystemProjectTracker.getInstance(project) project.service().getLinkedProjects().forEach { projectRoot -> @@ -88,14 +88,14 @@ internal class UvProjectAware( } } } - - private class UvListener(private val project: Project): UvSettingsListener { + + internal class UvListener(private val project: Project) : UvSettingsListener { init { - if (!Registry.`is`("python.project.model.uv")) { + if (!enablePyProjectToml) { throw ExtensionNotApplicableException.create() } } - + override fun onLinkedProjectAdded(projectRoot: Path) { val projectTracker = ExternalSystemProjectTracker.getInstance(project) val projectId = ExternalSystemProjectId(UvConstants.SYSTEM_ID, projectRoot.toCanonicalPath()) diff --git a/python/src/com/jetbrains/python/projectModel/uv/UvProjectOpenProcessor.kt b/python/src/com/jetbrains/python/projectModel/uv/UvProjectOpenProcessor.kt index 435e6d2cf4f2..5844a6a1781a 100644 --- a/python/src/com/jetbrains/python/projectModel/uv/UvProjectOpenProcessor.kt +++ b/python/src/com/jetbrains/python/projectModel/uv/UvProjectOpenProcessor.kt @@ -1,50 +1,14 @@ // 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.projectModel.uv -import com.intellij.ide.impl.runUnderModalProgressIfIsEdt -import com.intellij.openapi.project.Project -import com.intellij.openapi.util.registry.Registry -import com.intellij.openapi.vfs.VirtualFile -import com.intellij.projectImport.ProjectOpenProcessor import com.jetbrains.python.PyBundle import com.jetbrains.python.icons.PythonIcons +import com.jetbrains.python.projectModel.PyProjectTomlOpenProcessorBase import org.jetbrains.annotations.Nls import javax.swing.Icon -/** - * Automatically configures a new project without `.idea/` as a project managed by uv if there is - * a top-level pyproject.toml at the project root. - * The user will be asked if - * - There are several possible build systems for the project. - * - The top-level pyproject.toml is added afterward in a project with existing `.idea/`. - * - pyproject.toml files are found in non-top-level directories (requires IJPL-180733). - */ -internal class UvProjectOpenProcessor: ProjectOpenProcessor() { - private val importProvider = UvProjectOpenProvider() - +internal class UvProjectOpenProcessor : PyProjectTomlOpenProcessorBase() { + override val importProvider = UvProjectOpenProvider() override val name: @Nls String = PyBundle.message("python.project.model.uv") - - override val icon: Icon? - get() = PythonIcons.UV - - override fun canOpenProject(file: VirtualFile): Boolean { - return Registry.`is`("python.project.model.uv") && importProvider.canOpenProject(file) - } - - override fun doOpenProject(virtualFile: VirtualFile, projectToClose: Project?, forceOpenInNewFrame: Boolean): Project? { - return runUnderModalProgressIfIsEdt { importProvider.openProject(virtualFile, projectToClose, forceOpenInNewFrame) } - } - - override suspend fun openProjectAsync(virtualFile: VirtualFile, - projectToClose: Project?, - forceOpenInNewFrame: Boolean): Project? { - return importProvider.openProject(virtualFile, projectToClose, forceOpenInNewFrame) - } - - override fun canImportProjectAfterwards(): Boolean = true - - // TODO Requires IJPL-180733 - override suspend fun importProjectAfterwardsAsync(project: Project, file: VirtualFile) { - importProvider.linkToExistingProjectAsync(file, project) - } + override val icon: Icon = PythonIcons.UV } \ No newline at end of file diff --git a/python/src/com/jetbrains/python/projectModel/uv/UvSyncAction.kt b/python/src/com/jetbrains/python/projectModel/uv/UvSyncAction.kt index 036fa239f746..991c83a124a0 100644 --- a/python/src/com/jetbrains/python/projectModel/uv/UvSyncAction.kt +++ b/python/src/com/jetbrains/python/projectModel/uv/UvSyncAction.kt @@ -7,11 +7,11 @@ import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.components.Service import com.intellij.openapi.components.service import com.intellij.openapi.project.Project -import com.intellij.openapi.util.registry.Registry import com.intellij.platform.backend.observation.ActivityKey import com.intellij.platform.backend.observation.launchTracked import com.intellij.platform.backend.observation.trackActivityBlocking import com.jetbrains.python.PyBundle +import com.jetbrains.python.projectModel.enablePyProjectToml import com.jetbrains.python.projectModel.uv.UvSyncAction.CoroutineScopeService.Companion.coroutineScope import kotlinx.coroutines.CoroutineScope import org.jetbrains.annotations.Nls @@ -30,7 +30,7 @@ internal class UvSyncAction : AnAction() { } override fun update(e: AnActionEvent) { - e.presentation.isEnabledAndVisible = Registry.Companion.`is`("python.project.model.uv") + e.presentation.isEnabledAndVisible = enablePyProjectToml } override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT diff --git a/python/src/com/jetbrains/python/projectModel/uv/UvUnlinkedProjectAware.kt b/python/src/com/jetbrains/python/projectModel/uv/UvUnlinkedProjectAware.kt index 64bae96bdd9a..bf2a250cd8df 100644 --- a/python/src/com/jetbrains/python/projectModel/uv/UvUnlinkedProjectAware.kt +++ b/python/src/com/jetbrains/python/projectModel/uv/UvUnlinkedProjectAware.kt @@ -8,17 +8,17 @@ import com.intellij.openapi.externalSystem.autolink.ExternalSystemUnlinkedProjec import com.intellij.openapi.externalSystem.model.ProjectSystemId import com.intellij.openapi.project.Project import com.intellij.openapi.util.io.toCanonicalPath -import com.intellij.openapi.util.registry.Registry import com.intellij.openapi.vfs.VirtualFile +import com.jetbrains.python.projectModel.enablePyProjectToml import java.nio.file.Path internal class UvUnlinkedProjectAware : ExternalSystemUnlinkedProjectAware { private val openProvider = UvProjectOpenProvider() - + override val systemId: ProjectSystemId = UvConstants.SYSTEM_ID override fun isBuildFile(project: Project, buildFile: VirtualFile): Boolean { - return Registry.`is`("python.project.model.uv") && openProvider.canOpenProject(buildFile) + return enablePyProjectToml && openProvider.canOpenProject(buildFile) } override fun isLinkedProject(project: Project, externalProjectPath: String): Boolean { diff --git a/python/src/com/jetbrains/python/sdk/configuration/PyProjectSdkConfiguration.kt b/python/src/com/jetbrains/python/sdk/configuration/PyProjectSdkConfiguration.kt index cd41d9808296..c63138d0a096 100644 --- a/python/src/com/jetbrains/python/sdk/configuration/PyProjectSdkConfiguration.kt +++ b/python/src/com/jetbrains/python/sdk/configuration/PyProjectSdkConfiguration.kt @@ -21,6 +21,7 @@ import com.jetbrains.python.PySdkBundle import com.jetbrains.python.PythonPluginDisposable import com.jetbrains.python.errorProcessing.PyResult import com.jetbrains.python.packaging.utils.PyPackageCoroutine +import com.jetbrains.python.projectModel.enablePyProjectToml import com.jetbrains.python.projectModel.uv.UvProjectModelService import com.jetbrains.python.sdk.PySdkPopupFactory import com.jetbrains.python.sdk.configuration.suppressors.PyInterpreterInspectionSuppressor @@ -62,7 +63,7 @@ object PyProjectSdkConfiguration { } ?: return false // TODO Move this to PyUvSdkConfiguration, show better notification - if (sdk.isUv && Registry.`is`("python.project.model.uv", false)) { + if (sdk.isUv && enablePyProjectToml) { val ws = UvProjectModelService.findWorkspace(module) if (ws != null) { for (wsModule in ws.members + ws.root) { diff --git a/python/testSrc/com/jetbrains/python/projectModel/poetry/PoetryProjectOpenIntegrationTest.kt b/python/testSrc/com/jetbrains/python/projectModel/poetry/PoetryProjectOpenIntegrationTest.kt index a2017b09fda7..6c755b1e568d 100644 --- a/python/testSrc/com/jetbrains/python/projectModel/poetry/PoetryProjectOpenIntegrationTest.kt +++ b/python/testSrc/com/jetbrains/python/projectModel/poetry/PoetryProjectOpenIntegrationTest.kt @@ -17,7 +17,7 @@ import org.junit.jupiter.api.Test import kotlin.io.path.writeText import kotlin.time.Duration.Companion.seconds -@RegistryKey("python.project.model.poetry", "true") +@RegistryKey("python.pyproject.model", "true") @TestApplication class PoetryProjectOpenIntegrationTest { private val testRoot by tempPathFixture() @@ -57,7 +57,7 @@ class PoetryProjectOpenIntegrationTest { @Test fun `project with top-level PEP-621 pyproject-toml containing tool-poetry table is automatically linked`() = timeoutRunBlocking(timeout = 20.seconds) { val projectPath = testRoot.resolve("project") - + projectPath.createFile(PY_PROJECT_TOML).writeText(""" [project] name = "project" @@ -76,7 +76,7 @@ class PoetryProjectOpenIntegrationTest { @Test fun `project with top-level poetry-lock is automatically linked`() = timeoutRunBlocking(timeout = 20.seconds) { val projectPath = testRoot.resolve("project") - + projectPath.createFile("poetry.lock").writeText("""""") projectPath.createFile(PY_PROJECT_TOML).writeText(""" diff --git a/python/testSrc/com/jetbrains/python/projectModel/poetry/PoetryProjectSyncIntegrationTest.kt b/python/testSrc/com/jetbrains/python/projectModel/poetry/PoetryProjectSyncIntegrationTest.kt index 4dcce0e592ba..669079003aed 100644 --- a/python/testSrc/com/jetbrains/python/projectModel/poetry/PoetryProjectSyncIntegrationTest.kt +++ b/python/testSrc/com/jetbrains/python/projectModel/poetry/PoetryProjectSyncIntegrationTest.kt @@ -23,7 +23,7 @@ import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import kotlin.io.path.writeText -@RegistryKey("python.project.model.poetry", "true") +@RegistryKey("python.pyproject.model", "true") @TestApplication class PoetryProjectSyncIntegrationTest { private val testRootFixture = tempPathFixture() @@ -125,7 +125,7 @@ class PoetryProjectSyncIntegrationTest { DependencyAssertions.assertDependencies(module, INHERITED_SDK, MODULE_SOURCE) } } - + suspend fun syncAllProjects(project: Project) { multiprojectFixture.awaitProjectConfiguration(project) { PoetryProjectModelService.syncAllProjectModelRoots(project) diff --git a/python/testSrc/com/jetbrains/python/projectModel/uv/UvProjectOpenIntegrationTest.kt b/python/testSrc/com/jetbrains/python/projectModel/uv/UvProjectOpenIntegrationTest.kt index 615c3ef552f3..184e30c2c1a2 100644 --- a/python/testSrc/com/jetbrains/python/projectModel/uv/UvProjectOpenIntegrationTest.kt +++ b/python/testSrc/com/jetbrains/python/projectModel/uv/UvProjectOpenIntegrationTest.kt @@ -18,7 +18,7 @@ import org.junit.jupiter.api.Test import kotlin.io.path.writeText import kotlin.time.Duration.Companion.seconds -@RegistryKey("python.project.model.uv", "true") +@RegistryKey("python.pyproject.model", "true") @TestApplication class UvProjectOpenIntegrationTest { private val testRoot by tempPathFixture() diff --git a/python/testSrc/com/jetbrains/python/projectModel/uv/UvProjectSyncIntegrationTest.kt b/python/testSrc/com/jetbrains/python/projectModel/uv/UvProjectSyncIntegrationTest.kt index a7c000197f16..9c822b682103 100644 --- a/python/testSrc/com/jetbrains/python/projectModel/uv/UvProjectSyncIntegrationTest.kt +++ b/python/testSrc/com/jetbrains/python/projectModel/uv/UvProjectSyncIntegrationTest.kt @@ -26,7 +26,7 @@ import org.junit.jupiter.api.Test import java.nio.file.Path import kotlin.io.path.writeText -@RegistryKey("python.project.model.uv", "true") +@RegistryKey("python.pyproject.model", "true") @TestApplication class UvProjectSyncIntegrationTest { private val testRootFixture = tempPathFixture()