PY-26492 Warning about Pipenv interpreter not associated with the current project

This commit is contained in:
Andrey Vlasovskikh
2018-06-15 21:24:41 +03:00
parent cd54c4655c
commit 503dd63801
5 changed files with 58 additions and 21 deletions
@@ -29,7 +29,9 @@ import com.intellij.util.PlatformUtils;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.psi.LanguageLevel;
import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.sdk.PySdkExtKt;
import com.jetbrains.python.sdk.PythonSdkType;
import com.jetbrains.python.sdk.flavors.PipenvKt;
import com.jetbrains.python.sdk.flavors.UsePipEnvQuickFix;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
@@ -66,34 +68,43 @@ public class PyInterpreterInspection extends PyInspection {
public void visitPyFile(PyFile node) {
final Module module = ModuleUtilCore.findModuleForPsiElement(node);
if (module == null) return;
final Sdk sdk = PythonSdkType.findPythonSdk(module);
final boolean pyCharm = PlatformUtils.isPyCharm();
final String interpreterOwner = pyCharm ? "project" : "module";
final List<LocalQuickFix> fixes = new ArrayList<>();
if (UsePipEnvQuickFix.Companion.isApplicable(module)) {
fixes.add(new UsePipEnvQuickFix(sdk, module));
}
if (pyCharm) {
if (UsePipEnvQuickFix.Companion.isApplicable(module)) {
fixes.add(new UsePipEnvQuickFix());
}
fixes.add(new ConfigureInterpreterFix());
}
final String product = pyCharm ? "PyCharm" : "Python plugin";
final Sdk sdk = PythonSdkType.findPythonSdk(module);
if (sdk == null) {
registerProblem(node, "No Python interpreter configured for the " + interpreterOwner, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
}
else if (PythonSdkType.isInvalid(sdk)) {
registerProblem(node, "Invalid Python interpreter selected for the " + interpreterOwner, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
}
else {
final LanguageLevel languageLevel = PythonSdkType.getLanguageLevelForSdk(sdk);
if (!LanguageLevel.SUPPORTED_LEVELS.contains(languageLevel)) {
registerProblem(node,
"Python " + languageLevel + " has reached its end-of-life and is no longer supported by " + product,
fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
final Module associatedModule = PySdkExtKt.getAssociatedModule(sdk);
final String associatedName = associatedModule != null ? associatedModule.getName() : PySdkExtKt.getAssociatedModulePath(sdk);
if (PipenvKt.isPipEnv(sdk) && associatedModule != module) {
final String message = associatedName != null ?
"Pipenv interpreter is associated with another " + interpreterOwner + ": '" + associatedName + "'" :
"Pipenv interpreter is not associated with any " + interpreterOwner;
registerProblem(node, message, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
}
else if (PythonSdkType.isInvalid(sdk)) {
registerProblem(node, "Invalid Python interpreter selected for the " + interpreterOwner, fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
}
else {
final LanguageLevel languageLevel = PythonSdkType.getLanguageLevelForSdk(sdk);
if (!LanguageLevel.SUPPORTED_LEVELS.contains(languageLevel)) {
registerProblem(node,
"Python " + languageLevel + " has reached its end-of-life and is no longer supported by " + product,
fixes.toArray(LocalQuickFix.EMPTY_ARRAY));
}
}
}
}
@@ -18,9 +18,11 @@ package com.jetbrains.python.sdk
import com.intellij.execution.ExecutionException
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.progress.Task
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.ProjectManager
import com.intellij.openapi.project.rootManager
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil
@@ -46,6 +48,7 @@ import java.nio.file.Paths
*/
fun findBaseSdks(existingSdks: List<Sdk>): List<Sdk> {
// TODO: Filter out non-Python SDKs
val existing = existingSdks.filter { it.isSystemWide }
val detected = detectSystemWideSdks(existingSdks)
return existing + detected
@@ -117,6 +120,15 @@ val Sdk.associatedModulePath: String?
// TODO: Support .project associations
get() = associatedPathFromAdditionalData /*?: associatedPathFromDotProject*/
val Sdk.associatedModule: Module?
get() {
val associatedPath = associatedModulePath
return ProjectManager.getInstance().openProjects
.asSequence()
.flatMap { ModuleManager.getInstance(it).modules.asSequence() }
.firstOrNull { it?.basePath == associatedPath }
}
fun Sdk.adminPermissionsNeeded(): Boolean {
val homePath = homePath ?: return false
return !Files.isWritable(Paths.get(homePath))
@@ -133,12 +133,13 @@ public class PythonSdkAdditionalData implements SdkAdditionalData {
if (myAssociatedModulePath != null) {
rootElement.setAttribute(ASSOCIATED_PROJECT_PATH, myAssociatedModulePath);
// XXX: We have to persist the pipenv flag since pipenv is no different from a regular
// virtualenv and currently we want to handle pipenvs differently. Consider adding an SDK
// extension mechanism for that
if (myIsPipEnv) {
rootElement.setAttribute(IS_PIPENV, "true");
}
}
// XXX: We have to persist the pipenv flag since pipenv is no different from a regular
// virtualenv and currently we want to handle pipenvs differently. Consider adding an SDK
// extension mechanism for that
if (myIsPipEnv) {
rootElement.setAttribute(IS_PIPENV, "true");
}
}
@@ -64,6 +64,7 @@ import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase;
import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
import com.jetbrains.python.run.PyVirtualEnvReader;
import com.jetbrains.python.sdk.flavors.CPythonSdkFlavor;
import com.jetbrains.python.sdk.flavors.PipenvKt;
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
import icons.PythonIcons;
import org.jdom.Element;
@@ -220,6 +221,9 @@ public final class PythonSdkType extends SdkType {
return false;
}
final VirtualFile interpreter = sdk.getHomeDirectory();
if (PipenvKt.isPipEnv(sdk) && PySdkExtKt.getAssociatedModule(sdk) == null) {
return true;
}
return interpreter == null || !interpreter.exists();
}
@@ -218,7 +218,12 @@ private val Sdk.packageManager: PyPackageManager
/**
* A quick-fix for setting up the pipenv for the module of the current PSI element.
*/
class UsePipEnvQuickFix : LocalQuickFix {
class UsePipEnvQuickFix(sdk: Sdk?, module: Module) : LocalQuickFix {
private val quickFixName = when {
sdk != null && sdk.associatedModule != module -> "Fix Pipenv interpreter"
else -> "Use Pipenv interpreter"
}
companion object {
fun isApplicable(module: Module): Boolean = module.pipFile != null
@@ -234,12 +239,15 @@ class UsePipEnvQuickFix : LocalQuickFix {
if (sdk == newSdk) {
SdkConfigurationUtil.addSdk(newSdk)
}
else {
sdk.associateWithModule(module, false)
}
project.pythonSdk = sdk
module.pythonSdk = sdk
}
}
override fun getFamilyName() = "Use Pipenv interpreter"
override fun getFamilyName() = quickFixName
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val element = descriptor.psiElement ?: return
@@ -259,6 +267,7 @@ class PipEnvInstallQuickFix : LocalQuickFix {
companion object {
fun pipEnvInstall(project: Project, module: Module) {
val sdk = module.pythonSdk ?: return
if (!sdk.isPipEnv) return
val listener = PyPackageRequirementsInspection.RunningPackagingTasksListener(module)
val ui = PyPackageManagerUI(project, sdk, listener)
ui.install(null, listOf("--dev"))