Many pypi packages could install same modules. Support this in PyPIPackageUtil.PACKAGES_TOPLEVEL and its usages.

This commit is contained in:
Semyon Proshev
2017-06-28 17:01:48 +03:00
parent 7088d9d25f
commit 8e3b1cb107
7 changed files with 43 additions and 33 deletions
@@ -62,9 +62,10 @@ object PyTypeShed {
if (ApplicationManager.getApplication().isUnitTestMode) {
return true
}
val pyPIPackage = PyPIPackageUtil.PACKAGES_TOPLEVEL[topLevelPackage] ?: topLevelPackage
val pyPIPackages = PyPIPackageUtil.PACKAGES_TOPLEVEL[topLevelPackage] ?: emptyList()
val packages = PyPackageManagers.getInstance().forSdk(sdk).packages ?: return true
return PyPackageUtil.findPackage(packages, pyPIPackage) != null
return PyPackageUtil.findPackage(packages, topLevelPackage) != null ||
pyPIPackages.any { PyPackageUtil.findPackage(packages, it) != null }
}
return false
}
@@ -32,6 +32,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.profile.codeInspection.ProjectInspectionProfileManager;
import com.intellij.psi.*;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.codeInsight.imports.AddImportHelper;
import com.jetbrains.python.codeInsight.stdlib.PyStdlibUtil;
@@ -159,10 +160,11 @@ public class PyPackageRequirementsInspection extends PyInspection {
final String packageName = packageReferenceExpression.getName();
if (packageName != null && !myIgnoredPackages.contains(packageName)) {
final String possiblePyPIPackageName = PyPIPackageUtil.PACKAGES_TOPLEVEL.get(packageName);
final List<String> possiblePyPIPackageNames = PyPIPackageUtil.PACKAGES_TOPLEVEL.getOrDefault(packageName, Collections.emptyList());
if (!ApplicationManager.getApplication().isUnitTestMode() &&
StreamEx.of(packageName, possiblePyPIPackageName).nonNull().noneMatch(PyPIPackageUtil.INSTANCE::isInPyPI)) return;
!PyPIPackageUtil.INSTANCE.isInPyPI(packageName) &&
!ContainerUtil.exists(possiblePyPIPackageNames, PyPIPackageUtil.INSTANCE::isInPyPI)) return;
if (PyPackageUtil.SETUPTOOLS.equals(packageName)) return;
@@ -177,16 +179,16 @@ public class PyPackageRequirementsInspection extends PyInspection {
for (PyRequirement req : requirements) {
final String name = req.getName();
if (packageName.equalsIgnoreCase(name) || possiblePyPIPackageName != null && possiblePyPIPackageName.equalsIgnoreCase(name)) {
if (name.equalsIgnoreCase(packageName) || ContainerUtil.exists(possiblePyPIPackageNames, name::equalsIgnoreCase)) {
return;
}
final String nameWhereUnderscoreReplacedWithHyphen = name.replaceAll("_", "-");
if (possiblePyPIPackageName != null && possiblePyPIPackageName.equalsIgnoreCase(nameWhereUnderscoreReplacedWithHyphen)) {
if (ContainerUtil.exists(possiblePyPIPackageNames, nameWhereUnderscoreReplacedWithHyphen::equalsIgnoreCase)) {
return;
}
final String nameWhereHyphenReplacedWithUnderscore = name.replaceAll("-", "_");
if (packageName.equalsIgnoreCase(nameWhereHyphenReplacedWithUnderscore) ||
possiblePyPIPackageName != null && possiblePyPIPackageName.equalsIgnoreCase(nameWhereHyphenReplacedWithUnderscore)) {
if (nameWhereHyphenReplacedWithUnderscore.equalsIgnoreCase(packageName) ||
ContainerUtil.exists(possiblePyPIPackageNames, nameWhereHyphenReplacedWithUnderscore::equalsIgnoreCase)) {
return;
}
}
@@ -208,8 +210,8 @@ public class PyPackageRequirementsInspection extends PyInspection {
}
final String suggestedPackageName = StreamEx
.of(packageName, possiblePyPIPackageName)
.nonNull()
.of(packageName)
.append(possiblePyPIPackageNames)
.findFirst(PyPIPackageUtil.INSTANCE::isInPyPI)
.orElse(packageName);
@@ -635,15 +635,11 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
final Module module = ModuleUtilCore.findModuleForPsiElement(node);
final Sdk sdk = PythonSdkType.findPythonSdk(module);
if (module != null && sdk != null && PyPackageUtil.packageManagementEnabled(sdk)) {
if (PyPIPackageUtil.INSTANCE.isInPyPI(packageName)) {
addInstallPackageAction(actions, packageName, module, sdk);
}
else {
if (PyPIPackageUtil.PACKAGES_TOPLEVEL.containsKey(packageName)) {
final String suggestedPackage = PyPIPackageUtil.PACKAGES_TOPLEVEL.get(packageName);
addInstallPackageAction(actions, suggestedPackage, module, sdk);
}
}
StreamEx
.of(packageName)
.append(PyPIPackageUtil.PACKAGES_TOPLEVEL.getOrDefault(packageName, Collections.emptyList()))
.filter(PyPIPackageUtil.INSTANCE::isInPyPI)
.forEach(pkg -> addInstallPackageAction(actions, pkg, module, sdk));
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,9 +28,9 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.CatchingConsumer;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.io.HttpRequests;
import com.intellij.webcore.packaging.PackageVersionComparator;
@@ -44,11 +44,12 @@ import javax.swing.text.MutableAttributeSet;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.parser.ParserDelegator;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.regex.Matcher;
@@ -64,9 +65,9 @@ public class PyPIPackageUtil {
public static final String PYPI_LIST_URL = PYPI_HOST + "/simple";
/**
* Contains mapping "importable top-level package" -> "package name on PyPI".
* Contains mapping "importable top-level package" -> "package names on PyPI".
*/
public static final ImmutableMap<String, String> PACKAGES_TOPLEVEL = loadPackageAliases();
public static final ImmutableMap<String, List<String>> PACKAGES_TOPLEVEL = loadPackageAliases();
public static final PyPIPackageUtil INSTANCE = new PyPIPackageUtil();
@@ -138,15 +139,17 @@ public class PyPIPackageUtil {
}
@NotNull
private static ImmutableMap<String, String> loadPackageAliases() {
final ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
try (FileReader reader = new FileReader(PythonHelpersLocator.getHelperPath("/tools/packages"))) {
final String text = FileUtil.loadTextAndClose(reader);
final List<String> lines = StringUtil.split(text, "\n");
for (String line : lines) {
final List<String> split = StringUtil.split(line, " ");
builder.put(split.get(0), split.get(1));
}
private static ImmutableMap<String, List<String>> loadPackageAliases() {
final ImmutableMap.Builder<String, List<String>> builder = ImmutableMap.builder();
try {
Files
.lines(Paths.get(PythonHelpersLocator.getHelperPath("/tools/packages")))
.forEach(
line -> {
final List<String> split = StringUtil.split(line, " ");
builder.put(split.get(0), new SmartList<>(ContainerUtil.subList(split, 1)));
}
);
}
catch (IOException e) {
LOG.error("Cannot find \"packages\". " + e.getMessage());
@@ -0,0 +1,2 @@
django-recaptcha-mozilla
pyzmq-ctypes
@@ -0,0 +1,2 @@
<warning descr="Package requirements 'django-recaptcha-mozilla', 'pyzmq-ctypes' are not satisfied">import captcha
import zmq</warning>
@@ -65,6 +65,10 @@ public class PyPackageRequirementsInspectionTest extends PyTestCase {
doTest("test1.py");
}
public void testOnePackageManyPossibleRequirements() {
doTest("test1.py");
}
private void doTest(@NotNull final String filename) {
final String testName = getTestName(false);
myFixture.copyDirectoryToProject("inspections/PyPackageRequirementsInspection/" + testName, "");