PY-85634 Convert PyInterpreterInspection from Java to Kotlin

GitOrigin-RevId: 7a967a6bb948ec0ad5fd61f1bb44bb1e6620a5f4
This commit is contained in:
Alexey Katsman
2025-11-25 16:27:23 +00:00
committed by intellij-monorepo-bot
parent 473e007508
commit 6fc79e3d03
2 changed files with 371 additions and 424 deletions
@@ -1,424 +0,0 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.inspections;
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo;
import com.intellij.codeInspection.LocalInspectionToolSession;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.codeInspection.util.InspectionMessage;
import com.intellij.codeInspection.util.IntentionFamilyName;
import com.intellij.codeInspection.util.IntentionName;
import com.intellij.ide.actions.ShowSettingsUtilImpl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.options.ex.ConfigurableExtensionPointUtil;
import com.intellij.openapi.options.ex.ConfigurableVisitor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.roots.ui.configuration.ProjectSettingsService;
import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.util.PathUtil;
import com.intellij.util.PlatformUtils;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PyPsiBundle;
import com.jetbrains.python.PythonIdeLanguageCustomization;
import com.jetbrains.python.psi.LanguageLevel;
import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
import com.jetbrains.python.psi.types.TypeEvalContext;
import com.jetbrains.python.sdk.PyDetectedSdk;
import com.jetbrains.python.sdk.PySdkExtKt;
import com.jetbrains.python.sdk.PySdkPopupFactory;
import com.jetbrains.python.sdk.PythonSdkType;
import com.jetbrains.python.sdk.conda.PyCondaSdkCustomizer;
import com.jetbrains.python.sdk.configuration.CreateSdkInfoWithTool;
import com.jetbrains.python.sdk.configuration.PyProjectSdkConfiguration;
import com.jetbrains.python.sdk.configuration.PyProjectSdkConfigurationExtension;
import com.jetbrains.python.sdk.legacy.PythonSdkUtil;
import com.jetbrains.python.ui.PyUiUtil;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.jetbrains.python.inspections.PyInterpreterInspectionExKt.detectSdkForModulesForJvmIn;
import static com.jetbrains.python.inspections.PyInterpreterInspectionExKt.findAllSortedForModuleForJvm;
public final class PyInterpreterInspection extends PyInspection {
private static final @NotNull Pattern NAME = Pattern.compile("Python (?<version>\\d\\.\\d+)\\s*(\\((?<name>.+?)\\))?");
@Override
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder,
final boolean isOnTheFly,
final @NotNull LocalInspectionToolSession session) {
return new Visitor(holder, PyInspectionVisitor.getContext(session));
}
public static class Visitor extends PyInspectionVisitor {
public Visitor(@Nullable ProblemsHolder holder,
@NotNull TypeEvalContext context) {
super(holder, context);
}
@Override
public void visitPyFile(@NotNull PyFile node) {
if (isFileIgnored(node)) return;
@Nullable final Module module = ModuleUtilCore.findModuleForPsiElement(node);
@Nullable final Sdk sdk = PyBuiltinCache.findSdkForFile(node);
final boolean pyCharm = PythonIdeLanguageCustomization.isMainlyPythonIde();
final List<LocalQuickFix> fixes = new ArrayList<>();
if (sdk == null) {
final @InspectionMessage String message;
if (pyCharm) {
message = PyPsiBundle.message("INSP.interpreter.no.python.interpreter.configured.for.project");
}
else {
message = PyPsiBundle.message("INSP.interpreter.no.python.interpreter.configured.for.module");
}
registerProblemWithCommonFixes(node, message, module, fixes, pyCharm);
}
}
private void registerProblemWithCommonFixes(PyFile node,
@InspectionMessage String message,
@Nullable Module module,
List<LocalQuickFix> fixes,
boolean pyCharm) {
if (module != null && pyCharm) {
final String sdkName = ProjectRootManager.getInstance(node.getProject()).getProjectSdkName();
ContainerUtil.addIfNotNull(fixes, getSuitableSdkFix(sdkName, module));
}
if (module != null && pyCharm) {
fixes.add(new ConfigureInterpreterFix());
}
else {
fixes.add(new InterpreterSettingsQuickFix(module));
}
registerProblem(node, message, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
}
private static @Nullable LocalQuickFix getSuitableSdkFix(@Nullable String name, @NotNull Module module) {
// this method is based on com.jetbrains.python.sdk.PySdkExtKt.suggestAssociatedSdkName
// please keep it in sync with the mentioned method and com.jetbrains.python.PythonSdkConfigurator.configureSdk
final List<Sdk> existingSdks = getExistingSdks();
final var associatedSdk = PySdkExtKt.mostPreferred(PySdkExtKt.filterAssociatedSdks(module, existingSdks));
if (associatedSdk != null) return new UseExistingInterpreterFix(associatedSdk, module);
final UserDataHolderBase context = new UserDataHolderBase();
final List<CreateSdkInfoWithTool> createSdkInfos = findAllSortedForModuleForJvm(module);
if (!createSdkInfos.isEmpty()) {
return new UseProvidedInterpreterFix(module, createSdkInfos.getFirst());
}
if (name != null) {
final Matcher matcher = NAME.matcher(name);
if (matcher.matches()) {
final String venvName = matcher.group("name");
if (venvName != null) {
final PyDetectedSdk detectedAssociatedViaRootNameEnv = detectAssociatedViaRootNameEnv(venvName, module, existingSdks, context);
if (detectedAssociatedViaRootNameEnv != null) {
return new UseDetectedInterpreterFix(detectedAssociatedViaRootNameEnv, existingSdks, true, module);
}
}
else {
final PyDetectedSdk detectedSystemWideSdk = detectSystemWideSdk(matcher.group("version"), module, existingSdks, context);
if (detectedSystemWideSdk != null) {
return new UseDetectedInterpreterFix(detectedSystemWideSdk, existingSdks, false, module);
}
}
}
}
if (PyCondaSdkCustomizer.Companion.getInstance().getSuggestSharedCondaEnvironments()) {
final var sharedCondaEnv = PySdkExtKt.mostPreferred(PySdkExtKt.filterSharedCondaEnvs(module, existingSdks));
if (sharedCondaEnv != null) {
return new UseExistingInterpreterFix(sharedCondaEnv, module);
}
}
// TODO: We should use SystemPythonService here as well, postponing as it's quite unlikely we get here (although we can)
final var systemWideSdk = PySdkExtKt.mostPreferred(PySdkExtKt.filterSystemWideSdks(existingSdks));
if (systemWideSdk != null) {
return new UseExistingInterpreterFix(systemWideSdk, module);
}
PyProjectSdkConfigurationExtension configurator = PyCondaSdkCustomizer.Companion.getInstance().getFallbackConfigurator();
if (configurator != null) {
var fallbackCreateSdkInfo =
PyCondaSdkCustomizer.Companion.checkEnvironmentAndPrepareSdkCreatorBlocking(configurator, module);
if (fallbackCreateSdkInfo != null) {
return new UseProvidedInterpreterFix(module, fallbackCreateSdkInfo);
}
}
final var detectedSystemWideSdk = ContainerUtil.getFirstItem(PySdkExtKt.detectSystemWideSdks(module, existingSdks));
if (detectedSystemWideSdk != null) {
return new UseDetectedInterpreterFix(detectedSystemWideSdk, existingSdks, false, module);
}
return null;
}
private static @Unmodifiable @NotNull List<Sdk> getExistingSdks() {
final ProjectSdksModel model = new ProjectSdksModel();
model.reset(null);
return ContainerUtil.filter(model.getSdks(), it -> it.getSdkType() instanceof PythonSdkType);
}
private static @Nullable PyDetectedSdk detectAssociatedViaRootNameEnv(@NotNull String associatedName,
@NotNull Module module,
@NotNull List<Sdk> existingSdks,
@NotNull UserDataHolderBase context) {
return findAssociatedViaRootNameEnv(
associatedName,
PySdkExtKt.detectVirtualEnvs(module, existingSdks, context),
Visitor::getVirtualEnvRootName
);
}
private static @Nullable PyDetectedSdk detectSystemWideSdk(@NotNull String version,
@NotNull Module module,
@NotNull List<Sdk> existingSdks,
@NotNull UserDataHolderBase context) {
final LanguageLevel parsedVersion = LanguageLevel.fromPythonVersion(version);
if (parsedVersion.toString().equals(version)) {
return ContainerUtil.find(
PySdkExtKt.detectSystemWideSdks(module, existingSdks, context),
sdk -> PySdkExtKt.getGuessedLanguageLevel(sdk) == parsedVersion
);
}
return null;
}
private static @Nullable PyDetectedSdk findAssociatedViaRootNameEnv(@NotNull String associatedName,
@NotNull List<PyDetectedSdk> envs,
@NotNull Function<PyDetectedSdk, String> envRootName) {
return StreamEx
.of(envs)
.filter(sdk -> associatedName.equals(envRootName.apply(sdk)))
.max(
Comparator
.comparing(PySdkExtKt::getGuessedLanguageLevel)
.thenComparing(PyDetectedSdk::getHomePath)
)
.orElse(null);
}
private static @Nullable String getVirtualEnvRootName(@NotNull PyDetectedSdk sdk) {
final String path = sdk.getHomePath();
return path == null ? null : getEnvRootName(PythonSdkUtil.getVirtualEnvRoot(path));
}
private static @Nullable String getEnvRootName(@Nullable File envRoot) {
return envRoot == null ? null : PathUtil.getFileName(envRoot.getPath());
}
}
private static boolean isFileIgnored(@NotNull PyFile pyFile) {
return PyInspectionExtension.EP_NAME.getExtensionList().stream().anyMatch(ep -> ep.ignoreInterpreterWarnings(pyFile));
}
public static final class InterpreterSettingsQuickFix implements LocalQuickFix {
private final @Nullable Module myModule;
public InterpreterSettingsQuickFix(@Nullable Module module) {
myModule = module;
}
@Override
public @NotNull String getFamilyName() {
return PlatformUtils.isPyCharm()
? PyPsiBundle.message("INSP.interpreter.interpreter.settings")
: PyPsiBundle.message("INSP.interpreter.configure.python.interpreter");
}
@Override
public boolean startInWriteAction() {
return false;
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
showPythonInterpreterSettings(project, myModule);
}
public static void showPythonInterpreterSettings(@NotNull Project project, @Nullable Module module) {
final var id = "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable";
final var group = ConfigurableExtensionPointUtil.getConfigurableGroup(project, true);
if (ConfigurableVisitor.findById(id, Collections.singletonList(group)) != null) {
ShowSettingsUtilImpl.showSettingsDialog(project, id, null);
return;
}
final ProjectSettingsService settingsService = ProjectSettingsService.getInstance(project);
if (module == null || justOneModuleInheritingSdk(project, module)) {
settingsService.openProjectSettings();
}
else {
settingsService.openModuleSettings(module);
}
}
private static boolean justOneModuleInheritingSdk(@NotNull Project project, @NotNull Module module) {
return ProjectRootManager.getInstance(project).getProjectSdk() == null &&
ModuleRootManager.getInstance(module).isSdkInherited() &&
ModuleManager.getInstance(project).getModules().length < 2;
}
}
public static final class ConfigureInterpreterFix implements LocalQuickFix {
@Override
public @IntentionFamilyName @NotNull String getFamilyName() {
return PyPsiBundle.message("INSP.interpreter.configure.python.interpreter");
}
@Override
public boolean startInWriteAction() {
return false;
}
@Override
public void applyFix(final @NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement element = descriptor.getPsiElement();
if (element == null) return;
final Module module = ModuleUtilCore.findModuleForPsiElement(element);
if (module == null) return;
PySdkPopupFactory.Companion.createAndShow(module);
}
}
private static final class UseProvidedInterpreterFix implements LocalQuickFix {
private final @NotNull Module myModule;
private final @NotNull CreateSdkInfoWithTool myCreateSdkInfo;
private UseProvidedInterpreterFix(@NotNull Module module,
@NotNull CreateSdkInfoWithTool createSdkInfo) {
myModule = module;
myCreateSdkInfo = createSdkInfo;
}
@Override
public @IntentionFamilyName @NotNull String getFamilyName() {
return PyPsiBundle.message("INSP.interpreter.use.suggested.interpreter");
}
@Override
public @IntentionName @NotNull String getName() {
return myCreateSdkInfo.getCreateSdkInfo().getIntentionName();
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
if (!detectSdkForModulesForJvmIn(project)) {
PyProjectSdkConfiguration.INSTANCE.configureSdkUsingCreateSdkInfo(myModule, myCreateSdkInfo);
}
}
@Override
public @NotNull IntentionPreviewInfo generatePreview(@NotNull Project project, @NotNull ProblemDescriptor previewDescriptor) {
// The quick fix doesn't change the code and is suggested on a file level
return IntentionPreviewInfo.EMPTY;
}
}
private abstract static class UseInterpreterFix<T extends Sdk> implements LocalQuickFix {
protected final @NotNull T mySdk;
protected UseInterpreterFix(@NotNull T sdk) {
mySdk = sdk;
}
@Override
public @IntentionFamilyName @NotNull String getFamilyName() {
return PyPsiBundle.message("INSP.interpreter.use.suggested.interpreter");
}
@Override
public @IntentionName @NotNull String getName() {
return PyPsiBundle.message("INSP.interpreter.use.interpreter", PySdkPopupFactory.Companion.shortenNameInPopup(mySdk, 75));
}
@Override
public boolean startInWriteAction() {
return false;
}
}
private static final class UseExistingInterpreterFix extends UseInterpreterFix<Sdk> {
private final @NotNull Module myModule;
private UseExistingInterpreterFix(@NotNull Sdk existingSdk, @NotNull Module module) {
super(existingSdk);
myModule = module;
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PyUiUtil.clearFileLevelInspectionResults(descriptor.getPsiElement().getContainingFile());
ApplicationManager.getApplication().executeOnPooledThread(() -> {
PyProjectSdkConfiguration.INSTANCE.setReadyToUseSdkSync(project, myModule, mySdk);
});
}
}
private static final class UseDetectedInterpreterFix extends UseInterpreterFix<PyDetectedSdk> {
private final @NotNull List<Sdk> myExistingSdks;
private final boolean doAssociate;
private final @NotNull Module myModule;
private UseDetectedInterpreterFix(@NotNull PyDetectedSdk detectedSdk,
@NotNull List<Sdk> existingSdks,
boolean associate,
@NotNull Module module) {
super(detectedSdk);
myExistingSdks = existingSdks;
doAssociate = associate;
myModule = module;
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PyUiUtil.clearFileLevelInspectionResults(descriptor.getPsiElement().getContainingFile());
PySdkExtKt.setupSdkLaunch(mySdk, myModule, myExistingSdks, doAssociate);
}
}
}
@@ -0,0 +1,371 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.inspections
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo
import com.intellij.codeInspection.LocalInspectionToolSession
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.codeInspection.util.InspectionMessage
import com.intellij.codeInspection.util.IntentionFamilyName
import com.intellij.codeInspection.util.IntentionName
import com.intellij.ide.actions.ShowSettingsUtilImpl.Companion.showSettingsDialog
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager.Companion.getInstance
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.options.ConfigurableGroup
import com.intellij.openapi.options.ex.ConfigurableExtensionPointUtil
import com.intellij.openapi.options.ex.ConfigurableVisitor
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.roots.ui.configuration.ProjectSettingsService
import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel
import com.intellij.openapi.util.Condition
import com.intellij.openapi.util.UserDataHolderBase
import com.intellij.psi.PsiElementVisitor
import com.intellij.util.PathUtil
import com.intellij.util.PlatformUtils
import com.intellij.util.containers.ContainerUtil
import com.intellij.util.containers.toArray
import com.jetbrains.python.PyPsiBundle
import com.jetbrains.python.PythonIdeLanguageCustomization
import com.jetbrains.python.psi.LanguageLevel
import com.jetbrains.python.psi.PyFile
import com.jetbrains.python.psi.impl.PyBuiltinCache.Companion.findSdkForFile
import com.jetbrains.python.psi.types.TypeEvalContext
import com.jetbrains.python.sdk.*
import com.jetbrains.python.sdk.PySdkPopupFactory.Companion.createAndShow
import com.jetbrains.python.sdk.conda.PyCondaSdkCustomizer.Companion.checkEnvironmentAndPrepareSdkCreatorBlocking
import com.jetbrains.python.sdk.conda.PyCondaSdkCustomizer.Companion.instance
import com.jetbrains.python.sdk.configuration.CreateSdkInfoWithTool
import com.jetbrains.python.sdk.configuration.PyProjectSdkConfiguration
import com.jetbrains.python.sdk.configuration.PyProjectSdkConfiguration.configureSdkUsingCreateSdkInfo
import com.jetbrains.python.sdk.legacy.PythonSdkUtil
import com.jetbrains.python.ui.PyUiUtil
import one.util.streamex.StreamEx
import java.io.File
import java.util.function.Function
import java.util.regex.Matcher
import java.util.regex.Pattern
class PyInterpreterInspection : PyInspection() {
override fun buildVisitor(
holder: ProblemsHolder,
isOnTheFly: Boolean,
session: LocalInspectionToolSession
): PsiElementVisitor {
return Visitor(holder, PyInspectionVisitor.getContext(session))
}
class Visitor(
holder: ProblemsHolder?,
context: TypeEvalContext
) : PyInspectionVisitor(holder, context) {
override fun visitPyFile(node: PyFile) {
if (isFileIgnored(node)) return
val module = ModuleUtilCore.findModuleForPsiElement(node)
val sdk = findSdkForFile(node)
val pyCharm = PythonIdeLanguageCustomization.isMainlyPythonIde()
val fixes: MutableList<LocalQuickFix?> = ArrayList<LocalQuickFix?>()
if (sdk == null) {
val message: @InspectionMessage String
if (pyCharm) {
message = PyPsiBundle.message("INSP.interpreter.no.python.interpreter.configured.for.project")
}
else {
message = PyPsiBundle.message("INSP.interpreter.no.python.interpreter.configured.for.module")
}
registerProblemWithCommonFixes(node, message, module, fixes, pyCharm)
}
}
private fun registerProblemWithCommonFixes(
node: PyFile,
@InspectionMessage message: @InspectionMessage String,
module: Module?,
fixes: MutableList<LocalQuickFix?>,
pyCharm: Boolean
) {
if (module != null && pyCharm) {
val sdkName = ProjectRootManager.getInstance(node.getProject()).getProjectSdkName()
ContainerUtil.addIfNotNull<LocalQuickFix?>(fixes, getSuitableSdkFix(sdkName, module))
}
if (module != null && pyCharm) {
fixes.add(ConfigureInterpreterFix())
}
else {
fixes.add(InterpreterSettingsQuickFix(module))
}
registerProblem(node, message, *fixes.toArray<LocalQuickFix?>(LocalQuickFix.EMPTY_ARRAY))
}
companion object {
private fun getSuitableSdkFix(name: String?, module: Module): LocalQuickFix? {
// this method is based on com.jetbrains.python.sdk.PySdkExtKt.suggestAssociatedSdkName
// please keep it in sync with the mentioned method and com.jetbrains.python.PythonSdkConfigurator.configureSdk
val existingSdks: List<Sdk> = existingSdks
val associatedSdk = mostPreferred(filterAssociatedSdks(module, existingSdks))
if (associatedSdk != null) return UseExistingInterpreterFix(associatedSdk, module)
val context = UserDataHolderBase()
val createSdkInfos: List<CreateSdkInfoWithTool> = findAllSortedForModuleForJvm(module)
if (!createSdkInfos.isEmpty()) {
return UseProvidedInterpreterFix(module, createSdkInfos.first())
}
if (name != null) {
val matcher: Matcher = NAME.matcher(name)
if (matcher.matches()) {
val venvName = matcher.group("name")
if (venvName != null) {
val detectedAssociatedViaRootNameEnv: PyDetectedSdk? = detectAssociatedViaRootNameEnv(venvName, module, existingSdks, context)
if (detectedAssociatedViaRootNameEnv != null) {
return UseDetectedInterpreterFix(detectedAssociatedViaRootNameEnv, existingSdks, true, module)
}
}
else {
val detectedSystemWideSdk: PyDetectedSdk? = detectSystemWideSdk(matcher.group("version"), module, existingSdks, context)
if (detectedSystemWideSdk != null) {
return UseDetectedInterpreterFix(detectedSystemWideSdk, existingSdks, false, module)
}
}
}
}
if (instance.suggestSharedCondaEnvironments) {
val sharedCondaEnv = mostPreferred(filterSharedCondaEnvs(module, existingSdks))
if (sharedCondaEnv != null) {
return UseExistingInterpreterFix(sharedCondaEnv, module)
}
}
// TODO: We should use SystemPythonService here as well, postponing as it's quite unlikely we get here (although we can)
val systemWideSdk = mostPreferred(filterSystemWideSdks(existingSdks))
if (systemWideSdk != null) {
return UseExistingInterpreterFix(systemWideSdk, module)
}
val configurator = instance.fallbackConfigurator
if (configurator != null) {
val fallbackCreateSdkInfo =
checkEnvironmentAndPrepareSdkCreatorBlocking(configurator, module)
if (fallbackCreateSdkInfo != null) {
return UseProvidedInterpreterFix(module, fallbackCreateSdkInfo)
}
}
val detectedSystemWideSdk = ContainerUtil.getFirstItem<PyDetectedSdk>(detectSystemWideSdks(module, existingSdks))
if (detectedSystemWideSdk != null) {
return UseDetectedInterpreterFix(detectedSystemWideSdk, existingSdks, false, module)
}
return null
}
private val existingSdks: List<Sdk>
get() {
val model = ProjectSdksModel()
model.reset(null)
return ContainerUtil.filter(model.getSdks(),
Condition { it.getSdkType() is PythonSdkType })
}
private fun detectAssociatedViaRootNameEnv(
associatedName: String,
module: Module,
existingSdks: List<Sdk>,
context: UserDataHolderBase
): PyDetectedSdk? {
return Companion.findAssociatedViaRootNameEnv(
associatedName,
detectVirtualEnvs(module, existingSdks, context),
Function { sdk: PyDetectedSdk? -> Companion.getVirtualEnvRootName(sdk!!) }
)
}
private fun detectSystemWideSdk(
version: String,
module: Module,
existingSdks: List<Sdk>,
context: UserDataHolderBase
): PyDetectedSdk? {
val parsedVersion: LanguageLevel = LanguageLevel.fromPythonVersion(version)!!
if (parsedVersion.toString() == version) {
return ContainerUtil.find<PyDetectedSdk>(
detectSystemWideSdks(module, existingSdks, context),
Condition { sdk: PyDetectedSdk -> sdk.guessedLanguageLevel == parsedVersion }
)
}
return null
}
private fun findAssociatedViaRootNameEnv(
associatedName: String,
envs: List<PyDetectedSdk>,
envRootName: Function<PyDetectedSdk?, String?>
): PyDetectedSdk? {
return StreamEx
.of(envs)
.filter { sdk: PyDetectedSdk -> associatedName == envRootName.apply(sdk) }
.max(compareBy<PyDetectedSdk> { it.guessedLanguageLevel }.thenBy { it.homePath })
.orElse(null)
}
private fun getVirtualEnvRootName(sdk: PyDetectedSdk): String? {
val path = sdk.getHomePath()
return if (path == null) null else getEnvRootName(PythonSdkUtil.getVirtualEnvRoot(path))
}
private fun getEnvRootName(envRoot: File?): String? {
return if (envRoot == null) null else PathUtil.getFileName(envRoot.getPath())
}
}
}
class InterpreterSettingsQuickFix(private val myModule: Module?) : LocalQuickFix {
override fun getFamilyName(): String {
return if (PlatformUtils.isPyCharm())
PyPsiBundle.message("INSP.interpreter.interpreter.settings")
else
PyPsiBundle.message("INSP.interpreter.configure.python.interpreter")
}
override fun startInWriteAction(): Boolean {
return false
}
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
showPythonInterpreterSettings(project, myModule)
}
companion object {
fun showPythonInterpreterSettings(project: Project, module: Module?) {
val id = "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable"
val group = ConfigurableExtensionPointUtil.getConfigurableGroup(project, true)
if (ConfigurableVisitor.findById(id, mutableListOf<ConfigurableGroup?>(group)) != null) {
showSettingsDialog(project, id, null)
return
}
val settingsService = ProjectSettingsService.getInstance(project)
if (module == null || justOneModuleInheritingSdk(project, module)) {
settingsService.openProjectSettings()
}
else {
settingsService.openModuleSettings(module)
}
}
private fun justOneModuleInheritingSdk(project: Project, module: Module): Boolean {
return ProjectRootManager.getInstance(project).getProjectSdk() == null &&
ModuleRootManager.getInstance(module).isSdkInherited() && getInstance(project).modules.size < 2
}
}
}
class ConfigureInterpreterFix : LocalQuickFix {
@IntentionFamilyName
override fun getFamilyName(): @IntentionFamilyName String {
return PyPsiBundle.message("INSP.interpreter.configure.python.interpreter")
}
override fun startInWriteAction(): Boolean {
return false
}
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val element = descriptor.getPsiElement()
if (element == null) return
val module = ModuleUtilCore.findModuleForPsiElement(element)
if (module == null) return
createAndShow(module)
}
}
private class UseProvidedInterpreterFix(
private val myModule: Module,
private val myCreateSdkInfo: CreateSdkInfoWithTool
) : LocalQuickFix {
@IntentionFamilyName
override fun getFamilyName(): @IntentionFamilyName String {
return PyPsiBundle.message("INSP.interpreter.use.suggested.interpreter")
}
@IntentionName
override fun getName(): @IntentionName String {
return myCreateSdkInfo.createSdkInfo.intentionName
}
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
if (!detectSdkForModulesForJvmIn(project)) {
configureSdkUsingCreateSdkInfo(myModule, myCreateSdkInfo)
}
}
override fun generatePreview(project: Project, previewDescriptor: ProblemDescriptor): IntentionPreviewInfo {
// The quick fix doesn't change the code and is suggested on a file level
return IntentionPreviewInfo.EMPTY
}
}
private abstract class UseInterpreterFix<T : Sdk> protected constructor(protected val mySdk: T) : LocalQuickFix {
@IntentionFamilyName
override fun getFamilyName(): @IntentionFamilyName String {
return PyPsiBundle.message("INSP.interpreter.use.suggested.interpreter")
}
@IntentionName
override fun getName(): @IntentionName String {
return PyPsiBundle.message("INSP.interpreter.use.interpreter", PySdkPopupFactory.Companion.shortenNameInPopup(mySdk!!, 75))
}
override fun startInWriteAction(): Boolean {
return false
}
}
private class UseExistingInterpreterFix(existingSdk: Sdk, private val myModule: Module) : UseInterpreterFix<Sdk>(existingSdk) {
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
PyUiUtil.clearFileLevelInspectionResults(descriptor.getPsiElement().getContainingFile())
ApplicationManager.getApplication().executeOnPooledThread(
Runnable {
PyProjectSdkConfiguration.setReadyToUseSdkSync(project, myModule, mySdk)
})
}
}
private class UseDetectedInterpreterFix(
detectedSdk: PyDetectedSdk,
private val myExistingSdks: List<Sdk>,
private val doAssociate: Boolean,
private val myModule: Module
) : UseInterpreterFix<PyDetectedSdk>(detectedSdk) {
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
PyUiUtil.clearFileLevelInspectionResults(descriptor.getPsiElement().getContainingFile())
mySdk.setupSdkLaunch(myModule, myExistingSdks, doAssociate)
}
}
companion object {
private val NAME: Pattern = Pattern.compile("Python (?<version>\\d\\.\\d+)\\s*(\\((?<name>.+?)\\))?")
private fun isFileIgnored(pyFile: PyFile): Boolean {
return PyInspectionExtension.EP_NAME.extensionList.stream().anyMatch { ep: PyInspectionExtension? ->
ep!!.ignoreInterpreterWarnings(pyFile)
}
}
}
}