PY-12810 "imported" urls are not supported by {% url %} tag ( "patterns +=" now supported)

This commit is contained in:
Ilya.Kazakevich
2014-05-20 02:44:55 +04:00
parent d4fd095f93
commit 47fc79d09e
8 changed files with 164 additions and 304 deletions
@@ -61,6 +61,8 @@ public class NewDirectoryProjectDialog extends DialogWrapper {
setTitle("Create New Project");
init();
myLocationField = new TextFieldWithBrowseButton();
myLocationLabel = new JLabel();
myLocationLabel.setLabelFor(myLocationField.getChildComponent());
new LocationNameFieldsBinding(project, myLocationField, myProjectNameTextField, ProjectUtil.getBaseDir(), "Select Location for Project Directory");
@@ -62,7 +62,9 @@ public class PsiQuery {
/**
* TODO: Support types?
* Filter children by function call
* @return {@link com.jetbrains.python.psi.PsiQuery} backed by {@link com.jetbrains.python.psi.PyCallExpression}
*/
@NotNull
public PsiQuery childrenCall(@NotNull final FQNamesProvider name) {
@@ -850,6 +850,29 @@ public class PyUtil {
return null;
}
/**
* Converts collection to list of certain type
* @param expression expression of collection type
* @param elementClass expected element type
* @param <T> expected element type
* @return list of elements of expected element type
*/
@NotNull
public static <T> List<T> asList(@Nullable final Collection<?> expression, @NotNull final Class<T> elementClass) {
if ((expression == null) || expression.isEmpty()) {
return Collections.emptyList();
}
final List<T> result = new ArrayList<T>();
for (final Object element : expression) {
final T toAdd = as(element, elementClass);
if (toAdd != null) {
result.add(toAdd);
}
}
return result;
}
public static class KnownDecoratorProviderHolder {
public static PyKnownDecoratorProvider[] KNOWN_DECORATOR_PROVIDERS = Extensions.getExtensions(PyKnownDecoratorProvider.EP_NAME);
@@ -0,0 +1,105 @@
package com.jetbrains.python.psi.impl;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Evaluator tht supports expression of any type (it evaluates any expression, concatenating several expressions to list etc)
*/
public class PyAnyExpressionEvaluator extends PyEvaluator {
private final boolean myEvalSequence;
/**
* @param evalSequence evaluate sequencies ((a,b) + (c,d) == (a,b,c,d)) or store them as single ((a,b) + (c,d) = [(a,b), (c,d)])
*/
public PyAnyExpressionEvaluator(final boolean evalSequence) {
myEvalSequence = evalSequence;
}
@NotNull
@Override
public Object evaluate(final PyExpression expr) {
final Object evaluate = super.evaluate(expr);
return ((evaluate != null) ? evaluate : expr);
}
@Override
public Object concatenate(final Object lhs, final Object rhs) {
final Object evaluate = super.concatenate(lhs, rhs);
return ((evaluate != null) ? evaluate : Arrays.asList(lhs, rhs));
}
@Override
protected Object evaluateReferenceExpression(final PyReferenceExpression expr) {
final Object evaluate = super.evaluateReferenceExpression(expr);
return ((evaluate != null) ? evaluate : expr);
}
@Override
protected Object evaluateCall(final PyCallExpression call) {
final Object evaluate = super.evaluateCall(call);
return ((evaluate != null) ? evaluate : call);
}
@Override
protected Object evaluateSequenceExpression(final PySequenceExpression expr) {
return myEvalSequence ? super.evaluateSequenceExpression(expr) : expr;
}
/**
* Evaluates expression to single element
* @param expression exp to eval
* @param aClass expected class
* @param <T> expected class
* @return instance of aClass, or null if failed to eval
*/
@Nullable
public static <T> T evaluateOne(@NotNull final PyExpression expression, @NotNull final Class<T> aClass) {
final PyAnyExpressionEvaluator evaluator = new PyAnyExpressionEvaluator(false);
final Object evaluate = evaluator.evaluate(expression);
final T resultSingle = PyUtil.as(evaluate, aClass);
if (resultSingle != null) {
return resultSingle;
}
final List<?> resultMultiple = PyUtil.as(evaluate, List.class);
if ((resultMultiple != null) && !resultMultiple.isEmpty()) {
return PyUtil.as(resultMultiple.get(0), aClass);
}
return null;
}
/**
* Evaluates expression to string
* @param expression exp to eval
* @return string, or null if failed to eval
*/
@Nullable
public static String evaluateString(@NotNull final PyExpression expression) {
return PyUtil.as(new PyAnyExpressionEvaluator(false).evaluate(expression), String.class);
}
/**
* Evaluates expression as list of values
* @param expression exp to eval
* @param aClass expected element class
* @param <T> expected element class
* @return a list of elements of expected type
*/
@NotNull
public static <T>List<T> evaluateIterable(@NotNull final PyExpression expression, @NotNull final Class<T> aClass) {
final PyAnyExpressionEvaluator evaluator = new PyAnyExpressionEvaluator(true);
final Object evaluate = evaluator.evaluate(expression);
final T resultSingle = PyUtil.as(evaluate, aClass);
if (resultSingle != null) {
return Collections.singletonList(resultSingle);
}
return PyUtil.asList(PyUtil.as(evaluate, List.class), aClass);
}
}
@@ -26,7 +26,6 @@ import java.util.*;
/**
*
* @see {@link com.jetbrains.python.psi.impl.PyExpressionEvaluator} (TODO: merge both classes?)
* @author yole
*/
public class PyBlockEvaluator {
@@ -99,7 +98,7 @@ public class PyBlockEvaluator {
Object currentValue = myNamespace.get(name);
if (currentValue != null) {
Object rhs = prepareEvaluator().evaluate(node.getValue());
myNamespace.put(name, PyEvaluator.concatenate(currentValue, rhs));
myNamespace.put(name, prepareEvaluator().concatenate(currentValue, rhs));
}
if (myDeclarationsToTrack.contains(name)) {
List<PyExpression> declarations = myDeclarations.get(name);
@@ -189,7 +188,7 @@ public class PyBlockEvaluator {
Object value = myNamespace.get(nameBeingExtended);
if (value instanceof List) {
Object argValue = prepareEvaluator().evaluate(arg);
myNamespace.put(nameBeingExtended, PyEvaluator.concatenate(value, argValue));
myNamespace.put(nameBeingExtended, prepareEvaluator().concatenate(value, argValue));
}
if (myDeclarationsToTrack.contains(nameBeingExtended)) {
@@ -24,7 +24,7 @@ import com.jetbrains.python.psi.resolve.PyResolveContext;
import java.util.*;
/**
* TODO: Merge {@link com.jetbrains.python.psi.impl.PyExpressionEvaluator}, PythonDataflowUtil, {@link com.jetbrains.python.psi.impl.PyConstantExpressionEvaluator} and {@link com.jetbrains.python.psi.impl.PyEvaluator} and all its inheritors and improve Abstract Interpretation
* TODO: Merge PythonDataflowUtil, {@link com.jetbrains.python.psi.impl.PyConstantExpressionEvaluator} and {@link com.jetbrains.python.psi.impl.PyEvaluator} and all its inheritors and improve Abstract Interpretation
* @author yole
*/
public class PyEvaluator {
@@ -49,25 +49,7 @@ public class PyEvaluator {
return evaluate(((PyParenthesizedExpression)expr).getContainedExpression());
}
if (expr instanceof PySequenceExpression) {
PyExpression[] elements = ((PySequenceExpression)expr).getElements();
if (expr instanceof PyDictLiteralExpression) {
Map<Object, Object> result = new HashMap<Object, Object>();
for (PyKeyValueExpression keyValueExpression : ((PyDictLiteralExpression)expr).getElements()) {
Object dictKey = evaluate(keyValueExpression.getKey());
if (dictKey != null) {
PyExpression value = keyValueExpression.getValue();
result.put(dictKey, myEvaluateCollectionItems ? evaluate(value) : value);
}
}
return result;
}
else {
List<Object> result = new ArrayList<Object>();
for (PyExpression element : elements) {
result.add(myEvaluateCollectionItems ? evaluate(element) : element);
}
return result;
}
return evaluateSequenceExpression((PySequenceExpression)expr);
}
if (expr instanceof PyCallExpression) {
return evaluateCall((PyCallExpression)expr);
@@ -92,7 +74,34 @@ public class PyEvaluator {
return null;
}
public static Object concatenate(Object lhs, Object rhs) {
/**
* Evaluates some sequence (tuple, list)
* @param expr seq expression
* @return evaluated seq
*/
protected Object evaluateSequenceExpression(PySequenceExpression expr) {
PyExpression[] elements = expr.getElements();
if (expr instanceof PyDictLiteralExpression) {
Map<Object, Object> result = new HashMap<Object, Object>();
for (PyKeyValueExpression keyValueExpression : ((PyDictLiteralExpression)expr).getElements()) {
Object dictKey = evaluate(keyValueExpression.getKey());
if (dictKey != null) {
PyExpression value = keyValueExpression.getValue();
result.put(dictKey, myEvaluateCollectionItems ? evaluate(value) : value);
}
}
return result;
}
else {
List<Object> result = new ArrayList<Object>();
for (PyExpression element : elements) {
result.add(myEvaluateCollectionItems ? evaluate(element) : element);
}
return result;
}
}
public Object concatenate(Object lhs, Object rhs) {
if (lhs instanceof String && rhs instanceof String) {
return (String)lhs + (String)rhs;
}
@@ -1,147 +0,0 @@
package com.jetbrains.python.psi.impl;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* Engine to evaluate expressions.
* <p/>
* Supports references and concatinations (sometimes!)
*
* @author Ilya.Kazakevich
* @see {@link com.jetbrains.python.psi.impl.PyBlockEvaluator} (TODO: merge both classes?)
*/
public final class PyExpressionEvaluator {
private static final int MAX_STEP = 100;
private PyExpressionEvaluator() {
}
/**
* Evaluate expression to string
*
* @param expression expression to evaluate
* @return string (or empty if cant evaluate)
*/
@NotNull
public static String evaluateString(@NotNull final PyExpression expression) {
final List<PyStringLiteralExpression> expressions = evaluateRaw(expression, PyStringLiteralExpression.class);
final StringBuilder builder = new StringBuilder();
for (final PyStringLiteralExpression literalExpression : expressions) {
builder.append(literalExpression.getStringValue());
}
return builder.toString();
}
/**
* Evaluate expression to long
*
* @param expression expression to evaluate
* @return long (or 0 if cant evaluate)
*/
public static long evaluateLong(@NotNull final PyExpression expression) {
final List<PyNumericLiteralExpression> expressions = evaluateRaw(expression, PyNumericLiteralExpression.class);
long result = 0L;
for (final PyNumericLiteralExpression numericLiteralExpression : expressions) {
final Long value = numericLiteralExpression.getLongValue();
result += value != null ? value : 0L;
}
return result;
}
/**
* Evaluate expression to pack of elements (some iterables could be evaluated to it)
*
* @param expression expression to evaluate
* @param aClass expected class of elements
* @return pack of elements (or empty if cant evaluate)
*/
@NotNull
public static <T extends PyElement> List<T> evaluateIterable(@NotNull final PyExpression expression, @NotNull final Class<T> aClass) {
final List<T> list = new ArrayList<T>();
for (final PyElement possibleIterable : evaluateRaw(expression, PyElement.class)) {
if ((possibleIterable instanceof PyParenthesizedExpression) || (possibleIterable instanceof PyListLiteralExpression)) {
list.addAll(PsiTreeUtil.findChildrenOfType(possibleIterable, aClass));
}
}
return list;
}
/**
* Evaluates expression to some element
* @param expression expression to evaluate
* @param aClass expected element type
* @param <T> expected element type
* @return element or null if can't evaluate
*/
@Nullable
public static <T extends PyElement> T evaluateOne(@NotNull final PyExpression expression, @NotNull final Class<T> aClass) {
final List<T> list = evaluateRaw(expression, aClass);
if (list.isEmpty()) {
return null;
}
return list.get(0);
}
/**
* Evaluates expression to one or more elements (all elements should be concatinated).
* I.e: i = 1 + 2. Evaluating "i" would lead to "1" and "2" separately (and not summed like in {@link #evaluateLong(com.jetbrains.python.psi.PyExpression)}
* @param expression expression to evaluate
* @param aClass expected elements type
* @param <T> expected elements type
* @return a pack of elements to concatinate or empty if can't evaluate
*/
@NotNull
public static <T extends PyElement> List<T> evaluateRaw(@NotNull final PyExpression expression, @NotNull final Class<T> aClass) {
int step = 0;
PyElement currentElement = expression;
final List<T> result = new ArrayList<T>();
while (step < MAX_STEP) {
PyElement newElement = null;
// TODO: Use visitor?
if (currentElement instanceof PyReferenceExpression) {
newElement = PyUtil.as(((PyReferenceExpression)currentElement).getReference().resolve(), PyElement.class);
}
else if (currentElement instanceof PyTargetExpression) {
newElement = ((PyTargetExpression)currentElement).findAssignedValue();
}
else if (currentElement instanceof PyBinaryExpression) {
final PyBinaryExpression newExpression = (PyBinaryExpression)currentElement;
final PyElementType operator = newExpression.getOperator();
if (PyTokenTypes.PLUS.equals(operator)) {
final PyExpression leftExpression = newExpression.getLeftExpression();
final PyExpression rightExpression = newExpression.getRightExpression();
if (leftExpression != null) {
result.addAll(evaluateRaw(leftExpression, aClass));
}
if (rightExpression != null) {
result.addAll(evaluateRaw(rightExpression, aClass));
}
}
}
if (newElement == null) {
break;
}
currentElement = newElement;
step++;
}
final T elementToAdd = PyUtil.as(currentElement, aClass);
if (elementToAdd != null) {
result.add(elementToAdd);
}
return result;
}
}
@@ -1,133 +0,0 @@
package com.jetbrains.python;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiFile;
import com.jetbrains.python.fixtures.PyTestCase;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyExpressionEvaluator;
import org.hamcrest.Matchers;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
import java.util.List;
/**
* Tests {@link com.jetbrains.python.psi.impl.PyExpressionEvaluator}
*
* @author Ilya.Kazakevich
*/
public class PyExpressionEvaluatorTest extends PyTestCase {
public void testLiteralReferences() {
final PyTargetExpression myValue = createFileAndExpression("my_value",
"x = 2",
"y = x",
"my_value = y"
);
final List<PyNumericLiteralExpression> result = PyExpressionEvaluator.evaluateRaw(myValue, PyNumericLiteralExpression.class);
Assert.assertThat("Expected list of 1 long element", result.size(), Matchers.is(1));
Assert.assertNotNull("Failed to evaluate chain of literals (got null)", result);
final PyNumericLiteralExpression resultElement = result.get(0);
Assert.assertThat("Evaluated incorrectly", resultElement.getLongValue(), Matchers.equalTo(2L));
}
public void testCall() {
final PyTargetExpression myValue = createFileAndExpression("my_value",
"x = abs(42)",
"y = x",
"my_value = y"
);
final List<PyCallExpression> result = PyExpressionEvaluator.evaluateRaw(myValue, PyCallExpression.class);
Assert.assertNotNull("Failed to evaluate chain of functions (got null)", result);
Assert.assertThat("Expected list of 1 call element", result.size(), Matchers.is(1));
final PyExpression callee = result.get(0).getCallee();
Assert.assertNotNull("Found empty callee", callee);
Assert.assertThat("Evaluated incorrectly", callee.getName(), Matchers.equalTo("abs"));
}
public void testStringConcat() {
final PyTargetExpression myValue = createFileAndExpression("my_value",
"x = 'go'",
"y = x + ' away'",
"my_value = y"
);
final List<PyStringLiteralExpression> expressions = PyExpressionEvaluator.evaluateRaw(myValue, PyStringLiteralExpression.class);
Assert.assertThat("Expected list of 2 string elements", expressions.size(), Matchers.is(2));
Assert.assertThat("Evaluated incorrectly", expressions.get(0).getStringValue(), Matchers.equalTo("go"));
Assert.assertThat("Evaluated incorrectly", expressions.get(1).getStringValue(), Matchers.equalTo(" away"));
}
public void testNumberConcat() {
final PyTargetExpression myValue = createFileAndExpression("my_value",
"x = 2",
"y = x + 1",
"my_value = y"
);
final List<PyNumericLiteralExpression> expressions = PyExpressionEvaluator.evaluateRaw(myValue, PyNumericLiteralExpression.class);
Assert.assertThat("Expected list of 2 num elements", expressions.size(), Matchers.is(2));
Assert.assertThat("Evaluated incorrectly", expressions.get(0).getLongValue(), Matchers.equalTo(2L));
Assert.assertThat("Evaluated incorrectly", expressions.get(1).getLongValue(), Matchers.equalTo(1L));
}
public void testNumber() {
final long result = PyExpressionEvaluator.evaluateLong(createFileAndExpression("my_value", "x = 2", " y = 3", "my_value = x + y + 1"));
Assert.assertThat("Error summarizing numbers", result, Matchers.is(6L));
}
public void testString() {
final String result = PyExpressionEvaluator.evaluateString(createFileAndExpression("my_value",
"x = 'hello'",
" y = \" there\"",
"my_value = x + y + '!'"));
Assert.assertThat("Error in string concat", result, Matchers.is("hello there!"));
}
public void testIterableTuples() {
final List<PyStringLiteralExpression> value = PyExpressionEvaluator.evaluateIterable(createFileAndExpression("my_value",
"x = ('hello', 'from')",
" y = ('here', '!')",
"my_value = x + y"),
PyStringLiteralExpression.class
);
Assert.assertThat("Expected list of elements", value.size(), Matchers.is(4));
Assert.assertThat("Evaluated incorrectly", value.get(0).getStringValue(), Matchers.equalTo("hello"));
Assert.assertThat("Evaluated incorrectly", value.get(1).getStringValue(), Matchers.equalTo("from"));
Assert.assertThat("Evaluated incorrectly", value.get(2).getStringValue(), Matchers.equalTo("here"));
Assert.assertThat("Evaluated incorrectly", value.get(3).getStringValue(), Matchers.equalTo("!"));
}
public void testIterableLists() {
final List<PyStringLiteralExpression> value = PyExpressionEvaluator.evaluateIterable(createFileAndExpression("my_value",
"x = ['hello', 'from']",
" y = ['here'] + ['!']",
"my_value = x + y"),
PyStringLiteralExpression.class
);
Assert.assertThat("Expected list of elements", value.size(), Matchers.is(4));
Assert.assertThat("Evaluated incorrectly", value.get(0).getStringValue(), Matchers.equalTo("hello"));
Assert.assertThat("Evaluated incorrectly", value.get(1).getStringValue(), Matchers.equalTo("from"));
Assert.assertThat("Evaluated incorrectly", value.get(2).getStringValue(), Matchers.equalTo("here"));
Assert.assertThat("Evaluated incorrectly", value.get(3).getStringValue(), Matchers.equalTo("!"));
}
/**
* Create file, fill it with data and return some expression from it
*
* @param varName name of expression to return
* @param lines lines of code
* @return expression
*/
@NotNull
private PyTargetExpression createFileAndExpression(@NotNull final String varName, @NotNull final String... lines) {
final PsiFile file =
PyElementGenerator.getInstance(myFixture.getProject()).createDummyFile(LanguageLevel.PYTHON27, StringUtil.join(lines, "\n"));
assert file instanceof PyFile : "PyElementGenerator created not py file?";
final PyFile pyFile = (PyFile)file;
final PyTargetExpression result = pyFile.findTopLevelAttribute(varName);
assert result != null : "No var with name " + varName + " found";
return result;
}
}