From 6de7622d4b01f648e90f9ebe8b1997fa9b1a5727 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 24 Apr 2012 16:23:06 +0400 Subject: [PATCH 01/22] Don't create '__init__.py' when moving files if 'Search for references' is not selected (PY-6253) --- .../jetbrains/python/refactoring/move/PyMoveFileHandler.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/refactoring/move/PyMoveFileHandler.java b/python/src/com/jetbrains/python/refactoring/move/PyMoveFileHandler.java index 6d01082b129c..4940f0018a5b 100644 --- a/python/src/com/jetbrains/python/refactoring/move/PyMoveFileHandler.java +++ b/python/src/com/jetbrains/python/refactoring/move/PyMoveFileHandler.java @@ -4,6 +4,7 @@ import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.refactoring.RefactoringSettings; import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFileHandler; import com.intellij.usageView.UsageInfo; import com.intellij.util.IncorrectOperationException; @@ -38,7 +39,8 @@ public class PyMoveFileHandler extends MoveFileHandler { while (root != null && !roots.contains(root.getVirtualFile())) { root = root.getParentDirectory(); } - if (moveDestination != root && root != null) { + final boolean searchForReferences = RefactoringSettings.getInstance().MOVE_SEARCH_FOR_REFERENCES_FOR_FILE; + if (moveDestination != root && root != null && searchForReferences) { CreatePackageAction.createInitPyInHierarchy(moveDestination, root); } } From 4459fea3d49ad692b58b173cb73f7daf035ac315 Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Wed, 25 Apr 2012 13:47:09 +0200 Subject: [PATCH 02/22] Removed error message (PY-6324). --- .../python/packaging/PyPackageManager.java | 16 +--------------- .../remote/PyRemoteCancelledException.java | 10 ++++++++++ 2 files changed, 11 insertions(+), 15 deletions(-) create mode 100644 python/src/com/jetbrains/python/remote/PyRemoteCancelledException.java diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManager.java b/python/src/com/jetbrains/python/packaging/PyPackageManager.java index 02acf4c574c8..b328a2dc669f 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManager.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManager.java @@ -16,7 +16,6 @@ import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.SdkAdditionalData; -import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.SystemInfo; @@ -28,7 +27,6 @@ import com.intellij.util.ArrayUtil; import com.intellij.util.Function; import com.intellij.util.SystemProperties; import com.intellij.util.net.HttpConfigurable; -import com.intellij.util.ui.UIUtil; import com.jetbrains.python.PythonHelpersLocator; import com.jetbrains.python.psi.PyExpression; import com.jetbrains.python.psi.PyListLiteralExpression; @@ -564,19 +562,7 @@ public class PyPackageManager { do { processOutput = manager.runRemoteProcess(null, remoteSdkData, ArrayUtil.toStringArray(cmdline), askForSudo); if (askForSudo && processOutput.getStderr().contains("sudo: 3 incorrect password attempts")) { - final Ref cont = Ref.create(false); - UIUtil.invokeAndWaitIfNeeded(new Runnable() { - @Override - public void run() { - if (Messages.showOkCancelDialog("Incorrect sudo password", "Incorrect Password Attempt", Messages.getErrorIcon()) == - Messages.OK) { - cont.set(true); - } - } - }); - if (cont.get()) { - continue; - } + continue; } break; } diff --git a/python/src/com/jetbrains/python/remote/PyRemoteCancelledException.java b/python/src/com/jetbrains/python/remote/PyRemoteCancelledException.java new file mode 100644 index 000000000000..425747922612 --- /dev/null +++ b/python/src/com/jetbrains/python/remote/PyRemoteCancelledException.java @@ -0,0 +1,10 @@ +package com.jetbrains.python.remote; + +/** + * @author traff + */ +public class PyRemoteCancelledException extends PyRemoteInterpreterException { + public PyRemoteCancelledException(String s) { + super(s); + } +} From 0253ecc75a0d4a9a36484224b3d613c4707c6a3e Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Wed, 25 Apr 2012 13:48:12 +0200 Subject: [PATCH 03/22] Fixed pip installation (PY-6321). --- python/src/com/jetbrains/python/packaging/PyPackageManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManager.java b/python/src/com/jetbrains/python/packaging/PyPackageManager.java index b328a2dc669f..5b69efd92cdc 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManager.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManager.java @@ -269,7 +269,7 @@ public class PyPackageManager { } final String fileName = dirName + name + File.separatorChar + "setup.py"; try { - output = getProcessOutput(fileName, Collections.singletonList(INSTALL), true, dirName); + output = getProcessOutput(fileName, Collections.singletonList(INSTALL), true, dirName + name); final int retcode = output.getExitCode(); if (output.isTimeout()) { throw new PyExternalProcessException(ERROR_TIMEOUT, fileName, Lists.newArrayList(INSTALL), "Timed out"); From 73e5c6749a0a59089d0e6ed5f03d8ae5f0a5aab8 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 25 Apr 2012 18:52:15 +0200 Subject: [PATCH 04/22] more strict condition for injecting method references inside __all__ (PY-6370) --- .../python/codeInsight/PyStdReferenceContributor.java | 10 ++++++---- .../compoundDunderAll.py | 2 ++ .../PyUnresolvedReferencesInspectionTest.java | 4 ++++ 3 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/compoundDunderAll.py diff --git a/python/src/com/jetbrains/python/codeInsight/PyStdReferenceContributor.java b/python/src/com/jetbrains/python/codeInsight/PyStdReferenceContributor.java index ccf0af491d6a..3b5774bd053c 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyStdReferenceContributor.java +++ b/python/src/com/jetbrains/python/codeInsight/PyStdReferenceContributor.java @@ -4,6 +4,7 @@ import com.intellij.psi.*; import com.intellij.util.ProcessingContext; import com.jetbrains.python.PyNames; import com.jetbrains.python.psi.PyAssignmentStatement; +import com.jetbrains.python.psi.PySequenceExpression; import com.jetbrains.python.psi.PyStringLiteralExpression; import com.jetbrains.python.psi.PyTargetExpression; import org.jetbrains.annotations.NotNull; @@ -38,8 +39,9 @@ public class PyStdReferenceContributor extends PsiReferenceContributor { private static void registerClassAttributeReference(PsiReferenceRegistrar registrar, final String name, final PsiReferenceProvider provider) { - registrar.registerReferenceProvider(psiElement(PyStringLiteralExpression.class).inside( - psiElement(PyAssignmentStatement.class).withFirstChild( - psiElement(PyTargetExpression.class).withName(name))), provider); - } + registrar.registerReferenceProvider(psiElement(PyStringLiteralExpression.class).withParent( + psiElement(PySequenceExpression.class).withParent( + psiElement(PyAssignmentStatement.class).withFirstChild( + psiElement(PyTargetExpression.class).withName(name)))), provider); + } } diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/compoundDunderAll.py b/python/testData/inspections/PyUnresolvedReferencesInspection/compoundDunderAll.py new file mode 100644 index 000000000000..9533fc64871b --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/compoundDunderAll.py @@ -0,0 +1,2 @@ +__all__ = [ name for name, obj in locals().items() + if not (name.startswith('_')) ] diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index 559dd988529d..d9b498db8d5b 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -102,6 +102,10 @@ public class PyUnresolvedReferencesInspectionTest extends PyTestCase { doTest(); } + public void testCompoundDunderAll() { // PY-6370 + doTest(); + } + private void doMultiFileTest() { myFixture.copyFileToProject("inspections/PyUnresolvedReferencesInspection/__init__.py", "PyUnresolvedReferencesInspection/__init__.py"); myFixture.copyFileToProject("inspections/PyUnresolvedReferencesInspection/" + getTestName(true) + ".py", From 2b64b8d52ed1c250310dc2e26cc22863cd9ad388 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 25 Apr 2012 19:09:02 +0200 Subject: [PATCH 05/22] complete imported names inside __all__ (PY-6306) --- .../python/codeInsight/PyDunderAllReference.java | 14 ++++++++++++-- .../completion/dunderAllReferenceImport.after.py | 3 +++ .../completion/dunderAllReferenceImport.py | 3 +++ .../com/jetbrains/python/PythonCompletionTest.java | 4 ++++ 4 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 python/testData/completion/dunderAllReferenceImport.after.py create mode 100644 python/testData/completion/dunderAllReferenceImport.py diff --git a/python/src/com/jetbrains/python/codeInsight/PyDunderAllReference.java b/python/src/com/jetbrains/python/codeInsight/PyDunderAllReference.java index b06a549f2f69..73e987c90e9e 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyDunderAllReference.java +++ b/python/src/com/jetbrains/python/codeInsight/PyDunderAllReference.java @@ -1,5 +1,8 @@ package com.jetbrains.python.codeInsight; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.openapi.util.Iconable; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; @@ -9,6 +12,7 @@ import com.intellij.psi.ResolveState; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.util.ArrayUtil; import com.jetbrains.python.psi.PyFile; +import com.jetbrains.python.psi.PyImportElement; import com.jetbrains.python.psi.PyStringLiteralExpression; import com.jetbrains.python.psi.PyUtil; import com.jetbrains.python.psi.impl.LightNamedElement; @@ -41,7 +45,7 @@ public class PyDunderAllReference extends PsiReferenceBase result = new ArrayList(); + final List result = new ArrayList(); PyFile containingFile = (PyFile) getElement().getContainingFile().getOriginalFile(); final List dunderAll = containingFile.getDunderAll(); containingFile.processDeclarations(new PsiScopeProcessor() { @@ -50,7 +54,13 @@ public class PyDunderAllReference extends PsiReferenceBase'] diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index 92a4a2599df7..1d8ba03e122a 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -529,6 +529,10 @@ public class PythonCompletionTest extends PyTestCase { doTest(); } + public void testDunderAllReferenceImport() { // PY-6306 + doTest(); + } + public void testOldStyleClassAttributes() { doTest(); } From 0a7e0a6063cffb653e873e1bc48a356559f29aca Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 25 Apr 2012 19:41:11 +0200 Subject: [PATCH 06/22] don't try to append any import elements to a star import (PY-6302) --- .../python/codeInsight/imports/AddImportHelper.java | 5 ++++- .../inspections/importFromModuleStar/source.py | 4 ++++ .../inspections/importFromModuleStar/source_after.py | 5 +++++ .../inspections/importFromModuleStar/target.py | 4 ++++ .../testSrc/com/jetbrains/python/PyQuickFixTest.java | 11 +++++++++++ 5 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 python/testData/inspections/importFromModuleStar/source.py create mode 100644 python/testData/inspections/importFromModuleStar/source_after.py create mode 100644 python/testData/inspections/importFromModuleStar/target.py diff --git a/python/src/com/jetbrains/python/codeInsight/imports/AddImportHelper.java b/python/src/com/jetbrains/python/codeInsight/imports/AddImportHelper.java index e76c4ab5f56c..7b4cd06c6d35 100644 --- a/python/src/com/jetbrains/python/codeInsight/imports/AddImportHelper.java +++ b/python/src/com/jetbrains/python/codeInsight/imports/AddImportHelper.java @@ -191,6 +191,9 @@ public class AddImportHelper { if (target != null && existingImport.getTextRange().getStartOffset() > target.getTextRange().getStartOffset()) { continue; } + if (existingImport.isStarImport()) { + continue; + } final PyQualifiedName qName = existingImport.getImportSourceQName(); if (qName != null && qName.toString().equals(path)) { for (PyImportElement el : existingImport.getImportElements()) { @@ -220,7 +223,7 @@ public class AddImportHelper { else if (useQualified) { addImportStatement(file, path, null, priority); final PyElementGenerator elementGenerator = PyElementGenerator.getInstance(file.getProject()); - element.replace(elementGenerator.createExpressionFromText(qName + "." + target.getName())); + element.replace(elementGenerator.createExpressionFromText(LanguageLevel.forElement(target), qName + "." + target.getName())); } else { addImportFrom(file, null, path, target.getName(), null, priority); diff --git a/python/testData/inspections/importFromModuleStar/source.py b/python/testData/inspections/importFromModuleStar/source.py new file mode 100644 index 000000000000..55c27e1fb049 --- /dev/null +++ b/python/testData/inspections/importFromModuleStar/source.py @@ -0,0 +1,4 @@ +from target import * + +xyzzy +shazam() diff --git a/python/testData/inspections/importFromModuleStar/source_after.py b/python/testData/inspections/importFromModuleStar/source_after.py new file mode 100644 index 000000000000..b118609972b6 --- /dev/null +++ b/python/testData/inspections/importFromModuleStar/source_after.py @@ -0,0 +1,5 @@ +from target import * +from target import xyzzy + +xyzzy +shazam() diff --git a/python/testData/inspections/importFromModuleStar/target.py b/python/testData/inspections/importFromModuleStar/target.py new file mode 100644 index 000000000000..2214231d716c --- /dev/null +++ b/python/testData/inspections/importFromModuleStar/target.py @@ -0,0 +1,4 @@ +__all__ = ['shazam'] + +def shazam(): pass +def xyzzy(): pass diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index dd8aac6b309b..c0758fc44ae9 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -36,6 +36,17 @@ public class PyQuickFixTest extends PyTestCase { PyUnresolvedReferencesInspection.class, PyBundle.message("ACT.NAME.use.import"), true, true); } + public void testImportFromModuleStar() { // PY-6302 + myFixture.enableInspections(PyUnresolvedReferencesInspection.class); + myFixture.copyDirectoryToProject("importFromModuleStar", ""); + myFixture.configureFromTempProjectFile("source.py"); + myFixture.checkHighlighting(true, false, false); + final IntentionAction intentionAction = myFixture.findSingleIntention(PyBundle.message("ACT.NAME.use.import")); + assertNotNull(intentionAction); + myFixture.launchAction(intentionAction); + myFixture.checkResultByFile("importFromModuleStar/source_after.py"); + } + public void testQualifyByImport() { final PyCodeInsightSettings settings = PyCodeInsightSettings.getInstance(); boolean oldPreferFrom = settings.PREFER_FROM_IMPORT; From 2da4446a0c501440f68228b6678f66cae8b74a71 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 25 Apr 2012 21:59:01 +0200 Subject: [PATCH 07/22] don't offer top-level packages in completion of relative imports (PY-6304) --- .../impl/references/PyImportReference.java | 22 +++++++++---------- .../pack/__init__.py | 0 .../pack/subpack/__init__.py | 1 + .../pack/subpack/modX.py | 1 + .../python/PythonCompletionTest.java | 13 +++++++++++ 5 files changed, 26 insertions(+), 11 deletions(-) create mode 100644 python/testData/completion/relativeImportExcludeToplevel/pack/__init__.py create mode 100644 python/testData/completion/relativeImportExcludeToplevel/pack/subpack/__init__.py create mode 100644 python/testData/completion/relativeImportExcludeToplevel/pack/subpack/modX.py diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyImportReference.java b/python/src/com/jetbrains/python/psi/impl/references/PyImportReference.java index b7556bc63820..c922230690c5 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyImportReference.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyImportReference.java @@ -167,7 +167,7 @@ public class PyImportReference extends PyReferenceImpl { } public Object[] execute() { - int relative_level = -1; + int relativeLevel = -1; InsertHandler insertHandler = null; // NOTE: could use getPointInImport() @@ -191,9 +191,9 @@ public class PyImportReference extends PyReferenceImpl { } } else { // null source, must be a "from ... import" - relative_level = from_import.getRelativeLevel(); - if (relative_level > 0) { - PsiDirectory relative_dir = ResolveImportUtil.stepBackFrom(myCurrentFile, relative_level); + relativeLevel = from_import.getRelativeLevel(); + if (relativeLevel > 0) { + PsiDirectory relative_dir = ResolveImportUtil.stepBackFrom(myCurrentFile, relativeLevel); if (relative_dir != null) { addImportedNames(from_import.getImportElements()); fillFromDir(relative_dir, null); @@ -204,7 +204,7 @@ public class PyImportReference extends PyReferenceImpl { else { // in "import _" or "from _ import" ASTNode n = myElement.getNode().getTreePrev(); while (n != null && n.getElementType() == PyTokenTypes.DOT) { - relative_level += 1; + relativeLevel += 1; n = n.getTreePrev(); } if (from_import != null) { @@ -221,21 +221,21 @@ public class PyImportReference extends PyReferenceImpl { } } // look at dir by level - if (myCurrentFile != null && (relative_level >= 0 || !ResolveImportUtil.isAbsoluteImportEnabledFor(myCurrentFile))) { + if (myCurrentFile != null && (relativeLevel >= 0 || !ResolveImportUtil.isAbsoluteImportEnabledFor(myCurrentFile))) { final PsiDirectory containingDirectory = myCurrentFile.getContainingDirectory(); if (containingDirectory != null) { PyQualifiedName thisQName = ResolveImportUtil.findShortestImportableQName(containingDirectory); - if (thisQName == null) { - fillFromDir(ResolveImportUtil.stepBackFrom(myCurrentFile, relative_level), insertHandler); + if (thisQName == null || thisQName.getComponentCount() == relativeLevel) { + fillFromDir(ResolveImportUtil.stepBackFrom(myCurrentFile, relativeLevel), insertHandler); } - else if (thisQName.getComponentCount() >= relative_level) { - thisQName = thisQName.removeTail(relative_level); + else if (thisQName.getComponentCount() > relativeLevel) { + thisQName = thisQName.removeTail(relativeLevel); fillFromQName(thisQName, insertHandler); } } } } - if (relative_level == -1) { + if (relativeLevel == -1) { fillFromQName(PyQualifiedName.fromComponents(), insertHandler); } diff --git a/python/testData/completion/relativeImportExcludeToplevel/pack/__init__.py b/python/testData/completion/relativeImportExcludeToplevel/pack/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/completion/relativeImportExcludeToplevel/pack/subpack/__init__.py b/python/testData/completion/relativeImportExcludeToplevel/pack/subpack/__init__.py new file mode 100644 index 000000000000..6509788aae52 --- /dev/null +++ b/python/testData/completion/relativeImportExcludeToplevel/pack/subpack/__init__.py @@ -0,0 +1 @@ +__author__ = 'yole' diff --git a/python/testData/completion/relativeImportExcludeToplevel/pack/subpack/modX.py b/python/testData/completion/relativeImportExcludeToplevel/pack/subpack/modX.py new file mode 100644 index 000000000000..136608600f80 --- /dev/null +++ b/python/testData/completion/relativeImportExcludeToplevel/pack/subpack/modX.py @@ -0,0 +1 @@ +from ... import \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index 1d8ba03e122a..9bf9201be865 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -550,4 +550,17 @@ public class PythonCompletionTest extends PyTestCase { public void testQualifiedAssignment() { // PY-6121 doTest(); } + + public void testRelativeImportExcludeToplevel() { // PY-6304 + setLanguageLevel(LanguageLevel.PYTHON27); + try { + myFixture.copyDirectoryToProject("completion/relativeImportExcludeToplevel", ""); + myFixture.configureByFile("pack/subpack/modX.py"); + myFixture.completeBasic(); + assertFalse(myFixture.getLookupElementStrings().contains("sys")); + } + finally { + setLanguageLevel(null); + } + } } From 69688bf2caa7b612aa196982595cae25d439ffea Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 26 Apr 2012 09:40:00 +0200 Subject: [PATCH 08/22] correct semantics of __slots__ with inheritance (PY-5939) --- .../PyUnresolvedReferencesInspection.java | 18 ++++++++++++++++-- .../slotsSubclass.py | 15 +++++++++++++++ .../PyUnresolvedReferencesInspectionTest.java | 4 ++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection/slotsSubclass.py diff --git a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java index 53b14f82241d..676dba6b0d17 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java @@ -154,10 +154,24 @@ public class PyUnresolvedReferencesInspection extends PyInspection { final PyClass pyClass = ((PyClassType)type).getPyClass(); if (pyClass != null && pyClass.isNewStyleClass()) { final List slots = pyClass.getSlots(); - if (slots != null && !slots.contains(node.getReferencedName()) && !slots.contains("__dict__")) { + final String attrName = node.getReferencedName(); + if (slots != null && !slots.contains(attrName) && !slots.contains(PyNames.DICT)) { + for (PyClassRef ref : pyClass.iterateAncestors()) { + final PyClass ancestor = ref.getPyClass(); + if (ancestor == null) { + return; + } + if (PyNames.OBJECT.equals(ancestor.getName())) { + break; + } + final List ancestorSlots = ancestor.getSlots(); + if (ancestorSlots == null || ancestorSlots.contains(attrName) || ancestorSlots.contains(PyNames.DICT)) { + return; + } + } final ASTNode nameNode = node.getNameElement(); final PsiElement e = nameNode != null ? nameNode.getPsi() : node; - registerProblem(e, "'" + pyClass.getName() + "' object has no attribute '" + node.getReferencedName() + "'"); + registerProblem(e, "'" + pyClass.getName() + "' object has no attribute '" + attrName + "'"); } } } diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/slotsSubclass.py b/python/testData/inspections/PyUnresolvedReferencesInspection/slotsSubclass.py new file mode 100644 index 000000000000..dfd3f5e4cc50 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/slotsSubclass.py @@ -0,0 +1,15 @@ +class A(object): + __slots__ = ['a', 'b'] + def __init__(self): + self.a = None # <- all ok here + self.b = None # <- all ok here + +class C(A): + __slots__ = ['c', 'd'] + + def __init__(self, c): + super(C, self).__init__() + self.c = c + self.d = self.b + if self.c: + self.a = 10 diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index d9b498db8d5b..b74ba65d5de4 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -32,6 +32,10 @@ public class PyUnresolvedReferencesInspectionTest extends PyTestCase { doTest(); } + public void testSlotsSubclass() { // PY-5939 + doTest(); + } + public void testImportExceptImportError() { doTest(); } From 0e089d27ac4f646f087fa64e4cb572b660e1af43 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 26 Apr 2012 10:03:59 +0200 Subject: [PATCH 09/22] find usages of class does not do text search on __init__ (PY-5406) --- .../python/findUsages/PyClassFindUsagesHandler.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/python/src/com/jetbrains/python/findUsages/PyClassFindUsagesHandler.java b/python/src/com/jetbrains/python/findUsages/PyClassFindUsagesHandler.java index 5ff7942a2228..72d52d3a9e49 100644 --- a/python/src/com/jetbrains/python/findUsages/PyClassFindUsagesHandler.java +++ b/python/src/com/jetbrains/python/findUsages/PyClassFindUsagesHandler.java @@ -7,6 +7,9 @@ import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyFunction; import org.jetbrains.annotations.NotNull; +import java.util.Collection; +import java.util.Collections; + /** * @author yole */ @@ -32,4 +35,12 @@ public class PyClassFindUsagesHandler extends FindUsagesHandler { protected boolean isSearchForTextOccurencesAvailable(PsiElement psiElement, boolean isSingleFile) { return true; } + + @Override + protected Collection getStringsToSearch(PsiElement element) { + if (element instanceof PyFunction && PyNames.INIT.equals(((PyFunction) element).getName())) { + return Collections.emptyList(); + } + return super.getStringsToSearch(element); + } } From 35c2477d80da1ef9fe59423c99d2789ce39a65a5 Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Thu, 26 Apr 2012 11:02:50 +0200 Subject: [PATCH 10/22] Remote interpreter: correct interpreter version (PY-6430). --- .../jetbrains/python/remote/RemoteFile.java | 29 ++++++++++-- .../run/AbstractPythonRunConfiguration.java | 7 --- .../python/sdk/PyRemoteSdkFlavor.java | 47 +++++++++++++++++++ .../jetbrains/python/sdk/PythonSdkFlavor.java | 5 +- 4 files changed, 75 insertions(+), 13 deletions(-) create mode 100644 python/src/com/jetbrains/python/sdk/PyRemoteSdkFlavor.java diff --git a/python/src/com/jetbrains/python/remote/RemoteFile.java b/python/src/com/jetbrains/python/remote/RemoteFile.java index 3e80e0a22259..e5585c4a32dd 100644 --- a/python/src/com/jetbrains/python/remote/RemoteFile.java +++ b/python/src/com/jetbrains/python/remote/RemoteFile.java @@ -2,16 +2,19 @@ package com.jetbrains.python.remote; import com.intellij.openapi.util.io.FileUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * @author traff */ public class RemoteFile { + private final boolean myWin; private final String myPath; public RemoteFile(@NotNull String path, boolean isWin) { myPath = toSystemDependent(path, isWin); + myWin = isWin; } public RemoteFile(@NotNull String parent, String child) { @@ -22,14 +25,19 @@ public class RemoteFile { this(resolveChild(parent, child, isWin), isWin); } - private static String resolveChild(@NotNull String parent, @NotNull String child, boolean win) { - String separator; - if (win) { - separator = "\\"; + @Nullable + public String getName() { + int ind = myPath.lastIndexOf(getSeparator(myWin)); + if (ind != -1 && ind < myPath.length() - 1) { //not last char + return myPath.substring(ind + 1); } else { - separator = "/"; + return null; } + } + + private static String resolveChild(@NotNull String parent, @NotNull String child, boolean win) { + String separator = getSeparator(win); String path; if (parent.endsWith(separator)) { @@ -41,6 +49,17 @@ public class RemoteFile { return path; } + private static String getSeparator(boolean win) { + String separator; + if (win) { + separator = "\\"; + } + else { + separator = "/"; + } + return separator; + } + public String getPath() { return myPath; diff --git a/python/src/com/jetbrains/python/run/AbstractPythonRunConfiguration.java b/python/src/com/jetbrains/python/run/AbstractPythonRunConfiguration.java index 4fbc2afe9bbe..ea75bc2f1ba4 100644 --- a/python/src/com/jetbrains/python/run/AbstractPythonRunConfiguration.java +++ b/python/src/com/jetbrains/python/run/AbstractPythonRunConfiguration.java @@ -24,7 +24,6 @@ import com.jetbrains.python.PyBundle; import com.jetbrains.python.PythonModuleTypeBase; import com.jetbrains.python.debugger.remote.PyPathMappingSettings; import com.jetbrains.python.sdk.PythonEnvUtil; -import com.jetbrains.python.sdk.PythonSdkFlavor; import com.jetbrains.python.sdk.PythonSdkType; import org.jdom.Element; import org.jetbrains.annotations.Nullable; @@ -186,12 +185,6 @@ public abstract class AbstractPythonRunConfiguration suggestHomePaths() { + return Lists.newArrayList(); + } + + @Override + public boolean isValidSdkHome(String path) { + return StringUtil.isNotEmpty(path) && path.startsWith("ssh:") && checkName(NAMES, getExecutableName(path)); + } + + private static boolean checkName(String[] names, @Nullable String name) { + if (name == null) { + return false; + } + for (String n : names) { + if (name.startsWith(n)) { + return true; + } + } + return false; + } + + @Nullable + private static String getExecutableName(String path) { + return RemoteFile.detectSystemByPath(path).createRemoteFile(path).getName(); + } +} diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java index e93aa40c75f8..13d89bba101d 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java @@ -49,7 +49,7 @@ public abstract class PythonSdkFlavor { public static List getApplicableFlavors() { List result = new ArrayList(); - result.add(VirtualEnvSdkFlavor.INSTANCE); + if (SystemInfo.isWindows) { result.add(WinPythonSdkFlavor.INSTANCE); } @@ -71,6 +71,9 @@ public abstract class PythonSdkFlavor { result.add(JythonSdkFlavor.INSTANCE); result.add(IronPythonSdkFlavor.INSTANCE); result.add(PyPySdkFlavor.INSTANCE); + result.add(VirtualEnvSdkFlavor.INSTANCE); + result.add(PyRemoteSdkFlavor.INSTANCE); + return result; } From 32bca31f70c6fcaf86f1c3d3933183faf1d27c04 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 26 Apr 2012 11:21:12 +0200 Subject: [PATCH 11/22] fix SOE in new-style resolve (PY-6305) (oh god the contract of findExportedName() is so confusing) --- .../src/com/jetbrains/python/psi/impl/PyFileImpl.java | 10 ++++++---- .../fromPackageImportIntoInit/pack/__init__.py | 2 ++ .../multiFile/fromPackageImportIntoInit/pack/mod.py | 0 .../com/jetbrains/python/PyMultiFileResolveTest.java | 8 ++++++++ 4 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 python/testData/resolve/multiFile/fromPackageImportIntoInit/pack/__init__.py create mode 100644 python/testData/resolve/multiFile/fromPackageImportIntoInit/pack/mod.py diff --git a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java index f7bcb9668701..35512c774fac 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java @@ -183,9 +183,11 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression { return null; } + @Nullable private PsiElement resolveDeclaration(String name, PsiElement result) { if (result instanceof PyImportElement) { - return findNameInImportElement(name, (PyImportElement)result); + final PyImportElement importElement = (PyImportElement)result; + return findNameInImportElement(name, importElement, importElement.getContainingImportStatement() instanceof PyFromImportStatement); } else if (result instanceof PyFromImportStatement) { return ((PyFromImportStatement) result).resolveImportSource(); @@ -557,7 +559,7 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression { @Nullable private PsiElement findNameInImportStatement(String name, PyImportStatement child) { for (PyImportElement importElement: child.getImportElements()) { - final PsiElement result = findNameInImportElement(name, importElement); + final PsiElement result = findNameInImportElement(name, importElement, false); if (result != null) { return result; } @@ -566,8 +568,8 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression { } @Nullable - private PsiElement findNameInImportElement(String name, PyImportElement importElement) { - final PsiElement result = importElement.getElementNamed(name, false); + private PsiElement findNameInImportElement(String name, PyImportElement importElement, final boolean resolveImportElement) { + final PsiElement result = importElement.getElementNamed(name, resolveImportElement); if (result != null) { return result; } diff --git a/python/testData/resolve/multiFile/fromPackageImportIntoInit/pack/__init__.py b/python/testData/resolve/multiFile/fromPackageImportIntoInit/pack/__init__.py new file mode 100644 index 000000000000..d84254aa0698 --- /dev/null +++ b/python/testData/resolve/multiFile/fromPackageImportIntoInit/pack/__init__.py @@ -0,0 +1,2 @@ +from pack import mod +# diff --git a/python/testData/resolve/multiFile/fromPackageImportIntoInit/pack/mod.py b/python/testData/resolve/multiFile/fromPackageImportIntoInit/pack/mod.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java b/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java index 4540eb1a1480..250977bd7e2f 100644 --- a/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java +++ b/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java @@ -96,6 +96,14 @@ public class PyMultiFileResolveTest extends PyResolveTestCase { assertTrue("is target?", elt instanceof PyTargetExpression); } + public void testFromPackageImportIntoInit() { // PY-6305 + myFixture.copyDirectoryToProject("fromPackageImportIntoInit/pack", "pack"); + final PsiFile psiFile = myFixture.configureByFile("pack/__init__.py"); + final PsiElement result = doResolve(psiFile); + assertInstanceOf(result, PyFile.class); + assertEquals("mod.py", ((PyFile) result).getName()); + } + public void testResolveInPkg() { ResolveResult[] results = doMultiResolve(); assertTrue(results.length == 2); // func and import stmt From 9eff8e77f476a9e4b667d831a5ff4902362723b1 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 26 Apr 2012 14:51:56 +0400 Subject: [PATCH 12/22] Fixed unresolved reference in local variable of lambda inside default parameter value (PY-6435) --- .../codeInsight/dataflow/scope/ScopeUtil.java | 23 ++++++++++--------- .../resolve/LambdaParameterInDefaultValue.py | 2 ++ .../com/jetbrains/python/PyResolveTest.java | 5 ++++ 3 files changed, 19 insertions(+), 11 deletions(-) create mode 100644 python/testData/resolve/LambdaParameterInDefaultValue.py diff --git a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/ScopeUtil.java b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/ScopeUtil.java index 11b36602f708..4bd865e1ea90 100644 --- a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/ScopeUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/ScopeUtil.java @@ -56,20 +56,21 @@ public class ScopeUtil { } @Nullable - public static ScopeOwner getResolveScopeOwner(PsiElement element) { - // References in default values of parameters are defined somewhere in outer scopes, as well as references in decorators (if they are - // not inside a lambda, see PY-6083) and superclasses - final ScopeOwner initialScopeOwner = getScopeOwner(element); - if (!(initialScopeOwner instanceof PyLambdaExpression && PsiTreeUtil.getParentOfType(element, PyDecorator.class) != null) && - PsiTreeUtil.getParentOfType(element, PyParameter.class, PyDecorator.class) != null) { - element = initialScopeOwner; + public static ScopeOwner getResolveScopeOwner(@NotNull PsiElement element) { + final ScopeOwner firstOwner = getScopeOwner(element); + if (firstOwner == null) { + return null; + } + final ScopeOwner nextOwner = getScopeOwner(firstOwner); + final PyElement decoratorOrParameterAncestor = PsiTreeUtil.getParentOfType(element, PyDecorator.class, PyParameter.class); + if (decoratorOrParameterAncestor != null && !PsiTreeUtil.isAncestor(decoratorOrParameterAncestor, firstOwner, true)) { + return nextOwner; } final PyClass containingClass = PsiTreeUtil.getParentOfType(element, PyClass.class); - if (containingClass != null && element != null && - PsiTreeUtil.isAncestor(containingClass.getSuperClassExpressionList(), element, false)) { - element = containingClass; + if (containingClass != null && PsiTreeUtil.isAncestor(containingClass.getSuperClassExpressionList(), element, false)) { + return nextOwner; } - return PsiTreeUtil.getParentOfType(element, ScopeOwner.class); + return firstOwner; } @Nullable diff --git a/python/testData/resolve/LambdaParameterInDefaultValue.py b/python/testData/resolve/LambdaParameterInDefaultValue.py new file mode 100644 index 000000000000..1e0c6c0191cf --- /dev/null +++ b/python/testData/resolve/LambdaParameterInDefaultValue.py @@ -0,0 +1,2 @@ +def f(g=lambda xx: xx): + pass # \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyResolveTest.java b/python/testSrc/com/jetbrains/python/PyResolveTest.java index 0331ccfeb0ac..d0fde0cc6b62 100644 --- a/python/testSrc/com/jetbrains/python/PyResolveTest.java +++ b/python/testSrc/com/jetbrains/python/PyResolveTest.java @@ -462,4 +462,9 @@ public class PyResolveTest extends PyResolveTestCase { public void testLambdaParameterInDecorator() { assertResolvesTo(PyNamedParameter.class, "xx"); } + + // PY-6435 + public void testLambdaParameterInDefaultValue() { + assertResolvesTo(PyNamedParameter.class, "xx"); + } } From 2a21d8990426b387c108fa7b96ec050960ce018c Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 26 Apr 2012 17:01:57 +0400 Subject: [PATCH 13/22] Package names translate '_' in requirements to '-' (PY-6438) --- python/src/com/jetbrains/python/packaging/PyRequirement.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/packaging/PyRequirement.java b/python/src/com/jetbrains/python/packaging/PyRequirement.java index feb06b424df8..181b4c7e6d28 100644 --- a/python/src/com/jetbrains/python/packaging/PyRequirement.java +++ b/python/src/com/jetbrains/python/packaging/PyRequirement.java @@ -271,7 +271,7 @@ public class PyRequirement { @Nullable public PyPackage match(@NotNull List packages) { for (PyPackage pkg : packages) { - if (myName.equalsIgnoreCase(pkg.getName())) { + 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(); From 1deb0c5c9fc02508a89dfd2a080ea935ab67e273 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 26 Apr 2012 17:21:13 +0400 Subject: [PATCH 14/22] Refactored tests of unused imports --- .../inspections/PyUnusedImportTest.java | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnusedImportTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnusedImportTest.java index 820e18277352..efa52a212549 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnusedImportTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnusedImportTest.java @@ -1,34 +1,35 @@ package com.jetbrains.python.inspections; import com.jetbrains.python.fixtures.PyTestCase; +import org.jetbrains.annotations.NotNull; /** * @author yole */ public class PyUnusedImportTest extends PyTestCase { - public void testModuleAndSubmodule() { // PY-3626 - myFixture.copyDirectoryToProject("inspections/unusedImport/moduleAndSubmodule", ""); - myFixture.enableInspections(PyUnresolvedReferencesInspection.class); - myFixture.testHighlighting(true, false, false, "py3626.py"); + // PY-3626 + public void testModuleAndSubmodule() { + doTest("py3626.py"); } - public void testSubpackageInInitPy() { // PY-3201 - myFixture.copyDirectoryToProject("inspections/unusedImport/subpackageInInitPy", ""); - myFixture.enableInspections(PyUnresolvedReferencesInspection.class); - myFixture.testHighlighting(true, false, false, "package1/__init__.py"); + // PY-3201 + public void testSubpackageInInitPy() { + doTest("package1/__init__.py"); } // PY-5589 public void testUnusedPackageAndSubmodule() { - myFixture.copyDirectoryToProject("inspections/unusedImport/unusedPackageAndSubmodule", ""); - myFixture.enableInspections(PyUnresolvedReferencesInspection.class); - myFixture.testHighlighting(true, false, false, "test1.py"); + doTest("test1.py"); } // PY-5621 public void testUnusedSubmodule() { - myFixture.copyDirectoryToProject("inspections/unusedImport/unusedSubmodule", ""); + doTest("test1.py"); + } + + private void doTest(@NotNull String filename) { + myFixture.copyDirectoryToProject("inspections/unusedImport/" + getTestName(true), ""); myFixture.enableInspections(PyUnresolvedReferencesInspection.class); - myFixture.testHighlighting(true, false, false, "test1.py"); + myFixture.testHighlighting(true, false, false, filename); } } From cb6f0debaa8fcff2ced1ec2e02955bbeccae779b Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 26 Apr 2012 15:29:01 +0200 Subject: [PATCH 15/22] fix stub/AST mismatches and IOOBE in indices: use correct file content when building stubs for unsaved document (IDEA-85266) [r=peter, jeka] --- .../com/jetbrains/python/PyStubsTest.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/python/testSrc/com/jetbrains/python/PyStubsTest.java b/python/testSrc/com/jetbrains/python/PyStubsTest.java index afeeee9be96b..62647916c668 100644 --- a/python/testSrc/com/jetbrains/python/PyStubsTest.java +++ b/python/testSrc/com/jetbrains/python/PyStubsTest.java @@ -2,10 +2,15 @@ package com.jetbrains.python; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.project.DumbServiceImpl; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.impl.source.PsiFileImpl; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.stubs.StubElement; import com.intellij.psi.stubs.StubUpdatingIndex; @@ -16,6 +21,7 @@ import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyFileImpl; import com.jetbrains.python.psi.impl.PyQualifiedName; import com.jetbrains.python.psi.impl.PythonLanguageLevelPusher; +import com.jetbrains.python.psi.stubs.PyClassNameIndex; import com.jetbrains.python.psi.stubs.PyClassStub; import com.jetbrains.python.psi.stubs.PyVariableNameIndex; import com.jetbrains.python.toolbox.Maybe; @@ -365,4 +371,32 @@ public class PyStubsTest extends PyTestCase { PsiFile psiFile = myFixture.getPsiManager().findFile(sourceFile); return (PyFile)psiFile; } + + public void testStubIndexMismatch() { + VirtualFile vFile = myFixture.getTempDirFixture().createFile("foo.py"); + final Project project = myFixture.getProject(); + PsiFileImpl fooPyFile = (PsiFileImpl) PsiManager.getInstance(project).findFile(vFile); + final Document fooDocument = fooPyFile.getViewProvider().getDocument(); + Collection classes = PyClassNameIndex.find("Foo", project, GlobalSearchScope.allScope(project)); + assertEquals(classes.size(), 0); + new WriteCommandAction.Simple(project, fooPyFile) { + public void run() { + fooDocument.setText("class Foo: pass"); + } + }.execute(); + PsiDocumentManager.getInstance(project).commitDocument(fooDocument); + fooPyFile.setTreeElementPointer(null); + //classes = PyClassNameIndex.find("Foo", project, GlobalSearchScope.allScope(project)); + //fooPyFile.unloadContent(); + DumbServiceImpl.getInstance(project).setDumb(true); + try { + assertEquals(1, ((PyFile) fooPyFile).getTopLevelClasses().size()); + } + finally { + DumbServiceImpl.getInstance(project).setDumb(false); + } + classes = PyClassNameIndex.find("Foo", project, GlobalSearchScope.allScope(project)); + assertEquals(classes.size(), 1); + + } } From 8b3d3d1c699f57fc8d677fa97bece330ac2b0bce Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 26 Apr 2012 18:13:10 +0400 Subject: [PATCH 16/22] Removed unused return value --- .../python/codeInsight/dataflow/scope/impl/ScopeImpl.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java index 76f41fb459a4..1c92ab8f3e47 100644 --- a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java +++ b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java @@ -54,7 +54,7 @@ public class ScopeImpl implements Scope { return null; } - private synchronized List> computeScopeVariables() throws DFALimitExceededException { + private synchronized void computeScopeVariables() throws DFALimitExceededException { computeFlow(); if (myCachedScopeVariables == null) { final PyReachingDefsDfaInstance dfaInstance = new PyReachingDefsDfaInstance(); @@ -62,7 +62,6 @@ public class ScopeImpl implements Scope { final DFAMapEngine engine = new DFAMapEngine(myFlow, dfaInstance, semilattice); myCachedScopeVariables = engine.performDFA(); } - return myCachedScopeVariables; } public boolean isGlobal(final String name) { From 6d01adf1fb5402557c453ec4bcc906e52204c162 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 26 Apr 2012 18:15:15 +0400 Subject: [PATCH 17/22] Fixed local resolve prioriries for star imports (PY-6380) --- .../dataflow/scope/impl/ScopeImpl.java | 15 +++++++++++++++ .../unusedImport/unusedAfterStarImport/test1.py | 6 ++++++ .../unusedImport/unusedAfterStarImport/test2.py | 2 ++ .../unusedImport/unusedAfterStarImport/test3.py | 2 ++ .../unusedImport/unusedAfterStarImport/test4.py | 2 ++ .../python/inspections/PyUnusedImportTest.java | 5 +++++ 6 files changed, 32 insertions(+) create mode 100644 python/testData/inspections/unusedImport/unusedAfterStarImport/test1.py create mode 100644 python/testData/inspections/unusedImport/unusedAfterStarImport/test2.py create mode 100644 python/testData/inspections/unusedImport/unusedAfterStarImport/test3.py create mode 100644 python/testData/inspections/unusedImport/unusedAfterStarImport/test4.py diff --git a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java index 1c92ab8f3e47..b4b3520605d5 100644 --- a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java +++ b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java @@ -195,6 +195,21 @@ public class ScopeImpl implements Scope { } } }); + + Collections.sort(nameDefiners, new Comparator() { + @Override + public int compare(NameDefiner d1, NameDefiner d2) { + return getPriority(d2) - getPriority(d1); + }; + + private int getPriority(NameDefiner nameDefiner) { + if (nameDefiner instanceof PyStarImportElement) { + return -10; + } + return 0; + } + }); + myNamedElements = namedElements; myNameDefiners = nameDefiners; myNestedScopes = nestedScopes; diff --git a/python/testData/inspections/unusedImport/unusedAfterStarImport/test1.py b/python/testData/inspections/unusedImport/unusedAfterStarImport/test1.py new file mode 100644 index 000000000000..1cd116611054 --- /dev/null +++ b/python/testData/inspections/unusedImport/unusedAfterStarImport/test1.py @@ -0,0 +1,6 @@ +from test2 import * +from test2 import f #pass +from test3 import * #pass +from test4 import * + +print(f, g) diff --git a/python/testData/inspections/unusedImport/unusedAfterStarImport/test2.py b/python/testData/inspections/unusedImport/unusedAfterStarImport/test2.py new file mode 100644 index 000000000000..001f2e6d84c8 --- /dev/null +++ b/python/testData/inspections/unusedImport/unusedAfterStarImport/test2.py @@ -0,0 +1,2 @@ +def f(x): + return x \ No newline at end of file diff --git a/python/testData/inspections/unusedImport/unusedAfterStarImport/test3.py b/python/testData/inspections/unusedImport/unusedAfterStarImport/test3.py new file mode 100644 index 000000000000..504363eab9af --- /dev/null +++ b/python/testData/inspections/unusedImport/unusedAfterStarImport/test3.py @@ -0,0 +1,2 @@ +def g(): + pass \ No newline at end of file diff --git a/python/testData/inspections/unusedImport/unusedAfterStarImport/test4.py b/python/testData/inspections/unusedImport/unusedAfterStarImport/test4.py new file mode 100644 index 000000000000..09d9cea3d574 --- /dev/null +++ b/python/testData/inspections/unusedImport/unusedAfterStarImport/test4.py @@ -0,0 +1,2 @@ +def h(): + pass \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnusedImportTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnusedImportTest.java index efa52a212549..5faf7790ba97 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnusedImportTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnusedImportTest.java @@ -27,6 +27,11 @@ public class PyUnusedImportTest extends PyTestCase { doTest("test1.py"); } + // PY-6380 + public void testUnusedAfterStarImport() { + doTest("test1.py"); + } + private void doTest(@NotNull String filename) { myFixture.copyDirectoryToProject("inspections/unusedImport/" + getTestName(true), ""); myFixture.enableInspections(PyUnresolvedReferencesInspection.class); From 35d6c520109cdbf6b02f4fa34517a4d7d3622860 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 26 Apr 2012 18:22:03 +0400 Subject: [PATCH 18/22] Suppress warning for USE_CACHE in PyReferenceImpl --- .../jetbrains/python/psi/impl/references/PyReferenceImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java index 0362c6d4d1cf..96ee92a8f3c8 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java @@ -75,6 +75,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference } // it is *not* final so that it can be changed in debug time. if set to false, caching is off + @SuppressWarnings("FieldCanBeLocal") private static boolean USE_CACHE = true; /** From 5d90d6625cce7dcb7182de1ad560e69ae41cca43 Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Thu, 26 Apr 2012 16:41:41 +0200 Subject: [PATCH 19/22] Missing builtins for remote interpreter (PY-6191). --- .../jetbrains/python/sdk/PySkeletonGenerator.java | 14 +++++++++----- .../jetbrains/python/sdk/PySkeletonRefresher.java | 11 ++++++++--- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/python/src/com/jetbrains/python/sdk/PySkeletonGenerator.java b/python/src/com/jetbrains/python/sdk/PySkeletonGenerator.java index e7098bb9af0f..64b22c1785ae 100644 --- a/python/src/com/jetbrains/python/sdk/PySkeletonGenerator.java +++ b/python/src/com/jetbrains/python/sdk/PySkeletonGenerator.java @@ -4,20 +4,20 @@ import com.google.common.collect.Maps; import com.intellij.execution.process.ProcessOutput; import com.intellij.openapi.application.ex.ApplicationManagerEx; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ArrayUtil; import com.intellij.util.Consumer; -import com.intellij.util.Processor; import com.jetbrains.python.PythonHelpersLocator; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.io.File; -import java.util.*; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; import static com.jetbrains.python.sdk.SkeletonVersionChecker.fromVersionString; @@ -36,7 +36,11 @@ public class PySkeletonGenerator { public void finishSkeletonsGeneration() { } - static class ListBinariesResult { + public boolean exists(String name) { + return new File(name).exists(); + } + + protected static class ListBinariesResult { public final int generatorVersion; public final Map modules; diff --git a/python/src/com/jetbrains/python/sdk/PySkeletonRefresher.java b/python/src/com/jetbrains/python/sdk/PySkeletonRefresher.java index b9796826f6ef..b4b620df0a8d 100644 --- a/python/src/com/jetbrains/python/sdk/PySkeletonRefresher.java +++ b/python/src/com/jetbrains/python/sdk/PySkeletonRefresher.java @@ -18,7 +18,6 @@ import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Consumer; -import com.intellij.util.Processor; import com.intellij.util.SmartList; import com.intellij.util.io.ZipUtil; import com.jetbrains.python.PyBundle; @@ -425,7 +424,8 @@ public class PySkeletonRefresher { boolean canLive = headerMatcher != null && headerMatcher.matches(); if (canLive) { String sourceName = headerMatcher.group(1); - canLive = sourceName != null && (SkeletonVersionChecker.BUILTIN_NAME.equals(sourceName) || new File(sourceName).exists()); + canLive = + sourceName != null && (SkeletonVersionChecker.BUILTIN_NAME.equals(sourceName) || mySkeletonsGenerator.exists(sourceName)); } if (!canLive) { mySkeletonsGenerator.deleteOrLog(item); @@ -566,7 +566,7 @@ public class PySkeletonRefresher { return false; } - static class PyBinaryItem { + public static class PyBinaryItem { private String myPath; private String myModule; private long myLength; @@ -647,6 +647,11 @@ public class PySkeletonRefresher { if (versionString == null) { return null; } + + if (PySdkUtil.isRemote(mySdk)) { + return null; + } + String version = versionString.toLowerCase().replace(" ", "-"); File f; if (SystemInfo.isMac) { From 09e95d353a44715c36b0d76f4884cfe14fa5141d Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 26 Apr 2012 18:15:31 +0200 Subject: [PATCH 20/22] enable some more plugins in pycharm started under debugger --- python/main_pycharm.iml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/main_pycharm.iml b/python/main_pycharm.iml index a30072725174..598ba4739eb0 100644 --- a/python/main_pycharm.iml +++ b/python/main_pycharm.iml @@ -25,6 +25,9 @@ + + + From 71aebc444fc6c1775ec8d667fc30ee7426db96fb Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 26 Apr 2012 20:18:50 +0400 Subject: [PATCH 21/22] Fixed move refactoring for symbols that are used via star-import (PY-6432) --- .../move/PyMoveClassOrFunctionProcessor.java | 56 +++++++++++++------ .../move/starImportUsage/after/src/a.py | 3 + .../move/starImportUsage/after/src/b.py | 0 .../move/starImportUsage/after/src/c.py | 2 + .../move/starImportUsage/before/src/a.py | 3 + .../move/starImportUsage/before/src/b.py | 2 + .../move/starImportUsage/before/src/c.py | 0 .../python/refactoring/PyMoveTest.java | 5 ++ 8 files changed, 54 insertions(+), 17 deletions(-) create mode 100644 python/testData/refactoring/move/starImportUsage/after/src/a.py create mode 100644 python/testData/refactoring/move/starImportUsage/after/src/b.py create mode 100644 python/testData/refactoring/move/starImportUsage/after/src/c.py create mode 100644 python/testData/refactoring/move/starImportUsage/before/src/a.py create mode 100644 python/testData/refactoring/move/starImportUsage/before/src/b.py create mode 100644 python/testData/refactoring/move/starImportUsage/before/src/c.py diff --git a/python/src/com/jetbrains/python/refactoring/move/PyMoveClassOrFunctionProcessor.java b/python/src/com/jetbrains/python/refactoring/move/PyMoveClassOrFunctionProcessor.java index 05bb77c03268..b627ea1fc73e 100644 --- a/python/src/com/jetbrains/python/refactoring/move/PyMoveClassOrFunctionProcessor.java +++ b/python/src/com/jetbrains/python/refactoring/move/PyMoveClassOrFunctionProcessor.java @@ -4,10 +4,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiNamedElement; -import com.intellij.psi.PsiReference; +import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.BaseRefactoringProcessor; import com.intellij.refactoring.ui.UsageViewDescriptorAdapter; @@ -100,10 +97,10 @@ public class PyMoveClassOrFunctionProcessor extends BaseRefactoringProcessor { PyClassRefactoringUtil.rememberNamedReferences(oldElement); final PsiNamedElement newElement = (PsiNamedElement)(dest.add(oldElement)); for (UsageInfo usage : usages) { - final PsiElement oldExpr = usage.getElement(); + final PsiElement usageElement = usage.getElement(); // TODO: Respect the qualified import style - if (oldExpr instanceof PyQualifiedExpression) { - PyQualifiedExpression qexpr = (PyQualifiedExpression)oldExpr; + if (usageElement instanceof PyQualifiedExpression) { + PyQualifiedExpression qexpr = (PyQualifiedExpression)usageElement; if (oldElement instanceof PyClass && PyNames.INIT.equals(qexpr.getName())) { continue; } @@ -112,22 +109,25 @@ public class PyMoveClassOrFunctionProcessor extends BaseRefactoringProcessor { PyClassRefactoringUtil.insertImport(newExpr, newElement, null, true); } } - if (oldExpr instanceof PyStringLiteralExpression) { - final PsiReference[] references = oldExpr.getReferences(); - for (PsiReference ref : references) { + if (usageElement instanceof PyStringLiteralExpression) { + for (PsiReference ref : usageElement.getReferences()) { if (ref instanceof DocStringTypeReference && ref.isReferenceTo(oldElement)) { ref.bindToElement(newElement); } } } else { - final PyImportStatementBase importStmt = PsiTreeUtil.getParentOfType(usage.getElement(), PyImportStatementBase.class); + final PyImportStatementBase importStmt = PsiTreeUtil.getParentOfType(usageElement, PyImportStatementBase.class); if (importStmt != null) { PyClassRefactoringUtil.updateImportOfElement(importStmt, newElement); } - if (usage.getFile() == oldFile && (oldExpr == null || !PsiTreeUtil.isAncestor(oldElement, oldExpr, false))) { + if (usage.getFile() == oldFile && (usageElement == null || !PsiTreeUtil.isAncestor(oldElement, usageElement, false))) { PyClassRefactoringUtil.insertImport(oldElement, newElement); } + if (usageElement != null && resolvesToLocalStarImport(usageElement)) { + PyClassRefactoringUtil.insertImport(usageElement, newElement); + new PyImportOptimizer().processFile(usageElement.getContainingFile()).run(); + } } } PyClassRefactoringUtil.restoreNamedReferences(newElement, oldElement); @@ -141,16 +141,38 @@ public class PyMoveClassOrFunctionProcessor extends BaseRefactoringProcessor { }, REFACTORING_NAME, null); } + @Override + protected String getCommandName() { + return REFACTORING_NAME; + } + + private boolean resolvesToLocalStarImport(@NotNull PsiElement element) { + final PsiReference ref = element.getReference(); + final List resolvedElements = new ArrayList(); + if (ref instanceof PsiPolyVariantReference) { + for (ResolveResult result : ((PsiPolyVariantReference)ref).multiResolve(false)) { + resolvedElements.add(result.getElement()); + } + } + else if (ref != null) { + resolvedElements.add(ref.resolve()); + } + final PsiFile containingFile = element.getContainingFile(); + if (containingFile != null) { + for (PsiElement resolved : resolvedElements) { + if (resolved instanceof PyStarImportElement && resolved.getContainingFile() == containingFile) { + return true; + } + } + } + return false; + } + private static void checkValidImportableFile(PsiElement anchor, VirtualFile file) { final PyQualifiedName qName = ResolveImportUtil.findShortestImportableQName(anchor, file); if (!PyClassRefactoringUtil.isValidQualifiedName(qName)) { throw new IncorrectOperationException(PyBundle.message("refactoring.move.class.or.function.error.cannot.use.module.name.$0", qName)); } } - - @Override - protected String getCommandName() { - return REFACTORING_NAME; - } } diff --git a/python/testData/refactoring/move/starImportUsage/after/src/a.py b/python/testData/refactoring/move/starImportUsage/after/src/a.py new file mode 100644 index 000000000000..b51e032b9bbe --- /dev/null +++ b/python/testData/refactoring/move/starImportUsage/after/src/a.py @@ -0,0 +1,3 @@ +from c import f + +print(f()) \ No newline at end of file diff --git a/python/testData/refactoring/move/starImportUsage/after/src/b.py b/python/testData/refactoring/move/starImportUsage/after/src/b.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/refactoring/move/starImportUsage/after/src/c.py b/python/testData/refactoring/move/starImportUsage/after/src/c.py new file mode 100644 index 000000000000..39e1b2079d04 --- /dev/null +++ b/python/testData/refactoring/move/starImportUsage/after/src/c.py @@ -0,0 +1,2 @@ +def f(): + pass \ No newline at end of file diff --git a/python/testData/refactoring/move/starImportUsage/before/src/a.py b/python/testData/refactoring/move/starImportUsage/before/src/a.py new file mode 100644 index 000000000000..6c3eacab5b29 --- /dev/null +++ b/python/testData/refactoring/move/starImportUsage/before/src/a.py @@ -0,0 +1,3 @@ +from b import * + +print(f()) \ No newline at end of file diff --git a/python/testData/refactoring/move/starImportUsage/before/src/b.py b/python/testData/refactoring/move/starImportUsage/before/src/b.py new file mode 100644 index 000000000000..39e1b2079d04 --- /dev/null +++ b/python/testData/refactoring/move/starImportUsage/before/src/b.py @@ -0,0 +1,2 @@ +def f(): + pass \ No newline at end of file diff --git a/python/testData/refactoring/move/starImportUsage/before/src/c.py b/python/testData/refactoring/move/starImportUsage/before/src/c.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyMoveTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyMoveTest.java index 0738e56a286d..cb8645d368f6 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyMoveTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyMoveTest.java @@ -90,6 +90,11 @@ public class PyMoveTest extends PyTestCase { doMoveFileTest("p1/p2/m1.py", "nonp3"); } + // PY-6432 + public void testStarImportUsage() { + doMoveSymbolTest("f", "c.py"); + } + private void doMoveFileTest(String fileName, String toDirName) { Project project = myFixture.getProject(); PsiManager manager = PsiManager.getInstance(project); From ca79ee6c0336fd3296044a49704d5388d0d56c3f Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Thu, 26 Apr 2012 19:19:42 +0200 Subject: [PATCH 22/22] Python 2.4 compatibility (PY-6403). --- python/helpers/pydev/pydev_monkey.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/python/helpers/pydev/pydev_monkey.py b/python/helpers/pydev/pydev_monkey.py index 8a5471dd959d..33569151ef56 100644 --- a/python/helpers/pydev/pydev_monkey.py +++ b/python/helpers/pydev/pydev_monkey.py @@ -63,7 +63,14 @@ def patch_args(args): return new_args def args_to_str(args): - return ' '.join(x if x.startswith('"') and x.endswith('"') else '"%s"' % x for x in args) + quoted_args = [] + for x in args: + if x.startswith('"') and x.endswith('"'): + quoted_args.append(x) + else: + quoted_args.append('"%s"' % x) + + return ' '.join(quoted_args) def remove_quotes(str): if str.startswith('"') and str.endswith('"'):