Merge branch 'python-fixes'

This commit is contained in:
Andrey Vlasovskikh
2014-11-10 17:12:36 +03:00
28 changed files with 109 additions and 95 deletions
@@ -87,8 +87,9 @@ public class AddImportHelper {
}
@Nullable
private static PsiElement getInsertPosition(final PsiFile file, @Nullable String nameToImport, @Nullable ImportPriority priority) {
PsiElement feeler = file.getFirstChild();
private static PsiElement getInsertPosition(final PsiElement insertParent, @Nullable String nameToImport,
@Nullable ImportPriority priority) {
PsiElement feeler = insertParent.getFirstChild();
if (feeler == null) return null;
// skip initial comments and whitespace and try to get just below the last import stmt
boolean skippedOverImports = false;
@@ -97,7 +98,7 @@ public class AddImportHelper {
final boolean isInjected = InjectedLanguageManager.getInstance(feeler.getProject()).isInjectedFragment(feeler.getContainingFile());
do {
if (feeler instanceof PyImportStatementBase && !isInjected) {
if (nameToImport != null && priority != null && shouldInsertBefore(file, (PyImportStatementBase)feeler, nameToImport, priority)) {
if (nameToImport != null && priority != null && shouldInsertBefore(insertParent, (PyImportStatementBase)feeler, nameToImport, priority)) {
break;
}
seeker = feeler;
@@ -109,10 +110,9 @@ public class AddImportHelper {
feeler = feeler.getNextSibling();
}
// maybe we arrived at the doc comment stmt; skip over it, too
else if (!skippedOverImports && !skippedOverDoc && file instanceof PyFile) {
else if (!skippedOverImports && !skippedOverDoc && insertParent instanceof PyFile) {
PsiElement doc_elt =
DocStringUtil
.findDocStringExpression((PyElement)file); // this gives the literal; its parent is the expr seeker may have encountered
DocStringUtil.findDocStringExpression((PyElement)insertParent); // this gives the literal; its parent is the expr seeker may have encountered
if (doc_elt != null && doc_elt.getParent() == feeler) {
feeler = feeler.getNextSibling();
seeker = feeler; // skip over doc even if there's nothing below it
@@ -130,7 +130,8 @@ public class AddImportHelper {
return seeker;
}
private static boolean shouldInsertBefore(PsiFile file, PyImportStatementBase relativeTo, String nameToImport, ImportPriority priority) {
private static boolean shouldInsertBefore(PsiElement element, PyImportStatementBase relativeTo, String nameToImport,
ImportPriority priority) {
QualifiedName relativeToName;
PsiElement source;
if (relativeTo instanceof PyFromImportStatement) {
@@ -161,7 +162,7 @@ public class AddImportHelper {
}
ImportPriority relativeToPriority = source == null || containingFile == null
? ImportPriority.BUILTIN
: getImportPriority(file, containingFile);
: getImportPriority(element, containingFile);
final int rc = priority.compareTo(relativeToPriority);
if (rc < 0) {
return true;
@@ -213,22 +214,32 @@ public class AddImportHelper {
* @param from name of the module
* @param name imported name
* @param asName optional name for 'as' clause
* @param anchor place where the imported name was used
*/
public static void addImportFromStatement(PsiFile file, String from, String name, @Nullable String asName, ImportPriority priority) {
public static void addImportFromStatement(PsiFile file, String from, String name, @Nullable String asName, ImportPriority priority,
@Nullable PsiElement anchor) {
final PyElementGenerator generator = PyElementGenerator.getInstance(file.getProject());
final LanguageLevel languageLevel = LanguageLevel.forElement(file);
final PyFromImportStatement nodeToInsert = generator.createFromImportStatement(languageLevel, from, name, asName);
try {
final PyImportStatementBase importStatement = PsiTreeUtil.getParentOfType(anchor, PyImportStatementBase.class, false);
final PsiElement insertParent;
if (importStatement != null && importStatement.getContainingFile() == file) {
insertParent = importStatement.getParent();
}
else {
insertParent = file;
}
if (InjectedLanguageManager.getInstance(file.getProject()).isInjectedFragment(file)) {
final PsiElement element = file.addBefore(nodeToInsert, getInsertPosition(file, from, priority));
final PsiElement element = insertParent.addBefore(nodeToInsert, getInsertPosition(insertParent, from, priority));
PsiElement whitespace = element.getNextSibling();
if (!(whitespace instanceof PsiWhiteSpace)) {
whitespace = PsiParserFacade.SERVICE.getInstance(file.getProject()).createWhiteSpaceFromText(" >>> ");
}
file.addBefore(whitespace, element);
insertParent.addBefore(whitespace, element);
}
else {
file.addBefore(nodeToInsert, getInsertPosition(file, from, priority));
insertParent.addBefore(nodeToInsert, getInsertPosition(insertParent, from, priority));
}
}
catch (IncorrectOperationException e) {
@@ -237,7 +248,7 @@ public class AddImportHelper {
}
public static boolean addImportFrom(PsiFile file, @Nullable PsiElement target, String path, final String name,
@Nullable String asName, ImportPriority priority) {
@Nullable String asName, ImportPriority priority, @Nullable PsiElement anchor) {
final List<PyFromImportStatement> existingImports = ((PyFile)file).getFromImports();
for (PyFromImportStatement existingImport : existingImports) {
if (target != null && existingImport.getTextRange().getStartOffset() > target.getTextRange().getStartOffset()) {
@@ -259,7 +270,7 @@ public class AddImportHelper {
return true;
}
}
addImportFromStatement(file, path, name, asName, priority);
addImportFromStatement(file, path, name, asName, priority, anchor);
return true;
}
@@ -283,7 +294,7 @@ public class AddImportHelper {
element.replace(elementGenerator.createExpressionFromText(LanguageLevel.forElement(target), toImportQName + "." + targetName));
}
else {
addImportFrom(file, null, toImportQName.toString(), target.getName(), null, priority);
addImportFrom(file, null, toImportQName.toString(), target.getName(), null, priority, element);
}
}
}
@@ -181,7 +181,7 @@ public class ImportFromExistingAction implements QuestionAction {
AddImportHelper.addLocalFromImportStatement(myTarget, qualifiedName, myName);
}
else {
AddImportHelper.addImportFromStatement(file, qualifiedName, myName, null, priority);
AddImportHelper.addImportFromStatement(file, qualifiedName, myName, null, priority, null);
}
}
}
@@ -85,7 +85,7 @@ public class PyTestCreator implements TestCreator {
PsiFile psiFile = PyUtil.getOrCreateFile(
dialog.getTargetDir() + "/" + fileName, project);
AddImportHelper.addImportFrom(psiFile, null, "unittest", "TestCase", null, AddImportHelper.ImportPriority.BUILTIN);
AddImportHelper.addImportFrom(psiFile, null, "unittest", "TestCase", null, AddImportHelper.ImportPriority.BUILTIN, null);
PyElement createdClass = PyElementGenerator.getInstance(project).createFromText(
LanguageLevel.forElement(psiFile), PyClass.class,
@@ -161,15 +161,33 @@ public class PyBuiltinCache {
}
@Nullable
static PyType createLiteralCollectionType(final PySequenceExpression sequence, final String name) {
final PyBuiltinCache builtinCache = getInstance(sequence);
final PyClass setClass = builtinCache.getClass(name);
if (setClass != null) {
return new PyLiteralCollectionType(setClass, false, sequence);
public PyType createLiteralCollectionType(final PySequenceExpression sequence, final String name, @NotNull TypeEvalContext context) {
final PyClass cls = getClass(name);
if (cls != null) {
return new PyCollectionTypeImpl(cls, false, getSequenceElementType(sequence, context));
}
return null;
}
@Nullable
private static PyType getSequenceElementType(@NotNull PySequenceExpression sequence, @NotNull TypeEvalContext context) {
final PyExpression[] elements = sequence.getElements();
if (elements.length == 0 || elements.length > 10 /* performance */) {
return null;
}
final PyType result = context.getType(elements[0]);
if (result == null) {
return null;
}
for (int i = 1; i < elements.length; i++) {
final PyType elementType = context.getType(elements[i]);
if (elementType == null || !elementType.equals(result)) {
return null;
}
}
return result;
}
@Nullable
public PyFile getBuiltinsFile() {
return myBuiltinsFile;
@@ -38,7 +38,7 @@ public class PyDictLiteralExpressionImpl extends PyElementImpl implements PyDict
}
public PyType getType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) {
return PyBuiltinCache.createLiteralCollectionType(this, "dict");
return PyBuiltinCache.getInstance(this).createLiteralCollectionType(this, "dict", context);
}
@Override
@@ -270,6 +270,16 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements
public String getPresentableText() {
return notNullize(getName(), PyNames.UNNAMED_ELEMENT) + getParameterList().getPresentableText(true);
}
@Nullable
@Override
public String getLocationString() {
final PyClass containingClass = getContainingClass();
if (containingClass != null) {
return "(" + containingClass.getName() + " in " + getPackageForFile(getContainingFile()) + ")";
}
return super.getLocationString();
}
};
}
@@ -86,6 +86,6 @@ public class PyListLiteralExpressionImpl extends PyElementImpl implements PyList
}
public PyType getType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) {
return PyBuiltinCache.createLiteralCollectionType(this, "list");
return PyBuiltinCache.getInstance(this).createLiteralCollectionType(this, "list", context);
}
}
@@ -31,7 +31,7 @@ public class PySetLiteralExpressionImpl extends PyElementImpl implements PySetLi
}
public PyType getType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) {
return PyBuiltinCache.createLiteralCollectionType(this, "set");
return PyBuiltinCache.getInstance(this).createLiteralCollectionType(this, "set", context);
}
@Override
@@ -1,52 +0,0 @@
/*
* Copyright 2000-2014 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.psi.types;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PySequenceExpression;
import org.jetbrains.annotations.NotNull;
/**
* @author yole
*/
public class PyLiteralCollectionType extends PyClassTypeImpl implements PyCollectionType {
private final PySequenceExpression mySequence;
public PyLiteralCollectionType(@NotNull PyClass source, boolean isDefinition, PySequenceExpression sequence) {
super(source, isDefinition);
mySequence = sequence;
}
@Override
public PyType getElementType(@NotNull TypeEvalContext context) {
final PyExpression[] elements = mySequence.getElements();
if (elements.length == 0 || elements.length > 10 /* performance */) {
return null;
}
PyType result = context.getType(elements [0]);
if (result == null) {
return null;
}
for (int i = 1; i < elements.length; i++) {
PyType elementType = context.getType(elements[i]);
if (elementType == null || !elementType.equals(result)) {
return null;
}
}
return result;
}
}
@@ -318,7 +318,7 @@ public final class PyClassRefactoringUtil {
}
final AddImportHelper.ImportPriority priority = AddImportHelper.getImportPriority(anchor, newFile);
if (preferFromImport && !containingQName.getComponents().isEmpty()) {
return AddImportHelper.addImportFrom(file, null, containingQName.toString(), importedName, asName, priority);
return AddImportHelper.addImportFrom(file, null, containingQName.toString(), importedName, asName, priority, anchor);
}
else {
return AddImportHelper.addImportStatement(file, containingQName.append(importedName).toString(), asName, priority);
@@ -163,7 +163,7 @@ class MethodsManager extends MembersManager<PyFunction> {
*/
private static void addImportFromAbc(@NotNull final PsiFile file, @NotNull final String nameToImport) {
AddImportHelper.addImportFromStatement(file, ABC_META_PACKAGE, nameToImport, null,
AddImportHelper.ImportPriority.BUILTIN);
AddImportHelper.ImportPriority.BUILTIN, null);
}
/**
@@ -1,8 +1,8 @@
<node text="A.__init__(self) (hierarchy.call.Static.Constructor.main)" base="true">
<node text="A.__init__(self) (A in hierarchy.call.Static.Constructor.main)" base="true">
<node text="invoke1(p) (hierarchy.call.Static.Constructor.main)">
<node text="A.method1(self) (hierarchy.call.Static.Constructor.main)"/>
<node text="A.method1(self) (A in hierarchy.call.Static.Constructor.main)"/>
</node>
<node text="invoke2(p) (hierarchy.call.Static.Constructor.main)">
<node text="A.method2(self) (hierarchy.call.Static.Constructor.main)"/>
<node text="A.method2(self) (A in hierarchy.call.Static.Constructor.main)"/>
</node>
</node>
@@ -1,6 +1,6 @@
<node text="A.__init__(self) (hierarchy.call.Static.Constructor.main)" base="true">
<node text="A.__init__(self) (A in hierarchy.call.Static.Constructor.main)" base="true">
<node text="invokeA() (hierarchy.call.Static.Constructor.main)">
<node text="C.bar(self) (hierarchy.call.Static.Constructor.main)"/>
<node text="C.bar(self) (C in hierarchy.call.Static.Constructor.main)"/>
</node>
<node text="C.bar(self) (hierarchy.call.Static.Constructor.main)"/>
<node text="C.bar(self) (C in hierarchy.call.Static.Constructor.main)"/>
</node>
@@ -1 +1 @@
<node text="A.target_func(self) (hierarchy.call.Static.Inheritance.main)" base="true"/>
<node text="A.target_func(self) (A in hierarchy.call.Static.Inheritance.main)" base="true"/>
@@ -1,4 +1,4 @@
<node text="A.target_func(self) (hierarchy.call.Static.Inheritance.main)" base="true">
<node text="C.func(self, a) (hierarchy.call.Static.Inheritance.main)"/>
<node text="A.target_func(self) (A in hierarchy.call.Static.Inheritance.main)" base="true">
<node text="C.func(self, a) (C in hierarchy.call.Static.Inheritance.main)"/>
<node text="foo2(b) (hierarchy.call.Static.Inheritance.main)"/>
</node>
@@ -1,3 +1,3 @@
<node text="B.target_func(self, p) (hierarchy.call.Static.OverriddenMethod.main)" base="true">
<node text="A.another_func(self) (hierarchy.call.Static.OverriddenMethod.file_1)"/>
<node text="B.target_func(self, p) (B in hierarchy.call.Static.OverriddenMethod.main)" base="true">
<node text="A.another_func(self) (A in hierarchy.call.Static.OverriddenMethod.file_1)"/>
</node>
@@ -1,5 +1,5 @@
<node text="B.target_func(self, p) (hierarchy.call.Static.OverriddenMethod.main)" base="true">
<node text="C.func1(self, a) (hierarchy.call.Static.OverriddenMethod.main)"/>
<node text="B.target_func(self, p) (B in hierarchy.call.Static.OverriddenMethod.main)" base="true">
<node text="C.func1(self, a) (C in hierarchy.call.Static.OverriddenMethod.main)"/>
<node text="bar1(a) (hierarchy.call.Static.OverriddenMethod.main)"/>
<node text="C.func2(self) (hierarchy.call.Static.OverriddenMethod.main)"/>
<node text="C.func2(self) (C in hierarchy.call.Static.OverriddenMethod.main)"/>
</node>
@@ -0,0 +1,5 @@
class C:
def f(self, x):
self.foo = x
self.foo = {'foo': self.foo}
return self.foo['foo'] + 10
@@ -0,0 +1,6 @@
if True:
import pkgutil
else:
from pkg1 import mod2 as pkgutil
print(pkgutil)
@@ -0,0 +1,6 @@
if True:
import pkgutil
else:
import mod2 as pkgutil
print(pkgutil)
@@ -53,7 +53,7 @@ public class PyAddImportTest extends PyTestCase {
new WriteCommandAction(myFixture.getProject(), myFixture.getFile()) {
@Override
protected void run(Result result) throws Throwable {
AddImportHelper.addImportFrom(myFixture.getFile(), null, path, name, null, AddImportHelper.ImportPriority.BUILTIN);
AddImportHelper.addImportFrom(myFixture.getFile(), null, path, name, null, AddImportHelper.ImportPriority.BUILTIN, null);
}
}.execute();
myFixture.checkResultByFile("addImport/" + getTestName(true) + ".after.py");
@@ -505,7 +505,7 @@ public class PyTypeTest extends PyTestCase {
// EA-40207
public void testRecursion() {
doTest("list[list]",
doTest("list",
"def f():\n" +
" return [f()]\n" +
"expr = f()\n");
@@ -237,4 +237,9 @@ public class PyTypeCheckerInspectionTest extends PyTestCase {
public void testDefaultTupleParameter() {
doTest();
}
// PY-14222
public void testRecursiveDictAttribute() {
doTest();
}
}
@@ -133,6 +133,11 @@ public class PyMoveTest extends PyTestCase {
doMoveSymbolTest("g", "c.py");
}
// PY-13870
public void testConditionalImport() {
doMoveFileTest("mod2.py", "pkg1");
}
private void doMoveFileTest(String fileName, String toDirName) {
Project project = myFixture.getProject();
PsiManager manager = PsiManager.getInstance(project);