mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-26 19:06:24 +07:00
PyPackage and PyRequirement moved to python-openapi
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
<orderEntry type="module" module-name="platform-impl" />
|
||||
<orderEntry type="module" module-name="lang-api" />
|
||||
<orderEntry type="module" module-name="lang-impl" />
|
||||
<orderEntry type="library" name="Guava" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.jetbrains.python.packaging;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
public class PyPackage {
|
||||
private final String myName;
|
||||
private final String myVersion;
|
||||
private final String myLocation;
|
||||
private final List<PyRequirement> myRequirements;
|
||||
|
||||
public PyPackage(@NotNull String name, @NotNull String version, @Nullable String location, @NotNull List<PyRequirement> requirements) {
|
||||
myName = name;
|
||||
myVersion = version;
|
||||
myLocation = location;
|
||||
myRequirements = requirements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getVersion() {
|
||||
return myVersion;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<PyRequirement> getRequirements() {
|
||||
return myRequirements;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getLocation() {
|
||||
return myLocation;
|
||||
}
|
||||
|
||||
public boolean isInstalled() {
|
||||
return myLocation != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
PyPackage aPackage = (PyPackage)o;
|
||||
if (myName != null ? !myName.equals(aPackage.myName) : aPackage.myName != null) return false;
|
||||
if (myVersion != null ? !myVersion.equals(aPackage.myVersion) : aPackage.myVersion != null) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myName != null ? myName.hashCode() : 0;
|
||||
result = 31 * result + (myVersion != null ? myVersion.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,422 @@
|
||||
package com.jetbrains.python.packaging;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.Function;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author vlan
|
||||
*/
|
||||
public class PyRequirement {
|
||||
private static final Pattern NAME = Pattern.compile("\\s*(\\w(\\w|[-.])*)\\s*(.*)");
|
||||
private static final Pattern VERSION_SPEC = Pattern.compile("\\s*(<=?|>=?|==|!=)\\s*((\\w|[-.])+)");
|
||||
private static final Pattern EDITABLE_EGG = Pattern.compile("\\s*(-e)?\\s*([^#]*)#egg=(.*)");
|
||||
private static final Pattern RECURSIVE_REQUIREMENT = Pattern.compile("\\s*-r\\s+(.*)");
|
||||
|
||||
public enum Relation {
|
||||
LT("<"),
|
||||
LTE("<="),
|
||||
GT(">"),
|
||||
GTE(">="),
|
||||
EQ("=="),
|
||||
NE("!=");
|
||||
|
||||
@NotNull private final String myValue;
|
||||
|
||||
Relation(@NotNull String value) {
|
||||
myValue = value;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Relation fromString(@NotNull String value) {
|
||||
for (Relation relation : Relation.values()) {
|
||||
if (relation.myValue.equals(value)) {
|
||||
return relation;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isSuccessful(int comparisonResult) {
|
||||
switch (this) {
|
||||
case LT:
|
||||
return comparisonResult < 0;
|
||||
case LTE:
|
||||
return comparisonResult <= 0;
|
||||
case GT:
|
||||
return comparisonResult > 0;
|
||||
case GTE:
|
||||
return comparisonResult >= 0;
|
||||
case EQ:
|
||||
return comparisonResult == 0;
|
||||
case NE:
|
||||
return comparisonResult != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static class VersionSpec {
|
||||
@NotNull private final Relation myRelation;
|
||||
@NotNull private final String myVersion;
|
||||
|
||||
public VersionSpec(@NotNull Relation relation, @NotNull String version) {
|
||||
myRelation = relation;
|
||||
myVersion = version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return myRelation + myVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
VersionSpec spec = (VersionSpec)o;
|
||||
if (myRelation != spec.myRelation) return false;
|
||||
if (!myVersion.equals(spec.myVersion)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myRelation.hashCode();
|
||||
result = 31 * result + myVersion.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Relation getRelation() {
|
||||
return myRelation;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getVersion() {
|
||||
return myVersion;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull private final String myName;
|
||||
@NotNull private final List<VersionSpec> myVersionSpecs;
|
||||
@Nullable private final String myURL;
|
||||
private final boolean myEditable;
|
||||
|
||||
public static final Comparator<String> VERSION_COMPARATOR = new Comparator<String>() {
|
||||
@Override
|
||||
public int compare(String version1, String version2) {
|
||||
final List<String> vs1 = parse(version1);
|
||||
final List<String> vs2 = parse(version2);
|
||||
int result = 0;
|
||||
for (int i = 0; i < vs1.size() && i < vs2.size(); i++) {
|
||||
result = vs1.get(i).compareTo(vs2.get(i));
|
||||
if (result != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result == 0) {
|
||||
return vs1.size() - vs2.size();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String replace(@NotNull String s) {
|
||||
final Map<String, String> sub = ImmutableMap.of("pre", "c",
|
||||
"preview", "c",
|
||||
"rc", "c",
|
||||
"dev", "@");
|
||||
final String tmp = sub.get(s);
|
||||
if (tmp != null) {
|
||||
s = tmp;
|
||||
}
|
||||
if (s.equals(".") || s.equals("-")) {
|
||||
return null;
|
||||
}
|
||||
if (s.matches("[0-9]+")) {
|
||||
final long value = Long.parseLong(s);
|
||||
return String.format("%08d", value);
|
||||
}
|
||||
return "*" + s;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<String> parse(@Nullable String s) {
|
||||
// Version parsing from pkg_resources ensures that all the "pre", "alpha", "rc", etc. are sorted correctly
|
||||
final Pattern COMPONENT_RE = Pattern.compile("\\d+|[a-z]+|\\.|-|.+");
|
||||
final List<String> results = new ArrayList<String>();
|
||||
final Matcher matcher = COMPONENT_RE.matcher(s);
|
||||
while (matcher.find()) {
|
||||
final String component = replace(matcher.group());
|
||||
if (component == null) {
|
||||
continue;
|
||||
}
|
||||
results.add(component);
|
||||
}
|
||||
for (int i = results.size() - 1; i > 0; i--) {
|
||||
if ("00000000".equals(results.get(i))) {
|
||||
results.remove(i);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
results.add("*final");
|
||||
return results;
|
||||
}
|
||||
};
|
||||
|
||||
public PyRequirement(@NotNull String name) {
|
||||
this(name, Collections.<VersionSpec>emptyList());
|
||||
}
|
||||
|
||||
public PyRequirement(@NotNull String name, @NotNull String version) {
|
||||
this(name, Collections.singletonList(new VersionSpec(Relation.EQ, version)));
|
||||
}
|
||||
|
||||
public PyRequirement(@NotNull String name, @NotNull List<VersionSpec> versionSpecs) {
|
||||
myName = name;
|
||||
myVersionSpecs = versionSpecs;
|
||||
myURL = null;
|
||||
myEditable = false;
|
||||
}
|
||||
|
||||
public PyRequirement(@NotNull String name, @Nullable String version, @NotNull String url, boolean editable) {
|
||||
myName = name;
|
||||
if (version != null) {
|
||||
myVersionSpecs = Collections.singletonList(new VersionSpec(Relation.EQ, version));
|
||||
}
|
||||
else {
|
||||
myVersionSpecs = Collections.emptyList();
|
||||
}
|
||||
myURL = url;
|
||||
myEditable = editable;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return myName + StringUtil.join(myVersionSpecs,
|
||||
new Function<VersionSpec, String>() {
|
||||
@Override
|
||||
public String fun(VersionSpec spec) {
|
||||
return spec.toString();
|
||||
}
|
||||
},
|
||||
",");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<String> toOptions() {
|
||||
final List<String> results = new ArrayList<String>();
|
||||
if (myEditable) {
|
||||
results.add("-e");
|
||||
}
|
||||
if (myURL != null) {
|
||||
final int size = myVersionSpecs.size();
|
||||
assert size <= 1;
|
||||
final String urlAndName = myURL + "#egg=" + myName;
|
||||
if (size == 0) {
|
||||
results.add(urlAndName);
|
||||
}
|
||||
else {
|
||||
final VersionSpec versionSpec = myVersionSpecs.get(0);
|
||||
assert versionSpec.getRelation() == Relation.EQ;
|
||||
results.add(urlAndName + "-" + versionSpec.getVersion());
|
||||
}
|
||||
return results;
|
||||
}
|
||||
else {
|
||||
results.add(toString());
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
PyRequirement that = (PyRequirement)o;
|
||||
if (!myName.equals(that.myName)) return false;
|
||||
if (!myVersionSpecs.equals(that.myVersionSpecs)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myName.hashCode();
|
||||
result = 31 * result + myVersionSpecs.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PyPackage match(@NotNull List<PyPackage> packages) {
|
||||
for (PyPackage pkg : packages) {
|
||||
if (normalizeName(myName).equalsIgnoreCase(pkg.getName())) {
|
||||
for (VersionSpec spec : myVersionSpecs) {
|
||||
final int cmp = VERSION_COMPARATOR.compare(pkg.getVersion(), spec.getVersion());
|
||||
final Relation relation = spec.getRelation();
|
||||
if (!relation.isSuccessful(cmp)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return pkg;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PyRequirement fromString(@NotNull String line) {
|
||||
// TODO: Extras, multi-line requirements '\'
|
||||
final PyRequirement editableEgg = parseEditableEgg(line);
|
||||
if (editableEgg != null) {
|
||||
return editableEgg;
|
||||
}
|
||||
final Matcher nameMatcher = NAME.matcher(line);
|
||||
if (!nameMatcher.matches()) {
|
||||
return null;
|
||||
}
|
||||
final String name = nameMatcher.group(1);
|
||||
final String rest = nameMatcher.group(3);
|
||||
final List<VersionSpec> versionSpecs = new ArrayList<VersionSpec>();
|
||||
if (!rest.trim().isEmpty()) {
|
||||
final Matcher versionSpecMatcher = VERSION_SPEC.matcher(rest);
|
||||
while (versionSpecMatcher.find()) {
|
||||
final String rel = versionSpecMatcher.group(1);
|
||||
final String version = versionSpecMatcher.group(2);
|
||||
final Relation relation = Relation.fromString(rel);
|
||||
if (relation == null) {
|
||||
return null;
|
||||
}
|
||||
versionSpecs.add(new VersionSpec(relation, version));
|
||||
}
|
||||
}
|
||||
return new PyRequirement(name, versionSpecs);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<PyRequirement> parse(@NotNull String s) {
|
||||
return parse(s, null, new HashSet<VirtualFile>());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<PyRequirement> parse(@NotNull VirtualFile file) {
|
||||
return parse(file, new HashSet<VirtualFile>());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static List<PyRequirement> parse(@NotNull VirtualFile file, @NotNull Set<VirtualFile> visited) {
|
||||
if (!visited.contains(file)) {
|
||||
visited.add(file);
|
||||
final Document document = FileDocumentManager.getInstance().getDocument(file);
|
||||
if (document != null) {
|
||||
return parse(document.getText(), file, visited);
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<PyRequirement> parse(@NotNull String s, @Nullable VirtualFile anchor, @NotNull Set<VirtualFile> visited) {
|
||||
final List<PyRequirement> result = new ArrayList<PyRequirement>();
|
||||
for (String line : StringUtil.splitByLines(s)) {
|
||||
final String trimmed = line.trim();
|
||||
if (!trimmed.isEmpty()) {
|
||||
final PyRequirement req = fromString(line);
|
||||
if (req != null) {
|
||||
result.add(req);
|
||||
}
|
||||
else if (anchor != null) {
|
||||
result.addAll(parseRecursiveRequirement(trimmed, anchor, visited));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<PyRequirement> parseRecursiveRequirement(@NotNull String line, @NotNull VirtualFile anchor,
|
||||
@NotNull Set<VirtualFile> visited) {
|
||||
final Matcher matcher = RECURSIVE_REQUIREMENT.matcher(line);
|
||||
if (matcher.matches()) {
|
||||
final String fileName = FileUtil.toSystemIndependentName(matcher.group(1));
|
||||
final VirtualFile dir = anchor.getParent();
|
||||
if (dir != null) {
|
||||
VirtualFile file = dir.findFileByRelativePath(fileName);
|
||||
if (file == null) {
|
||||
file = LocalFileSystem.getInstance().findFileByPath(fileName);
|
||||
}
|
||||
if (file != null) {
|
||||
return parse(file, visited);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PyRequirement parseEditableEgg(@NotNull String line) {
|
||||
final Matcher editableEggMatcher = EDITABLE_EGG.matcher(line);
|
||||
if (!editableEggMatcher.matches()) {
|
||||
return null;
|
||||
}
|
||||
final boolean editable = editableEggMatcher.group(1) != null;
|
||||
final String url = editableEggMatcher.group(2);
|
||||
final String egg = editableEggMatcher.group(3);
|
||||
if (url == null) {
|
||||
return null;
|
||||
}
|
||||
boolean isName = true;
|
||||
final List<String> nameParts = new ArrayList<String>();
|
||||
final List<String> versionParts = new ArrayList<String>();
|
||||
for (String part : StringUtil.split(egg, "-")) {
|
||||
if (part.matches("[0-9].*") || "dev".equals(part)) {
|
||||
isName = false;
|
||||
}
|
||||
if (isName) {
|
||||
nameParts.add(part);
|
||||
}
|
||||
else {
|
||||
versionParts.add(part);
|
||||
}
|
||||
}
|
||||
final String name = normalizeName(StringUtil.join(nameParts, "-"));
|
||||
final String version = !versionParts.isEmpty() ? normalizeVersion(StringUtil.join(versionParts, "-")) : null;
|
||||
return new PyRequirement(name, version, url, editable);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String normalizeName(@NotNull String s) {
|
||||
return s.replace("_", "-");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String normalizeVersion(@NotNull String s) {
|
||||
return s.replace("_", "-").replaceAll("-?py[0-9\\.]+", "");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user