PY-18966 Fixed: setup.py dependency_links not respected by PyCharm "Install requirements"

Parse "dependency_links" and merge its requirements with requirements from "requires" args
This commit is contained in:
Semyon Proshev
2016-06-10 16:47:55 +03:00
parent 9413efec7b
commit 875e34945f
3 changed files with 50 additions and 4 deletions

View File

@@ -45,9 +45,7 @@ import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -67,6 +65,9 @@ public class PyPackageUtil {
REQUIRES, INSTALL_REQUIRES, "setup_requires", "tests_require"
};
@NotNull
private static final String DEPENDENCY_LINKS = "dependency_links";
private PyPackageUtil() {
}
@@ -134,9 +135,19 @@ public class PyPackageUtil {
return null;
}
final List<PyRequirement> requirementsFromRequires = getSetupPyRequiresFromArguments(module, setupCall, SETUP_PY_REQUIRES_KWARGS_NAMES);
final List<PyRequirement> requirementsFromLinks = getSetupPyRequiresFromArguments(module, setupCall, DEPENDENCY_LINKS);
return mergeSetupPyRequirements(requirementsFromRequires, requirementsFromLinks);
}
@NotNull
private static List<PyRequirement> getSetupPyRequiresFromArguments(@NotNull Module module,
@NotNull PyCallExpression setupCall,
@NotNull String... argumentNames) {
return PyRequirement.fromText(
Stream
.of(SETUP_PY_REQUIRES_KWARGS_NAMES)
.of(argumentNames)
.map(setupCall::getKeywordArgument)
.map(requires -> resolveRequiresValue(module, requires))
.filter(requires -> requires != null)
@@ -147,6 +158,23 @@ public class PyPackageUtil {
);
}
@NotNull
private static List<PyRequirement> mergeSetupPyRequirements(@NotNull List<PyRequirement> requirementsFromRequires,
@NotNull List<PyRequirement> requirementsFromLinks) {
if (!requirementsFromLinks.isEmpty()) {
final Map<String, List<PyRequirement>> nameToRequirements =
requirementsFromRequires.stream().collect(Collectors.groupingBy(PyRequirement::getName, LinkedHashMap::new, Collectors.toList()));
for (PyRequirement requirementFromLinks : requirementsFromLinks) {
nameToRequirements.replace(requirementFromLinks.getName(), Collections.singletonList(requirementFromLinks));
}
return nameToRequirements.values().stream().flatMap(Collection::stream).collect(Collectors.toCollection(ArrayList::new));
}
return requirementsFromRequires;
}
@Nullable
private static PyListLiteralExpression resolveRequiresValue(@NotNull Module module, @Nullable PyExpression requires) {
if (requires instanceof PyListLiteralExpression) {

View File

@@ -0,0 +1,9 @@
from setuptools import setup
setup(name='foo',
version=0.1,
install_requires = ["sqlalchemy >=1.0.12, <1.1",
"mysql-connector-python >=2.1.3, <2.2",],
dependency_links = [
"git+https://github.com/mysql/mysql-connector-python.git@2.1.3#egg=mysql-connector-python-2.1.3",
])

View File

@@ -52,6 +52,15 @@ public class PyPackageUtilTest extends PyTestCase {
doTestSetupPyReading(true, true, true);
}
// PY-18966
public void testSetupPyDependencyLinksReading() {
final List<PyRequirement> actual = PyPackageUtil.findSetupPyRequires(myFixture.getModule());
final List<PyRequirement> expected = PyRequirement.fromText(
"sqlalchemy >=1.0.12, <1.1\ngit+https://github.com/mysql/mysql-connector-python.git@2.1.3#egg=mysql-connector-python-2.1.3");
assertEquals(expected, actual);
}
public void testAbsentRequirementsTxtReading() {
doTestRequirementsTxtReading(false, false);
}