[python] support Pathlib-style path declarations in PyPathEvaluator (PY-13911)

(cherry picked from commit 3673aa3bac948bae5c7e8fd6521f251285d70cf9)

IJ-MR-17579

GitOrigin-RevId: 17d336d8f6d5e16bd3dca815c2f9935911e050ee
This commit is contained in:
Aleksei Kniazev
2021-11-23 16:36:38 +03:00
committed by intellij-monorepo-bot
parent effa066a2d
commit 05a99b5e99
3 changed files with 51 additions and 4 deletions

View File

@@ -137,7 +137,7 @@ public class PyEvaluator {
}
@Nullable
private Object evaluateBinary(@NotNull PyBinaryExpression expression) {
protected Object evaluateBinary(@NotNull PyBinaryExpression expression) {
final PyElementType op = expression.getOperator();
final Object lhs = evaluate(expression.getLeftExpression());
final Object rhs = evaluate(expression.getRightExpression());

View File

@@ -18,9 +18,8 @@ package com.jetbrains.python.psi.impl;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.PyCallExpression;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyReferenceExpression;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -49,6 +48,15 @@ public class PyPathEvaluator extends PyEvaluator {
@Nullable
protected Object evaluateCall(@NotNull PyCallExpression expression) {
final PyExpression[] args = expression.getArguments();
if (expression.isCalleeText("resolve")) {
PyReferenceExpression callee = (PyReferenceExpression)expression.getCallee();
if (callee != null && callee.getQualifier() instanceof PyCallExpression) {
return evaluate(callee.getQualifier());
}
}
if (expression.isCalleeText("Path") && args.length >= 1) {
return evaluate(args[0]);
}
if (expression.isCalleeText(PyNames.DIRNAME) && args.length == 1) {
Object argValue = evaluate(args[0]);
return argValue instanceof String ? Paths.get((String) argValue).getParent().toFile().getPath() : null;
@@ -89,6 +97,12 @@ public class PyPathEvaluator extends PyEvaluator {
if (!expression.isQualified() && PyNames.FILE.equals(expression.getReferencedName())) {
return FileUtil.toSystemIndependentName(myContainingFilePath);
}
if ("parent".equals(expression.getName()) && expression.isQualified()) {
Object qualifier = evaluate(expression.getQualifier());
if (qualifier instanceof String) {
return Paths.get((String)qualifier).getParent().toFile().getPath();
}
}
return super.evaluateReference(expression);
}
@@ -108,4 +122,17 @@ public class PyPathEvaluator extends PyEvaluator {
}
return result;
}
@Override
protected @Nullable Object evaluateBinary(@NotNull PyBinaryExpression expression) {
final PyElementType operator = expression.getOperator();
if (operator == PyTokenTypes.DIV) {
final Object lhs = evaluate(expression.getLeftExpression());
final Object rhs = evaluate(expression.getRightExpression());
if (lhs instanceof String && rhs instanceof String) {
return FileUtil.toSystemIndependentName(Paths.get((String)lhs, (String)rhs).toFile().getPath());
}
}
return super.evaluateBinary(expression);
}
}

View File

@@ -50,6 +50,26 @@ public class PyPathEvaluatorTest extends PyTestCase {
assertEquals("/foo/subfolder/../bar.py", doEvaluate("os.path.abspath(os.path.join(os.path.join('/foo/subfolder', os.path.pardir, 'bar.py')))", "/foo/bar.py"));
}
// PY-13911
public void testPathlibRoot() {
assertEquals("/foo/bar/baz.py", doEvaluate("Path(__file__)", "/foo/bar/baz.py"));
}
// PY-13911
public void testPathlibParent() {
assertEquals("/foo", doEvaluate("Path(__file__).resolve().parent.parent", "/foo/bar/baz.py"));
}
// PY-13911
public void testPathlibAbsolutePath() {
assertEquals("/foo/bar", doEvaluate("Path('/foo/bar')", "/irrelevant"));
}
// PY-13911
public void testPathlibJoin() {
assertEquals("/foo/bar/baz.py", doEvaluate("Path(__file__).resolve() / 'bar' / 'baz.py'", "/foo"));
}
private String doEvaluate(final String text, final String file) {
final PyElementGenerator generator = PyElementGenerator.getInstance(myFixture.getProject());
final PyExpression expression = generator.createExpressionFromText(LanguageLevel.getLatest(), text);