From 7a580fd48b0bf1e621b6959fe1b97f40a3fe10ea Mon Sep 17 00:00:00 2001 From: Ilya Kazakevich Date: Mon, 24 Nov 2025 22:50:38 +0000 Subject: [PATCH] link/PY-85711/skip-non-python-modules-for-non-pycharm-ides [python]: PY-85709: Support Python facet for non-PyCharm IDEs. The following code doesn't work for anything but PyCharm. ```kotlin ModuleRootManager.getInstance(module).sdk ``` We now call `com.jetbrains.python.module.PyModuleService.findPythonSdk` which supports both Python SDK and Facet. [python]: PY-85711 : Do not touch non-python modules in `removeFakeModuleEntity` and do not set `inherited` for newly created modules. 1. We should never delete any module which isn't python. 2. No need to look for sdk in workspace as we already do that for all modules by reverting their SDK settings. Moreover, `InheritedSdkDependency` was wrong: it set project-level Java JDK as Python SDK [python]: PY-85711 : Make `PyActiveSdkModuleConfigurable` filter non-python modules. See `com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable.isSuitableForModule` comment [python]: PY-85711 : Move `isPythonModule` to `PyModuleService`. We are going to reuse it all over the code [python]: PY-85711 : Refactor `PyModuleService`. Drop unneeded class Merge-request: IJ-MR-183088 Merged-by: Ilya Kazakevich GitOrigin-RevId: 19f65ba65ea7ea8d8ff8698a64e6a3cbe6803cb0 --- .../src/impl/InterpreterServiceImpl.kt | 2 +- .../python/module/PyModuleService.java | 8 ++++- .../plugin/impl/run/PyPluginSdkFragment.kt | 2 +- .../facet/PyPluginSdkModuleConfigurable.kt | 10 ++----- .../jetbrains/python/extensions/ModuleExt.kt | 4 +-- .../model/internal/workspaceTools.kt | 29 ++++++++++--------- .../src/impl/ModulesSdkConfigurator.kt | 5 ++-- .../python/sdk/legacy/PythonSdkUtil.java | 8 +---- .../PyActiveSdkConfigurable.java | 2 +- .../PyActiveSdkModuleConfigurable.java | 13 +++++++-- .../python/module/PyModuleServiceEx.java | 5 ---- .../python/module/PyModuleServiceImpl.java | 27 +++++++++++++++-- .../python/run/configuration/PySdkComboBox.kt | 4 +-- ...PythonTestLegacyConfigurationProducer.java | 23 +++------------ .../PythonDocTestConfigurationProducer.java | 1 + 15 files changed, 77 insertions(+), 66 deletions(-) delete mode 100644 python/src/com/jetbrains/python/module/PyModuleServiceEx.java diff --git a/python/interpreters/src/impl/InterpreterServiceImpl.kt b/python/interpreters/src/impl/InterpreterServiceImpl.kt index 749f75be31d9..ba3e817be404 100644 --- a/python/interpreters/src/impl/InterpreterServiceImpl.kt +++ b/python/interpreters/src/impl/InterpreterServiceImpl.kt @@ -41,7 +41,7 @@ internal object InterpreterServiceImpl : InterpreterService { } override suspend fun getForModule(module: Module): Interpreter? { - val pythonSdk = ModuleRootManager.getInstance(module).sdk?.takeIf { isPythonSdk(it) } ?: return null + val pythonSdk = PythonSdkUtil.findPythonSdk(module)?.takeIf { isPythonSdk(it) } ?: return null val data = pythonSdk.getOrCreateAdditionalData() return findInterpreter(data, pythonSdk) diff --git a/python/openapi/src/com/jetbrains/python/module/PyModuleService.java b/python/openapi/src/com/jetbrains/python/module/PyModuleService.java index 84130e26bf3d..0759c3aab711 100644 --- a/python/openapi/src/com/jetbrains/python/module/PyModuleService.java +++ b/python/openapi/src/com/jetbrains/python/module/PyModuleService.java @@ -4,14 +4,18 @@ package com.jetbrains.python.module; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Consumer; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public abstract class PyModuleService { - public abstract @Nullable Sdk findPythonSdk(@NotNull Module module); + public @Nullable Sdk findPythonSdk(@NotNull Module module) { + return ModuleRootManager.getInstance(module).getSdk(); + } public void forAllFacets(@NotNull Module module, @NotNull Consumer facetConsumer) { } @@ -24,4 +28,6 @@ public abstract class PyModuleService { public boolean isFileIgnored(@NotNull VirtualFile file) { return false; } + @ApiStatus.Internal + public abstract boolean isPythonModule(@NotNull Module module); } diff --git a/python/pluginCore/impl/src/com/intellij/python/community/plugin/impl/run/PyPluginSdkFragment.kt b/python/pluginCore/impl/src/com/intellij/python/community/plugin/impl/run/PyPluginSdkFragment.kt index 992945aea10f..5eccf6dde9d9 100644 --- a/python/pluginCore/impl/src/com/intellij/python/community/plugin/impl/run/PyPluginSdkFragment.kt +++ b/python/pluginCore/impl/src/com/intellij/python/community/plugin/impl/run/PyPluginSdkFragment.kt @@ -155,7 +155,7 @@ class PyPluginSdkFragment> : SettingsEdito private fun getSelectedSdk(): Sdk? { if (currentMode == SDK_OF_MODULE) { ((modulesCombo?.selectedItem) as? Module)?.let { - return ModuleRootManager.getInstance(it).sdk + return PythonSdkUtil.findPythonSdk(it) } } else if (currentMode == SDK_FROM_LIST) { diff --git a/python/pluginMinor/src/com/intellij/python/community/plugin/minor/facet/PyPluginSdkModuleConfigurable.kt b/python/pluginMinor/src/com/intellij/python/community/plugin/minor/facet/PyPluginSdkModuleConfigurable.kt index 312cdb80bb99..c9058b74980d 100644 --- a/python/pluginMinor/src/com/intellij/python/community/plugin/minor/facet/PyPluginSdkModuleConfigurable.kt +++ b/python/pluginMinor/src/com/intellij/python/community/plugin/minor/facet/PyPluginSdkModuleConfigurable.kt @@ -10,6 +10,7 @@ import com.intellij.openapi.projectRoots.Sdk import com.intellij.python.community.plugin.impl.facet.PythonFacetUtil import com.jetbrains.python.configuration.PyActiveSdkConfigurable import com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable +import com.jetbrains.python.module.PyModuleService import com.jetbrains.python.sdk.removeTransferredRoots import com.jetbrains.python.sdk.transferRoots import org.jetbrains.annotations.ApiStatus @@ -22,7 +23,7 @@ internal class PyPluginSdkModuleConfigurable(project: Project?) : PyActiveSdkMod } override fun getSdk(): Sdk? { - return getSdkFromFacet(module) + return PyModuleService.getInstance().findPythonSdk(module) } } } @@ -42,13 +43,6 @@ fun setSdkToFacet(item: Sdk?, module: Module) { } } -@ApiStatus.Internal -fun getSdkFromFacet(module: Module): Sdk? { - val facetManager = FacetManager.getInstance(module) - val facet = facetManager.getFacetByType(MinorPythonFacet.ID) - return facet?.configuration?.sdk -} - private fun setFacetSdk(facet: MinorPythonFacet, item: Sdk?, module: Module) { diff --git a/python/python-core-impl/src/com/jetbrains/python/extensions/ModuleExt.kt b/python/python-core-impl/src/com/jetbrains/python/extensions/ModuleExt.kt index 0a89ce5ff4c7..d5ed3e3a3b42 100644 --- a/python/python-core-impl/src/com/jetbrains/python/extensions/ModuleExt.kt +++ b/python/python-core-impl/src/com/jetbrains/python/extensions/ModuleExt.kt @@ -17,7 +17,7 @@ package com.jetbrains.python.extensions import com.intellij.openapi.module.Module import com.intellij.openapi.projectRoots.Sdk -import com.intellij.openapi.roots.ModuleRootManager +import com.jetbrains.python.sdk.legacy.PythonSdkUtil -fun Module.getSdk(): Sdk? = ModuleRootManager.getInstance(this).sdk +fun Module.getSdk(): Sdk? = PythonSdkUtil.findPythonSdk(this) diff --git a/python/python-pyproject/src/com/intellij/python/pyproject/model/internal/workspaceTools.kt b/python/python-pyproject/src/com/intellij/python/pyproject/model/internal/workspaceTools.kt index 587af5562748..facaa315f340 100644 --- a/python/python-pyproject/src/com/intellij/python/pyproject/model/internal/workspaceTools.kt +++ b/python/python-pyproject/src/com/intellij/python/pyproject/model/internal/workspaceTools.kt @@ -6,7 +6,6 @@ import com.intellij.openapi.externalSystem.autoimport.ExternalSystemRefreshStatu import com.intellij.openapi.project.Project import com.intellij.openapi.project.modules import com.intellij.openapi.projectRoots.ProjectJdkTable -import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.roots.ModuleRootModificationUtil import com.intellij.openapi.util.Key import com.intellij.openapi.util.NlsSafe @@ -27,6 +26,10 @@ import com.intellij.python.pyproject.model.spi.PyProjectTomlProject import com.intellij.python.pyproject.model.spi.Tool import com.intellij.python.pyproject.model.spi.WorkspaceName import com.intellij.util.messages.Topic +import com.intellij.workspaceModel.ide.impl.legacyBridge.module.findModule +import com.jetbrains.python.PyNames +import com.jetbrains.python.module.PyModuleService +import com.jetbrains.python.sdk.legacy.PythonSdkUtil import com.jetbrains.python.venvReader.Directory import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.sync.Mutex @@ -70,7 +73,7 @@ internal suspend fun linkProject(project: Project, projectModelRoot: Path) { val entries = generatePyProjectTomlEntries(files, excludeDirs) if (entries.isNotEmpty()) { - val sdks = project.modules.associate { Pair(it.name, ModuleRootManager.getInstance(it).sdk?.name) } + val sdks = project.modules.associate { Pair(it.name, PythonSdkUtil.findPythonSdk(it)?.name) } project.workspaceModel.currentSnapshot.entities(ModuleEntity::class.java) unlinkProjectImpl(project, externalProjectPath) @@ -90,7 +93,7 @@ internal suspend fun linkProject(project: Project, projectModelRoot: Path) { // Restore SDK assoc for (module in project.modules) { - if (ModuleRootManager.getInstance(module).sdk == null) { + if (PythonSdkUtil.findPythonSdk(module) == null) { val sdkName = sdks[module.name] ?: continue ProjectJdkTable.getInstance().findJdk(sdkName)?.let { sdk -> ModuleRootModificationUtil.setModuleSdk(module, sdk) @@ -177,17 +180,8 @@ private suspend fun createEntityStorage( val fileUrlManager = project.workspaceModel.getVirtualFileUrlManager() val storage = MutableEntityStorage.create() for (pyProject in graph) { - val existingModuleEntity = project.workspaceModel.currentSnapshot - .entitiesBySource { it is PyProjectTomlEntitySource } - .filterIsInstance() - .find { it.name == pyProject.name.name } - val existingSdkEntity = existingModuleEntity - ?.dependencies - ?.find { it is SdkDependency } as? SdkDependency - val sdkDependency = existingSdkEntity ?: InheritedSdkDependency val entitySource = PyProjectTomlEntitySource(pyProject.tomlFile.toVirtualFileUrl(virtualFileUrlManager)) val moduleEntity = storage addEntity ModuleEntity(pyProject.name.name, emptyList(), entitySource) { - dependencies += sdkDependency dependencies += ModuleSourceDependency for (moduleName in pyProject.dependencies) { dependencies += ModuleDependency(ModuleId(moduleName.name), true, DependencyScope.COMPILE, false) @@ -210,6 +204,7 @@ private suspend fun createEntityStorage( } } + type = PYTHON_MODULE_ID_DATA_CLASS pyProjectTomlEntity = PyProjectTomlWorkspaceEntity(participatedTools = participatedTools, pyProject.tomlFile.parent.toVirtualFileUrl(fileUrlManager), entitySource) exModuleOptions = ExternalSystemModuleOptionsEntity(entitySource) { externalSystem = PYTHON_SOURCE_ROOT_TYPE.name @@ -220,6 +215,7 @@ private suspend fun createEntityStorage( return@withContext storage } + private class PyProjectTomlEntitySource(tomlFile: VirtualFileUrl) : EntitySource { override val virtualFileUrl: VirtualFileUrl = tomlFile } @@ -247,9 +243,14 @@ private data class PyProjectTomlBasedEntryImpl( * @see com.intellij.openapi.project.impl.getOrInitializeModule */ private fun removeFakeModuleEntity(storage: MutableEntityStorage, modulesToRemove: Set) { + val moduleService = PyModuleService.getInstance() val contentRoots = storage .entitiesBySource { it !is PyProjectTomlEntitySource } .filterIsInstance() + .filter { + val module = it.module.findModule(storage) ?: return@filter false + moduleService.isPythonModule(module) + } .toList() for (entity in contentRoots) { if (entity.module.name in modulesToRemove) { @@ -285,4 +286,6 @@ private suspend fun findSrc(root: Directory): Set = withContext(Dispatchers.IO) { val src = root.resolve("src") if (src.exists()) setOf(src) else emptySet() - } \ No newline at end of file + } + +private val PYTHON_MODULE_ID_DATA_CLASS: ModuleTypeId = ModuleTypeId(PyNames.PYTHON_MODULE_ID) diff --git a/python/python-sdk-configurator/backend/src/impl/ModulesSdkConfigurator.kt b/python/python-sdk-configurator/backend/src/impl/ModulesSdkConfigurator.kt index 7a17f7af3e2e..ebe0613c3d9b 100644 --- a/python/python-sdk-configurator/backend/src/impl/ModulesSdkConfigurator.kt +++ b/python/python-sdk-configurator/backend/src/impl/ModulesSdkConfigurator.kt @@ -25,6 +25,7 @@ import com.jetbrains.python.sdk.configuration.CreateSdkInfo import com.jetbrains.python.sdk.configuration.CreateSdkInfoWithTool import com.jetbrains.python.sdk.configuration.PyProjectSdkConfigurationExtension import com.jetbrains.python.sdk.getOrCreateAdditionalData +import com.jetbrains.python.sdk.legacy.PythonSdkUtil import com.jetbrains.python.sdk.setAssociationToPath import com.jetbrains.python.venvReader.Directory import kotlinx.collections.immutable.toPersistentList @@ -108,7 +109,7 @@ internal class ModulesSdkConfigurator private constructor( val tools = PyProjectSdkConfigurationExtension.createMap() val limit = Semaphore(permits = Registry.intValue("intellij.python.sdkConfigurator.backend.sdk.parallel")) val now = System.currentTimeMillis() - val resultDef = project.modules.filter { ModuleRootManager.getInstance(it).sdk == null }.map { module -> + val resultDef = project.modules.filter { PythonSdkUtil.findPythonSdk(it) == null }.map { module -> limit.withPermit { async { val moduleInfo = getModuleInfo(module, tools) ?: return@async null @@ -190,7 +191,7 @@ internal class ModulesSdkConfigurator private constructor( } // Link workspace members with their workspace val reportedBrokenModules = mutableSetOf() for ((module, parentModule) in modulesWithSameSdk) { - val parentSdk = ModuleRootManager.getInstance(parentModule).sdk + val parentSdk = PythonSdkUtil.findPythonSdk(module) if (parentSdk != null) { ModuleRootModificationUtil.setModuleSdk(module, parentSdk) // This SDK is shared, no need to associate it // TODO: Support association with multiple modules diff --git a/python/python-sdk/src/com/jetbrains/python/sdk/legacy/PythonSdkUtil.java b/python/python-sdk/src/com/jetbrains/python/sdk/legacy/PythonSdkUtil.java index 4c309526844e..51493fcaffa2 100644 --- a/python/python-sdk/src/com/jetbrains/python/sdk/legacy/PythonSdkUtil.java +++ b/python/python-sdk/src/com/jetbrains/python/sdk/legacy/PythonSdkUtil.java @@ -97,13 +97,7 @@ public final class PythonSdkUtil { if (module == null || module.isDisposed()) { return null; } - - Sdk sdk = ModuleRootManager.getInstance(module).getSdk(); - if (sdk != null && isPythonSdk(sdk)) { - return sdk; - } - - sdk = PyModuleService.getInstance().findPythonSdk(module); + var sdk = PyModuleService.getInstance().findPythonSdk(module); if (sdk != null && isPythonSdk(sdk)) { return sdk; } diff --git a/python/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java b/python/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java index 2573468ff920..a938a09bc3a2 100644 --- a/python/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java +++ b/python/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java @@ -282,7 +282,7 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable { sdk = ProjectRootManager.getInstance(myProject).getProjectSdk(); } else { - sdk = ModuleRootManager.getInstance(myModule).getSdk(); + sdk = com.jetbrains.python.sdk.PythonSdkUtil.findPythonSdk(myModule); } if (sdk != null && PythonSdkUtil.isPythonSdk(sdk)) { diff --git a/python/src/com/jetbrains/python/configuration/PyActiveSdkModuleConfigurable.java b/python/src/com/jetbrains/python/configuration/PyActiveSdkModuleConfigurable.java index 0f1b8aa5d0f3..80eb4c742951 100644 --- a/python/src/com/jetbrains/python/configuration/PyActiveSdkModuleConfigurable.java +++ b/python/src/com/jetbrains/python/configuration/PyActiveSdkModuleConfigurable.java @@ -6,20 +6,29 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.options.UnnamedConfigurable; import com.intellij.openapi.project.Project; import com.jetbrains.python.PyBundle; +import com.jetbrains.python.module.PyModuleService; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; // Inherit in the module you are going to use it @ApiStatus.Internal -public abstract class PyActiveSdkModuleConfigurable extends ModuleAwareProjectConfigurable { +public abstract class PyActiveSdkModuleConfigurable extends ModuleAwareProjectConfigurable { private final Project myProject; protected PyActiveSdkModuleConfigurable(Project project) { - super(project, PyBundle.message("configurable.PyActiveSdkModuleConfigurable.python.interpreter.display.name"), "reference.settings.project.interpreter"); + super(project, PyBundle.message("configurable.PyActiveSdkModuleConfigurable.python.interpreter.display.name"), + "reference.settings.project.interpreter"); myProject = project; } + @Override + protected final boolean isSuitableForModule(@NotNull Module module) { + // One can't configure Python SDK for a random module as random module doesn't have a `baseDir` + // which is a requirement for various SDK types. + return PyModuleService.getInstance().isPythonModule(module); + } + @Override protected @NotNull UnnamedConfigurable createModuleConfigurable(Module module) { return new PyActiveSdkConfigurable(module); diff --git a/python/src/com/jetbrains/python/module/PyModuleServiceEx.java b/python/src/com/jetbrains/python/module/PyModuleServiceEx.java deleted file mode 100644 index ddc8fb453223..000000000000 --- a/python/src/com/jetbrains/python/module/PyModuleServiceEx.java +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2000-2019 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. -package com.jetbrains.python.module; - -public abstract class PyModuleServiceEx extends PyModuleService { -} diff --git a/python/src/com/jetbrains/python/module/PyModuleServiceImpl.java b/python/src/com/jetbrains/python/module/PyModuleServiceImpl.java index 2788610778a4..862cb1172a14 100644 --- a/python/src/com/jetbrains/python/module/PyModuleServiceImpl.java +++ b/python/src/com/jetbrains/python/module/PyModuleServiceImpl.java @@ -6,14 +6,18 @@ import com.intellij.facet.FacetConfiguration; import com.intellij.facet.FacetManager; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleType; import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Consumer; +import com.jetbrains.python.PyNames; +import com.jetbrains.python.PythonModuleTypeBase; import com.jetbrains.python.facet.PythonFacetSettings; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -final class PyModuleServiceImpl extends PyModuleServiceEx { +final class PyModuleServiceImpl extends PyModuleService { @Override public boolean isFileIgnored(@NotNull VirtualFile file) { @@ -22,13 +26,17 @@ final class PyModuleServiceImpl extends PyModuleServiceEx { @Override public @Nullable Sdk findPythonSdk(@NotNull Module module) { + var sdk = super.findPythonSdk(module); + if (sdk != null) { + return sdk; + } for (Facet facet : FacetManager.getInstance(module).getAllFacets()) { final FacetConfiguration configuration = facet.getConfiguration(); if (configuration instanceof PythonFacetSettings) { return ((PythonFacetSettings)configuration).getSdk(); } } - return null; + return ModuleRootManager.getInstance(module).getSdk(); } @Override @@ -37,4 +45,19 @@ final class PyModuleServiceImpl extends PyModuleServiceEx { facetConsumer.consume(f); } } + + @Override + public boolean isPythonModule(@NotNull Module module) { + ModuleType type = ModuleType.get(module); + if (type instanceof PythonModuleTypeBase || type.getId().equals(PyNames.PYTHON_MODULE_ID)) { + return true; + } + final Facet[] allFacets = FacetManager.getInstance(module).getAllFacets(); + for (Facet facet : allFacets) { + if (facet.getConfiguration() instanceof PythonFacetSettings) { + return true; + } + } + return false; + } } diff --git a/python/src/com/jetbrains/python/run/configuration/PySdkComboBox.kt b/python/src/com/jetbrains/python/run/configuration/PySdkComboBox.kt index af4067920163..8ee05b60ad6c 100644 --- a/python/src/com/jetbrains/python/run/configuration/PySdkComboBox.kt +++ b/python/src/com/jetbrains/python/run/configuration/PySdkComboBox.kt @@ -3,13 +3,13 @@ package com.jetbrains.python.run.configuration import com.intellij.openapi.module.Module import com.intellij.openapi.projectRoots.Sdk -import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.ui.ComboBox import com.intellij.openapi.util.Computable import com.jetbrains.python.PyBundle import com.jetbrains.python.run.AbstractPythonRunConfigurationParams import com.jetbrains.python.sdk.PySdkListCellRenderer import com.jetbrains.python.sdk.legacy.PythonSdkUtil +import com.jetbrains.python.sdk.pythonSdk import java.util.function.Consumer class PySdkComboBox(private val addDefault: Boolean, @@ -48,7 +48,7 @@ class PySdkComboBox(private val addDefault: Boolean, } private fun updateDefaultInterpreter(module: Module?) { - val sdk = if (module == null) null else ModuleRootManager.getInstance(module).sdk + val sdk = module?.pythonSdk setRenderer( if (sdk == null) PySdkListCellRenderer() else PySdkListCellRenderer(PyBundle.message("python.sdk.rendering.project.default.0", sdk.name), sdk) diff --git a/python/src/com/jetbrains/python/testing/PythonTestLegacyConfigurationProducer.java b/python/src/com/jetbrains/python/testing/PythonTestLegacyConfigurationProducer.java index bd50dbaf31d7..7420ecb48942 100644 --- a/python/src/com/jetbrains/python/testing/PythonTestLegacyConfigurationProducer.java +++ b/python/src/com/jetbrains/python/testing/PythonTestLegacyConfigurationProducer.java @@ -3,11 +3,8 @@ package com.jetbrains.python.testing; import com.intellij.execution.Location; import com.intellij.execution.actions.ConfigurationContext; -import com.intellij.facet.Facet; -import com.intellij.facet.FacetManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; -import com.intellij.openapi.module.ModuleType; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectRootManager; @@ -17,8 +14,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ThreeState; -import com.jetbrains.python.PythonModuleTypeBase; -import com.jetbrains.python.facet.PythonFacetSettings; +import com.jetbrains.python.module.PyModuleService; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.types.TypeEvalContext; import com.jetbrains.python.run.RunnableScriptFilter; @@ -108,6 +104,7 @@ public abstract class PythonTestLegacyConfigurationProducer getTestCaseClassesFromFile(final @NotNull PyFile pyFile) { diff --git a/python/src/com/jetbrains/python/testing/doctest/PythonDocTestConfigurationProducer.java b/python/src/com/jetbrains/python/testing/doctest/PythonDocTestConfigurationProducer.java index 30fb19155ea1..44d6c1a0ae5e 100644 --- a/python/src/com/jetbrains/python/testing/doctest/PythonDocTestConfigurationProducer.java +++ b/python/src/com/jetbrains/python/testing/doctest/PythonDocTestConfigurationProducer.java @@ -53,6 +53,7 @@ public final class PythonDocTestConfigurationProducer extends PythonTestLegacyCo @Override protected boolean isAvailable(final @NotNull Location location) { final Module module = location.getModule(); + if (module == null) return false; if (!isPythonModule(module)) return false; final PsiElement element = location.getPsiElement(); if (element instanceof PsiFile) {