summaryrefslogtreecommitdiff
path: root/python/src
diff options
context:
space:
mode:
Diffstat (limited to 'python/src')
-rw-r--r--python/src/META-INF/pycharm-core.xml8
-rw-r--r--python/src/META-INF/python-core.xml25
-rw-r--r--python/src/com/jetbrains/python/PyBundle.properties3
-rw-r--r--python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java17
-rw-r--r--python/src/com/jetbrains/python/codeInsight/editorActions/moveUpDown/PyStatementMover.java31
-rw-r--r--python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyWithFixer.java10
-rw-r--r--python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java2
-rw-r--r--python/src/com/jetbrains/python/console/PydevConsoleCommunication.java10
-rw-r--r--python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java8
-rw-r--r--python/src/com/jetbrains/python/debugger/PyDebugProcess.java28
-rw-r--r--python/src/com/jetbrains/python/debugger/PyDebugSupportUtils.java22
-rw-r--r--python/src/com/jetbrains/python/debugger/PyExceptionBreakpointProperties.java5
-rw-r--r--python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java14
-rw-r--r--python/src/com/jetbrains/python/findUsages/PyFunctionFindUsagesHandler.java4
-rw-r--r--python/src/com/jetbrains/python/hierarchy/PyHierarchyNodeDescriptor.java (renamed from python/src/com/jetbrains/python/hierarchy/PyTypeHierarchyNodeDescriptor.java)31
-rw-r--r--python/src/com/jetbrains/python/hierarchy/PyTypeHierarchyBrowser.java4
-rw-r--r--python/src/com/jetbrains/python/hierarchy/call/PyCallHierarchyBrowser.java101
-rw-r--r--python/src/com/jetbrains/python/hierarchy/call/PyCallHierarchyProvider.java76
-rw-r--r--python/src/com/jetbrains/python/hierarchy/call/PyCallHierarchyTreeStructureBase.java85
-rw-r--r--python/src/com/jetbrains/python/hierarchy/call/PyCalleeFunctionTreeStructure.java42
-rw-r--r--python/src/com/jetbrains/python/hierarchy/call/PyCallerFunctionTreeStructure.java42
-rw-r--r--python/src/com/jetbrains/python/hierarchy/call/PyStaticCallHierarchyUtil.java163
-rw-r--r--python/src/com/jetbrains/python/hierarchy/treestructures/PySubTypesHierarchyTreeStructure.java18
-rw-r--r--python/src/com/jetbrains/python/hierarchy/treestructures/PySuperTypesHierarchyTreeStructure.java25
-rw-r--r--python/src/com/jetbrains/python/hierarchy/treestructures/PyTypeHierarchyTreeStructure.java27
-rw-r--r--python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java97
-rw-r--r--python/src/com/jetbrains/python/inspections/quickfix/GenerateBinaryStubsFix.java2
-rw-r--r--python/src/com/jetbrains/python/inspections/quickfix/PyDefaultArgumentQuickFix.java2
-rw-r--r--python/src/com/jetbrains/python/inspections/quickfix/StatementEffectFunctionCallQuickFix.java21
-rw-r--r--python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java5
-rw-r--r--python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java665
-rw-r--r--python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java367
-rw-r--r--python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java24
-rw-r--r--python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java177
-rw-r--r--python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java119
-rw-r--r--python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java15
-rw-r--r--python/src/com/jetbrains/python/projectView/PyElementNode.java24
-rw-r--r--python/src/com/jetbrains/python/psi/PyUtil.java4
-rw-r--r--python/src/com/jetbrains/python/psi/impl/PyClassImpl.java32
-rw-r--r--python/src/com/jetbrains/python/psi/impl/PyElementPresentation.java76
-rw-r--r--python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java84
-rw-r--r--python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java8
-rw-r--r--python/src/com/jetbrains/python/psi/impl/PyPresentableElementImpl.java73
-rw-r--r--python/src/com/jetbrains/python/psi/impl/PySingleStarParameterImpl.java20
-rw-r--r--python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionImpl.java2
-rw-r--r--python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java22
-rw-r--r--python/src/com/jetbrains/python/psi/impl/PyTupleParameterImpl.java15
-rw-r--r--python/src/com/jetbrains/python/psi/impl/PythonLanguageLevelPusher.java5
-rw-r--r--python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureHandler.java2
-rw-r--r--python/src/com/jetbrains/python/refactoring/move/PyMoveClassOrFunctionDelegate.java3
-rw-r--r--python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java6
-rw-r--r--python/src/com/jetbrains/python/sdk/PySdkUtil.java153
-rw-r--r--python/src/com/jetbrains/python/sdk/PythonSdkDetailsStep.java6
-rw-r--r--python/src/com/jetbrains/python/sdk/PythonSdkType.java26
-rw-r--r--python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java3
-rw-r--r--python/src/com/jetbrains/python/sdk/flavors/WinPythonSdkFlavor.java33
-rw-r--r--python/src/com/jetbrains/python/sdk/skeletons/PySkeletonGenerator.java39
-rw-r--r--python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java152
-rw-r--r--python/src/com/jetbrains/python/statistics/PyPackageUsagesCollector.java6
-rw-r--r--python/src/com/jetbrains/python/structureView/PyStructureViewElement.java33
-rw-r--r--python/src/com/jetbrains/python/testing/PyRerunFailedTestsAction.java33
-rw-r--r--python/src/com/jetbrains/python/testing/PythonTestCommandLineStateBase.java4
-rw-r--r--python/src/com/jetbrains/python/testing/TestRunConfigurationReRunResponsible.java30
-rw-r--r--python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java5
-rw-r--r--python/src/com/jetbrains/python/testing/pytest/PyTestConfigurationProducer.java5
-rw-r--r--python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java3
-rw-r--r--python/src/com/jetbrains/python/validation/StringLiteralQuotesAnnotator.java45
67 files changed, 1979 insertions, 1268 deletions
diff --git a/python/src/META-INF/pycharm-core.xml b/python/src/META-INF/pycharm-core.xml
index a9f7824b5340..ac42944c9953 100644
--- a/python/src/META-INF/pycharm-core.xml
+++ b/python/src/META-INF/pycharm-core.xml
@@ -40,10 +40,10 @@
<projectAttachProcessor implementation="com.intellij.platform.ModuleAttachProcessor"/>
- <projectConfigurable instance="com.jetbrains.python.configuration.PythonContentEntriesConfigurable"/>
- <projectConfigurable instance="com.jetbrains.python.buildout.BuildoutModulesConfigurable"/>
+ <projectConfigurable groupId="project" instance="com.jetbrains.python.configuration.PythonContentEntriesConfigurable"/>
+ <projectConfigurable groupId="build" instance="com.jetbrains.python.buildout.BuildoutModulesConfigurable"/>
<projectConfigurable groupId="project" instance="com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable"/>
- <projectConfigurable instance="com.jetbrains.python.configuration.PyDependenciesConfigurable"/>
+ <projectConfigurable groupId="project" instance="com.jetbrains.python.configuration.PyDependenciesConfigurable"/>
<directoryProjectConfigurator implementation="com.jetbrains.python.PythonSdkConfigurator" id="sdk"
order="after PlatformProjectConfigurator"/>
@@ -104,7 +104,7 @@
<add-to-group group-id="FileOpenGroup" anchor="after" relative-to-action="OpenFile"/>
</action>
- <action id="RerunFailedTests" class="com.intellij.execution.testframework.actions.AbstractRerunFailedTestsAction"
+ <action id="RerunFailedTests" class="com.intellij.execution.testframework.actions.RerunFailedTestsAction"
icon="AllIcons.RunConfigurations.RerunFailedTests"/>
<group id="WelcomeScreen.Platform.NewProject">
diff --git a/python/src/META-INF/python-core.xml b/python/src/META-INF/python-core.xml
index 7adbc46c189c..5091d43386a1 100644
--- a/python/src/META-INF/python-core.xml
+++ b/python/src/META-INF/python-core.xml
@@ -137,6 +137,7 @@
<gotoTargetRendererProvider implementation="com.jetbrains.python.codeInsight.PyGotoTargetRendererProvider"/>
<typeHierarchyProvider language="Python" implementationClass="com.jetbrains.python.hierarchy.PyTypeHierachyProvider"/>
+ <callHierarchyProvider language="Python" implementationClass="com.jetbrains.python.hierarchy.call.PyCallHierarchyProvider"/>
<highlightUsagesHandlerFactory implementation="com.jetbrains.python.codeInsight.highlighting.PyHighlightExitPointsHandlerFactory"/>
<joinLinesHandler implementation="com.jetbrains.python.editor.PyJoinLinesHandler"/>
@@ -531,7 +532,8 @@
</extensions>
- <extensionPoints>
+ <extensionPoints>
+ <extensionPoint qualifiedName="Pythonid.pythonDocumentationQuickInfoProvider" interface="com.jetbrains.python.documentation.PythonDocumentationQuickInfoProvider"/>
<extensionPoint qualifiedName="Pythonid.importResolver" interface="com.jetbrains.python.psi.impl.PyImportResolver"/>
<extensionPoint qualifiedName="Pythonid.magicLiteral" interface="com.jetbrains.python.magicLiteral.PyMagicLiteralExtensionPoint"/>
<extensionPoint qualifiedName="Pythonid.unresolvedReferenceSkipper" interface="com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferenceSkipperExtPoint"/>
@@ -633,8 +635,6 @@
<separator/>
<reference ref="AddToFavorites"/>
<separator/>
- <reference ref="RunContextPopupGroup"/>
- <separator/>
<reference ref="ReformatCode"/>
<reference ref="OptimizeImports"/>
<reference ref="$Delete"/>
@@ -647,7 +647,26 @@
<reference ref="CompareFileWithEditor"/>
</group>
+ <group id="PyCallHierarchyPopupMenu">
+ <reference ref="EditSource"/>
+ <separator/>
+ <reference ref="FindUsages"/>
+ <reference ref="RefactoringMenu"/>
+ <separator/>
+ <reference ref="AddToFavorites"/>
+ <separator/>
+ <reference ref="ReformatCode"/>
+ <reference ref="OptimizeImports"/>
+ <separator/>
+ <reference ref="VersionControlsGroup"/>
+
+ <separator/>
+ <reference ref="ExternalToolsGroup"/>
+ <separator/>
+ <reference ref="CompareTwoFiles"/>
+ <reference ref="CompareFileWithEditor"/>
+ </group>
<action id="com.jetbrains.python.console.PyOpenDebugConsoleAction"
class="com.jetbrains.python.console.PyOpenDebugConsoleAction"
diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties
index 482daa88b66c..d82e9a6fde62 100644
--- a/python/src/com/jetbrains/python/PyBundle.properties
+++ b/python/src/com/jetbrains/python/PyBundle.properties
@@ -676,6 +676,9 @@ ANN.tuple.py3=tuple parameter unpacking is not supported in Python 3
ANN.star.import.at.top.only='import *' only allowed at module level
+ANN.missing.closing.quote=Missing closing quote [{0}]
+ANN.missing.closing.triple.quotes=Missing closing triple quotes
+
ANN.method.$0.removed.use.$1=Method ''{0}'' has been removed, use ''{1}'' instead
ANN.method.$0.removed=Method ''{0}'' removed
diff --git a/python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java b/python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java
index 2ecf3b54a8d0..ab13a59ff0b8 100644
--- a/python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java
+++ b/python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java
@@ -278,7 +278,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor {
private static final PsiElementPattern.Capture<PsiElement> IN_IF_BODY =
psiElement().inside(psiElement(PyStatementList.class).inside(psiElement(PyIfPart.class)));
- private static final PsiElementPattern.Capture<PsiElement> IN_LOOP =
+ private static final PsiElementPattern.Capture<PsiElement> IN_LOOP =
psiElement().inside(false, psiElement(PyLoopStatement.class), or(psiElement(PyFunction.class), psiElement(PyClass.class)));
// not exactly a beauty
@@ -645,6 +645,20 @@ public class PyKeywordCompletionContributor extends CompletionContributor {
new PyKeywordCompletionProvider(PyNames.FROM));
}
+ private void addYieldExpression() {
+ extend(CompletionType.BASIC,
+ psiElement()
+ .withLanguage(PythonLanguage.getInstance())
+ .andOr(psiElement()
+ .inside(false, psiElement(PyAssignmentStatement.class), psiElement(PyTargetExpression.class))
+ .afterLeaf(psiElement().withElementType(PyTokenTypes.EQ)),
+ psiElement()
+ .inside(false, psiElement(PyAugAssignmentStatement.class), psiElement(PyTargetExpression.class))
+ .afterLeaf(psiElement().withElementType(PyTokenTypes.AUG_ASSIGN_OPERATIONS)),
+ psiElement().inside(true, psiElement(PyParenthesizedExpression.class))),
+ new PyKeywordCompletionProvider(PyNames.YIELD));
+ }
+
private void addYieldFrom() {
extend(CompletionType.BASIC,
psiElement()
@@ -671,6 +685,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor {
//addExprIf();
addExprElse();
addRaiseFrom();
+ addYieldExpression();
addYieldFrom();
addForToComprehensions();
addInToFor();
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/moveUpDown/PyStatementMover.java b/python/src/com/jetbrains/python/codeInsight/editorActions/moveUpDown/PyStatementMover.java
index 65cd5e1a79ce..89b6d9bc4a5d 100644
--- a/python/src/com/jetbrains/python/codeInsight/editorActions/moveUpDown/PyStatementMover.java
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/moveUpDown/PyStatementMover.java
@@ -108,7 +108,6 @@ public class PyStatementMover extends LineMover {
if (moveOutsideFile(document, lineNumber)) return null;
int lineEndOffset = document.getLineEndOffset(lineNumber);
final int startOffset = document.getLineStartOffset(lineNumber);
- lineEndOffset = startOffset != lineEndOffset ? lineEndOffset - 1 : lineEndOffset;
final PyStatementList statementList = getStatementList(elementToMove);
@@ -119,10 +118,6 @@ public class PyStatementMover extends LineMover {
final int startLine = document.getLineNumber(start);
final int endLine = document.getLineNumber(end);
- if (elementToMove instanceof PsiComment && destination instanceof PsiComment) {
- return new LineRange(lineNumber, lineNumber + 1);
- }
-
if (elementToMove instanceof PyClass || elementToMove instanceof PyFunction) {
PyElement scope = statementList == null ? (PyElement)elementToMove.getContainingFile() : statementList;
if (destination != null)
@@ -137,6 +132,11 @@ public class PyStatementMover extends LineMover {
scopeRange = moveInto(elementToMove, file, editor, down, lineEndOffset);
if (scopeRange != null) return scopeRange;
+ if (elementToMove instanceof PsiComment && ( PsiTreeUtil.isAncestor(destination, elementToMove, true)) ||
+ destination instanceof PsiComment) {
+ return new LineRange(lineNumber, lineNumber + 1);
+ }
+
final PyElement scope = statementList == null ? (PyElement)elementToMove.getContainingFile() : statementList;
if ((elementToMove instanceof PyClass) || (elementToMove instanceof PyFunction))
return new ScopeRange(scope, scope.getFirstChild(), !down, true);
@@ -185,7 +185,6 @@ public class PyStatementMover extends LineMover {
if (sibling != null) {
final PyStatementList list = sibling.getStatementList();
- assert list != null;
return new ScopeRange(list, down ? list.getFirstChild() : list.getLastChild(), !addBefore);
}
else {
@@ -278,11 +277,23 @@ public class PyStatementMover extends LineMover {
private static PsiElement getDestinationElement(@NotNull final PsiElement elementToMove, @NotNull final Document document,
int lineEndOffset, boolean down) {
- PsiElement destination = elementToMove.getContainingFile().findElementAt(lineEndOffset);
- if (destination == null) return null;
- if (destination instanceof PsiComment) return destination;
+ PsiElement destination = PyUtil.findPrevAtOffset(elementToMove.getContainingFile(), lineEndOffset, PsiWhiteSpace.class);
PsiElement sibling = down ? PsiTreeUtil.getNextSiblingOfType(elementToMove, PyStatement.class) :
- PsiTreeUtil.getPrevSiblingOfType(elementToMove, PyStatement.class);
+ PsiTreeUtil.getPrevSiblingOfType(elementToMove, PyStatement.class);
+ if (destination == null) {
+ if (elementToMove instanceof PyClass) {
+ destination = sibling;
+ }
+ else if (elementToMove instanceof PyFunction) {
+ if (!(sibling instanceof PyClass))
+ destination = sibling;
+ else destination = null;
+ }
+ else {
+ return null;
+ }
+ }
+ if (destination instanceof PsiComment) return destination;
if (elementToMove instanceof PyClass) {
destination = sibling;
}
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyWithFixer.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyWithFixer.java
index ec236d9242f5..b2b71cb9e0d2 100644
--- a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyWithFixer.java
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyWithFixer.java
@@ -18,6 +18,7 @@ package com.jetbrains.python.codeInsight.editorActions.smartEnter.fixers;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
+import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.codeInsight.editorActions.smartEnter.PySmartEnterProcessor;
@@ -27,8 +28,6 @@ import com.jetbrains.python.psi.PyWithItem;
import com.jetbrains.python.psi.PyWithStatement;
import org.jetbrains.annotations.NotNull;
-import static com.jetbrains.python.psi.PyUtil.sure;
-
/**
* @author Mikhail Golubev
*/
@@ -42,11 +41,10 @@ public class PyWithFixer extends PyFixer<PyWithStatement> {
final PsiElement colonToken = PyUtil.getFirstChildOfType(withStatement, PyTokenTypes.COLON);
final PsiElement withToken = PyUtil.getFirstChildOfType(withStatement, PyTokenTypes.WITH_KEYWORD);
final Document document = editor.getDocument();
- if (colonToken == null) {
- int insertAt = sure(withToken).getTextRange().getEndOffset();
+ if (colonToken == null && withToken != null) {
+ int insertAt = withToken.getTextRange().getEndOffset();
String textToInsert = ":";
- final PyWithItem[] withItems = withStatement.getWithItems();
- final PyWithItem lastItem = withItems.length != 0 ? withItems[withItems.length - 1] : null;
+ final PyWithItem lastItem = ArrayUtil.getLastElement(withStatement.getWithItems());
if (lastItem == null || lastItem.getExpression() == null) {
textToInsert = " :";
processor.registerUnresolvedError(insertAt + 1);
diff --git a/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java b/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java
index fa869bfca359..e9585a5cd952 100644
--- a/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java
+++ b/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java
@@ -156,7 +156,7 @@ public class PyIntegratedToolsConfigurable implements SearchableConfigurable, No
return new FacetConfigurationQuickFix() {
@Override
public void run(JComponent place) {
- final PyPackageManagerImpl.UI ui = new PyPackageManagerImpl.UI(myProject, sdk, new PyPackageManagerImpl.UI.Listener() {
+ final PyPackageManagerUI ui = new PyPackageManagerUI(myProject, sdk, new PyPackageManagerUI.Listener() {
@Override
public void started() {}
diff --git a/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java b/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java
index 9c95b1cffda8..5bf06782d376 100644
--- a/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java
+++ b/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java
@@ -31,10 +31,7 @@ import com.intellij.util.Function;
import com.intellij.xdebugger.frame.XValueChildrenList;
import com.jetbrains.python.console.parsing.PythonConsoleData;
import com.jetbrains.python.console.pydev.*;
-import com.jetbrains.python.debugger.PyDebugValue;
-import com.jetbrains.python.debugger.PyDebuggerException;
-import com.jetbrains.python.debugger.PyFrameAccessor;
-import com.jetbrains.python.debugger.PydevXmlUtils;
+import com.jetbrains.python.debugger.*;
import com.jetbrains.python.debugger.pydev.GetVariableCommand;
import com.jetbrains.python.debugger.pydev.ProtocolParser;
import org.apache.xmlrpc.WebServer;
@@ -528,6 +525,11 @@ public class PydevConsoleCommunication extends AbstractConsoleCommunication impl
}
}
+ @Nullable
+ @Override
+ public PyReferrersLoader getReferrersLoader() {
+ return null;
+ }
/**
* Request that pydevconsole connect (with pydevd) to the specified port
diff --git a/python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java b/python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java
index da1ac53113ef..5f361b30228a 100644
--- a/python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java
+++ b/python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java
@@ -23,7 +23,7 @@ import com.jetbrains.python.console.pydev.InterpreterResponse;
import com.jetbrains.python.console.pydev.PydevCompletionVariant;
import com.jetbrains.python.debugger.PyDebugProcess;
import com.jetbrains.python.debugger.PyDebuggerException;
-import com.jetbrains.python.debugger.pydev.ProcessDebugger;
+import com.jetbrains.python.debugger.pydev.PyDebugCallback;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@@ -63,8 +63,8 @@ public class PythonDebugConsoleCommunication extends AbstractConsoleCommunicatio
return false;
}
- protected void exec(final ConsoleCodeFragment command, final ProcessDebugger.DebugCallback<Pair<String, Boolean>> callback) {
- myDebugProcess.consoleExec(command.getText(), new ProcessDebugger.DebugCallback<String>() {
+ protected void exec(final ConsoleCodeFragment command, final PyDebugCallback<Pair<String, Boolean>> callback) {
+ myDebugProcess.consoleExec(command.getText(), new PyDebugCallback<String>() {
@Override
public void ok(String value) {
callback.ok(parseExecResponseString(value));
@@ -79,7 +79,7 @@ public class PythonDebugConsoleCommunication extends AbstractConsoleCommunicatio
public void execInterpreter(ConsoleCodeFragment code, final Function<InterpreterResponse, Object> callback) {
myExpression.append(code.getText());
- exec(new ConsoleCodeFragment(myExpression.toString(), false), new ProcessDebugger.DebugCallback<Pair<String, Boolean>>() {
+ exec(new ConsoleCodeFragment(myExpression.toString(), false), new PyDebugCallback<Pair<String, Boolean>>() {
@Override
public void ok(Pair<String, Boolean> executed) {
boolean more = executed.second;
diff --git a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java
index cdea41298a4e..ea492e43cf2d 100644
--- a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java
+++ b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java
@@ -17,7 +17,6 @@ package com.jetbrains.python.debugger;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
-import com.intellij.execution.console.DuplexConsoleView;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.process.ProcessListener;
@@ -88,6 +87,7 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
private boolean myWaitingForConnection = false;
private PyStackFrame myStackFrameBeforeResume;
private PyStackFrame myConsoleContextFrame = null;
+ private PyReferrersLoader myReferrersProvider;
public PyDebugProcess(final @NotNull XDebugSession session,
@NotNull final ServerSocket serverSocket,
@@ -489,7 +489,7 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
return myDebugger.evaluate(frame.getThreadId(), frame.getFrameId(), expression, execute, trimResult);
}
- public void consoleExec(String command, ProcessDebugger.DebugCallback<String> callback) {
+ public void consoleExec(String command, PyDebugCallback<String> callback) {
dropFrameCaches();
try {
final PyStackFrame frame = currentFrame();
@@ -539,6 +539,17 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
}
@Override
+ public void loadReferrers(PyReferringObjectsValue var, PyDebugCallback<XValueChildrenList> callback) {
+ try {
+ final PyStackFrame frame = currentFrame();
+ myDebugger.loadReferrers(frame.getThreadId(), frame.getFrameId(), var, callback);
+ }
+ catch (PyDebuggerException e) {
+ callback.error(e);
+ }
+ }
+
+ @Override
public void changeVariable(final PyDebugValue var, final String value) throws PyDebuggerException {
final PyStackFrame frame = currentFrame();
PyDebugValue newValue = myDebugger.changeVariable(frame.getThreadId(), frame.getFrameId(), var, value);
@@ -546,14 +557,23 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
}
@Nullable
+ @Override
+ public PyReferrersLoader getReferrersLoader() {
+ if (myReferrersProvider == null) {
+ myReferrersProvider = new PyReferrersLoader(this);
+ }
+ return myReferrersProvider;
+ }
+
+ @Nullable
public String loadSource(String path) {
return myDebugger.loadSource(path);
}
@Override
- public boolean isVariable(String name) {
+ public boolean canSaveToTemp(String name) {
final Project project = getSession().getProject();
- return PyDebugSupportUtils.isVariable(project, name);
+ return PyDebugSupportUtils.canSaveToTemp(project, name);
}
private PyStackFrame currentFrame() throws PyDebuggerException {
diff --git a/python/src/com/jetbrains/python/debugger/PyDebugSupportUtils.java b/python/src/com/jetbrains/python/debugger/PyDebugSupportUtils.java
index d29259d559c6..d25596a13f2a 100644
--- a/python/src/com/jetbrains/python/debugger/PyDebugSupportUtils.java
+++ b/python/src/com/jetbrains/python/debugger/PyDebugSupportUtils.java
@@ -80,25 +80,29 @@ public class PyDebugSupportUtils {
element instanceof PyNamedParameter;
}
- // is expression a variable reference
+ // is expression a variable reference and can be evaluated
// todo: use patterns (?)
- public static boolean isVariable(final Project project, final String expression) {
+ public static boolean canSaveToTemp(final Project project, final String expression) {
return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
public Boolean compute() {
final PsiFile file = PyElementGenerator.getInstance(project).createDummyFile(LanguageLevel.getDefault(), expression);
final PsiElement root = file.getFirstChild();
- return root instanceof PyExpressionStatement &&
- root.getFirstChild() instanceof PyReferenceExpression &&
- root.getFirstChild() == root.getLastChild() &&
- root.getFirstChild().getFirstChild() != null &&
- root.getFirstChild().getFirstChild().getNode().getElementType() == PyTokenTypes.IDENTIFIER &&
- root.getFirstChild().getFirstChild() == root.getFirstChild().getLastChild() &&
- root.getFirstChild().getFirstChild().getFirstChild() == null;
+ return !isVariable(root) && (root instanceof PyExpressionStatement);
}
});
}
+ private static Boolean isVariable(PsiElement root) {
+ return root instanceof PyExpressionStatement &&
+ root.getFirstChild() instanceof PyReferenceExpression &&
+ root.getFirstChild() == root.getLastChild() &&
+ root.getFirstChild().getFirstChild() != null &&
+ root.getFirstChild().getFirstChild().getNode().getElementType() == PyTokenTypes.IDENTIFIER &&
+ root.getFirstChild().getFirstChild() == root.getFirstChild().getLastChild() &&
+ root.getFirstChild().getFirstChild().getFirstChild() == null;
+ }
+
@Nullable
private static String getLineText(@NotNull Document document, int line) {
if (line > 0 && line < document.getLineCount()) {
diff --git a/python/src/com/jetbrains/python/debugger/PyExceptionBreakpointProperties.java b/python/src/com/jetbrains/python/debugger/PyExceptionBreakpointProperties.java
index d6bfd13f488c..93f9d89e386c 100644
--- a/python/src/com/jetbrains/python/debugger/PyExceptionBreakpointProperties.java
+++ b/python/src/com/jetbrains/python/debugger/PyExceptionBreakpointProperties.java
@@ -39,6 +39,7 @@ public class PyExceptionBreakpointProperties extends ExceptionBreakpointProperti
public PyExceptionBreakpointProperties(@NotNull final String exception) {
myException = exception;
+ myNotifyOnTerminate = true;
}
@Override
@@ -78,6 +79,10 @@ public class PyExceptionBreakpointProperties extends ExceptionBreakpointProperti
myNotifyOnlyOnFirst = notifyOnlyOnFirst;
}
+ public String getException() {
+ return "python-" + myException;
+ }
+
@Override
public ExceptionBreakpointCommand createAddCommand(RemoteDebugger debugger) {
return ExceptionBreakpointCommand.addExceptionBreakpointCommand(debugger, getException(),
diff --git a/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java b/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java
index 913ed65f1e53..73a0cb604091 100644
--- a/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java
+++ b/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java
@@ -81,7 +81,16 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i
@NonNls private static final String EPYDOC_PREFIX = "@";
// provides ctrl+hover info
- public String getQuickNavigateInfo(final PsiElement element, PsiElement originalElement) {
+ @Override
+ @Nullable
+ public String getQuickNavigateInfo(final PsiElement element, final PsiElement originalElement) {
+ for (final PythonDocumentationQuickInfoProvider point : PythonDocumentationQuickInfoProvider.EP_NAME.getExtensions()) {
+ String info = point.getQuickInfo(originalElement);
+ if (info != null) {
+ return info;
+ }
+ }
+
if (element instanceof PyFunction) {
PyFunction func = (PyFunction)element;
StringBuilder cat = new StringBuilder();
@@ -637,8 +646,9 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i
String raiseTarget = visitor.myRaiseTarget.getText();
if (visitor.myRaiseTarget instanceof PyCallExpression) {
final PyExpression callee = ((PyCallExpression)visitor.myRaiseTarget).getCallee();
- if (callee != null)
+ if (callee != null) {
raiseTarget = callee.getText();
+ }
}
builder.append(" ").append(raiseTarget);
}
diff --git a/python/src/com/jetbrains/python/findUsages/PyFunctionFindUsagesHandler.java b/python/src/com/jetbrains/python/findUsages/PyFunctionFindUsagesHandler.java
index 8c96916c9d80..fa7cf1b429c4 100644
--- a/python/src/com/jetbrains/python/findUsages/PyFunctionFindUsagesHandler.java
+++ b/python/src/com/jetbrains/python/findUsages/PyFunctionFindUsagesHandler.java
@@ -27,12 +27,12 @@ import java.util.List;
public class PyFunctionFindUsagesHandler extends FindUsagesHandler {
private final List<PsiElement> myAllElements;
- protected PyFunctionFindUsagesHandler(@NotNull PsiElement psiElement) {
+ public PyFunctionFindUsagesHandler(@NotNull PsiElement psiElement) {
super(psiElement);
myAllElements = null;
}
- protected PyFunctionFindUsagesHandler(@NotNull PsiElement psiElement, List<PsiElement> allElements) {
+ public PyFunctionFindUsagesHandler(@NotNull PsiElement psiElement, List<PsiElement> allElements) {
super(psiElement);
myAllElements = allElements;
}
diff --git a/python/src/com/jetbrains/python/hierarchy/PyTypeHierarchyNodeDescriptor.java b/python/src/com/jetbrains/python/hierarchy/PyHierarchyNodeDescriptor.java
index ea8c250b2e4f..c9720fb01af3 100644
--- a/python/src/com/jetbrains/python/hierarchy/PyTypeHierarchyNodeDescriptor.java
+++ b/python/src/com/jetbrains/python/hierarchy/PyHierarchyNodeDescriptor.java
@@ -19,15 +19,14 @@ import com.intellij.ide.IdeBundle;
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
import com.intellij.ide.util.treeView.NodeDescriptor;
import com.intellij.navigation.ItemPresentation;
-import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.roots.ui.util.CompositeAppearance;
import com.intellij.openapi.util.Comparing;
import com.intellij.psi.NavigatablePsiElement;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyFunction;
import org.jetbrains.annotations.NotNull;
-
-import java.awt.*;
+import org.jetbrains.annotations.Nullable;
/**
* Created by IntelliJ IDEA.
@@ -35,14 +34,14 @@ import java.awt.*;
* Date: Jul 31, 2009
* Time: 6:26:37 PM
*/
-public class PyTypeHierarchyNodeDescriptor extends HierarchyNodeDescriptor {
-
- public PyTypeHierarchyNodeDescriptor(final NodeDescriptor parentDescriptor, @NotNull final PsiElement element, final boolean isBase) {
+public class PyHierarchyNodeDescriptor extends HierarchyNodeDescriptor {
+ public PyHierarchyNodeDescriptor(final NodeDescriptor parentDescriptor, @NotNull final PsiElement element, final boolean isBase) {
super(element.getProject(), parentDescriptor, element, isBase);
}
- public PyClass getClassElement() {
- return (PyClass)myElement;
+ @Nullable
+ public PsiElement getPsiElement() {
+ return myElement;
}
public boolean isValid() {
@@ -55,10 +54,6 @@ public class PyTypeHierarchyNodeDescriptor extends HierarchyNodeDescriptor {
final CompositeAppearance oldText = myHighlightedText;
myHighlightedText = new CompositeAppearance();
- TextAttributes classNameAttributes = null;
- if (myColor != null) {
- classNameAttributes = new TextAttributes(myColor, null, null, null, Font.PLAIN);
- }
NavigatablePsiElement element = (NavigatablePsiElement)myElement;
if (element == null) {
@@ -71,10 +66,14 @@ public class PyTypeHierarchyNodeDescriptor extends HierarchyNodeDescriptor {
final ItemPresentation presentation = element.getPresentation();
if (presentation != null) {
- final PyClass cl = getClassElement();
- myHighlightedText.getEnding().addText(cl.getName(), classNameAttributes);
- myHighlightedText.getEnding()
- .addText(" (" + cl.getContainingFile().getName() + ")", HierarchyNodeDescriptor.getPackageNameAttributes());
+ if (element instanceof PyFunction) {
+ final PyClass cls = ((PyFunction)element).getContainingClass();
+ if (cls != null) {
+ myHighlightedText.getEnding().addText(cls.getName() + ".");
+ }
+ }
+ myHighlightedText.getEnding().addText(presentation.getPresentableText());
+ myHighlightedText.getEnding().addText(" " + presentation.getLocationString(), HierarchyNodeDescriptor.getPackageNameAttributes());
}
myName = myHighlightedText.getText();
diff --git a/python/src/com/jetbrains/python/hierarchy/PyTypeHierarchyBrowser.java b/python/src/com/jetbrains/python/hierarchy/PyTypeHierarchyBrowser.java
index 643440571618..817d462044fe 100644
--- a/python/src/com/jetbrains/python/hierarchy/PyTypeHierarchyBrowser.java
+++ b/python/src/com/jetbrains/python/hierarchy/PyTypeHierarchyBrowser.java
@@ -52,10 +52,10 @@ public class PyTypeHierarchyBrowser extends TypeHierarchyBrowserBase {
@Nullable
protected PsiElement getElementFromDescriptor(@NotNull HierarchyNodeDescriptor descriptor) {
- if (!(descriptor instanceof PyTypeHierarchyNodeDescriptor)) {
+ if (!(descriptor instanceof PyHierarchyNodeDescriptor)) {
return null;
}
- return ((PyTypeHierarchyNodeDescriptor)descriptor).getClassElement();
+ return ((PyHierarchyNodeDescriptor)descriptor).getPsiElement();
}
protected void createTrees(@NotNull Map<String, JTree> trees) {
diff --git a/python/src/com/jetbrains/python/hierarchy/call/PyCallHierarchyBrowser.java b/python/src/com/jetbrains/python/hierarchy/call/PyCallHierarchyBrowser.java
new file mode 100644
index 000000000000..c5224e617ec9
--- /dev/null
+++ b/python/src/com/jetbrains/python/hierarchy/call/PyCallHierarchyBrowser.java
@@ -0,0 +1,101 @@
+/*
+ * 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.hierarchy.call;
+
+import com.intellij.ide.hierarchy.CallHierarchyBrowserBase;
+import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
+import com.intellij.ide.hierarchy.HierarchyTreeStructure;
+import com.intellij.ide.util.treeView.NodeDescriptor;
+import com.intellij.openapi.actionSystem.*;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.psi.PsiElement;
+import com.intellij.ui.PopupHandler;
+import com.jetbrains.python.hierarchy.PyHierarchyNodeDescriptor;
+import com.jetbrains.python.hierarchy.PyHierarchyUtils;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyFile;
+import com.jetbrains.python.psi.PyFunction;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.Comparator;
+import java.util.Map;
+
+/**
+ * @author novokrest
+ */
+public class PyCallHierarchyBrowser extends CallHierarchyBrowserBase {
+ private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.hierarchy.call.PyCallHierarchyBrowser");
+ private static final String GROUP_PY_CALL_HIERARCHY_POPUP = "PyCallHierarchyPopupMenu";
+
+ public PyCallHierarchyBrowser(PsiElement function) {
+ super(function.getProject(), function);
+ }
+
+ @Nullable
+ @Override
+ protected PsiElement getElementFromDescriptor(@NotNull HierarchyNodeDescriptor descriptor) {
+ if (descriptor instanceof PyHierarchyNodeDescriptor) {
+ PyHierarchyNodeDescriptor pyDescriptor = (PyHierarchyNodeDescriptor)descriptor;
+ return pyDescriptor.getPsiElement();
+ }
+ return null;
+ }
+
+ @Override
+ protected void createTrees(@NotNull Map<String, JTree> type2TreeMap) {
+ final ActionGroup group = (ActionGroup)ActionManager.getInstance().getAction(GROUP_PY_CALL_HIERARCHY_POPUP);
+
+ final JTree callerTree = createHierarchyTree(group);
+ final JTree calleeTree = createHierarchyTree(group);
+
+ type2TreeMap.put(CALLER_TYPE, callerTree);
+ type2TreeMap.put(CALLEE_TYPE, calleeTree);
+ }
+
+ private JTree createHierarchyTree(ActionGroup group) {
+ final JTree tree = createTree(false);
+ PopupHandler.installPopupHandler(tree, group, ActionPlaces.CALL_HIERARCHY_VIEW_POPUP, ActionManager.getInstance());
+ return tree;
+ }
+
+ @Override
+ protected boolean isApplicableElement(@NotNull PsiElement element) {
+ return element instanceof PyFunction || element instanceof PyClass || element instanceof PyFile;
+ }
+
+ @Nullable
+ @Override
+ protected HierarchyTreeStructure createHierarchyTreeStructure(@NotNull String typeName, @NotNull PsiElement psiElement) {
+ if (CALLER_TYPE.equals(typeName)) {
+ return new PyCallerFunctionTreeStructure(myProject, psiElement, getCurrentScopeType());
+ }
+ else if (CALLEE_TYPE.equals(typeName)) {
+ return new PyCalleeFunctionTreeStructure(myProject, psiElement, getCurrentScopeType());
+ }
+ else {
+ LOG.error("unexpected type: " + typeName);
+ return null;
+ }
+ }
+
+ @Nullable
+ @Override
+ protected Comparator<NodeDescriptor> getComparator() {
+ return PyHierarchyUtils.getComparator(myProject);
+ }
+}
diff --git a/python/src/com/jetbrains/python/hierarchy/call/PyCallHierarchyProvider.java b/python/src/com/jetbrains/python/hierarchy/call/PyCallHierarchyProvider.java
new file mode 100644
index 000000000000..67c0d6ddfa0b
--- /dev/null
+++ b/python/src/com/jetbrains/python/hierarchy/call/PyCallHierarchyProvider.java
@@ -0,0 +1,76 @@
+/*
+ * 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.hierarchy.call;
+
+import com.intellij.codeInsight.TargetElementUtilBase;
+import com.intellij.ide.hierarchy.CallHierarchyBrowserBase;
+import com.intellij.ide.hierarchy.HierarchyBrowser;
+import com.intellij.ide.hierarchy.HierarchyProvider;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyFile;
+import com.jetbrains.python.psi.PyFunction;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author novokrest
+ */
+public class PyCallHierarchyProvider implements HierarchyProvider {
+ @Nullable
+ @Override
+ public PsiElement getTarget(@NotNull DataContext dataContext) {
+ Project project = CommonDataKeys.PROJECT.getData(dataContext);
+ if (project == null) return null;
+
+ PsiElement element = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
+ if (element == null) {
+ Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
+ if (editor != null) {
+ PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
+ if (file == null) return null;
+
+ element = TargetElementUtilBase.findTargetElement(editor, TargetElementUtilBase.ELEMENT_NAME_ACCEPTED |
+ TargetElementUtilBase.REFERENCED_ELEMENT_ACCEPTED |
+ TargetElementUtilBase.LOOKUP_ITEM_ACCEPTED);
+ if (element instanceof PyFunction || element instanceof PyClass || element instanceof PyFile) {
+ return element;
+ }
+
+ element = file.findElementAt(editor.getCaretModel().getOffset());
+ }
+ }
+ return PsiTreeUtil.getNonStrictParentOfType(element, PyFunction.class, PyClass.class, PyFile.class);
+ }
+
+ @NotNull
+ @Override
+ public HierarchyBrowser createHierarchyBrowser(PsiElement target) {
+ return new PyCallHierarchyBrowser(target);
+ }
+
+ @Override
+ public void browserActivated(@NotNull HierarchyBrowser hierarchyBrowser) {
+ ((PyCallHierarchyBrowser)hierarchyBrowser).changeView(CallHierarchyBrowserBase.CALLER_TYPE);
+ }
+}
diff --git a/python/src/com/jetbrains/python/hierarchy/call/PyCallHierarchyTreeStructureBase.java b/python/src/com/jetbrains/python/hierarchy/call/PyCallHierarchyTreeStructureBase.java
new file mode 100644
index 000000000000..c5d60138299c
--- /dev/null
+++ b/python/src/com/jetbrains/python/hierarchy/call/PyCallHierarchyTreeStructureBase.java
@@ -0,0 +1,85 @@
+/*
+ * 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.hierarchy.call;
+
+import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
+import com.intellij.ide.hierarchy.HierarchyTreeStructure;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.intellij.util.ArrayUtil;
+import com.jetbrains.python.hierarchy.PyHierarchyNodeDescriptor;
+import com.jetbrains.python.psi.PyClass;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.psi.PyFile;
+import com.jetbrains.python.psi.PyFunction;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * @author novokrest
+ */
+public abstract class PyCallHierarchyTreeStructureBase extends HierarchyTreeStructure {
+ private final String myScopeType;
+
+ public PyCallHierarchyTreeStructureBase(Project project, PsiElement element, String currentScopeType) {
+ super(project, new PyHierarchyNodeDescriptor(null, element, true));
+ myScopeType = currentScopeType;
+ }
+
+ @NotNull
+ protected abstract List<PsiElement> getChildren(@NotNull PyElement element);
+
+ @NotNull
+ @Override
+ protected Object[] buildChildren(@NotNull HierarchyNodeDescriptor descriptor) {
+ final List<PyHierarchyNodeDescriptor> descriptors = new ArrayList<PyHierarchyNodeDescriptor>();
+ if (descriptor instanceof PyHierarchyNodeDescriptor) {
+ final PyHierarchyNodeDescriptor pyDescriptor = (PyHierarchyNodeDescriptor)descriptor;
+ final PsiElement element = pyDescriptor.getPsiElement();
+ final boolean isCallable = element instanceof PyFunction || element instanceof PyClass || element instanceof PyFile;
+ HierarchyNodeDescriptor nodeDescriptor = getBaseDescriptor();
+ if (!(element instanceof PyElement) || !isCallable || nodeDescriptor == null) {
+ return ArrayUtil.EMPTY_OBJECT_ARRAY;
+ }
+
+ final List<PsiElement> children = getChildren((PyElement)element);
+
+ final HashMap<PsiElement, PyHierarchyNodeDescriptor> callerToDescriptorMap = new HashMap<PsiElement, PyHierarchyNodeDescriptor>();
+ PsiElement baseClass = element instanceof PyFunction ? ((PyFunction)element).getContainingClass() : null;
+
+ for (PsiElement caller : children) {
+ if (isInScope(baseClass, caller, myScopeType)) {
+ PyHierarchyNodeDescriptor callerDescriptor = callerToDescriptorMap.get(caller);
+ if (callerDescriptor == null) {
+ callerDescriptor = new PyHierarchyNodeDescriptor(descriptor, caller, false);
+ callerToDescriptorMap.put(caller, callerDescriptor);
+ descriptors.add(callerDescriptor);
+ }
+ }
+ }
+
+ }
+ return ArrayUtil.toObjectArray(descriptors);
+ }
+
+ @Override
+ public boolean isAlwaysShowPlus() {
+ return true;
+ }
+}
diff --git a/python/src/com/jetbrains/python/hierarchy/call/PyCalleeFunctionTreeStructure.java b/python/src/com/jetbrains/python/hierarchy/call/PyCalleeFunctionTreeStructure.java
new file mode 100644
index 000000000000..b0465083ef05
--- /dev/null
+++ b/python/src/com/jetbrains/python/hierarchy/call/PyCalleeFunctionTreeStructure.java
@@ -0,0 +1,42 @@
+/*
+ * 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.hierarchy.call;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.jetbrains.python.psi.PyElement;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author novokrest
+ */
+public class PyCalleeFunctionTreeStructure extends PyCallHierarchyTreeStructureBase {
+ public PyCalleeFunctionTreeStructure(Project project, PsiElement element, String currentScopeType) {
+ super(project, element, currentScopeType);
+ }
+
+ @NotNull
+ @Override
+ protected List<PsiElement> getChildren(@NotNull PyElement element) {
+ final List<PsiElement> callees = new ArrayList<PsiElement>();
+ // TODO: Add callees from the dynamic call data manager
+ callees.addAll(PyStaticCallHierarchyUtil.getCallees(element));
+ return callees;
+ }
+}
diff --git a/python/src/com/jetbrains/python/hierarchy/call/PyCallerFunctionTreeStructure.java b/python/src/com/jetbrains/python/hierarchy/call/PyCallerFunctionTreeStructure.java
new file mode 100644
index 000000000000..d45e79ce8d95
--- /dev/null
+++ b/python/src/com/jetbrains/python/hierarchy/call/PyCallerFunctionTreeStructure.java
@@ -0,0 +1,42 @@
+/*
+ * 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.hierarchy.call;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.jetbrains.python.psi.PyElement;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author novokrest
+ */
+public class PyCallerFunctionTreeStructure extends PyCallHierarchyTreeStructureBase {
+ public PyCallerFunctionTreeStructure(Project project, PsiElement element, String currentScopeType) {
+ super(project, element, currentScopeType);
+ }
+
+ @NotNull
+ @Override
+ protected List<PsiElement> getChildren(@NotNull PyElement element) {
+ final List<PsiElement> callers = new ArrayList<PsiElement>();
+ // TODO: Add callers from the dynamic call data manager
+ callers.addAll(PyStaticCallHierarchyUtil.getCallers(element));
+ return callers;
+ }
+}
diff --git a/python/src/com/jetbrains/python/hierarchy/call/PyStaticCallHierarchyUtil.java b/python/src/com/jetbrains/python/hierarchy/call/PyStaticCallHierarchyUtil.java
new file mode 100644
index 000000000000..5657dcb589e9
--- /dev/null
+++ b/python/src/com/jetbrains/python/hierarchy/call/PyStaticCallHierarchyUtil.java
@@ -0,0 +1,163 @@
+/*
+ * 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.hierarchy.call;
+
+import com.google.common.collect.Lists;
+import com.intellij.find.findUsages.FindUsagesHandler;
+import com.intellij.find.findUsages.FindUsagesOptions;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.usageView.UsageInfo;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.CommonProcessors;
+import com.jetbrains.python.PyNames;
+import com.jetbrains.python.findUsages.PyClassFindUsagesHandler;
+import com.jetbrains.python.findUsages.PyFunctionFindUsagesHandler;
+import com.jetbrains.python.psi.*;
+import com.jetbrains.python.psi.impl.PyBuiltinCache;
+import com.jetbrains.python.psi.resolve.PyResolveContext;
+import com.jetbrains.python.psi.search.PySuperMethodsSearch;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @author novokrest
+ */
+public class PyStaticCallHierarchyUtil {
+ public static Collection<PsiElement> getCallees(@NotNull PyElement element) {
+ final List<PsiElement> callees = Lists.newArrayList();
+
+ final PyRecursiveElementVisitor visitor = new PyRecursiveElementVisitor() {
+ @Override
+ public void visitPyParameterList(PyParameterList node) {
+ }
+
+ @Override
+ public void visitPyLambdaExpression(PyLambdaExpression node) {
+ }
+
+ @Override
+ public void visitPyFunction(PyFunction innerFunction) {
+ for (PyParameter parameter : innerFunction.getParameterList().getParameters()) {
+ PsiElement defaultValue = parameter.getDefaultValue();
+ if (defaultValue != null) {
+ defaultValue.accept(this);
+ }
+ }
+ }
+
+ @Override
+ public void visitPyCallExpression(PyCallExpression callExpression) {
+ super.visitPyCallExpression(callExpression);
+ PsiElement calleeFunction = callExpression.resolveCalleeFunction(PyResolveContext.defaultContext());
+ if (calleeFunction instanceof PyFunction) {
+ callees.add(calleeFunction);
+ }
+ }
+ };
+
+ visitor.visitElement(element);
+
+ return callees;
+ }
+
+ public static Collection<PsiElement> getCallers(@NotNull PyElement pyElement) {
+ final List<PsiElement> callers = Lists.newArrayList();
+ final Collection<UsageInfo> usages = findUsages(pyElement);
+
+ for (UsageInfo usage : usages) {
+ PsiElement element = usage.getElement();
+ if (element == null) {
+ continue;
+ }
+
+ element = element.getParent();
+ while (element instanceof PyParenthesizedExpression) {
+ element = element.getParent();
+ }
+
+ if (element instanceof PyCallExpression) {
+ PsiElement caller = PsiTreeUtil.getParentOfType(element, PyParameterList.class, PyFunction.class);
+ if (caller instanceof PyFunction) {
+ callers.add(caller);
+ }
+ else if (caller instanceof PyParameterList) {
+ PsiElement innerFunction = PsiTreeUtil.getParentOfType(caller, PyFunction.class);
+ PsiElement outerFunction = PsiTreeUtil.getParentOfType(innerFunction, PyFunction.class);
+ if (innerFunction != null && outerFunction != null) {
+ callers.add(outerFunction);
+ }
+ }
+ }
+ }
+
+ return callers;
+ }
+
+ private static Collection<UsageInfo> findUsages(@NotNull final PsiElement element) {
+ final FindUsagesHandler handler = createFindUsageHandler(element);
+ if (handler == null) {
+ return Lists.newArrayList();
+ }
+ final CommonProcessors.CollectProcessor<UsageInfo> processor = new CommonProcessors.CollectProcessor<UsageInfo>();
+ final PsiElement[] psiElements = ArrayUtil.mergeArrays(handler.getPrimaryElements(), handler.getSecondaryElements());
+ final FindUsagesOptions options = handler.getFindUsagesOptions(null);
+ for (PsiElement psiElement : psiElements) {
+ handler.processElementUsages(psiElement, processor, options);
+ }
+ return processor.getResults();
+ }
+
+ /**
+ * @see {@link com.jetbrains.python.findUsages.PyFindUsagesHandlerFactory#createFindUsagesHandler(com.intellij.psi.PsiElement, boolean) createFindUsagesHandler}
+ */
+ @Nullable
+ private static FindUsagesHandler createFindUsageHandler(@NotNull final PsiElement element) {
+ if (element instanceof PyFunction) {
+ final Collection<PsiElement> superMethods = PySuperMethodsSearch.search((PyFunction)element, true).findAll();
+ if (superMethods.size() > 0) {
+ final PsiElement next = superMethods.iterator().next();
+ if (next instanceof PyFunction && !isInObject((PyFunction)next)) {
+ List<PsiElement> allMethods = Lists.newArrayList();
+ allMethods.add(element);
+ allMethods.addAll(superMethods);
+
+ return new PyFunctionFindUsagesHandler(element, allMethods);
+ }
+ }
+ return new PyFunctionFindUsagesHandler(element);
+ }
+ if (element instanceof PyClass) {
+ return new PyClassFindUsagesHandler((PyClass)element);
+ }
+ return null;
+ }
+
+ /**
+ * @see {@link com.jetbrains.python.findUsages.PyFindUsagesHandlerFactory#isInObject(com.jetbrains.python.psi.PyFunction) isInObject}
+ */
+ private static boolean isInObject(PyFunction fun) {
+ final PyClass containingClass = fun.getContainingClass();
+ if (containingClass == null) {
+ return false;
+ }
+ return (PyNames.FAKE_OLD_BASE.equals(containingClass.getName()) ||
+ (PyNames.OBJECT.equals(containingClass.getName()) && PyBuiltinCache.getInstance(fun).isBuiltin(containingClass)));
+ }
+}
diff --git a/python/src/com/jetbrains/python/hierarchy/treestructures/PySubTypesHierarchyTreeStructure.java b/python/src/com/jetbrains/python/hierarchy/treestructures/PySubTypesHierarchyTreeStructure.java
index 7b43fe7329cf..f4287b28bd6c 100644
--- a/python/src/com/jetbrains/python/hierarchy/treestructures/PySubTypesHierarchyTreeStructure.java
+++ b/python/src/com/jetbrains/python/hierarchy/treestructures/PySubTypesHierarchyTreeStructure.java
@@ -18,9 +18,10 @@ package com.jetbrains.python.hierarchy.treestructures;
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
import com.intellij.ide.hierarchy.HierarchyTreeStructure;
import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Query;
-import com.jetbrains.python.hierarchy.PyTypeHierarchyNodeDescriptor;
+import com.jetbrains.python.hierarchy.PyHierarchyNodeDescriptor;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.search.PyClassInheritorsSearch;
import org.jetbrains.annotations.NotNull;
@@ -40,17 +41,20 @@ public class PySubTypesHierarchyTreeStructure extends HierarchyTreeStructure {
}
public PySubTypesHierarchyTreeStructure(@NotNull final PyClass cl) {
- super(cl.getProject(), new PyTypeHierarchyNodeDescriptor(null, cl, true));
+ super(cl.getProject(), new PyHierarchyNodeDescriptor(null, cl, true));
}
@NotNull
protected Object[] buildChildren(@NotNull HierarchyNodeDescriptor descriptor) {
- final PyClass classElement = ((PyTypeHierarchyNodeDescriptor)descriptor).getClassElement();
- Query<PyClass> subClasses = PyClassInheritorsSearch.search(classElement, false);
+ final List<PyHierarchyNodeDescriptor> res = new ArrayList<PyHierarchyNodeDescriptor>();
+ final PsiElement element = ((PyHierarchyNodeDescriptor)descriptor).getPsiElement();
+ if (element instanceof PyClass) {
+ final PyClass cls = (PyClass)element;
+ Query<PyClass> subClasses = PyClassInheritorsSearch.search(cls, false);
+ for (PyClass subClass : subClasses) {
+ res.add(new PyHierarchyNodeDescriptor(descriptor, subClass, false));
+ }
- List<PyTypeHierarchyNodeDescriptor> res = new ArrayList<PyTypeHierarchyNodeDescriptor>();
- for (PyClass cl : subClasses) {
- res.add(new PyTypeHierarchyNodeDescriptor(descriptor, cl, false));
}
return ArrayUtil.toObjectArray(res);
diff --git a/python/src/com/jetbrains/python/hierarchy/treestructures/PySuperTypesHierarchyTreeStructure.java b/python/src/com/jetbrains/python/hierarchy/treestructures/PySuperTypesHierarchyTreeStructure.java
index 38b4c6ee7f76..8087767c61d8 100644
--- a/python/src/com/jetbrains/python/hierarchy/treestructures/PySuperTypesHierarchyTreeStructure.java
+++ b/python/src/com/jetbrains/python/hierarchy/treestructures/PySuperTypesHierarchyTreeStructure.java
@@ -17,8 +17,8 @@ package com.jetbrains.python.hierarchy.treestructures;
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
import com.intellij.ide.hierarchy.HierarchyTreeStructure;
-import com.intellij.openapi.project.Project;
-import com.jetbrains.python.hierarchy.PyTypeHierarchyNodeDescriptor;
+import com.intellij.psi.PsiElement;
+import com.jetbrains.python.hierarchy.PyHierarchyNodeDescriptor;
import com.jetbrains.python.psi.PyClass;
import org.jetbrains.annotations.NotNull;
@@ -32,20 +32,23 @@ import java.util.List;
* Time: 7:04:07 PM
*/
public class PySuperTypesHierarchyTreeStructure extends HierarchyTreeStructure {
- protected PySuperTypesHierarchyTreeStructure(final Project project, final HierarchyNodeDescriptor baseDescriptor) {
- super(project, baseDescriptor);
- }
-
public PySuperTypesHierarchyTreeStructure(@NotNull final PyClass cl) {
- super(cl.getProject(), new PyTypeHierarchyNodeDescriptor(null, cl, true));
+ super(cl.getProject(), new PyHierarchyNodeDescriptor(null, cl, true));
}
@NotNull
protected Object[] buildChildren(@NotNull HierarchyNodeDescriptor descriptor) {
- final PyClass[] superClasses = ((PyTypeHierarchyNodeDescriptor)descriptor).getClassElement().getSuperClasses();
- List<PyTypeHierarchyNodeDescriptor> res = new ArrayList<PyTypeHierarchyNodeDescriptor>();
- for (PyClass superClass : superClasses) {
- res.add(new PyTypeHierarchyNodeDescriptor(descriptor, superClass, false));
+ final List<PyHierarchyNodeDescriptor> res = new ArrayList<PyHierarchyNodeDescriptor>();
+ if (descriptor instanceof PyHierarchyNodeDescriptor) {
+ final PyHierarchyNodeDescriptor pyDescriptor = (PyHierarchyNodeDescriptor)descriptor;
+ final PsiElement element = pyDescriptor.getPsiElement();
+ if (element instanceof PyClass) {
+ final PyClass cls = (PyClass)element;
+ final PyClass[] superClasses = cls.getSuperClasses();
+ for (PyClass superClass : superClasses) {
+ res.add(new PyHierarchyNodeDescriptor(descriptor, superClass, false));
+ }
+ }
}
return res.toArray();
}
diff --git a/python/src/com/jetbrains/python/hierarchy/treestructures/PyTypeHierarchyTreeStructure.java b/python/src/com/jetbrains/python/hierarchy/treestructures/PyTypeHierarchyTreeStructure.java
index 6707ac9b7e14..0eb505f4857b 100644
--- a/python/src/com/jetbrains/python/hierarchy/treestructures/PyTypeHierarchyTreeStructure.java
+++ b/python/src/com/jetbrains/python/hierarchy/treestructures/PyTypeHierarchyTreeStructure.java
@@ -16,8 +16,7 @@
package com.jetbrains.python.hierarchy.treestructures;
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
-import com.intellij.openapi.project.Project;
-import com.jetbrains.python.hierarchy.PyTypeHierarchyNodeDescriptor;
+import com.jetbrains.python.hierarchy.PyHierarchyNodeDescriptor;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyUtil;
import org.jetbrains.annotations.NotNull;
@@ -28,30 +27,26 @@ import java.util.List;
* @author Alexey.Ivanov
*/
public class PyTypeHierarchyTreeStructure extends PySubTypesHierarchyTreeStructure {
- private static PyTypeHierarchyNodeDescriptor buildHierarchyElement(@NotNull final PyClass cl) {
- PyTypeHierarchyNodeDescriptor descriptor = null;
+ public PyTypeHierarchyTreeStructure(@NotNull final PyClass cl) {
+ super(cl.getProject(), buildHierarchyElement(cl));
+ setBaseElement(myBaseDescriptor);
+ }
+
+ private static PyHierarchyNodeDescriptor buildHierarchyElement(@NotNull final PyClass cl) {
+ PyHierarchyNodeDescriptor descriptor = null;
List<PyClass> superClasses = PyUtil.getAllSuperClasses(cl);
for (int i = superClasses.size() - 1; i >= 0; --i) {
final PyClass superClass = superClasses.get(i);
- final PyTypeHierarchyNodeDescriptor newDescriptor = new PyTypeHierarchyNodeDescriptor(descriptor, superClass, false);
+ final PyHierarchyNodeDescriptor newDescriptor = new PyHierarchyNodeDescriptor(descriptor, superClass, false);
if (descriptor != null) {
- descriptor.setCachedChildren(new PyTypeHierarchyNodeDescriptor[]{newDescriptor});
+ descriptor.setCachedChildren(new PyHierarchyNodeDescriptor[]{newDescriptor});
}
descriptor = newDescriptor;
}
- final PyTypeHierarchyNodeDescriptor newDescriptor = new PyTypeHierarchyNodeDescriptor(descriptor, cl, true);
+ final PyHierarchyNodeDescriptor newDescriptor = new PyHierarchyNodeDescriptor(descriptor, cl, true);
if (descriptor != null) {
descriptor.setCachedChildren(new HierarchyNodeDescriptor[]{newDescriptor});
}
return newDescriptor;
}
-
- protected PyTypeHierarchyTreeStructure(final Project project, final HierarchyNodeDescriptor baseDescriptor) {
- super(project, baseDescriptor);
- }
-
- public PyTypeHierarchyTreeStructure(@NotNull final PyClass cl) {
- super(cl.getProject(), buildHierarchyElement(cl));
- setBaseElement(myBaseDescriptor);
- }
}
diff --git a/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java b/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java
index ec3761154f28..0b73adc3fa4c 100644
--- a/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java
@@ -27,6 +27,7 @@ import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.JDOMExternalizableStringList;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
@@ -42,6 +43,7 @@ import com.jetbrains.python.packaging.*;
import com.jetbrains.python.packaging.ui.PyChooseRequirementsDialog;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyPsiUtils;
+import com.jetbrains.python.sdk.PySdkUtil;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -111,9 +113,7 @@ public class PyPackageRequirementsInspection extends PyInspection {
unsatisfiedNames.add(req.getName());
}
final List<LocalQuickFix> quickFixes = new ArrayList<LocalQuickFix>();
- if (PyPackageManager.getInstance(sdk).hasPip()) {
- quickFixes.add(new PyInstallRequirementsFix(null, module, sdk, unsatisfied));
- }
+ quickFixes.add(new PyInstallRequirementsFix(null, module, sdk, unsatisfied));
quickFixes.add(new IgnoreRequirementFix(unsatisfiedNames));
registerProblem(node, msg,
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, null,
@@ -160,15 +160,16 @@ public class PyPackageRequirementsInspection extends PyInspection {
return;
}
}
- if (PyPackageManagerImpl.PACKAGE_SETUPTOOLS.equals(packageName)) {
+ if (PyPackageManager.PACKAGE_SETUPTOOLS.equals(packageName)) {
return;
}
final Module module = ModuleUtilCore.findModuleForPsiElement(packageReferenceExpression);
if (module != null) {
- Collection<PyRequirement> requirements = PyPackageManagerImpl.getRequirements(module);
- if (requirements != null) {
- final Sdk sdk = PythonSdkType.findPythonSdk(module);
- if (sdk != null) {
+ final Sdk sdk = PythonSdkType.findPythonSdk(module);
+ if (sdk != null) {
+ final PyPackageManager manager = PyPackageManager.getInstance(sdk);
+ Collection<PyRequirement> requirements = manager.getRequirements(module);
+ if (requirements != null) {
requirements = getTransitiveRequirements(sdk, requirements, new HashSet<PyPackage>());
}
if (requirements == null) return;
@@ -191,9 +192,7 @@ public class PyPackageRequirementsInspection extends PyInspection {
}
}
final List<LocalQuickFix> quickFixes = new ArrayList<LocalQuickFix>();
- if (sdk != null && PyPackageManager.getInstance(sdk).hasPip()) {
- quickFixes.add(new AddToRequirementsFix(module, packageName, LanguageLevel.forElement(importedExpression)));
- }
+ quickFixes.add(new AddToRequirementsFix(module, packageName, LanguageLevel.forElement(importedExpression)));
quickFixes.add(new IgnoreRequirementFix(Collections.singleton(packageName)));
registerProblem(packageReferenceExpression, String.format("Package '%s' is not listed in project requirements", packageName),
ProblemHighlightType.WEAK_WARNING, null,
@@ -211,7 +210,7 @@ public class PyPackageRequirementsInspection extends PyInspection {
final Set<PyRequirement> results = new HashSet<PyRequirement>(requirements);
final List<PyPackage> packages;
try {
- packages = ((PyPackageManagerImpl) PyPackageManager.getInstance(sdk)).getPackagesFast();
+ packages = PyPackageManager.getInstance(sdk).getPackages(PySdkUtil.isRemote(sdk));
}
catch (PyExternalProcessException e) {
return null;
@@ -242,12 +241,12 @@ public class PyPackageRequirementsInspection extends PyInspection {
@Nullable
private static List<PyRequirement> findUnsatisfiedRequirements(@NotNull Module module, @NotNull Sdk sdk,
@NotNull Set<String> ignoredPackages) {
- final PyPackageManagerImpl manager = (PyPackageManagerImpl)PyPackageManager.getInstance(sdk);
- List<PyRequirement> requirements = PyPackageManagerImpl.getRequirements(module);
+ final PyPackageManager manager = PyPackageManager.getInstance(sdk);
+ List<PyRequirement> requirements = manager.getRequirements(module);
if (requirements != null) {
final List<PyPackage> packages;
try {
- packages = manager.getPackagesFast();
+ packages = manager.getPackages(PySdkUtil.isRemote(sdk));
}
catch (PyExternalProcessException e) {
return null;
@@ -265,11 +264,11 @@ public class PyPackageRequirementsInspection extends PyInspection {
}
private static void setRunningPackagingTasks(@NotNull Module module, boolean value) {
- module.putUserData(PyPackageManagerImpl.RUNNING_PACKAGING_TASKS, value);
+ module.putUserData(PyPackageManager.RUNNING_PACKAGING_TASKS, value);
}
private static boolean isRunningPackagingTasks(@NotNull Module module) {
- final Boolean value = module.getUserData(PyPackageManagerImpl.RUNNING_PACKAGING_TASKS);
+ final Boolean value = module.getUserData(PyPackageManager.RUNNING_PACKAGING_TASKS);
return value != null && value;
}
@@ -302,6 +301,21 @@ public class PyPackageRequirementsInspection extends PyInspection {
@Override
public void applyFix(@NotNull final Project project, @NotNull ProblemDescriptor descriptor) {
+ boolean installManagement = false;
+ final PyPackageManager manager = PyPackageManager.getInstance(mySdk);
+ if (!manager.hasManagement(false)) {
+ final int result = Messages.showYesNoDialog(project,
+ "Python packaging tools are required for installing packages. Do you want to " +
+ "install 'pip' and 'setuptools' for your interpreter?",
+ "Install Python Packaging Tools",
+ Messages.getQuestionIcon());
+ if (result == Messages.YES) {
+ installManagement = true;
+ }
+ else {
+ return;
+ }
+ }
final List<PyRequirement> chosen;
if (myUnsatisfied.size() > 1) {
final PyChooseRequirementsDialog dialog = new PyChooseRequirementsDialog(project, myUnsatisfied);
@@ -313,21 +327,48 @@ public class PyPackageRequirementsInspection extends PyInspection {
if (chosen.isEmpty()) {
return;
}
- final PyPackageManagerImpl.UI ui = new PyPackageManagerImpl.UI(project, mySdk, new PyPackageManagerImpl.UI.Listener() {
- @Override
- public void started() {
- setRunningPackagingTasks(myModule, true);
- }
+ if (installManagement) {
+ final PyPackageManagerUI ui = new PyPackageManagerUI(project, mySdk, new UIListener(myModule) {
+ @Override
+ public void finished(List<PyExternalProcessException> exceptions) {
+ super.finished(exceptions);
+ if (exceptions.isEmpty()) {
+ installRequirements(project, chosen);
+ }
+ }
+ });
+ ui.installManagement();
+ }
+ else {
+ installRequirements(project, chosen);
+ }
+ }
- @Override
- public void finished(List<PyExternalProcessException> exceptions) {
- setRunningPackagingTasks(myModule, false);
- }
- });
- ui.install(chosen, Collections.<String>emptyList());
+ private void installRequirements(Project project, List<PyRequirement> requirements) {
+ final PyPackageManagerUI ui = new PyPackageManagerUI(project, mySdk, new UIListener(myModule));
+ ui.install(requirements, Collections.<String>emptyList());
+ }
+ }
+
+ private static class UIListener implements PyPackageManagerUI.Listener {
+ private final Module myModule;
+
+ public UIListener(Module module) {
+ myModule = module;
+ }
+
+ @Override
+ public void started() {
+ setRunningPackagingTasks(myModule, true);
+ }
+
+ @Override
+ public void finished(List<PyExternalProcessException> exceptions) {
+ setRunningPackagingTasks(myModule, false);
}
}
+
private static class IgnoreRequirementFix implements LocalQuickFix {
@NotNull private final Set<String> myPackageNames;
diff --git a/python/src/com/jetbrains/python/inspections/quickfix/GenerateBinaryStubsFix.java b/python/src/com/jetbrains/python/inspections/quickfix/GenerateBinaryStubsFix.java
index 7d087bd3eb08..17600d32befb 100644
--- a/python/src/com/jetbrains/python/inspections/quickfix/GenerateBinaryStubsFix.java
+++ b/python/src/com/jetbrains/python/inspections/quickfix/GenerateBinaryStubsFix.java
@@ -170,7 +170,7 @@ public class GenerateBinaryStubsFix implements LocalQuickFix {
new String[]{
homePath,
PythonHelpersLocator.getHelperPath("extra_syspath.py"), myQualifiedName},
- PythonSdkType.getVirtualEnvAdditionalEnv(homePath), 5000
+ PythonSdkType.getVirtualEnvExtraEnv(homePath), 5000
);
if (runResult.getExitCode() == 0 && !runResult.isTimeout()) {
final String extraPath = runResult.getStdout();
diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyDefaultArgumentQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyDefaultArgumentQuickFix.java
index db47f17f8ea8..6e0d5c465d0e 100644
--- a/python/src/com/jetbrains/python/inspections/quickfix/PyDefaultArgumentQuickFix.java
+++ b/python/src/com/jetbrains/python/inspections/quickfix/PyDefaultArgumentQuickFix.java
@@ -61,7 +61,7 @@ public class PyDefaultArgumentQuickFix implements LocalQuickFix {
PyStatementList list = function.getStatementList();
PyParameterList paramList = function.getParameterList();
- final StringBuilder functionText = new StringBuilder("def foo(");
+ final StringBuilder functionText = new StringBuilder("def " + function.getName() + "(");
int size = paramList.getParameters().length;
for (int i = 0; i != size; ++i) {
PyParameter p = paramList.getParameters()[i];
diff --git a/python/src/com/jetbrains/python/inspections/quickfix/StatementEffectFunctionCallQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/StatementEffectFunctionCallQuickFix.java
index 43f4f0394015..017bf1a1af29 100644
--- a/python/src/com/jetbrains/python/inspections/quickfix/StatementEffectFunctionCallQuickFix.java
+++ b/python/src/com/jetbrains/python/inspections/quickfix/StatementEffectFunctionCallQuickFix.java
@@ -88,7 +88,13 @@ public class StatementEffectFunctionCallQuickFix implements LocalQuickFix {
if (next instanceof PyExpressionStatement) {
final PyExpression expr = ((PyExpressionStatement)next).getExpression();
if (expr instanceof PyBinaryExpression) {
- addInArguments(stringBuilder, (PyBinaryExpression)expr);
+ final PsiElement operator = ((PyBinaryExpression)expr).getPsiOperator();
+ if (operator instanceof LeafPsiElement && ((LeafPsiElement)operator).getElementType() == PyTokenTypes.IN_KEYWORD) {
+ addInArguments(stringBuilder, (PyBinaryExpression)expr);
+ }
+ else {
+ stringBuilder.append(next.getText());
+ }
}
else if (expr instanceof PyTupleExpression) {
final PyExpression[] elements = ((PyTupleExpression)expr).getElements();
@@ -114,14 +120,11 @@ public class StatementEffectFunctionCallQuickFix implements LocalQuickFix {
}
private static void addInArguments(@NotNull final StringBuilder stringBuilder, @NotNull final PyBinaryExpression binaryExpression) {
- final PsiElement operator = binaryExpression.getPsiOperator();
- if (operator instanceof LeafPsiElement && ((LeafPsiElement)operator).getElementType() == PyTokenTypes.IN_KEYWORD) {
- stringBuilder.append(binaryExpression.getLeftExpression().getText());
- stringBuilder.append(", ");
- final PyExpression rightExpression = binaryExpression.getRightExpression();
- if (rightExpression != null)
- stringBuilder.append(rightExpression.getText());
- }
+ stringBuilder.append(binaryExpression.getLeftExpression().getText());
+ stringBuilder.append(", ");
+ final PyExpression rightExpression = binaryExpression.getRightExpression();
+ if (rightExpression != null)
+ stringBuilder.append(rightExpression.getText());
}
private static void replacePrint(@NotNull final PsiElement expression) {
diff --git a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java
index b7523caf90e7..8049546a6e92 100644
--- a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java
+++ b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java
@@ -48,7 +48,6 @@ import com.jetbrains.python.documentation.DocStringTypeReference;
import com.jetbrains.python.inspections.*;
import com.jetbrains.python.inspections.quickfix.*;
import com.jetbrains.python.packaging.PyPIPackageUtil;
-import com.jetbrains.python.packaging.PyPackageManager;
import com.jetbrains.python.packaging.PyRequirement;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
@@ -598,9 +597,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
if (PyPIPackageUtil.INSTANCE.isInPyPI(packageName)) {
final List<PyRequirement> requirements = Collections.singletonList(new PyRequirement(packageName));
final String name = "Install package " + packageName;
- if (PyPackageManager.getInstance(sdk).hasPip()) {
- actions.add(new PyPackageRequirementsInspection.PyInstallRequirementsFix(name, module, sdk, requirements));
- }
+ actions.add(new PyPackageRequirementsInspection.PyInstallRequirementsFix(name, module, sdk, requirements));
}
}
}
diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java
index 65d95eb62713..9b5f669168f7 100644
--- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java
+++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java
@@ -15,31 +15,17 @@
*/
package com.jetbrains.python.packaging;
-import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.process.ProcessOutput;
import com.intellij.execution.util.ExecUtil;
-import com.intellij.icons.AllIcons;
-import com.intellij.notification.Notification;
-import com.intellij.notification.NotificationListener;
-import com.intellij.notification.NotificationType;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
-import com.intellij.openapi.progress.ProgressIndicator;
-import com.intellij.openapi.progress.ProgressManager;
-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.projectRoots.impl.ProjectJdkImpl;
import com.intellij.openapi.roots.OrderRootType;
-import com.intellij.openapi.ui.Messages;
-import com.intellij.openapi.util.Key;
-import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
@@ -49,46 +35,36 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
-import com.intellij.remote.RemoteFile;
-import com.intellij.remote.RemoteSdkAdditionalData;
-import com.intellij.remote.RemoteSdkCredentials;
-import com.intellij.remote.VagrantNotStartedException;
import com.intellij.util.ArrayUtil;
-import com.intellij.util.Function;
-import com.intellij.util.PathMappingSettings;
-import com.intellij.util.SystemProperties;
import com.intellij.util.containers.HashSet;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.net.HttpConfigurable;
-import com.intellij.webcore.packaging.PackagesNotificationPanel;
import com.jetbrains.python.PythonHelpersLocator;
import com.jetbrains.python.psi.LanguageLevel;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyListLiteralExpression;
import com.jetbrains.python.psi.PyStringLiteralExpression;
-import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase;
-import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
import com.jetbrains.python.sdk.PySdkUtil;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import javax.swing.event.HyperlinkEvent;
-import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
-import java.util.List;
/**
* @author vlan
*/
-@SuppressWarnings({"UnusedDeclaration", "FieldAccessedSynchronizedAndUnsynchronized"})
public class PyPackageManagerImpl extends PyPackageManager {
- private static final Logger LOG = Logger.getInstance(PyPackageManagerImpl.class);
+ // Bundled versions of package management tools
+ public static final String SETUPTOOLS_VERSION = "1.1.5";
+ public static final String PIP_VERSION = "1.4.1";
+
+ public static final String SETUPTOOLS = PACKAGE_SETUPTOOLS + "-" + SETUPTOOLS_VERSION;
+ public static final String PIP = PACKAGE_PIP + "-" + PIP_VERSION;
public static final int OK = 0;
- public static final int ERROR_WRONG_USAGE = 1;
public static final int ERROR_NO_PIP = 2;
public static final int ERROR_NO_SETUPTOOLS = 3;
public static final int ERROR_INVALID_SDK = -1;
@@ -97,260 +73,24 @@ public class PyPackageManagerImpl extends PyPackageManager {
public static final int ERROR_INVALID_OUTPUT = -4;
public static final int ERROR_ACCESS_DENIED = -5;
public static final int ERROR_EXECUTION = -6;
- public static final int ERROR_INTERRUPTED = -7;
- public static final int ERROR_VAGRANT_NOT_LAUNCHED = 101;
- public static final int ERROR_REMOTE_ACCESS = 102;
-
- public static final String PACKAGE_PIP = "pip";
- public static final String PACKAGE_DISTRIBUTE = "distribute";
- public static final String PACKAGE_SETUPTOOLS = "setuptools";
-
- public static final Key<Boolean> RUNNING_PACKAGING_TASKS = Key.create("PyPackageRequirementsInspection.RunningPackagingTasks");
+ private static final Logger LOG = Logger.getInstance(PyPackageManagerImpl.class);
private static final String PACKAGING_TOOL = "packaging_tool.py";
private static final String VIRTUALENV = "virtualenv.py";
private static final int TIMEOUT = 10 * 60 * 1000;
private static final String BUILD_DIR_OPTION = "--build-dir";
- public static final String USE_USER_SITE = "--user";
public static final String INSTALL = "install";
public static final String UNINSTALL = "uninstall";
public static final String UNTAR = "untar";
- // Bundled versions of package management tools
- public static final String SETUPTOOLS_VERSION = "1.1.5";
- public static final String PIP_VERSION = "1.4.1";
-
- public static final String SETUPTOOLS = PACKAGE_SETUPTOOLS + "-" + SETUPTOOLS_VERSION;
- public static final String PIP = PACKAGE_PIP + "-" + PIP_VERSION;
- private static final String LAUNCH_VAGRANT = "launchVagrant";
-
private List<PyPackage> myPackagesCache = null;
private Map<String, Set<PyPackage>> myDependenciesCache = null;
private PyExternalProcessException myExceptionCache = null;
- private Sdk mySdk;
-
- public static class UI {
- @Nullable private Listener myListener;
- @NotNull private Project myProject;
- @NotNull private Sdk mySdk;
-
- public interface Listener {
- void started();
-
- void finished(List<PyExternalProcessException> exceptions);
- }
-
- public UI(@NotNull Project project, @NotNull Sdk sdk, @Nullable Listener listener) {
- myProject = project;
- mySdk = sdk;
- myListener = listener;
- }
-
- public void installManagement(@NotNull final String name) {
- final String progressTitle;
- final String successTitle;
- progressTitle = "Installing package " + name;
- successTitle = "Packages installed successfully";
- run(new MultiExternalRunnable() {
- @Override
- public List<PyExternalProcessException> run(@NotNull ProgressIndicator indicator) {
- final List<PyExternalProcessException> exceptions = new ArrayList<PyExternalProcessException>();
- indicator.setText(String.format("Installing package '%s'...", name));
- final PyPackageManagerImpl manager = (PyPackageManagerImpl)PyPackageManagers.getInstance().forSdk(mySdk);
- try {
- manager.installManagement(name);
- }
- catch (PyExternalProcessException e) {
- exceptions.add(e);
- }
- return exceptions;
- }
- }, progressTitle, successTitle, "Installed package " + name,
- "Install package failed"
- );
- }
-
- public void install(@NotNull final List<PyRequirement> requirements, @NotNull final List<String> extraArgs) {
- final String progressTitle;
- final String successTitle;
- progressTitle = "Installing packages";
- successTitle = "Packages installed successfully";
- run(new MultiExternalRunnable() {
- @Override
- public List<PyExternalProcessException> run(@NotNull ProgressIndicator indicator) {
- final int size = requirements.size();
- final List<PyExternalProcessException> exceptions = new ArrayList<PyExternalProcessException>();
- final PyPackageManagerImpl manager = (PyPackageManagerImpl)PyPackageManagers.getInstance().forSdk(mySdk);
- for (int i = 0; i < size; i++) {
- final PyRequirement requirement = requirements.get(i);
- if (myListener != null) {
- indicator.setText(String.format("Installing package '%s'...", requirement));
- indicator.setFraction((double)i / size);
- }
- try {
- manager.install(list(requirement), extraArgs);
- }
- catch (PyExternalProcessException e) {
- exceptions.add(e);
- }
- }
- manager.refresh();
- return exceptions;
- }
- }, progressTitle, successTitle, "Installed packages: " + PyPackageUtil.requirementsToString(requirements),
- "Install packages failed"
- );
- }
-
- public void uninstall(@NotNull final List<PyPackage> packages) {
- final String packagesString = StringUtil.join(packages, new Function<PyPackage, String>() {
- @Override
- public String fun(PyPackage pkg) {
- return "'" + pkg.getName() + "'";
- }
- }, ", ");
- if (checkDependents(packages)) return;
-
- run(new MultiExternalRunnable() {
- @Override
- public List<PyExternalProcessException> run(@NotNull ProgressIndicator indicator) {
- final PyPackageManagerImpl manager = (PyPackageManagerImpl)PyPackageManagers.getInstance().forSdk(mySdk);
- try {
- manager.uninstall(packages);
- return list();
- }
- catch (PyExternalProcessException e) {
- return list(e);
- }
- finally {
- manager.refresh();
- }
- }
- }, "Uninstalling packages", "Packages uninstalled successfully", "Uninstalled packages: " + packagesString,
- "Uninstall packages failed"
- );
- }
-
- private boolean checkDependents(@NotNull final List<PyPackage> packages) {
- try {
- final Map<String, Set<PyPackage>> dependentPackages = collectDependents(packages, mySdk);
- final int[] warning = {0};
- if (!dependentPackages.isEmpty()) {
- ApplicationManager.getApplication().invokeAndWait(new Runnable() {
- @Override
- public void run() {
- if (dependentPackages.size() == 1) {
- String message = "You are attempting to uninstall ";
- List<String> dep = new ArrayList<String>();
- int size = 1;
- for (Map.Entry<String, Set<PyPackage>> entry : dependentPackages.entrySet()) {
- final Set<PyPackage> value = entry.getValue();
- size = value.size();
- dep.add(entry.getKey() + " package which is required for " + StringUtil.join(value, ", "));
- }
- message += StringUtil.join(dep, "\n");
- message += size == 1 ? " package" : " packages";
- message += "\n\nDo you want to proceed?";
- warning[0] = Messages.showYesNoDialog(message, "Warning",
- AllIcons.General.BalloonWarning);
- }
- else {
- String message = "You are attempting to uninstall packages which are required for another packages.\n\n";
- List<String> dep = new ArrayList<String>();
- for (Map.Entry<String, Set<PyPackage>> entry : dependentPackages.entrySet()) {
- dep.add(entry.getKey() + " -> " + StringUtil.join(entry.getValue(), ", "));
- }
- message += StringUtil.join(dep, "\n");
- message += "\n\nDo you want to proceed?";
- warning[0] = Messages.showYesNoDialog(message, "Warning",
- AllIcons.General.BalloonWarning);
- }
- }
- }, ModalityState.current());
- }
- if (warning[0] != Messages.YES) return true;
- }
- catch (PyExternalProcessException e) {
- LOG.info("Error loading packages dependents: " + e.getMessage(), e);
- }
- return false;
- }
-
- private interface MultiExternalRunnable {
- List<PyExternalProcessException> run(@NotNull ProgressIndicator indicator);
- }
-
- private void run(@NotNull final MultiExternalRunnable runnable, @NotNull final String progressTitle,
- @NotNull final String successTitle, @NotNull final String successDescription, @NotNull final String failureTitle) {
- ProgressManager.getInstance().run(new Task.Backgroundable(myProject, progressTitle, false) {
- @Override
- public void run(@NotNull ProgressIndicator indicator) {
- indicator.setText(progressTitle + "...");
- final Ref<Notification> notificationRef = new Ref<Notification>(null);
- final String PACKAGING_GROUP_ID = "Packaging";
- final Application application = ApplicationManager.getApplication();
- if (myListener != null) {
- application.invokeLater(new Runnable() {
- @Override
- public void run() {
- myListener.started();
- }
- });
- }
-
- final List<PyExternalProcessException> exceptions = runnable.run(indicator);
- if (exceptions.isEmpty()) {
- notificationRef.set(new Notification(PACKAGING_GROUP_ID, successTitle, successDescription, NotificationType.INFORMATION));
- }
- else {
- final String progressLower = progressTitle.toLowerCase();
- final String firstLine = String.format("Error%s occurred when %s.", exceptions.size() > 1 ? "s" : "", progressLower);
-
- final String description = createDescription(exceptions, firstLine);
- notificationRef.set(new Notification(PACKAGING_GROUP_ID, failureTitle,
- firstLine + " <a href=\"xxx\">Details...</a>",
- NotificationType.ERROR,
- new NotificationListener() {
- @Override
- public void hyperlinkUpdate(@NotNull Notification notification,
- @NotNull HyperlinkEvent event) {
- assert myProject != null;
- PackagesNotificationPanel.showError(myProject, failureTitle, description);
- }
- }
- ));
- }
- application.invokeLater(new Runnable() {
- @Override
- public void run() {
- if (myListener != null) {
- myListener.finished(exceptions);
- }
- final Notification notification = notificationRef.get();
- if (notification != null) {
- notification.notify(myProject);
- }
- }
- });
- }
- });
- }
-
- public static String createDescription(List<PyExternalProcessException> exceptions, String firstLine) {
- final StringBuilder b = new StringBuilder();
- b.append(firstLine);
- b.append("\n\n");
- for (PyExternalProcessException exception : exceptions) {
- b.append(exception.toString());
- b.append("\n");
- }
- return b.toString();
- }
- }
+ protected Sdk mySdk;
@Override
public void refresh() {
@@ -373,7 +113,23 @@ public class PyPackageManagerImpl extends PyPackageManager {
});
}
- private void installManagement(String name) throws PyExternalProcessException {
+ @Override
+ public void installManagement() throws PyExternalProcessException {
+ if (!hasPackage(PACKAGE_SETUPTOOLS, false) && !hasPackage(PACKAGE_DISTRIBUTE, false)) {
+ installManagement(SETUPTOOLS);
+ }
+ if (!hasPackage(PACKAGE_PIP, false)) {
+ installManagement(PIP);
+ }
+ }
+
+ @Override
+ public boolean hasManagement(boolean cachedOnly) {
+ return (hasPackage(PACKAGE_SETUPTOOLS, cachedOnly) || hasPackage(PACKAGE_DISTRIBUTE, cachedOnly)) &&
+ hasPackage(PACKAGE_PIP, cachedOnly);
+ }
+
+ protected void installManagement(@NotNull String name) throws PyExternalProcessException {
final String helperPath = getHelperPath(name);
ArrayList<String> args = Lists.newArrayList(UNTAR, helperPath);
@@ -390,7 +146,7 @@ public class PyPackageManagerImpl extends PyPackageManager {
}
final String fileName = dirName + name + File.separatorChar + "setup.py";
try {
- output = getProcessOutput(fileName, Collections.<String>singletonList(INSTALL), true, dirName + name);
+ 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");
@@ -406,17 +162,28 @@ public class PyPackageManagerImpl extends PyPackageManager {
}
finally {
clearCaches();
- FileUtil.delete(new File(dirName)); //TODO: remove temp directory for remote interpreter
+ FileUtil.delete(new File(dirName));
+ }
+ }
+
+ private boolean hasPackage(@NotNull String name, boolean cachedOnly) {
+ try {
+ return findPackage(name, cachedOnly) != null;
+ }
+ catch (PyExternalProcessException ignored) {
+ return false;
}
}
PyPackageManagerImpl(@NotNull Sdk sdk) {
mySdk = sdk;
+ subscribeToLocalChanges(sdk);
+ }
+
+ protected void subscribeToLocalChanges(Sdk sdk) {
final Application app = ApplicationManager.getApplication();
final MessageBusConnection connection = app.getMessageBus().connect();
- if (!PySdkUtil.isRemote(sdk)) {
- connection.subscribe(VirtualFileManager.VFS_CHANGES, new MySdkRootWatcher());
- }
+ connection.subscribe(VirtualFileManager.VFS_CHANGES, new MySdkRootWatcher());
}
public Sdk getSdk() {
@@ -424,27 +191,13 @@ public class PyPackageManagerImpl extends PyPackageManager {
}
@Override
- public void install(String requirementString) throws PyExternalProcessException {
- boolean hasSetuptools = false;
- boolean hasPip = false;
- try {
- hasSetuptools = findInstalledPackage(SETUPTOOLS) != null;
- }
- catch (PyExternalProcessException ignored) {
- }
- try {
- hasPip = findInstalledPackage(PIP) != null;
- }
- catch (PyExternalProcessException ignored) {
- }
-
- if (!hasSetuptools) installManagement(SETUPTOOLS);
- if (!hasPip) installManagement(PIP);
+ public void install(@NotNull String requirementString) throws PyExternalProcessException {
+ installManagement();
install(Collections.singletonList(PyRequirement.fromString(requirementString)), Collections.<String>emptyList());
}
- public void install(@NotNull List<PyRequirement> requirements, @NotNull List<String> extraArgs)
- throws PyExternalProcessException {
+ @Override
+ public void install(@NotNull List<PyRequirement> requirements, @NotNull List<String> extraArgs) throws PyExternalProcessException {
final List<String> args = new ArrayList<String>();
args.add(INSTALL);
final File buildDir;
@@ -455,7 +208,7 @@ public class PyPackageManagerImpl extends PyPackageManager {
throw new PyExternalProcessException(ERROR_ACCESS_DENIED, PACKAGING_TOOL, args, "Cannot create temporary build directory");
}
if (!extraArgs.contains(BUILD_DIR_OPTION)) {
- args.addAll(list(BUILD_DIR_OPTION, buildDir.getAbsolutePath()));
+ args.addAll(Arrays.asList(BUILD_DIR_OPTION, buildDir.getAbsolutePath()));
}
boolean useUserSite = extraArgs.contains(USE_USER_SITE);
@@ -499,69 +252,22 @@ public class PyPackageManagerImpl extends PyPackageManager {
}
}
- private static Map<String, Set<PyPackage>> collectDependents(@NotNull final List<PyPackage> packages, Sdk sdk)
- throws PyExternalProcessException {
- Map<String, Set<PyPackage>> dependentPackages = new HashMap<String, Set<PyPackage>>();
- for (PyPackage pkg : packages) {
- final Set<PyPackage> dependents =
- ((PyPackageManagerImpl)PyPackageManager.getInstance(sdk)).getDependents(pkg.getName());
- if (dependents != null && !dependents.isEmpty()) {
- for (PyPackage dependent : dependents) {
- if (!packages.contains(dependent)) {
- dependentPackages.put(pkg.getName(), dependents);
- }
- }
- }
- }
- return dependentPackages;
- }
-
- public static String getUserSite() {
- if (SystemInfo.isWindows) {
- final String appdata = System.getenv("APPDATA");
- return appdata + File.separator + "Python";
- }
- else {
- final String userHome = SystemProperties.getUserHome();
- return userHome + File.separator + ".local";
- }
- }
-
-
- public boolean cacheIsNotNull() {
- return myPackagesCache != null;
- }
-
- /**
- * Returns the list of packages for the SDK without initiating a remote connection. Returns null
- * for a remote interpreter if the list of packages was not loaded.
- *
- * @return the list of packages or null
- */
@Nullable
- public synchronized List<PyPackage> getPackagesFast() throws PyExternalProcessException {
- if (myPackagesCache != null) {
- return myPackagesCache;
- }
- if (PySdkUtil.isRemote(mySdk)) {
- return null;
- }
- return getPackages();
- }
-
- @NotNull
- public synchronized List<PyPackage> getPackages() throws PyExternalProcessException {
+ public synchronized List<PyPackage> getPackages(boolean cachedOnly) throws PyExternalProcessException {
if (myPackagesCache == null) {
if (myExceptionCache != null) {
throw myExceptionCache;
}
-
+ if (cachedOnly) {
+ return null;
+ }
loadPackages();
}
return myPackagesCache;
}
- public synchronized Set<PyPackage> getDependents(String pkg) throws PyExternalProcessException {
+ @Nullable
+ public synchronized Set<PyPackage> getDependents(@NotNull PyPackage pkg) throws PyExternalProcessException {
if (myDependenciesCache == null) {
if (myExceptionCache != null) {
throw myExceptionCache;
@@ -569,12 +275,12 @@ public class PyPackageManagerImpl extends PyPackageManager {
loadPackages();
}
- return myDependenciesCache.get(pkg);
+ return myDependenciesCache.get(pkg.getName());
}
public synchronized void loadPackages() throws PyExternalProcessException {
try {
- final String output = runPythonHelper(PACKAGING_TOOL, list("list"));
+ final String output = runPythonHelper(PACKAGING_TOOL, Arrays.asList("list"));
myPackagesCache = parsePackagingToolOutput(output);
Collections.sort(myPackagesCache, new Comparator<PyPackage>() {
@Override
@@ -592,7 +298,7 @@ public class PyPackageManagerImpl extends PyPackageManager {
}
}
- private void calculateDependents() {
+ private synchronized void calculateDependents() {
myDependenciesCache = new HashMap<String, Set<PyPackage>>();
for (PyPackage p : myPackagesCache) {
final List<PyRequirement> requirements = p.getRequirements();
@@ -608,47 +314,18 @@ public class PyPackageManagerImpl extends PyPackageManager {
@Override
@Nullable
- public PyPackage findInstalledPackage(String name) throws PyExternalProcessException {
- return findPackageByName(name, getPackages());
- }
-
- @Override
- public boolean findPackage(@NotNull final String name) {
- try {
- final String output = runPythonHelper(PACKAGING_TOOL, list("search", name));
- return StringUtil.containsIgnoreCase(output, name + " ");
- }
- catch (PyExternalProcessException e) {
- LOG.error(e.getMessage());
- return false;
- }
- }
-
- @Nullable
- public PyPackage findPackageFast(String name) throws PyExternalProcessException {
- final List<PyPackage> packages = getPackagesFast();
- return packages != null ? findPackageByName(name, packages) : null;
- }
-
- @Nullable
- private static PyPackage findPackageByName(String name, List<PyPackage> packages) {
- for (PyPackage pkg : packages) {
- if (name.equalsIgnoreCase(pkg.getName())) {
- return pkg;
+ public PyPackage findPackage(@NotNull String name, boolean cachedOnly) throws PyExternalProcessException {
+ final List<PyPackage> packages = getPackages(cachedOnly);
+ if (packages != null) {
+ for (PyPackage pkg : packages) {
+ if (name.equalsIgnoreCase(pkg.getName())) {
+ return pkg;
+ }
}
}
return null;
}
- public boolean hasPip() {
- try {
- return findPackageFast(PACKAGE_PIP) != null;
- }
- catch (PyExternalProcessException e) {
- return false;
- }
- }
-
@NotNull
public String createVirtualEnv(@NotNull String destinationDir, boolean useGlobalSite) throws PyExternalProcessException {
final List<String> args = new ArrayList<String>();
@@ -674,30 +351,21 @@ public class PyPackageManagerImpl extends PyPackageManager {
final String path = (binary != null) ? binary : binaryFallback;
if (usePyVenv) {
- // TODO: Still no 'packaging' and 'pysetup3' for Python 3.3rc1, see PEP 405
+ // Still no 'packaging' and 'pysetup3' for Python 3.3rc1, see PEP 405
final VirtualFile binaryFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(path);
if (binaryFile != null) {
final ProjectJdkImpl tmpSdk = new ProjectJdkImpl("", PythonSdkType.getInstance());
tmpSdk.setHomePath(path);
- final PyPackageManagerImpl manager = new PyPackageManagerImpl(tmpSdk);
- manager.installManagement(SETUPTOOLS);
- manager.installManagement(PIP);
+ final PyPackageManager manager = PyPackageManager.getInstance(tmpSdk);
+ manager.installManagement();
}
}
return path;
}
- public static void deleteVirtualEnv(@NotNull String sdkHome) throws PyExternalProcessException {
- final File root = PythonSdkType.getVirtualEnvRoot(sdkHome);
- if (root != null) {
- FileUtil.delete(root);
- }
- }
-
@Nullable
- public static List<PyRequirement> getRequirements(@NotNull Module module) {
- // TODO: Cache requirements, clear cache on requirements.txt or setup.py updates
- List<PyRequirement> requirements = getRequirementsFromTxt(module);
+ public List<PyRequirement> getRequirements(@NotNull Module module) {
+ List<PyRequirement> requirements = PySdkUtil.getRequirementsFromTxt(module);
if (requirements != null) {
return requirements;
}
@@ -721,25 +389,12 @@ public class PyPackageManagerImpl extends PyPackageManager {
return null;
}
- @Nullable
- public static List<PyRequirement> getRequirementsFromTxt(Module module) {
- final VirtualFile requirementsTxt = PyPackageUtil.findRequirementsTxt(module);
- if (requirementsTxt != null) {
- return PyRequirement.parse(requirementsTxt);
- }
- return null;
- }
-
- private void clearCaches() {
+ protected synchronized void clearCaches() {
myPackagesCache = null;
myDependenciesCache = null;
myExceptionCache = null;
}
- private static <T> List<T> list(T... xs) {
- return Arrays.asList(xs);
- }
-
@Nullable
private static String getProxyString() {
final HttpConfigurable settings = HttpConfigurable.getInstance();
@@ -792,164 +447,61 @@ public class PyPackageManagerImpl extends PyPackageManager {
}
@Nullable
- private String getHelperPath(String helper) {
- String helperPath;
- final SdkAdditionalData sdkData = mySdk.getSdkAdditionalData();
- if (sdkData instanceof PyRemoteSdkAdditionalDataBase) {
- PyRemoteSdkAdditionalDataBase remoteSdkData = (PyRemoteSdkAdditionalDataBase)mySdk.getSdkAdditionalData();
-
- try {
- final RemoteSdkCredentials remoteSdkCredentials = remoteSdkData.getRemoteSdkCredentials(false);
- if (!StringUtil.isEmpty(remoteSdkCredentials.getHelpersPath())) {
- helperPath = new RemoteFile(remoteSdkCredentials.getHelpersPath(),
- helper).getPath();
- }
- else {
- helperPath = null;
- }
- }
- catch (Exception e) {
- helperPath = null;
- LOG.error(e);
- }
- }
- else {
- helperPath = PythonHelpersLocator.getHelperPath(helper);
- }
- return helperPath;
+ protected String getHelperPath(String helper) {
+ return PythonHelpersLocator.getHelperPath(helper);
}
- private ProcessOutput getProcessOutput(@NotNull String helperPath,
+ protected ProcessOutput getProcessOutput(@NotNull String helperPath,
@NotNull List<String> args,
boolean askForSudo,
- @Nullable String workingDir)
- throws PyExternalProcessException {
- final SdkAdditionalData sdkData = mySdk.getSdkAdditionalData();
+ @Nullable String workingDir) throws PyExternalProcessException {
final String homePath = mySdk.getHomePath();
if (homePath == null) {
throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, "Cannot find interpreter for SDK");
}
- if (sdkData instanceof PyRemoteSdkAdditionalDataBase) { //remote interpreter
- RemoteSdkCredentials remoteSdkCredentials;
+ if (workingDir == null) {
+ workingDir = new File(homePath).getParent();
+ }
+ final List<String> cmdline = new ArrayList<String>();
+ cmdline.add(homePath);
+ cmdline.add(helperPath);
+ cmdline.addAll(args);
+ LOG.info("Running packaging tool: " + StringUtil.join(cmdline, " "));
+
+ final boolean canCreate = FileUtil.ensureCanCreateFile(new File(homePath));
+ if (!canCreate && !SystemInfo.isWindows && askForSudo) { //is system site interpreter --> we need sudo privileges
try {
- remoteSdkCredentials = ((RemoteSdkAdditionalData)sdkData).getRemoteSdkCredentials(false);
- }
- catch (InterruptedException e) {
- LOG.error(e);
- remoteSdkCredentials = null;
- }
- catch (final ExecutionException e) {
- if (e.getCause() instanceof VagrantNotStartedException) {
- throw new PyExternalProcessException(ERROR_VAGRANT_NOT_LAUNCHED, helperPath, args, "Vagrant instance is down. <a href=\"" +
- LAUNCH_VAGRANT +
- "\">Launch vagrant</a>")
- .withHandler(LAUNCH_VAGRANT, new Runnable() {
- @Override
- public void run() {
- final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
- if (manager != null) {
-
- try {
- manager.runVagrant(((VagrantNotStartedException)e.getCause()).getVagrantFolder());
- clearCaches();
- }
- catch (ExecutionException e1) {
- throw new RuntimeException(e1);
- }
- }
- }
- });
- }
- else {
- throw new PyExternalProcessException(ERROR_REMOTE_ACCESS, helperPath, args, e.getMessage());
- }
- }
- final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
- if (manager != null && remoteSdkCredentials != null) {
- final List<String> cmdline = new ArrayList<String>();
- cmdline.add(homePath);
- cmdline.add(RemoteFile.detectSystemByPath(homePath).createRemoteFile(helperPath).getPath());
- cmdline.addAll(Collections2.transform(args, new com.google.common.base.Function<String, String>() {
- @Override
- public String apply(@Nullable String input) {
- return quoteIfNeeded(input);
+ final ProcessOutput result = ExecUtil.sudoAndGetOutput(cmdline,
+ "Please enter your password to make changes in system packages: ",
+ workingDir);
+ String message = result.getStderr();
+ if (result.getExitCode() != 0) {
+ final String stdout = result.getStdout();
+ if (StringUtil.isEmptyOrSpaces(message)) {
+ message = stdout;
}
- }));
- try {
- if (askForSudo) {
- askForSudo = !manager.ensureCanWrite(null, remoteSdkCredentials, remoteSdkCredentials.getInterpreterPath());
+ if (StringUtil.isEmptyOrSpaces(message)) {
+ message = "Failed to perform action. Permission denied.";
}
- ProcessOutput processOutput;
- do {
- PathMappingSettings mappings = manager.setupMappings(null, (PyRemoteSdkAdditionalDataBase)sdkData, null);
- processOutput =
- manager.runRemoteProcess(null, remoteSdkCredentials, mappings, ArrayUtil.toStringArray(cmdline), workingDir, askForSudo);
- if (askForSudo && processOutput.getStderr().contains("sudo: 3 incorrect password attempts")) {
- continue;
- }
- break;
- }
- while (true);
- return processOutput;
+ throw new PyExternalProcessException(result.getExitCode(), helperPath, args, message);
}
- catch (ExecutionException e) {
- throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, "Error running SDK: " + e.getMessage(), e);
+ if (SystemInfo.isMac && !StringUtil.isEmptyOrSpaces(message)) {
+ throw new PyExternalProcessException(result.getExitCode(), helperPath, args, message);
}
+ return result;
}
- else {
- throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args,
- PythonRemoteInterpreterManager.WEB_DEPLOYMENT_PLUGIN_IS_DISABLED);
+ catch (ExecutionException e) {
+ throw new PyExternalProcessException(ERROR_EXECUTION, helperPath, args, e.getMessage());
+ }
+ catch (IOException e) {
+ throw new PyExternalProcessException(ERROR_ACCESS_DENIED, helperPath, args, e.getMessage());
}
}
else {
- if (workingDir == null) {
- workingDir = new File(homePath).getParent();
- }
- final List<String> cmdline = new ArrayList<String>();
- cmdline.add(homePath);
- cmdline.add(helperPath);
- cmdline.addAll(args);
- LOG.info("Running packaging tool: " + StringUtil.join(cmdline, " "));
-
- final boolean canCreate = FileUtil.ensureCanCreateFile(new File(homePath));
- if (!canCreate && !SystemInfo.isWindows && askForSudo) { //is system site interpreter --> we need sudo privileges
- try {
- final ProcessOutput result = ExecUtil.sudoAndGetOutput(cmdline,
- "Please enter your password to make changes in system packages: ",
- workingDir);
- String message = result.getStderr();
- if (result.getExitCode() != 0) {
- final String stdout = result.getStdout();
- if (StringUtil.isEmptyOrSpaces(message)) {
- message = stdout;
- }
- if (StringUtil.isEmptyOrSpaces(message)) {
- message = "Failed to perform action. Permission denied.";
- }
- throw new PyExternalProcessException(result.getExitCode(), helperPath, args, message);
- }
- if (SystemInfo.isMac && !StringUtil.isEmptyOrSpaces(message)) {
- throw new PyExternalProcessException(result.getExitCode(), helperPath, args, message);
- }
- return result;
- }
- catch (ExecutionException e) {
- throw new PyExternalProcessException(ERROR_EXECUTION, helperPath, args, e.getMessage());
- }
- catch (IOException e) {
- throw new PyExternalProcessException(ERROR_ACCESS_DENIED, helperPath, args, e.getMessage());
- }
- }
- else {
- return PySdkUtil.getProcessOutput(workingDir, ArrayUtil.toStringArray(cmdline), TIMEOUT);
- }
+ return PySdkUtil.getProcessOutput(workingDir, ArrayUtil.toStringArray(cmdline), TIMEOUT);
}
}
- private static String quoteIfNeeded(String arg) {
- return arg.replace("<", "\\<").replace(">", "\\>"); //TODO: move this logic to ParametersListUtil.encode
- }
-
@NotNull
private static List<PyPackage> parsePackagingToolOutput(@NotNull String s) throws PyExternalProcessException {
final String[] lines = StringUtil.splitByLines(s);
@@ -976,17 +528,6 @@ public class PyPackageManagerImpl extends PyPackageManager {
return packages;
}
-
- @Override
- public void showInstallationError(Project project, String title, String description) {
- PackagesNotificationPanel.showError(project, title, description);
- }
-
- @Override
- public void showInstallationError(Component owner, String title, String description) {
- PackagesNotificationPanel.showError(owner, title, description);
- }
-
private class MySdkRootWatcher extends BulkFileListener.Adapter {
@Override
public void after(@NotNull List<? extends VFileEvent> events) {
diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java
new file mode 100644
index 000000000000..28dfa6c834a4
--- /dev/null
+++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java
@@ -0,0 +1,367 @@
+/*
+ * 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.packaging;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.notification.Notification;
+import com.intellij.notification.NotificationListener;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.Function;
+import com.intellij.webcore.packaging.PackagesNotificationPanel;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.event.HyperlinkEvent;
+import java.util.*;
+
+/**
+* @author vlan
+*/
+public class PyPackageManagerUI {
+ private static final Logger LOG = Logger.getInstance(PyPackageManagerUI.class);
+ @Nullable private Listener myListener;
+ @NotNull private Project myProject;
+ @NotNull private Sdk mySdk;
+
+ public interface Listener {
+ void started();
+
+ void finished(List<PyExternalProcessException> exceptions);
+ }
+
+ public PyPackageManagerUI(@NotNull Project project, @NotNull Sdk sdk, @Nullable Listener listener) {
+ myProject = project;
+ mySdk = sdk;
+ myListener = listener;
+ }
+
+ public void installManagement() {
+ ProgressManager.getInstance().run(new InstallManagementTask(myProject, mySdk, myListener));
+ }
+
+ public void install(@NotNull final List<PyRequirement> requirements, @NotNull final List<String> extraArgs) {
+ ProgressManager.getInstance().run(new InstallTask(myProject, mySdk, requirements, extraArgs, myListener));
+ }
+
+ public void uninstall(@NotNull final List<PyPackage> packages) {
+ if (checkDependents(packages)) {
+ return;
+ }
+ ProgressManager.getInstance().run(new UninstallTask(myProject, mySdk, myListener, packages));
+ }
+
+ private boolean checkDependents(@NotNull final List<PyPackage> packages) {
+ try {
+ final Map<String, Set<PyPackage>> dependentPackages = collectDependents(packages, mySdk);
+ final int[] warning = {0};
+ if (!dependentPackages.isEmpty()) {
+ ApplicationManager.getApplication().invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ if (dependentPackages.size() == 1) {
+ String message = "You are attempting to uninstall ";
+ List<String> dep = new ArrayList<String>();
+ int size = 1;
+ for (Map.Entry<String, Set<PyPackage>> entry : dependentPackages.entrySet()) {
+ final Set<PyPackage> value = entry.getValue();
+ size = value.size();
+ dep.add(entry.getKey() + " package which is required for " + StringUtil.join(value, ", "));
+ }
+ message += StringUtil.join(dep, "\n");
+ message += size == 1 ? " package" : " packages";
+ message += "\n\nDo you want to proceed?";
+ warning[0] = Messages.showYesNoDialog(message, "Warning",
+ AllIcons.General.BalloonWarning);
+ }
+ else {
+ String message = "You are attempting to uninstall packages which are required for another packages.\n\n";
+ List<String> dep = new ArrayList<String>();
+ for (Map.Entry<String, Set<PyPackage>> entry : dependentPackages.entrySet()) {
+ dep.add(entry.getKey() + " -> " + StringUtil.join(entry.getValue(), ", "));
+ }
+ message += StringUtil.join(dep, "\n");
+ message += "\n\nDo you want to proceed?";
+ warning[0] = Messages.showYesNoDialog(message, "Warning",
+ AllIcons.General.BalloonWarning);
+ }
+ }
+ }, ModalityState.current());
+ }
+ if (warning[0] != Messages.YES) return true;
+ }
+ catch (PyExternalProcessException e) {
+ LOG.info("Error loading packages dependents: " + e.getMessage(), e);
+ }
+ return false;
+ }
+
+ private static Map<String, Set<PyPackage>> collectDependents(@NotNull final List<PyPackage> packages, Sdk sdk)
+ throws PyExternalProcessException {
+ Map<String, Set<PyPackage>> dependentPackages = new HashMap<String, Set<PyPackage>>();
+ for (PyPackage pkg : packages) {
+ final Set<PyPackage> dependents = PyPackageManager.getInstance(sdk).getDependents(pkg);
+ if (dependents != null && !dependents.isEmpty()) {
+ for (PyPackage dependent : dependents) {
+ if (!packages.contains(dependent)) {
+ dependentPackages.put(pkg.getName(), dependents);
+ }
+ }
+ }
+ }
+ return dependentPackages;
+ }
+
+ private abstract static class PackagingTask extends Task.Backgroundable {
+ private static final String PACKAGING_GROUP_ID = "Packaging";
+
+ @Nullable protected final Listener myListener;
+
+ public PackagingTask(@Nullable Project project, @NotNull String title, @Nullable Listener listener) {
+ super(project, title);
+ myListener = listener;
+ }
+
+ @Override
+ public void run(@NotNull ProgressIndicator indicator) {
+ taskStarted(indicator);
+ taskFinished(runTask(indicator));
+ }
+
+ @NotNull
+ protected abstract List<PyExternalProcessException> runTask(@NotNull ProgressIndicator indicator);
+
+ @NotNull
+ protected abstract String getSuccessTitle();
+
+ @NotNull
+ protected abstract String getSuccessDescription();
+
+ @NotNull
+ protected abstract String getFailureTitle();
+
+ protected void taskStarted(@NotNull ProgressIndicator indicator) {
+ indicator.setText(getTitle() + "...");
+ if (myListener != null) {
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ myListener.started();
+ }
+ });
+ }
+ }
+
+ protected void taskFinished(@NotNull final List<PyExternalProcessException> exceptions) {
+ final Ref<Notification> notificationRef = new Ref<Notification>(null);
+ if (exceptions.isEmpty()) {
+ notificationRef.set(new Notification(PACKAGING_GROUP_ID, getSuccessTitle(), getSuccessDescription(),
+ NotificationType.INFORMATION));
+ }
+ else {
+ final String firstLine = getTitle() + ": error occurred.";
+ final String description = createDescription(exceptions, firstLine);
+ notificationRef.set(new Notification(PACKAGING_GROUP_ID, getFailureTitle(),
+ firstLine + " <a href=\"xxx\">Details...</a>",
+ NotificationType.ERROR,
+ new NotificationListener() {
+ @Override
+ public void hyperlinkUpdate(@NotNull Notification notification,
+ @NotNull HyperlinkEvent event) {
+ assert myProject != null;
+ PackagesNotificationPanel.showError(myProject, getFailureTitle(), description);
+ }
+ }
+ ));
+ }
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ if (myListener != null) {
+ myListener.finished(exceptions);
+ }
+ final Notification notification = notificationRef.get();
+ if (notification != null) {
+ notification.notify(myProject);
+ }
+ }
+ });
+ }
+ }
+
+ private static class InstallTask extends PackagingTask {
+ @NotNull protected final Sdk mySdk;
+ @NotNull private final List<PyRequirement> myRequirements;
+ @NotNull private final List<String> myExtraArgs;
+
+ public InstallTask(@Nullable Project project,
+ @NotNull Sdk sdk,
+ @NotNull List<PyRequirement> requirements,
+ @NotNull List<String> extraArgs,
+ @Nullable Listener listener) {
+ super(project, "Installing packages", listener);
+ mySdk = sdk;
+ myRequirements = requirements;
+ myExtraArgs = extraArgs;
+ }
+
+ @NotNull
+ @Override
+ protected List<PyExternalProcessException> runTask(@NotNull ProgressIndicator indicator) {
+ final List<PyExternalProcessException> exceptions = new ArrayList<PyExternalProcessException>();
+ final int size = myRequirements.size();
+ final PyPackageManager manager = PyPackageManagers.getInstance().forSdk(mySdk);
+ for (int i = 0; i < size; i++) {
+ final PyRequirement requirement = myRequirements.get(i);
+ indicator.setText(String.format("Installing package '%s'...", requirement));
+ indicator.setFraction((double)i / size);
+ try {
+ manager.install(Arrays.asList(requirement), myExtraArgs);
+ }
+ catch (PyExternalProcessException e) {
+ exceptions.add(e);
+ }
+ }
+ manager.refresh();
+ return exceptions;
+ }
+
+ @NotNull
+ @Override
+ protected String getSuccessTitle() {
+ return "Packages installed successfully";
+ }
+
+ @NotNull
+ @Override
+ protected String getSuccessDescription() {
+ return "Installed packages: " + PyPackageUtil.requirementsToString(myRequirements);
+ }
+
+ @NotNull
+ @Override
+ protected String getFailureTitle() {
+ return "Install packages failed";
+ }
+ }
+
+ private static class InstallManagementTask extends InstallTask {
+
+ public InstallManagementTask(@Nullable Project project,
+ @NotNull Sdk sdk,
+ @Nullable Listener listener) {
+ super(project, sdk, Collections.<PyRequirement>emptyList(), Collections.<String>emptyList(), listener);
+ }
+
+ @NotNull
+ @Override
+ protected List<PyExternalProcessException> runTask(@NotNull ProgressIndicator indicator) {
+ final List<PyExternalProcessException> exceptions = new ArrayList<PyExternalProcessException>();
+ final PyPackageManager manager = PyPackageManagers.getInstance().forSdk(mySdk);
+ indicator.setText("Installing packaging tools...");
+ indicator.setIndeterminate(true);
+ try {
+ manager.installManagement();
+ }
+ catch (PyExternalProcessException e) {
+ exceptions.add(e);
+ }
+ manager.refresh();
+ return exceptions;
+ }
+
+ @NotNull
+ @Override
+ protected String getSuccessDescription() {
+ return "Installed Python packaging tools";
+ }
+ }
+
+ private static class UninstallTask extends PackagingTask {
+ @NotNull private final Sdk mySdk;
+ @NotNull private final List<PyPackage> myPackages;
+
+ public UninstallTask(@Nullable Project project,
+ @NotNull Sdk sdk,
+ @Nullable Listener listener,
+ @NotNull List<PyPackage> packages) {
+ super(project, "Uninstalling packages", listener);
+ mySdk = sdk;
+ myPackages = packages;
+ }
+
+ @NotNull
+ @Override
+ protected List<PyExternalProcessException> runTask(@NotNull ProgressIndicator indicator) {
+ final PyPackageManager manager = PyPackageManagers.getInstance().forSdk(mySdk);
+ try {
+ manager.uninstall(myPackages);
+ return Arrays.asList();
+ }
+ catch (PyExternalProcessException e) {
+ return Arrays.asList(e);
+ }
+ finally {
+ manager.refresh();
+ }
+ }
+
+ @NotNull
+ @Override
+ protected String getSuccessTitle() {
+ return "Packages uninstalled successfully";
+ }
+
+ @NotNull
+ @Override
+ protected String getSuccessDescription() {
+ final String packagesString = StringUtil.join(myPackages, new Function<PyPackage, String>() {
+ @Override
+ public String fun(PyPackage pkg) {
+ return "'" + pkg.getName() + "'";
+ }
+ }, ", ");
+ return "Uninstalled packages: " + packagesString;
+ }
+
+ @NotNull
+ @Override
+ protected String getFailureTitle() {
+ return "Uninstall packages failed";
+ }
+ }
+
+ public static String createDescription(List<PyExternalProcessException> exceptions, String firstLine) {
+ final StringBuilder b = new StringBuilder();
+ b.append(firstLine);
+ b.append("\n\n");
+ for (PyExternalProcessException exception : exceptions) {
+ b.append(exception.toString());
+ b.append("\n");
+ }
+ return b.toString();
+ }
+}
diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java
index 054e9e01d23f..e68cede3f799 100644
--- a/python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java
+++ b/python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java
@@ -15,13 +15,11 @@
*/
package com.jetbrains.python.packaging;
-import com.intellij.openapi.module.Module;
import com.intellij.openapi.projectRoots.Sdk;
+import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
/**
@@ -36,22 +34,14 @@ public class PyPackageManagersImpl extends PyPackageManagers {
final String name = sdk.getName();
PyPackageManagerImpl manager = myInstances.get(name);
if (manager == null) {
- manager = new PyPackageManagerImpl(sdk);
+ if (PythonSdkType.isRemote(sdk)) {
+ manager = new PyRemotePackageManagerImpl(sdk);
+ }
+ else {
+ manager = new PyPackageManagerImpl(sdk);
+ }
myInstances.put(name, manager);
}
return manager;
}
-
- @Nullable
- @Override
- public List<PyRequirement> getRequirements(Module module) {
- return PyPackageManagerImpl.getRequirements(module);
- }
-
-
- @Nullable
- @Override
- public List<PyRequirement> getRequirementsFromTxt(Module module) {
- return PyPackageManagerImpl.getRequirementsFromTxt(module);
- }
}
diff --git a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java
new file mode 100644
index 000000000000..6c6de77d9454
--- /dev/null
+++ b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java
@@ -0,0 +1,177 @@
+/*
+ * 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.packaging;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.process.ProcessOutput;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkAdditionalData;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.remote.RemoteFile;
+import com.intellij.remote.RemoteSdkAdditionalData;
+import com.intellij.remote.RemoteSdkCredentials;
+import com.intellij.remote.VagrantNotStartedException;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.PathMappingSettings;
+import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase;
+import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author vlan
+ */
+public class PyRemotePackageManagerImpl extends PyPackageManagerImpl {
+ private static final String LAUNCH_VAGRANT = "launchVagrant";
+ public static final int ERROR_VAGRANT_NOT_LAUNCHED = 101;
+ public static final int ERROR_REMOTE_ACCESS = 102;
+
+ private static final Logger LOG = Logger.getInstance(PyRemotePackageManagerImpl.class);
+
+ PyRemotePackageManagerImpl(@NotNull Sdk sdk) {
+ super(sdk);
+ }
+
+ @Nullable
+ @Override
+ protected String getHelperPath(String helper) {
+ final SdkAdditionalData sdkData = mySdk.getSdkAdditionalData();
+ if (sdkData instanceof PyRemoteSdkAdditionalDataBase) {
+ final PyRemoteSdkAdditionalDataBase remoteSdkData = (PyRemoteSdkAdditionalDataBase)mySdk.getSdkAdditionalData();
+ try {
+ final RemoteSdkCredentials remoteSdkCredentials = remoteSdkData.getRemoteSdkCredentials(false);
+ if (!StringUtil.isEmpty(remoteSdkCredentials.getHelpersPath())) {
+ return new RemoteFile(remoteSdkCredentials.getHelpersPath(), helper).getPath();
+ }
+ else {
+ return null;
+ }
+ }
+ catch (Exception e) {
+ LOG.error(e);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected ProcessOutput getProcessOutput(@NotNull String helperPath,
+ @NotNull List<String> args,
+ boolean askForSudo,
+ @Nullable String workingDir) throws PyExternalProcessException {
+ final String homePath = mySdk.getHomePath();
+ if (homePath == null) {
+ throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, "Cannot find interpreter for SDK");
+ }
+ final SdkAdditionalData sdkData = mySdk.getSdkAdditionalData();
+ if (sdkData instanceof PyRemoteSdkAdditionalDataBase) { //remote interpreter
+ RemoteSdkCredentials remoteSdkCredentials;
+ try {
+ remoteSdkCredentials = ((RemoteSdkAdditionalData)sdkData).getRemoteSdkCredentials(false);
+ }
+ catch (InterruptedException e) {
+ LOG.error(e);
+ remoteSdkCredentials = null;
+ }
+ catch (final ExecutionException e) {
+ if (e.getCause() instanceof VagrantNotStartedException) {
+ throw new PyExternalProcessException(ERROR_VAGRANT_NOT_LAUNCHED, helperPath, args, "Vagrant instance is down. <a href=\"" +
+ LAUNCH_VAGRANT +
+ "\">Launch vagrant</a>")
+ .withHandler(LAUNCH_VAGRANT, new Runnable() {
+ @Override
+ public void run() {
+ final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
+ if (manager != null) {
+
+ try {
+ manager.runVagrant(((VagrantNotStartedException)e.getCause()).getVagrantFolder());
+ clearCaches();
+ }
+ catch (ExecutionException e1) {
+ throw new RuntimeException(e1);
+ }
+ }
+ }
+ });
+ }
+ else {
+ throw new PyExternalProcessException(ERROR_REMOTE_ACCESS, helperPath, args, e.getMessage());
+ }
+ }
+ final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
+ if (manager != null && remoteSdkCredentials != null) {
+ final List<String> cmdline = new ArrayList<String>();
+ cmdline.add(homePath);
+ cmdline.add(RemoteFile.detectSystemByPath(homePath).createRemoteFile(helperPath).getPath());
+ cmdline.addAll(Collections2.transform(args, new Function<String, String>() {
+ @Override
+ public String apply(@Nullable String input) {
+ return quoteIfNeeded(input);
+ }
+ }));
+ try {
+ if (askForSudo) {
+ askForSudo = !manager.ensureCanWrite(null, remoteSdkCredentials, remoteSdkCredentials.getInterpreterPath());
+ }
+ ProcessOutput processOutput;
+ do {
+ PathMappingSettings mappings = manager.setupMappings(null, (PyRemoteSdkAdditionalDataBase)sdkData, null);
+ processOutput =
+ manager.runRemoteProcess(null, remoteSdkCredentials, mappings, ArrayUtil.toStringArray(cmdline), workingDir, askForSudo);
+ if (askForSudo && processOutput.getStderr().contains("sudo: 3 incorrect password attempts")) {
+ continue;
+ }
+ break;
+ }
+ while (true);
+ return processOutput;
+ }
+ catch (ExecutionException e) {
+ throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, "Error running SDK: " + e.getMessage(), e);
+ }
+ }
+ else {
+ throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args,
+ PythonRemoteInterpreterManager.WEB_DEPLOYMENT_PLUGIN_IS_DISABLED);
+ }
+ }
+ else {
+ throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, "Invalid remote SDK");
+ }
+ }
+
+ @Override
+ protected void subscribeToLocalChanges(Sdk sdk) {
+ // Local VFS changes aren't needed
+ }
+
+ @Override
+ protected void installManagement(@NotNull String name) throws PyExternalProcessException {
+ super.installManagement(name);
+ // TODO: remove temp directory for remote interpreter
+ }
+
+ private static String quoteIfNeeded(String arg) {
+ return arg.replace("<", "\\<").replace(">", "\\>"); //TODO: move this logic to ParametersListUtil.encode
+ }
+}
diff --git a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java
index 348d2817ec75..094149336d5d 100644
--- a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java
+++ b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java
@@ -25,10 +25,8 @@ import com.intellij.util.Consumer;
import com.intellij.webcore.packaging.InstalledPackage;
import com.intellij.webcore.packaging.InstalledPackagesPanel;
import com.intellij.webcore.packaging.PackagesNotificationPanel;
-import com.jetbrains.python.packaging.PyExternalProcessException;
-import com.jetbrains.python.packaging.PyPackage;
-import com.jetbrains.python.packaging.PyPackageManager;
-import com.jetbrains.python.packaging.PyPackageManagerImpl;
+import com.jetbrains.python.packaging.*;
+import com.jetbrains.python.sdk.PySdkUtil;
import com.jetbrains.python.sdk.PythonSdkType;
import com.jetbrains.python.sdk.flavors.IronPythonSdkFlavor;
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
@@ -42,33 +40,13 @@ import java.util.Set;
* @author yole
*/
public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
- public static final String INSTALL_SETUPTOOLS = "installSetuptools";
- public static final String INSTALL_PIP = "installPip";
+ public static final String INSTALL_MANAGEMENT = "installManagement";
public static final String CREATE_VENV = "createVEnv";
- private boolean myHasSetuptools;
- private boolean myHasPip = true;
+ private boolean myHasManagement = false;
public PyInstalledPackagesPanel(Project project, PackagesNotificationPanel area) {
super(project, area);
- myNotificationArea.addLinkHandler(INSTALL_SETUPTOOLS, new Runnable() {
- @Override
- public void run() {
- final Sdk sdk = getSelectedSdk();
- if (sdk != null) {
- installManagementTool(sdk, PyPackageManagerImpl.SETUPTOOLS);
- }
- }
- });
- myNotificationArea.addLinkHandler(INSTALL_PIP, new Runnable() {
- @Override
- public void run() {
- final Sdk sdk = getSelectedSdk();
- if (sdk != null) {
- installManagementTool(sdk, PyPackageManagerImpl.PIP);
- }
- }
- });
}
private Sdk getSelectedSdk() {
@@ -85,19 +63,8 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
application.executeOnPooledThread(new Runnable() {
@Override
public void run() {
- PyExternalProcessException exc = null;
- try {
- PyPackageManagerImpl packageManager = (PyPackageManagerImpl)PyPackageManager.getInstance(selectedSdk);
- myHasSetuptools = packageManager.findInstalledPackage(PyPackageManagerImpl.PACKAGE_SETUPTOOLS) != null;
- if (!myHasSetuptools) {
- myHasSetuptools = packageManager.findInstalledPackage(PyPackageManagerImpl.PACKAGE_DISTRIBUTE) != null;
- }
- myHasPip = packageManager.findInstalledPackage(PyPackageManagerImpl.PACKAGE_PIP) != null;
- }
- catch (PyExternalProcessException e) {
- exc = e;
- }
- final PyExternalProcessException externalProcessException = exc;
+ PyPackageManager packageManager = PyPackageManager.getInstance(selectedSdk);
+ myHasManagement = packageManager.hasManagement(false);
application.invokeLater(new Runnable() {
@Override
public void run() {
@@ -112,42 +79,24 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
myNotificationArea.hide();
if (!invalid) {
String text = null;
- if (externalProcessException != null) {
- final int retCode = externalProcessException.getRetcode();
- if (retCode == PyPackageManagerImpl.ERROR_NO_PIP) {
- myHasPip = false;
- }
- else if (retCode == PyPackageManagerImpl.ERROR_NO_SETUPTOOLS) {
- myHasSetuptools = false;
- }
- else {
- text = externalProcessException.getMessage();
- }
- final boolean hasPackagingTools = myHasPip && myHasSetuptools;
- allowCreateVirtualEnv &= !hasPackagingTools;
-
- if (externalProcessException.hasHandler()) {
- final String key = externalProcessException.getHandler().first;
- myNotificationArea.addLinkHandler(key,
- new Runnable() {
- @Override
- public void run() {
- externalProcessException.getHandler().second.run();
- myNotificationArea.removeLinkHandler(key);
- updateNotifications(selectedSdk);
+ if (!myHasManagement) {
+ myNotificationArea.addLinkHandler(INSTALL_MANAGEMENT,
+ new Runnable() {
+ @Override
+ public void run() {
+ final Sdk sdk = getSelectedSdk();
+ if (sdk != null) {
+ installManagementTools(sdk);
}
+ myNotificationArea.removeLinkHandler(INSTALL_MANAGEMENT);
+ updateNotifications(selectedSdk);
}
- );
- }
+ }
+ );
}
- if (text == null) {
- if (!myHasSetuptools) {
- text = "Python package management tools not found. <a href=\"" + INSTALL_SETUPTOOLS + "\">Install 'setuptools'</a>";
- }
- else if (!myHasPip) {
- text = "Python packaging tool 'pip' not found. <a href=\"" + INSTALL_PIP + "\">Install 'pip'</a>";
- }
+ if (!myHasManagement) {
+ text = "Python packaging tools not found. <a href=\"" + INSTALL_MANAGEMENT + "\">Install packaging tools</a>";
}
if (text != null) {
if (allowCreateVirtualEnv) {
@@ -157,7 +106,7 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
}
}
- myInstallButton.setEnabled(!invalid && externalProcessException == null && myHasPip);
+ myInstallButton.setEnabled(!invalid && myHasManagement);
}
}
}, ModalityState.any());
@@ -170,8 +119,8 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
return Sets.newHashSet("pip", "distutils", "setuptools");
}
- private void installManagementTool(@NotNull final Sdk sdk, final String name) {
- final PyPackageManagerImpl.UI ui = new PyPackageManagerImpl.UI(myProject, sdk, new PyPackageManagerImpl.UI.Listener() {
+ private void installManagementTools(@NotNull final Sdk sdk) {
+ final PyPackageManagerUI ui = new PyPackageManagerUI(myProject, sdk, new PyPackageManagerUI.Listener() {
@Override
public void started() {
myPackagesTable.setPaintBusy(true);
@@ -180,11 +129,11 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
@Override
public void finished(List<PyExternalProcessException> exceptions) {
myPackagesTable.setPaintBusy(false);
- PyPackageManagerImpl packageManager = (PyPackageManagerImpl)PyPackageManager.getInstance(sdk);
+ PyPackageManager packageManager = PyPackageManager.getInstance(sdk);
if (!exceptions.isEmpty()) {
- final String firstLine = "Install package failed. ";
- final String description = PyPackageManagerImpl.UI.createDescription(exceptions, firstLine);
- packageManager.showInstallationError(myProject, "Failed to install " + name, description);
+ final String firstLine = "Install Python packaging tools failed. ";
+ final String description = PyPackageManagerUI.createDescription(exceptions, firstLine);
+ PackagesNotificationPanel.showError(myProject, "Failed to install Python packaging tools", description);
}
packageManager.refresh();
updatePackages(new PyPackageManagementService(myProject, sdk));
@@ -194,22 +143,22 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
updateNotifications(sdk);
}
});
- ui.installManagement(name);
+ ui.installManagement();
}
@Override
protected boolean canUninstallPackage(InstalledPackage pkg) {
- if (!myHasPip) return false;
+ if (!myHasManagement) return false;
if (PythonSdkType.isVirtualEnv(getSelectedSdk()) && pkg instanceof PyPackage) {
final String location = ((PyPackage)pkg).getLocation();
- if (location != null && location.startsWith(PyPackageManagerImpl.getUserSite())) {
+ if (location != null && location.startsWith(PySdkUtil.getUserSite())) {
return false;
}
}
final String name = pkg.getName();
- if (PyPackageManagerImpl.PACKAGE_PIP.equals(name) ||
- PyPackageManagerImpl.PACKAGE_SETUPTOOLS.equals(name) ||
- PyPackageManagerImpl.PACKAGE_DISTRIBUTE.equals(name)) {
+ if (PyPackageManager.PACKAGE_PIP.equals(name) ||
+ PyPackageManager.PACKAGE_SETUPTOOLS.equals(name) ||
+ PyPackageManager.PACKAGE_DISTRIBUTE.equals(name)) {
return false;
}
return true;
@@ -217,6 +166,6 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
@Override
protected boolean canUpgradePackage(InstalledPackage pyPackage) {
- return myHasPip;
+ return myHasManagement;
}
}
diff --git a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java
index ffd291a995b1..95330b361dfb 100644
--- a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java
+++ b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java
@@ -23,6 +23,7 @@ import com.intellij.webcore.packaging.InstalledPackage;
import com.intellij.webcore.packaging.PackageManagementService;
import com.intellij.webcore.packaging.RepoPackage;
import com.jetbrains.python.packaging.*;
+import com.jetbrains.python.sdk.PySdkUtil;
import com.jetbrains.python.sdk.PythonSdkType;
import org.apache.xmlrpc.AsyncCallback;
import org.jetbrains.annotations.NonNls;
@@ -112,7 +113,7 @@ public class PyPackageManagementService extends PackageManagementService {
public String getInstallToUserText() {
String userSiteText = "Install to user's site packages directory";
if (!PythonSdkType.isRemote(mySdk))
- userSiteText += " (" + PyPackageManagerImpl.getUserSite() + ")";
+ userSiteText += " (" + PySdkUtil.getUserSite() + ")";
return userSiteText;
}
@@ -130,12 +131,12 @@ public class PyPackageManagementService extends PackageManagementService {
public Collection<InstalledPackage> getInstalledPackages() throws IOException {
List<PyPackage> packages;
try {
- packages = ((PyPackageManagerImpl)PyPackageManager.getInstance(mySdk)).getPackages();
+ packages = PyPackageManager.getInstance(mySdk).getPackages(false);
}
catch (PyExternalProcessException e) {
throw new IOException(e);
}
- return new ArrayList<InstalledPackage>(packages);
+ return packages != null ? new ArrayList<InstalledPackage>(packages) : new ArrayList<InstalledPackage>();
}
@Override
@@ -145,7 +146,7 @@ public class PyPackageManagementService extends PackageManagementService {
final String repository = PyPIPackageUtil.PYPI_URL.equals(repoPackage.getRepoUrl()) ? null : repoPackage.getRepoUrl();
final List<String> extraArgs = new ArrayList<String>();
if (installToUser) {
- extraArgs.add(PyPackageManagerImpl.USE_USER_SITE);
+ extraArgs.add(PyPackageManager.USE_USER_SITE);
}
if (extraOptions != null) {
// TODO: Respect arguments quotation
@@ -166,7 +167,7 @@ public class PyPackageManagementService extends PackageManagementService {
req = new PyRequirement(packageName);
}
- final PyPackageManagerImpl.UI ui = new PyPackageManagerImpl.UI(myProject, mySdk, new PyPackageManagerImpl.UI.Listener() {
+ final PyPackageManagerUI ui = new PyPackageManagerUI(myProject, mySdk, new PyPackageManagerUI.Listener() {
@Override
public void started() {
listener.operationStarted(packageName);
@@ -183,7 +184,7 @@ public class PyPackageManagementService extends PackageManagementService {
private String toErrorDescription(List<PyExternalProcessException> exceptions) {
String errorDescription = null;
if (exceptions != null && exceptions.size() > 0) {
- errorDescription = PyPackageManagerImpl.UI.createDescription(exceptions, "");
+ errorDescription = PyPackageManagerUI.createDescription(exceptions, "");
}
return errorDescription;
}
@@ -191,7 +192,7 @@ public class PyPackageManagementService extends PackageManagementService {
@Override
public void uninstallPackages(List<InstalledPackage> installedPackages, final Listener listener) {
final String packageName = installedPackages.size() == 1 ? installedPackages.get(0).getName() : null;
- PyPackageManagerImpl.UI ui = new PyPackageManagerImpl.UI(myProject, mySdk, new PyPackageManagerImpl.UI.Listener() {
+ PyPackageManagerUI ui = new PyPackageManagerUI(myProject, mySdk, new PyPackageManagerUI.Listener() {
@Override
public void started() {
listener.operationStarted(packageName);
diff --git a/python/src/com/jetbrains/python/projectView/PyElementNode.java b/python/src/com/jetbrains/python/projectView/PyElementNode.java
index 20c7b811b8a6..6ec347236773 100644
--- a/python/src/com/jetbrains/python/projectView/PyElementNode.java
+++ b/python/src/com/jetbrains/python/projectView/PyElementNode.java
@@ -19,11 +19,14 @@ import com.intellij.ide.projectView.PresentationData;
import com.intellij.ide.projectView.ViewSettings;
import com.intellij.ide.projectView.impl.nodes.BasePsiNode;
import com.intellij.ide.util.treeView.AbstractTreeNode;
+import com.intellij.navigation.ItemPresentation;
import com.intellij.openapi.project.Project;
+import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyElement;
import com.jetbrains.python.psi.PyFunction;
+import javax.swing.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -57,14 +60,23 @@ public class PyElementNode extends BasePsiNode<PyElement> {
@Override
protected void updateImpl(PresentationData data) {
- PyElement value = getValue();
+ final PyElement value = getValue();
final String name = value.getName();
- StringBuilder presentableText = new StringBuilder(name != null ? name : "<unnamed>");
- if (value instanceof PyFunction) {
- presentableText.append(((PyFunction) value).getParameterList().getPresentableText(false));
+ final ItemPresentation presentation = value.getPresentation();
+ String presentableText = name != null ? name : PyNames.UNNAMED_ELEMENT;
+ Icon presentableIcon = value.getIcon(0);
+ if (presentation != null) {
+ final String text = presentation.getPresentableText();
+ if (text != null) {
+ presentableText = text;
+ }
+ final Icon icon = presentation.getIcon(false);
+ if (icon != null) {
+ presentableIcon = icon;
+ }
}
- data.setPresentableText(presentableText.toString());
- data.setIcon(value.getIcon(0));
+ data.setPresentableText(presentableText);
+ data.setIcon(presentableIcon);
}
@Override
diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java
index cd4e582bb6f2..dc5cbf4917a5 100644
--- a/python/src/com/jetbrains/python/psi/PyUtil.java
+++ b/python/src/com/jetbrains/python/psi/PyUtil.java
@@ -1394,8 +1394,8 @@ public class PyUtil {
@Nullable
public static PsiElement findPrevAtOffset(PsiFile psiFile, int caretOffset, Class... toSkip) {
- PsiElement element = psiFile.findElementAt(caretOffset);
- if (element == null || caretOffset < 0) {
+ PsiElement element;
+ if (caretOffset < 0) {
return null;
}
int lineStartOffset = 0;
diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java
index bb2104fc0c77..611cbc84ad2e 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java
@@ -17,6 +17,7 @@ package com.jetbrains.python.psi.impl;
import com.intellij.codeInsight.completion.CompletionUtil;
import com.intellij.lang.ASTNode;
+import com.intellij.navigation.ItemPresentation;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.NotNullLazyValue;
@@ -53,10 +54,13 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
+import static com.intellij.openapi.util.text.StringUtil.join;
+import static com.intellij.openapi.util.text.StringUtil.notNullize;
+
/**
* @author yole
*/
-public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implements PyClass {
+public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyClass {
public static final PyClass[] EMPTY_ARRAY = new PyClassImpl[0];
private List<PyTargetExpression> myInstanceAttributes;
@@ -288,6 +292,32 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
return result.toArray(new PyClass[result.size()]);
}
+ @Override
+ public ItemPresentation getPresentation() {
+ return new PyElementPresentation(this) {
+ @Nullable
+ @Override
+ public String getPresentableText() {
+ if (!isValid()) {
+ return null;
+ }
+ final StringBuilder result = new StringBuilder(notNullize(getName(), PyNames.UNNAMED_ELEMENT));
+ final PyExpression[] superClassExpressions = getSuperClassExpressions();
+ if (superClassExpressions.length > 0) {
+ result.append("(");
+ result.append(join(Arrays.asList(superClassExpressions), new Function<PyExpression, String>() {
+ public String fun(PyExpression expr) {
+ String name = expr.getText();
+ return notNullize(name, PyNames.UNNAMED_ELEMENT);
+ }
+ }, ", "));
+ result.append(")");
+ }
+ return result.toString();
+ }
+ };
+ }
+
@NotNull
private static List<PyClassLikeType> mroMerge(@NotNull List<List<PyClassLikeType>> sequences) {
List<PyClassLikeType> result = new LinkedList<PyClassLikeType>(); // need to insert to 0th position on linearize
diff --git a/python/src/com/jetbrains/python/psi/impl/PyElementPresentation.java b/python/src/com/jetbrains/python/psi/impl/PyElementPresentation.java
new file mode 100644
index 000000000000..7888071c7321
--- /dev/null
+++ b/python/src/com/jetbrains/python/psi/impl/PyElementPresentation.java
@@ -0,0 +1,76 @@
+/*
+ * 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.impl;
+
+import com.intellij.navigation.ColoredItemPresentation;
+import com.intellij.openapi.editor.colors.TextAttributesKey;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiFile;
+import com.jetbrains.python.PyNames;
+import com.jetbrains.python.psi.PyElement;
+import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+
+/**
+* @author vlan
+*/
+public class PyElementPresentation implements ColoredItemPresentation {
+ @NotNull private final PyElement myElement;
+
+ public PyElementPresentation(@NotNull PyElement element) {
+ myElement = element;
+ }
+
+ @Nullable
+ @Override
+ public TextAttributesKey getTextAttributesKey() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String getPresentableText() {
+ final String name = myElement.getName();
+ return name != null ? name : PyNames.UNNAMED_ELEMENT;
+ }
+
+ @Nullable
+ @Override
+ public String getLocationString() {
+ return "(" + getPackageForFile(myElement.getContainingFile()) + ")";
+ }
+
+ @Nullable
+ @Override
+ public Icon getIcon(boolean unused) {
+ return myElement.getIcon(0);
+ }
+
+ public static String getPackageForFile(@NotNull PsiFile containingFile) {
+ final VirtualFile vFile = containingFile.getVirtualFile();
+
+ if (vFile != null) {
+ final String importableName = QualifiedNameFinder.findShortestImportableName(containingFile, vFile);
+ if (importableName != null) {
+ return importableName;
+ }
+ }
+ return "";
+ }
+}
diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java
index 86f84abb7fb6..2c1842f3337f 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java
@@ -16,6 +16,7 @@
package com.jetbrains.python.psi.impl;
import com.intellij.lang.ASTNode;
+import com.intellij.navigation.ItemPresentation;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
@@ -54,6 +55,7 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
+import static com.intellij.openapi.util.text.StringUtil.notNullize;
import static com.jetbrains.python.psi.PyFunction.Modifier.CLASSMETHOD;
import static com.jetbrains.python.psi.PyFunction.Modifier.STATICMETHOD;
import static com.jetbrains.python.psi.impl.PyCallExpressionHelper.interpretAsModifierWrappingCall;
@@ -61,7 +63,7 @@ import static com.jetbrains.python.psi.impl.PyCallExpressionHelper.interpretAsMo
/**
* Implements PyFunction.
*/
-public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> implements PyFunction {
+public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements PyFunction {
public PyFunctionImpl(ASTNode astNode) {
super(astNode);
@@ -259,6 +261,17 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp
return type;
}
+ @Override
+ public ItemPresentation getPresentation() {
+ return new PyElementPresentation(this) {
+ @Nullable
+ @Override
+ public String getPresentableText() {
+ return notNullize(getName(), PyNames.UNNAMED_ELEMENT) + getParameterList().getPresentableText(true);
+ }
+ };
+ }
+
@Nullable
private PyType replaceSelf(@Nullable PyType returnType, @Nullable PyExpression receiver, @NotNull TypeEvalContext context) {
if (receiver != null) {
@@ -292,32 +305,30 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp
final PyBuiltinCache cache = PyBuiltinCache.getInstance(this);
final PyStatementList statements = getStatementList();
final Set<PyType> types = new LinkedHashSet<PyType>();
- if (statements != null) {
- statements.accept(new PyRecursiveElementVisitor() {
- @Override
- public void visitPyYieldExpression(PyYieldExpression node) {
- final PyType type = context.getType(node);
- if (node.isDelegating() && type instanceof PyCollectionType) {
- final PyCollectionType collectionType = (PyCollectionType)type;
- types.add(collectionType.getElementType(context));
- }
- else {
- types.add(type);
- }
+ statements.accept(new PyRecursiveElementVisitor() {
+ @Override
+ public void visitPyYieldExpression(PyYieldExpression node) {
+ final PyType type = context.getType(node);
+ if (node.isDelegating() && type instanceof PyCollectionType) {
+ final PyCollectionType collectionType = (PyCollectionType)type;
+ types.add(collectionType.getElementType(context));
}
-
- @Override
- public void visitPyFunction(PyFunction node) {
- // Ignore nested functions
+ else {
+ types.add(type);
}
- });
- final int n = types.size();
- if (n == 1) {
- elementType = Ref.create(types.iterator().next());
}
- else if (n > 0) {
- elementType = Ref.create(PyUnionType.union(types));
+
+ @Override
+ public void visitPyFunction(PyFunction node) {
+ // Ignore nested functions
}
+ });
+ final int n = types.size();
+ if (n == 1) {
+ elementType = Ref.create(types.iterator().next());
+ }
+ else if (n > 0) {
+ elementType = Ref.create(PyUnionType.union(types));
}
if (elementType != null) {
final PyClass generator = cache.getClass(PyNames.FAKE_GENERATOR);
@@ -335,14 +346,12 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp
public PyType getReturnStatementType(TypeEvalContext typeEvalContext) {
ReturnVisitor visitor = new ReturnVisitor(this, typeEvalContext);
final PyStatementList statements = getStatementList();
- if (statements != null) {
- statements.accept(visitor);
- if (isGeneratedStub() && !visitor.myHasReturns) {
- if (PyNames.INIT.equals(getName())) {
- return PyNoneType.INSTANCE;
- }
- return null;
+ statements.accept(visitor);
+ if (isGeneratedStub() && !visitor.myHasReturns) {
+ if (PyNames.INIT.equals(getName())) {
+ return PyNoneType.INSTANCE;
}
+ return null;
}
return visitor.result();
}
@@ -376,9 +385,6 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp
@Nullable
public String extractDeprecationMessage() {
PyStatementList statementList = getStatementList();
- if (statementList == null) {
- return null;
- }
return extractDeprecationMessage(Arrays.asList(statementList.getStatements()));
}
@@ -430,7 +436,7 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp
@Nullable
@Override
public StructuredDocString getStructuredDocString() {
- return CachedValuesManager.getManager(getProject()).getCachedValue(this, myCachedStructuredDocStringProvider);
+ return CachedValuesManager.getCachedValue(this, myCachedStructuredDocStringProvider);
}
private boolean isGeneratedStub() {
@@ -527,15 +533,7 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp
public PyStringLiteralExpression getDocStringExpression() {
final PyStatementList stmtList = getStatementList();
- return stmtList != null ? DocStringUtil.findDocStringExpression(stmtList) : null;
- }
-
- protected String getElementLocation() {
- final PyClass containingClass = getContainingClass();
- if (containingClass != null) {
- return "(" + containingClass.getName() + " in " + getPackageForFile(getContainingFile()) + ")";
- }
- return super.getElementLocation();
+ return DocStringUtil.findDocStringExpression(stmtList);
}
@NotNull
diff --git a/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java b/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java
index cc9178c07f9e..64108441c347 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java
@@ -16,6 +16,7 @@
package com.jetbrains.python.psi.impl;
import com.intellij.lang.ASTNode;
+import com.intellij.navigation.ItemPresentation;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
@@ -48,7 +49,7 @@ import java.util.Map;
/**
* @author yole
*/
-public class PyNamedParameterImpl extends PyPresentableElementImpl<PyNamedParameterStub> implements PyNamedParameter {
+public class PyNamedParameterImpl extends PyBaseElementImpl<PyNamedParameterStub> implements PyNamedParameter {
public PyNamedParameterImpl(ASTNode astNode) {
super(astNode);
}
@@ -280,6 +281,11 @@ public class PyNamedParameterImpl extends PyPresentableElementImpl<PyNamedParame
return null;
}
+ @Override
+ public ItemPresentation getPresentation() {
+ return new PyElementPresentation(this);
+ }
+
private static void processLocalCalls(@NotNull PyFunction function, @NotNull Processor<PyCallExpression> processor) {
final PsiFile file = function.getContainingFile();
final String name = function.getName();
diff --git a/python/src/com/jetbrains/python/psi/impl/PyPresentableElementImpl.java b/python/src/com/jetbrains/python/psi/impl/PyPresentableElementImpl.java
deleted file mode 100644
index d04d04062651..000000000000
--- a/python/src/com/jetbrains/python/psi/impl/PyPresentableElementImpl.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 2000-2013 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.impl;
-
-import com.intellij.lang.ASTNode;
-import com.intellij.navigation.ItemPresentation;
-import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.psi.PsiFile;
-import com.intellij.psi.PsiNamedElement;
-import com.intellij.psi.stubs.IStubElementType;
-import com.intellij.psi.stubs.StubElement;
-import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
-
-import javax.swing.*;
-
-/**
- * @author yole
- */
-public abstract class PyPresentableElementImpl<T extends StubElement> extends PyBaseElementImpl<T> implements PsiNamedElement {
- public PyPresentableElementImpl(ASTNode astNode) {
- super(astNode);
- }
-
- protected PyPresentableElementImpl(final T stub, final IStubElementType nodeType) {
- super(stub, nodeType);
- }
-
- public ItemPresentation getPresentation() {
- return new ItemPresentation() {
- public String getPresentableText() {
- final String name = getName();
- return name != null ? name : "<none>";
- }
-
- public String getLocationString() {
- return getElementLocation();
- }
-
- public Icon getIcon(final boolean open) {
- return PyPresentableElementImpl.this.getIcon(0);
- }
- };
- }
-
- protected String getElementLocation() {
- return "(" + getPackageForFile(getContainingFile()) + ")";
- }
-
- public static String getPackageForFile(final PsiFile containingFile) {
- final VirtualFile vFile = containingFile.getVirtualFile();
-
- if (vFile != null) {
- final String importableName = QualifiedNameFinder.findShortestImportableName(containingFile, vFile);
- if (importableName != null) {
- return importableName;
- }
- }
- return "";
- }
-}
diff --git a/python/src/com/jetbrains/python/psi/impl/PySingleStarParameterImpl.java b/python/src/com/jetbrains/python/psi/impl/PySingleStarParameterImpl.java
index 70a46fbe39f9..44a01759f403 100644
--- a/python/src/com/jetbrains/python/psi/impl/PySingleStarParameterImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PySingleStarParameterImpl.java
@@ -16,21 +16,18 @@
package com.jetbrains.python.psi.impl;
import com.intellij.lang.ASTNode;
-import com.intellij.psi.PsiElement;
-import com.intellij.util.IncorrectOperationException;
+import com.intellij.navigation.ItemPresentation;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyNamedParameter;
import com.jetbrains.python.psi.PySingleStarParameter;
import com.jetbrains.python.psi.PyTupleParameter;
import com.jetbrains.python.psi.stubs.PySingleStarParameterStub;
-import org.jetbrains.annotations.NonNls;
-import org.jetbrains.annotations.NotNull;
/**
* @author yole
*/
-public class PySingleStarParameterImpl extends PyPresentableElementImpl<PySingleStarParameterStub> implements PySingleStarParameter {
+public class PySingleStarParameterImpl extends PyBaseElementImpl<PySingleStarParameterStub> implements PySingleStarParameter {
public PySingleStarParameterImpl(ASTNode astNode) {
super(astNode);
}
@@ -39,22 +36,22 @@ public class PySingleStarParameterImpl extends PyPresentableElementImpl<PySingle
super(stub, PyElementTypes.SINGLE_STAR_PARAMETER);
}
- public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
- throw new UnsupportedOperationException();
- }
-
+ @Override
public PyNamedParameter getAsNamed() {
return null;
}
+ @Override
public PyTupleParameter getAsTuple() {
return null;
}
+ @Override
public PyExpression getDefaultValue() {
return null;
}
+ @Override
public boolean hasDefaultValue() {
return false;
}
@@ -63,4 +60,9 @@ public class PySingleStarParameterImpl extends PyPresentableElementImpl<PySingle
public boolean isSelf() {
return false;
}
+
+ @Override
+ public ItemPresentation getPresentation() {
+ return new PyElementPresentation(this);
+ }
}
diff --git a/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionImpl.java
index bf519737f0f4..084aeecc3df0 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionImpl.java
@@ -327,7 +327,7 @@ public class PyStringLiteralExpressionImpl extends PyElementImpl implements PySt
@Nullable
@Override
public String getLocationString() {
- return "(" + PyPresentableElementImpl.getPackageForFile(getContainingFile()) + ")";
+ return "(" + PyElementPresentation.getPackageForFile(getContainingFile()) + ")";
}
@Nullable
diff --git a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java
index 3805c8df525e..d125190b77e9 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java
@@ -16,6 +16,7 @@
package com.jetbrains.python.psi.impl;
import com.intellij.lang.ASTNode;
+import com.intellij.navigation.ItemPresentation;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
@@ -61,7 +62,7 @@ import java.util.List;
/**
* @author yole
*/
-public class PyTargetExpressionImpl extends PyPresentableElementImpl<PyTargetExpressionStub> implements PyTargetExpression {
+public class PyTargetExpressionImpl extends PyBaseElementImpl<PyTargetExpressionStub> implements PyTargetExpression {
QualifiedName myQualifiedName;
public PyTargetExpressionImpl(ASTNode astNode) {
@@ -659,12 +660,19 @@ public class PyTargetExpressionImpl extends PyPresentableElementImpl<PyTargetExp
return null;
}
- protected String getElementLocation() {
- final PyClass containingClass = getContainingClass();
- if (containingClass != null) {
- return "(" + containingClass.getName() + " in " + getPackageForFile(getContainingFile()) + ")";
- }
- return super.getElementLocation();
+ @Override
+ public ItemPresentation getPresentation() {
+ return new PyElementPresentation(this) {
+ @Nullable
+ @Override
+ public String getLocationString() {
+ final PyClass containingClass = getContainingClass();
+ if (containingClass != null) {
+ return "(" + containingClass.getName() + " in " + getPackageForFile(getContainingFile()) + ")";
+ }
+ return super.getLocationString();
+ }
+ };
}
@Nullable
diff --git a/python/src/com/jetbrains/python/psi/impl/PyTupleParameterImpl.java b/python/src/com/jetbrains/python/psi/impl/PyTupleParameterImpl.java
index 09e097cb0e99..9935841c33bb 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyTupleParameterImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyTupleParameterImpl.java
@@ -16,19 +16,17 @@
package com.jetbrains.python.psi.impl;
import com.intellij.lang.ASTNode;
-import com.intellij.psi.PsiElement;
-import com.intellij.util.IncorrectOperationException;
+import com.intellij.navigation.ItemPresentation;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.PythonDialectsTokenSetProvider;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.stubs.PyTupleParameterStub;
-import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
/**
* Represents a tuple parameter as stubbed element.
*/
-public class PyTupleParameterImpl extends PyPresentableElementImpl<PyTupleParameterStub> implements PyTupleParameter {
+public class PyTupleParameterImpl extends PyBaseElementImpl<PyTupleParameterStub> implements PyTupleParameter {
public PyTupleParameterImpl(ASTNode astNode) {
super(astNode);
@@ -62,10 +60,6 @@ public class PyTupleParameterImpl extends PyPresentableElementImpl<PyTupleParame
return getDefaultValue() != null;
}
- public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
- throw new IncorrectOperationException("Can't rename a tuple parameter to '" + name +"'");
- }
-
@Override
protected void acceptPyVisitor(PyElementVisitor pyVisitor) {
pyVisitor.visitPyTupleParameter(this);
@@ -80,4 +74,9 @@ public class PyTupleParameterImpl extends PyPresentableElementImpl<PyTupleParame
public boolean isSelf() {
return false;
}
+
+ @Override
+ public ItemPresentation getPresentation() {
+ return new PyElementPresentation(this);
+ }
}
diff --git a/python/src/com/jetbrains/python/psi/impl/PythonLanguageLevelPusher.java b/python/src/com/jetbrains/python/psi/impl/PythonLanguageLevelPusher.java
index 7e15cab6ffda..af767684643e 100644
--- a/python/src/com/jetbrains/python/psi/impl/PythonLanguageLevelPusher.java
+++ b/python/src/com/jetbrains/python/psi/impl/PythonLanguageLevelPusher.java
@@ -19,7 +19,9 @@ import com.intellij.facet.Facet;
import com.intellij.facet.FacetManager;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.fileTypes.FileTypeRegistry;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleType;
@@ -163,7 +165,8 @@ public class PythonLanguageLevelPusher implements FilePropertyPusher<LanguageLev
oStream.close();
for (VirtualFile child : fileOrDir.getChildren()) {
- if (!child.isDirectory() && PythonFileType.INSTANCE.equals(child.getFileType())) {
+ final FileType fileType = FileTypeRegistry.getInstance().getFileTypeByFileName(child.getName());
+ if (!child.isDirectory() && PythonFileType.INSTANCE.equals(fileType)) {
PushedFilePropertiesUpdater.getInstance(project).filePropertiesChanged(child);
}
}
diff --git a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureHandler.java b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureHandler.java
index 6895249a641f..e360c9e46685 100644
--- a/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureHandler.java
+++ b/python/src/com/jetbrains/python/refactoring/changeSignature/PyChangeSignatureHandler.java
@@ -126,7 +126,7 @@ public class PyChangeSignatureHandler implements ChangeSignatureHandler {
private static boolean isNotUnderSourceRoot(@NotNull final Project project,
@Nullable final PsiFile psiFile,
- @NotNull final Editor editor) {
+ @Nullable final Editor editor) {
if (psiFile == null) return true;
final VirtualFile virtualFile = psiFile.getVirtualFile();
if (virtualFile != null) {
diff --git a/python/src/com/jetbrains/python/refactoring/move/PyMoveClassOrFunctionDelegate.java b/python/src/com/jetbrains/python/refactoring/move/PyMoveClassOrFunctionDelegate.java
index cf1b4d19dd4e..8306a4faec23 100644
--- a/python/src/com/jetbrains/python/refactoring/move/PyMoveClassOrFunctionDelegate.java
+++ b/python/src/com/jetbrains/python/refactoring/move/PyMoveClassOrFunctionDelegate.java
@@ -39,11 +39,10 @@ import org.jetbrains.annotations.Nullable;
* @author vlan
*/
public class PyMoveClassOrFunctionDelegate extends MoveHandlerDelegate {
-
@Override
public boolean canMove(PsiElement[] elements, @Nullable PsiElement targetContainer) {
for (PsiElement element : elements) {
- if (element instanceof PyClass || element instanceof PyFunction) continue;
+ if ((element instanceof PyClass || element instanceof PyFunction) && PyUtil.isTopLevel(element)) continue;
return false;
}
return super.canMove(elements, targetContainer);
diff --git a/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java b/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java
index 39fc2c909cf6..1d0a4bc3ad54 100644
--- a/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java
+++ b/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java
@@ -48,9 +48,9 @@ import com.intellij.ui.components.JBLabel;
import com.intellij.util.NullableConsumer;
import com.intellij.util.PathUtil;
import com.intellij.util.PlatformUtils;
+import com.intellij.webcore.packaging.PackagesNotificationPanel;
import com.jetbrains.python.packaging.PyExternalProcessException;
import com.jetbrains.python.packaging.PyPackageManager;
-import com.jetbrains.python.packaging.PyPackageManagerImpl;
import com.jetbrains.python.packaging.PyPackageService;
import com.jetbrains.python.sdk.flavors.VirtualEnvSdkFlavor;
import com.jetbrains.python.ui.IdeaDialog;
@@ -416,7 +416,7 @@ public class CreateVirtualEnvDialog extends IdeaDialog {
String myPath;
public void run(@NotNull final ProgressIndicator indicator) {
- final PyPackageManagerImpl packageManager = (PyPackageManagerImpl)PyPackageManager.getInstance(basicSdk);
+ final PyPackageManager packageManager = PyPackageManager.getInstance(basicSdk);
try {
indicator.setText("Creating virtual environment for " + basicSdk.getName());
myPath = packageManager.createVirtualEnv(getDestination(), useGlobalSitePackages());
@@ -425,7 +425,7 @@ public class CreateVirtualEnvDialog extends IdeaDialog {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
- packageManager.showInstallationError(getOwner(), "Failed to Create Virtual Environment", e.toString());
+ PackagesNotificationPanel.showError(getOwner(), "Failed to Create Virtual Environment", e.toString());
}
}, ModalityState.any());
}
diff --git a/python/src/com/jetbrains/python/sdk/PySdkUtil.java b/python/src/com/jetbrains/python/sdk/PySdkUtil.java
index 8f2b62b1b414..97d5ee545cbb 100644
--- a/python/src/com/jetbrains/python/sdk/PySdkUtil.java
+++ b/python/src/com/jetbrains/python/sdk/PySdkUtil.java
@@ -15,10 +15,13 @@
*/
package com.jetbrains.python.sdk;
+import com.intellij.execution.ExecutionException;
import com.intellij.execution.process.CapturingProcessHandler;
import com.intellij.execution.process.ProcessOutput;
+import com.intellij.execution.util.ExecUtil;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.module.Module;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.util.SystemInfo;
@@ -29,8 +32,10 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.remote.RemoteSdkAdditionalData;
-import com.intellij.util.ArrayUtil;
+import com.intellij.util.SystemProperties;
import com.intellij.util.containers.HashMap;
+import com.jetbrains.python.packaging.PyPackageUtil;
+import com.jetbrains.python.packaging.PyRequirement;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -38,8 +43,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Collections;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -56,6 +60,7 @@ public class PySdkUtil {
// Windows EOF marker, Ctrl+Z
public static final int SUBSTITUTE = 26;
+ public static final String PATH_ENV_VARIABLE = "PATH";
private PySdkUtil() {
// explicitly none
@@ -87,57 +92,29 @@ public class PySdkUtil {
return getProcessOutput(homePath, command, null, timeout);
}
- /**
- * Executes a process and returns its stdout and stderr outputs as lists of lines.
- * Waits for process for possibly limited duration.
- *
- * @param homePath process run directory
- * @param command command to execute and its arguments
- * @param addEnv items are prepended to same-named values of inherited process environment.
- * @param timeout how many milliseconds to wait until the process terminates; non-positive means inifinity.
- * @return a tuple of (stdout lines, stderr lines, exit_code), lines in them have line terminators stripped, or may be null.
- */
@NotNull
public static ProcessOutput getProcessOutput(String homePath,
@NonNls String[] command,
- @Nullable @NonNls String[] addEnv,
+ @Nullable @NonNls Map<String, String> extraEnv,
final int timeout) {
- return getProcessOutput(homePath, command, addEnv, timeout, null, true);
+ return getProcessOutput(homePath, command, extraEnv, timeout, null, true);
}
- /**
- * Executes a process and returns its stdout and stderr outputs as lists of lines.
- * Waits for process for possibly limited duration.
- *
- * @param homePath process run directory
- * @param command command to execute and its arguments
- * @param addEnv items are prepended to same-named values of inherited process environment.
- * @param timeout how many milliseconds to wait until the process terminates; non-positive means infinity.
- * @param stdin the data to write to the process standard input stream
- * @param needEOFMarker
- * @return a tuple of (stdout lines, stderr lines, exit_code), lines in them have line terminators stripped, or may be null.
- */
@NotNull
public static ProcessOutput getProcessOutput(String homePath,
@NonNls String[] command,
- @Nullable @NonNls String[] addEnv,
+ @Nullable @NonNls Map<String, String> extraEnv,
final int timeout,
@Nullable byte[] stdin,
boolean needEOFMarker) {
- final ProcessOutput failureOutput = new ProcessOutput();
if (homePath == null || !new File(homePath).exists()) {
- return failureOutput;
+ return new ProcessOutput();
}
+ final Map<String, String> systemEnv = System.getenv();
+ final Map<String, String> env = extraEnv != null ? mergeEnvVariables(systemEnv, extraEnv) : systemEnv;
try {
- List<String> commands = new ArrayList<String>();
- if (SystemInfo.isWindows && StringUtil.endsWithIgnoreCase(command[0], ".bat")) {
- commands.add("cmd");
- commands.add("/c");
- }
- Collections.addAll(commands, command);
- String[] newEnv = buildAdditionalEnv(addEnv);
- Process process = Runtime.getRuntime().exec(ArrayUtil.toStringArray(commands), newEnv, new File(homePath));
- CapturingProcessHandler processHandler = new CapturingProcessHandler(process);
+ final Process process = ExecUtil.exec(Arrays.asList(command), homePath, env);
+ final CapturingProcessHandler processHandler = new CapturingProcessHandler(process);
if (stdin != null) {
final OutputStream processInput = processHandler.getProcessInput();
assert processInput != null;
@@ -152,72 +129,61 @@ public class PySdkUtil {
}
return processHandler.runProcess(timeout);
}
- catch (final IOException ex) {
- LOG.warn(ex);
- return new ProcessOutput() {
- @Override
- public String getStderr() {
- String err = super.getStderr();
- if (!StringUtil.isEmpty(err)) {
- err += "\n" + ex.getMessage();
- }
- else {
- err = ex.getMessage();
- }
- return err;
- }
- };
+ catch (ExecutionException e) {
+ return getOutputForException(e);
+ }
+ catch (IOException e) {
+ return getOutputForException(e);
}
}
- private static String[] buildAdditionalEnv(String[] addEnv) {
- String[] newEnv = null;
- if (addEnv != null) {
- Map<String, String> envMap = buildEnvMap(addEnv);
- newEnv = new String[envMap.size()];
- int i = 0;
- for (Map.Entry<String, String> entry : envMap.entrySet()) {
- newEnv[i] = entry.getKey() + "=" + entry.getValue();
- i += 1;
+ private static ProcessOutput getOutputForException(final Exception e) {
+ LOG.warn(e);
+ return new ProcessOutput() {
+ @Override
+ public String getStderr() {
+ String err = super.getStderr();
+ if (!StringUtil.isEmpty(err)) {
+ err += "\n" + e.getMessage();
+ }
+ else {
+ err = e.getMessage();
+ }
+ return err;
}
- }
- return newEnv;
+ };
}
- public static Map<String, String> buildEnvMap(String[] addEnv) {
- Map<String, String> envMap = new HashMap<String, String>(System.getenv());
- // turn additional ent into map
- Map<String, String> addMap = new HashMap<String, String>();
- for (String envItem : addEnv) {
- int pos = envItem.indexOf('=');
- if (pos > 0) {
- String key = envItem.substring(0, pos);
- String value = envItem.substring(pos + 1, envItem.length());
- addMap.put(key, value);
- }
- else {
- LOG.warn(String.format("Invalid env value: '%s'", envItem));
- }
- }
- // fuse old and new
- for (Map.Entry<String, String> entry : addMap.entrySet()) {
- final String key = entry.getKey();
- final String value = entry.getValue();
- final String oldValue = envMap.get(key);
- if (oldValue != null) {
- envMap.put(key, value + oldValue);
+ @NotNull
+ public static Map<String, String> mergeEnvVariables(@NotNull Map<String, String> environment,
+ @NotNull Map<String, String> extraEnvironment) {
+ final Map<String, String> result = new HashMap<String, String>(environment);
+ for (Map.Entry<String, String> entry : extraEnvironment.entrySet()) {
+ if (PATH_ENV_VARIABLE.equals(entry.getKey()) && result.containsKey(PATH_ENV_VARIABLE)) {
+ result.put(PATH_ENV_VARIABLE, result.get(PATH_ENV_VARIABLE) + File.pathSeparator + entry.getValue());
}
else {
- envMap.put(key, value);
+ result.put(entry.getKey(), entry.getValue());
}
}
- return envMap;
+ return result;
}
public static boolean isRemote(@Nullable Sdk sdk) {
return sdk != null && sdk.getSdkAdditionalData() instanceof RemoteSdkAdditionalData;
}
+ public static String getUserSite() {
+ if (SystemInfo.isWindows) {
+ final String appdata = System.getenv("APPDATA");
+ return appdata + File.separator + "Python";
+ }
+ else {
+ final String userHome = SystemProperties.getUserHome();
+ return userHome + File.separator + ".local";
+ }
+ }
+
public static boolean isElementInSkeletons(@NotNull final PsiElement element) {
final PsiFile file = element.getContainingFile();
if (file != null) {
@@ -266,4 +232,13 @@ public class PySdkUtil {
}
return null;
}
+
+ @Nullable
+ public static List<PyRequirement> getRequirementsFromTxt(Module module) {
+ final VirtualFile requirementsTxt = PyPackageUtil.findRequirementsTxt(module);
+ if (requirementsTxt != null) {
+ return PyRequirement.parse(requirementsTxt);
+ }
+ return null;
+ }
}
diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkDetailsStep.java b/python/src/com/jetbrains/python/sdk/PythonSdkDetailsStep.java
index 923629ce4473..cc6a2c6b6121 100644
--- a/python/src/com/jetbrains/python/sdk/PythonSdkDetailsStep.java
+++ b/python/src/com/jetbrains/python/sdk/PythonSdkDetailsStep.java
@@ -46,7 +46,7 @@ import java.util.List;
import java.util.Set;
public class PythonSdkDetailsStep extends BaseListPopupStep<String> {
- private DialogWrapper myMore;
+ @Nullable private DialogWrapper myMore;
private final Project myProject;
private final Component myOwnerComponent;
private final Sdk[] myExistingSdks;
@@ -120,6 +120,8 @@ public class PythonSdkDetailsStep extends BaseListPopupStep<String> {
}
private void optionSelected(final String selectedValue) {
+ if (!MORE.equals(selectedValue) && myMore != null)
+ Disposer.dispose(myMore.getDisposable());
if (LOCAL.equals(selectedValue)) {
createLocalSdk();
}
@@ -129,7 +131,7 @@ public class PythonSdkDetailsStep extends BaseListPopupStep<String> {
else if (VIRTUALENV.equals(selectedValue)) {
createVirtualEnvSdk();
}
- else {
+ else if (myMore != null) {
myMore.show();
}
}
diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkType.java b/python/src/com/jetbrains/python/sdk/PythonSdkType.java
index 58de09370d37..460c875491ce 100644
--- a/python/src/com/jetbrains/python/sdk/PythonSdkType.java
+++ b/python/src/com/jetbrains/python/sdk/PythonSdkType.java
@@ -15,6 +15,7 @@
*/
package com.jetbrains.python.sdk;
+import com.google.common.collect.ImmutableMap;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.process.ProcessOutput;
@@ -771,16 +772,12 @@ public class PythonSdkType extends SdkType {
}
@NotNull
- public static List<String> getSysPathsFromScript(String bin_path) throws InvalidSdkException {
+ public static List<String> getSysPathsFromScript(@NotNull String binaryPath) throws InvalidSdkException {
String scriptFile = PythonHelpersLocator.getHelperPath("syspath.py");
// to handle the situation when PYTHONPATH contains ., we need to run the syspath script in the
// directory of the script itself - otherwise the dir in which we run the script (e.g. /usr/bin) will be added to SDK path
- String[] add_environment = getVirtualEnvAdditionalEnv(bin_path);
- final ProcessOutput run_result = PySdkUtil.getProcessOutput(
- new File(scriptFile).getParent(),
- new String[]{bin_path, scriptFile},
- add_environment, MINUTE
- );
+ final ProcessOutput run_result = PySdkUtil.getProcessOutput(new File(scriptFile).getParent(), new String[]{binaryPath, scriptFile},
+ getVirtualEnvExtraEnv(binaryPath), MINUTE);
if (!run_result.checkSuccess(LOG)) {
throw new InvalidSdkException(String.format("Failed to determine Python's sys.path value:\nSTDOUT: %s\nSTDERR: %s",
run_result.getStdout(),
@@ -789,15 +786,16 @@ public class PythonSdkType extends SdkType {
return run_result.getStdoutLines();
}
- // Returns a piece of env good as additional env for getProcessOutput.
+ /**
+ * Returns a piece of env good as additional env for getProcessOutput.
+ */
@Nullable
- public static String[] getVirtualEnvAdditionalEnv(String bin_path) {
- File virtualenv_root = getVirtualEnvRoot(bin_path);
- String[] add_environment = null;
- if (virtualenv_root != null) {
- add_environment = new String[]{"PATH=" + virtualenv_root + File.pathSeparator};
+ public static Map<String, String> getVirtualEnvExtraEnv(@NotNull String binaryPath) {
+ final File root = getVirtualEnvRoot(binaryPath);
+ if (root != null) {
+ return ImmutableMap.of("PATH", root.toString());
}
- return add_environment;
+ return null;
}
@Nullable
diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java b/python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java
index b4b968fdb108..fb77cdb26f8f 100644
--- a/python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java
+++ b/python/src/com/jetbrains/python/sdk/PythonSdkUpdater.java
@@ -32,7 +32,6 @@ import com.intellij.openapi.projectRoots.SdkModificator;
import com.intellij.openapi.projectRoots.SdkTypeId;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.startup.StartupActivity;
-import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtilRt;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
@@ -149,7 +148,7 @@ public class PythonSdkUpdater implements StartupActivity {
}
public static void updateSdk(@Nullable Project project, @Nullable Component ownerComponent, @NotNull final Sdk sdk, String skeletonsPath) throws InvalidSdkException {
- PySkeletonRefresher.refreshSkeletonsOfSdk(project, ownerComponent, skeletonsPath, new Ref<Boolean>(false), sdk); // NOTE: whole thing would need a rename
+ PySkeletonRefresher.refreshSkeletonsOfSdk(project, ownerComponent, skeletonsPath, sdk); // NOTE: whole thing would need a rename
if (!PySdkUtil.isRemote(sdk)) {
updateSysPath(sdk);
}
diff --git a/python/src/com/jetbrains/python/sdk/flavors/WinPythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/flavors/WinPythonSdkFlavor.java
index 9d22af8c03bf..3a9ac951509d 100644
--- a/python/src/com/jetbrains/python/sdk/flavors/WinPythonSdkFlavor.java
+++ b/python/src/com/jetbrains/python/sdk/flavors/WinPythonSdkFlavor.java
@@ -37,6 +37,8 @@ public class WinPythonSdkFlavor extends CPythonSdkFlavor {
"HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Python\\PythonCore", "python.exe",
"HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython", "ipy.exe");
+ private static Set<String> ourRegistryCache;
+
private WinPythonSdkFlavor() {
}
@@ -78,18 +80,25 @@ public class WinPythonSdkFlavor extends CPythonSdkFlavor {
}
public static void findInRegistry(Collection<String> candidates) {
- for (Map.Entry<String, String> entry : ourRegistryMap.entrySet()) {
- final String prefix = entry.getKey();
- final String exePath = entry.getValue();
- List<String> strings = WindowsRegistryUtil.readRegistryBranch(prefix);
- for (String string : strings) {
- final String path =
- WindowsRegistryUtil.readRegistryDefault(prefix + "\\" + string +
- "\\InstallPath");
- if (path != null) {
- File f = new File(path, exePath);
- if (f.exists()) {
- candidates.add(FileUtil.toSystemDependentName(f.getPath()));
+ fillRegistryCache();
+ candidates.addAll(ourRegistryCache);
+ }
+
+ private static void fillRegistryCache() {
+ if (ourRegistryCache == null) {
+ ourRegistryCache = new HashSet<String>();
+ for (Map.Entry<String, String> entry : ourRegistryMap.entrySet()) {
+ final String prefix = entry.getKey();
+ final String exePath = entry.getValue();
+ List<String> strings = WindowsRegistryUtil.readRegistryBranch(prefix);
+ for (String string : strings) {
+ final String path = WindowsRegistryUtil.readRegistryDefault(prefix + "\\" + string +
+ "\\InstallPath");
+ if (path != null) {
+ File f = new File(path, exePath);
+ if (f.exists()) {
+ ourRegistryCache.add(FileUtil.toSystemDependentName(f.getPath()));
+ }
}
}
}
diff --git a/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonGenerator.java b/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonGenerator.java
index 901472e6d2c3..7e099041e16a 100644
--- a/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonGenerator.java
+++ b/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonGenerator.java
@@ -15,6 +15,7 @@
*/
package com.jetbrains.python.sdk.skeletons;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.intellij.execution.process.ProcessOutput;
import com.intellij.openapi.application.ex.ApplicationManagerEx;
@@ -52,21 +53,12 @@ public class PySkeletonGenerator {
ENV_PATH_PARAM.put(IronPythonSdkFlavor.class, "IRONPYTHONPATH"); // TODO: Make strategy and move to PythonSdkFlavor?
}
-
protected static final Logger LOG = Logger.getInstance("#" + PySkeletonGenerator.class.getName());
-
-
protected static final int MINUTE = 60 * 1000;
-
protected static final String GENERATOR3 = "generator3.py";
- private static final String[] EMPTY_ENVS = new String[0];
private final String mySkeletonsPath;
- /**
- * Env variables to be added to skeleton generator
- */
- @NotNull
- private final String[] myEnvs;
+ @NotNull private final Map<String, String> myEnv;
public void finishSkeletonsGeneration() {
}
@@ -85,7 +77,6 @@ public class PySkeletonGenerator {
}
}
-
/**
* @param skeletonPath path where skeletons should be generated
* @param pySdk SDK
@@ -94,11 +85,11 @@ public class PySkeletonGenerator {
public PySkeletonGenerator(String skeletonPath, @NotNull final Sdk pySdk, @Nullable final String currentFolder) {
mySkeletonsPath = skeletonPath;
final PythonSdkFlavor flavor = PythonSdkFlavor.getFlavor(pySdk);
- if ((currentFolder != null) && (flavor != null) && ENV_PATH_PARAM.containsKey(flavor.getClass())) {
- myEnvs = new String[]{String.format("%s=%s", ENV_PATH_PARAM.get(flavor.getClass()), currentFolder)};
+ if (currentFolder != null && flavor != null && ENV_PATH_PARAM.containsKey(flavor.getClass())) {
+ myEnv = ImmutableMap.of(ENV_PATH_PARAM.get(flavor.getClass()), currentFolder);
}
else {
- myEnvs = EMPTY_ENVS;
+ myEnv = Collections.emptyMap();
}
}
@@ -171,23 +162,19 @@ public class PySkeletonGenerator {
if (modfilename != null) {
commandLine.add(modfilename);
}
- final List<String> envs = new ArrayList<String>(Arrays.asList(myEnvs));
- final String[] virtualEnvAdditionalEnv = PythonSdkType.getVirtualEnvAdditionalEnv(binaryPath);
- if (virtualEnvAdditionalEnv != null) {
- envs.addAll(Arrays.asList(virtualEnvAdditionalEnv));
- }
+ final Map<String, String> extraEnv = PythonSdkType.getVirtualEnvExtraEnv(binaryPath);
+ final Map<String, String> env = extraEnv != null ? PySdkUtil.mergeEnvVariables(myEnv, extraEnv) : myEnv;
- return getProcessOutput(parent_dir, ArrayUtil.toStringArray(commandLine), envs.toArray(new String[envs.size()]),
- MINUTE * 10
- );
+ return getProcessOutput(parent_dir, ArrayUtil.toStringArray(commandLine), env, MINUTE * 10);
}
- protected ProcessOutput getProcessOutput(String homePath, String[] commandLine, String[] env, int timeout) throws InvalidSdkException {
+ protected ProcessOutput getProcessOutput(String homePath, String[] commandLine, Map<String, String> extraEnv,
+ int timeout) throws InvalidSdkException {
return PySdkUtil.getProcessOutput(
homePath,
commandLine,
- env,
+ extraEnv,
timeout
);
}
@@ -207,7 +194,7 @@ public class PySkeletonGenerator {
"-d", mySkeletonsPath, // output dir
"-b", // for builtins
},
- PythonSdkType.getVirtualEnvAdditionalEnv(binaryPath), MINUTE * 5
+ PythonSdkType.getVirtualEnvExtraEnv(binaryPath), MINUTE * 5
);
runResult.checkSuccess(LOG);
LOG.info("Rebuilding builtin skeletons took " + (System.currentTimeMillis() - startTime) + " ms");
@@ -228,7 +215,7 @@ public class PySkeletonGenerator {
final ProcessOutput process = getProcessOutput(parentDir,
ArrayUtil.toStringArray(cmd),
- PythonSdkType.getVirtualEnvAdditionalEnv(homePath),
+ PythonSdkType.getVirtualEnvExtraEnv(homePath),
MINUTE * 4); // see PY-3898
LOG.info("Retrieving binary module list took " + (System.currentTimeMillis() - startTime) + " ms");
diff --git a/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java b/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java
index 4e4a43102202..b1e8ca19acf8 100644
--- a/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java
+++ b/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java
@@ -19,9 +19,6 @@ import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.execution.ExecutionException;
-import com.intellij.notification.Notification;
-import com.intellij.notification.NotificationType;
-import com.intellij.notification.Notifications;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger;
@@ -31,7 +28,6 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.util.Pair;
-import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
@@ -48,7 +44,6 @@ import com.jetbrains.python.PyNames;
import com.jetbrains.python.codeInsight.userSkeletons.PyUserSkeletonsUtil;
import com.jetbrains.python.packaging.PyExternalProcessException;
import com.jetbrains.python.packaging.PyPackageManager;
-import com.jetbrains.python.packaging.PyPackageManagerImpl;
import com.jetbrains.python.psi.resolve.PythonSdkPathCache;
import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
import com.jetbrains.python.sdk.InvalidSdkException;
@@ -105,10 +100,6 @@ public class PySkeletonRefresher {
private PySkeletonGenerator mySkeletonsGenerator;
- public static void refreshSkeletonsOfSdk(@NotNull Project project, @NotNull Sdk sdk) throws InvalidSdkException {
- refreshSkeletonsOfSdk(project, null, PythonSdkType.findSkeletonsPath(sdk), new Ref<Boolean>(false), sdk);
- }
-
public static synchronized boolean isGeneratingSkeletons() {
return ourGeneratingCount > 0;
}
@@ -120,7 +111,6 @@ public class PySkeletonRefresher {
public static void refreshSkeletonsOfSdk(@Nullable Project project,
Component ownerComponent,
String skeletonsPath,
- @Nullable Ref<Boolean> migrationFlag,
@NotNull Sdk sdk)
throws InvalidSdkException {
final Map<String, List<String>> errors = new TreeMap<String, List<String>>();
@@ -137,7 +127,7 @@ public class PySkeletonRefresher {
changeGeneratingSkeletons(1);
try {
- List<String> sdkErrors = refresher.regenerateSkeletons(checker, migrationFlag);
+ List<String> sdkErrors = refresher.regenerateSkeletons(checker);
if (sdkErrors.size() > 0) {
String sdkName = sdk.getName();
List<String> knownErrors = errors.get(sdkName);
@@ -286,8 +276,7 @@ public class PySkeletonRefresher {
return mySkeletonsPath;
}
- public List<String> regenerateSkeletons(@Nullable SkeletonVersionChecker cachedChecker,
- @Nullable Ref<Boolean> migrationFlag) throws InvalidSdkException {
+ public List<String> regenerateSkeletons(@Nullable SkeletonVersionChecker cachedChecker) throws InvalidSdkException {
final List<String> errorList = new SmartList<String>();
final String homePath = mySdk.getHomePath();
final String skeletonsPath = getSkeletonsPath();
@@ -299,14 +288,13 @@ public class PySkeletonRefresher {
final String readablePath = FileUtil.getLocationRelativeToUserHome(homePath);
mySkeletonsGenerator.prepare();
-
myBlacklist = loadBlacklist();
indicate(PyBundle.message("sdk.gen.querying.$0", readablePath));
// get generator version and binary libs list in one go
- final PySkeletonGenerator.ListBinariesResult binaries =
- mySkeletonsGenerator.listBinaries(mySdk, calculateExtraSysPath(mySdk, getSkeletonsPath()));
+ final String extraSysPath = calculateExtraSysPath(mySdk, getSkeletonsPath());
+ final PySkeletonGenerator.ListBinariesResult binaries = mySkeletonsGenerator.listBinaries(mySdk, extraSysPath);
myGeneratorVersion = binaries.generatorVersion;
myPregeneratedSkeletons = findPregeneratedSkeletons();
@@ -325,77 +313,19 @@ public class PySkeletonRefresher {
final SkeletonHeader oldHeader = readSkeletonHeader(builtinsFile);
final boolean oldOrNonExisting = oldHeader == null || oldHeader.getVersion() == 0;
- if (migrationFlag != null && !migrationFlag.get() && oldOrNonExisting) {
- migrationFlag.set(true);
- Notifications.Bus.notify(
- new Notification(
- PythonSdkType.SKELETONS_TOPIC, PyBundle.message("sdk.gen.notify.converting.old.skels"),
- PyBundle.message("sdk.gen.notify.converting.text"),
- NotificationType.INFORMATION
- )
- );
- }
-
if (myPregeneratedSkeletons != null && oldOrNonExisting) {
- indicate("Unpacking pregenerated skeletons...");
- try {
- final VirtualFile jar = JarFileSystem.getInstance().getVirtualFileForJar(myPregeneratedSkeletons);
- if (jar != null) {
- ZipUtil.extract(new File(jar.getPath()),
- new File(getSkeletonsPath()), null);
- }
- }
- catch (IOException e) {
- LOG.info("Error unpacking pregenerated skeletons", e);
- }
+ unpackPreGeneratedSkeletons();
}
if (oldOrNonExisting) {
- final Sdk base = PythonSdkType.getInstance().getVirtualEnvBaseSdk(mySdk);
- if (base != null) {
- indicate("Copying base SDK skeletons for virtualenv...");
- final String baseSkeletonsPath = PythonSdkType.getSkeletonsPath(PathManager.getSystemPath(), base.getHomePath());
- final PySkeletonGenerator.ListBinariesResult baseBinaries =
- mySkeletonsGenerator.listBinaries(base, calculateExtraSysPath(base, baseSkeletonsPath));
- for (Map.Entry<String, PyBinaryItem> entry : binaries.modules.entrySet()) {
- final String module = entry.getKey();
- final PyBinaryItem binary = entry.getValue();
- final PyBinaryItem baseBinary = baseBinaries.modules.get(module);
- final File fromFile = getSkeleton(module, baseSkeletonsPath);
- if (baseBinaries.modules.containsKey(module) &&
- fromFile.exists() &&
- binary.length() == baseBinary.length()) { // Weak binary modules equality check
- final File toFile = fromFile.isDirectory() ?
- getPackageSkeleton(module, skeletonsPath) :
- getModuleSkeleton(module, skeletonsPath);
- try {
- FileUtil.copy(fromFile, toFile);
- }
- catch (IOException e) {
- LOG.info("Error copying base virtualenv SDK skeleton for " + module, e);
- }
- }
- }
- }
+ copyBaseSdkSkeletonsToVirtualEnv(skeletonsPath, binaries);
}
- final SkeletonHeader newHeader = readSkeletonHeader(builtinsFile);
- final boolean mustUpdateBuiltins = myPregeneratedSkeletons == null &&
- (newHeader == null || newHeader.getVersion() < myVersionChecker.getBuiltinVersion());
- if (mustUpdateBuiltins) {
- indicate(PyBundle.message("sdk.gen.updating.builtins.$0", readablePath));
- mySkeletonsGenerator.generateBuiltinSkeletons(mySdk);
- if (myProject != null) {
- PythonSdkPathCache.getInstance(myProject, mySdk).clearBuiltins();
- }
- }
+ final boolean builtinsUpdated = updateSkeletonsForBuiltins(readablePath, builtinsFile);
if (!binaries.modules.isEmpty()) {
-
indicate(PyBundle.message("sdk.gen.updating.$0", readablePath));
-
- List<UpdateResult> updateErrors = updateOrCreateSkeletons(binaries.modules); //Skeletons regeneration
-
+ final List<UpdateResult> updateErrors = updateOrCreateSkeletons(binaries.modules);
if (updateErrors.size() > 0) {
indicateMinor(BLACKLIST_FILE_NAME);
for (UpdateResult error : updateErrors) {
@@ -410,7 +340,6 @@ public class PySkeletonRefresher {
}
indicate(PyBundle.message("sdk.gen.reloading"));
-
mySkeletonsGenerator.refreshGeneratedSkeletons();
if (!oldOrNonExisting) {
@@ -419,14 +348,15 @@ public class PySkeletonRefresher {
}
if (PySdkUtil.isRemote(mySdk)) {
try {
- ((PyPackageManagerImpl)PyPackageManager.getInstance(mySdk)).loadPackages();
+ // Force loading packages
+ PyPackageManager.getInstance(mySdk).getPackages(false);
}
catch (PyExternalProcessException e) {
// ignore - already logged
}
}
- if ((mustUpdateBuiltins || PySdkUtil.isRemote(mySdk)) && myProject != null) {
+ if ((builtinsUpdated || PySdkUtil.isRemote(mySdk)) && myProject != null) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
@@ -438,6 +368,64 @@ public class PySkeletonRefresher {
return errorList;
}
+ private boolean updateSkeletonsForBuiltins(String readablePath, File builtinsFile) throws InvalidSdkException {
+ final SkeletonHeader newHeader = readSkeletonHeader(builtinsFile);
+ final boolean mustUpdateBuiltins = myPregeneratedSkeletons == null &&
+ (newHeader == null || newHeader.getVersion() < myVersionChecker.getBuiltinVersion());
+ if (mustUpdateBuiltins) {
+ indicate(PyBundle.message("sdk.gen.updating.builtins.$0", readablePath));
+ mySkeletonsGenerator.generateBuiltinSkeletons(mySdk);
+ if (myProject != null) {
+ PythonSdkPathCache.getInstance(myProject, mySdk).clearBuiltins();
+ }
+ }
+ return mustUpdateBuiltins;
+ }
+
+ private void copyBaseSdkSkeletonsToVirtualEnv(String skeletonsPath, PySkeletonGenerator.ListBinariesResult binaries)
+ throws InvalidSdkException {
+ final Sdk base = PythonSdkType.getInstance().getVirtualEnvBaseSdk(mySdk);
+ if (base != null) {
+ indicate("Copying base SDK skeletons for virtualenv...");
+ final String baseSkeletonsPath = PythonSdkType.getSkeletonsPath(PathManager.getSystemPath(), base.getHomePath());
+ final PySkeletonGenerator.ListBinariesResult baseBinaries =
+ mySkeletonsGenerator.listBinaries(base, calculateExtraSysPath(base, baseSkeletonsPath));
+ for (Map.Entry<String, PyBinaryItem> entry : binaries.modules.entrySet()) {
+ final String module = entry.getKey();
+ final PyBinaryItem binary = entry.getValue();
+ final PyBinaryItem baseBinary = baseBinaries.modules.get(module);
+ final File fromFile = getSkeleton(module, baseSkeletonsPath);
+ if (baseBinaries.modules.containsKey(module) &&
+ fromFile.exists() &&
+ binary.length() == baseBinary.length()) { // Weak binary modules equality check
+ final File toFile = fromFile.isDirectory() ?
+ getPackageSkeleton(module, skeletonsPath) :
+ getModuleSkeleton(module, skeletonsPath);
+ try {
+ FileUtil.copy(fromFile, toFile);
+ }
+ catch (IOException e) {
+ LOG.info("Error copying base virtualenv SDK skeleton for " + module, e);
+ }
+ }
+ }
+ }
+ }
+
+ private void unpackPreGeneratedSkeletons() throws InvalidSdkException {
+ indicate("Unpacking pregenerated skeletons...");
+ try {
+ final VirtualFile jar = JarFileSystem.getInstance().getVirtualFileForJar(myPregeneratedSkeletons);
+ if (jar != null) {
+ ZipUtil.extract(new File(jar.getPath()),
+ new File(getSkeletonsPath()), null);
+ }
+ }
+ catch (IOException e) {
+ LOG.info("Error unpacking pregenerated skeletons", e);
+ }
+ }
+
@Nullable
public static SkeletonHeader readSkeletonHeader(@NotNull File file) {
try {
@@ -835,7 +823,7 @@ public class PySkeletonRefresher {
return null;
}
LOG.info("Pregenerated skeletons root is " + root);
- final String versionString = mySdk.getVersionString();
+ @NonNls final String versionString = mySdk.getVersionString();
if (versionString == null) {
return null;
}
diff --git a/python/src/com/jetbrains/python/statistics/PyPackageUsagesCollector.java b/python/src/com/jetbrains/python/statistics/PyPackageUsagesCollector.java
index f8b4adde5b33..8e029e042aa0 100644
--- a/python/src/com/jetbrains/python/statistics/PyPackageUsagesCollector.java
+++ b/python/src/com/jetbrains/python/statistics/PyPackageUsagesCollector.java
@@ -25,7 +25,7 @@ import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.jetbrains.python.packaging.PyPIPackageUtil;
-import com.jetbrains.python.packaging.PyPackageManagerImpl;
+import com.jetbrains.python.packaging.PyPackageManager;
import com.jetbrains.python.packaging.PyRequirement;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
@@ -46,12 +46,12 @@ public class PyPackageUsagesCollector extends AbstractApplicationUsagesCollector
public Set<UsageDescriptor> getProjectUsages(@NotNull Project project) throws CollectUsagesException {
final Set<UsageDescriptor> result = new HashSet<UsageDescriptor>();
for(final Module m: ModuleManager.getInstance(project).getModules()) {
- Sdk pythonSdk = PythonSdkType.findPythonSdk(m);
+ final Sdk pythonSdk = PythonSdkType.findPythonSdk(m);
if (pythonSdk != null) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
- List<PyRequirement> requirements = PyPackageManagerImpl.getRequirements(m);
+ List<PyRequirement> requirements = PyPackageManager.getInstance(pythonSdk).getRequirements(m);
if (requirements != null) {
Collection<String> packages = new HashSet<String>(PyPIPackageUtil.INSTANCE.getPackageNames());
for (PyRequirement requirement : requirements) {
diff --git a/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java b/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java
index 93c44f7f2a17..de9f7bf5a5ca 100644
--- a/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java
+++ b/python/src/com/jetbrains/python/structureView/PyStructureViewElement.java
@@ -23,7 +23,6 @@ import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.ui.LayeredIcon;
-import com.intellij.util.Function;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.*;
import icons.PythonIcons;
@@ -33,9 +32,6 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
-import static com.intellij.openapi.util.text.StringUtil.join;
-import static com.intellij.openapi.util.text.StringUtil.notNullize;
-
/**
* Handles nodes in Structure View.
* @author yole
@@ -237,32 +233,15 @@ public class PyStructureViewElement implements StructureViewTreeElement {
@NotNull
@Override
public ItemPresentation getPresentation() {
+ final ItemPresentation presentation = myElement.getPresentation();
return new ColoredItemPresentation() {
+ @Nullable
+ @Override
public String getPresentableText() {
- final String unnamed = "<unnamed>";
- if (myElement instanceof PyFunction) {
- PyParameterList argList = ((PyFunction) myElement).getParameterList();
- StringBuilder result = new StringBuilder(notNullize(myElement.getName(), unnamed));
- result.append(argList.getPresentableText(true));
- return result.toString();
- }
- else if (myElement instanceof PyClass && myElement.isValid()) {
- PyClass c = (PyClass) myElement;
- StringBuilder result = new StringBuilder(notNullize(c.getName(), unnamed));
- PyExpression[] superClassExpressions = c.getSuperClassExpressions();
- if (superClassExpressions.length > 0) {
- result.append("(");
- result.append(join(Arrays.asList(superClassExpressions), new Function<PyExpression, String>() {
- public String fun(PyExpression expr) {
- String name = expr.getText();
- return notNullize(name, unnamed);
- }
- }, ", "));
- result.append(")");
- }
- return result.toString();
+ if (myElement instanceof PyFile) {
+ return myElement.getName();
}
- return notNullize(myElement.getName(), unnamed);
+ return presentation != null ? presentation.getPresentableText() : PyNames.UNNAMED_ELEMENT;
}
@Nullable
diff --git a/python/src/com/jetbrains/python/testing/PyRerunFailedTestsAction.java b/python/src/com/jetbrains/python/testing/PyRerunFailedTestsAction.java
index 0b922514e52e..01c8ac68540c 100644
--- a/python/src/com/jetbrains/python/testing/PyRerunFailedTestsAction.java
+++ b/python/src/com/jetbrains/python/testing/PyRerunFailedTestsAction.java
@@ -28,33 +28,29 @@ import com.intellij.execution.testframework.actions.AbstractRerunFailedTestsActi
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComponentContainer;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.search.GlobalSearchScope;
import com.jetbrains.python.run.AbstractPythonRunConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
-/*
- * User: ktisha
- */
public class PyRerunFailedTestsAction extends AbstractRerunFailedTestsAction {
-
protected PyRerunFailedTestsAction(@NotNull ComponentContainer componentContainer) {
super(componentContainer);
}
@Override
@Nullable
- public MyRunProfile getRunProfile() {
+ protected MyRunProfile getRunProfile(@NotNull ExecutionEnvironment environment) {
final TestFrameworkRunningModel model = getModel();
- if (model == null) return null;
- final AbstractPythonRunConfiguration configuration = (AbstractPythonRunConfiguration)model.getProperties().getConfiguration();
- return new MyTestRunProfile(configuration);
+ if (model == null) {
+ return null;
+ }
+ return new MyTestRunProfile((AbstractPythonRunConfiguration)model.getProperties().getConfiguration());
}
-
private class MyTestRunProfile extends MyRunProfile {
public MyTestRunProfile(RunConfigurationBase configuration) {
@@ -71,6 +67,19 @@ public class PyRerunFailedTestsAction extends AbstractRerunFailedTestsAction {
@Override
public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment env) throws ExecutionException {
final AbstractPythonRunConfiguration configuration = ((AbstractPythonRunConfiguration)getPeer());
+
+ // If configuration wants to take care about rerun itself
+ if (configuration instanceof TestRunConfigurationReRunResponsible) {
+ // TODO: Extract method
+ final Set<PsiElement> failedTestElements = new HashSet<PsiElement>();
+ for (final AbstractTestProxy proxy : getFailedTests(getProject())) {
+ final Location<?> location = proxy.getLocation(getProject(), GlobalSearchScope.allScope(getProject()));
+ if (location != null) {
+ failedTestElements.add(location.getPsiElement());
+ }
+ }
+ return ((TestRunConfigurationReRunResponsible)configuration).rerunTests(executor, env, failedTestElements);
+ }
return new FailedPythonTestCommandLineStateBase(configuration, env,
(PythonTestCommandLineStateBase)configuration.getState(executor, env));
}
diff --git a/python/src/com/jetbrains/python/testing/PythonTestCommandLineStateBase.java b/python/src/com/jetbrains/python/testing/PythonTestCommandLineStateBase.java
index 1a5aafe59051..c73a488dc7f5 100644
--- a/python/src/com/jetbrains/python/testing/PythonTestCommandLineStateBase.java
+++ b/python/src/com/jetbrains/python/testing/PythonTestCommandLineStateBase.java
@@ -63,6 +63,7 @@ public abstract class PythonTestCommandLineStateBase extends PythonCommandLineSt
myConfiguration = configuration;
}
+ @Override
@NotNull
protected ConsoleView createAndAttachConsole(Project project, ProcessHandler processHandler, Executor executor)
throws ExecutionException {
@@ -89,6 +90,7 @@ public abstract class PythonTestCommandLineStateBase extends PythonCommandLineSt
return new PythonTRunnerConsoleProperties(myConfiguration, executor, false);
}
+ @Override
public GeneralCommandLine generateCommandLine() throws ExecutionException {
GeneralCommandLine cmd = super.generateCommandLine();
@@ -135,7 +137,7 @@ public abstract class PythonTestCommandLineStateBase extends PythonCommandLineSt
PyRerunFailedTestsAction rerunFailedTestsAction = new PyRerunFailedTestsAction(console);
if (console instanceof SMTRunnerConsoleView) {
- rerunFailedTestsAction.init(((BaseTestsOutputConsoleView)console).getProperties(), getEnvironment());
+ rerunFailedTestsAction.init(((BaseTestsOutputConsoleView)console).getProperties());
rerunFailedTestsAction.setModelProvider(new Getter<TestFrameworkRunningModel>() {
@Override
public TestFrameworkRunningModel get() {
diff --git a/python/src/com/jetbrains/python/testing/TestRunConfigurationReRunResponsible.java b/python/src/com/jetbrains/python/testing/TestRunConfigurationReRunResponsible.java
new file mode 100644
index 000000000000..a8e9e1f8d5e4
--- /dev/null
+++ b/python/src/com/jetbrains/python/testing/TestRunConfigurationReRunResponsible.java
@@ -0,0 +1,30 @@
+package com.jetbrains.python.testing;
+
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.Executor;
+import com.intellij.execution.configurations.RunProfileState;
+import com.intellij.execution.runners.ExecutionEnvironment;
+import com.intellij.psi.PsiElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+
+/**
+ * Configuration that handles rerun failed tests itself.
+ *
+ * @author Ilya.Kazakevich
+ */
+public interface TestRunConfigurationReRunResponsible {
+ /**
+ * Rerun failed tests
+ * @param executor test executor
+ * @param environment test environment
+ * @param failedTests a pack of psi elements, indicating failed tests (to retrn)
+ * @return state to run or null if no rerun actions found (i.e. no errors in failedTest, empty etc)
+ * @throws ExecutionException failed to run
+ */
+ @Nullable
+ RunProfileState rerunTests(@NotNull final Executor executor, @NotNull final ExecutionEnvironment environment,
+ @NotNull Collection<PsiElement> failedTests) throws ExecutionException;
+}
diff --git a/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java b/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java
index 9d763e2ce286..64adf2093028 100644
--- a/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java
+++ b/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java
@@ -35,7 +35,6 @@ import com.intellij.util.ui.update.Update;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.packaging.PyExternalProcessException;
import com.jetbrains.python.packaging.PyPackageManager;
-import com.jetbrains.python.packaging.PyPackageManagerImpl;
import com.jetbrains.python.sdk.PySdkUtil;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
@@ -131,9 +130,9 @@ public class VFSTestFrameworkListener implements ApplicationComponent {
LOG.info("Searching test runner in empty sdk");
return null;
}
- final PyPackageManagerImpl packageManager = (PyPackageManagerImpl)PyPackageManager.getInstance(sdk);
+ final PyPackageManager packageManager = PyPackageManager.getInstance(sdk);
try {
- return packageManager.findInstalledPackage(testPackageName) != null;
+ return packageManager.findPackage(testPackageName, false) != null;
}
catch (PyExternalProcessException e) {
LOG.info("Can't load package list " + e.getMessage());
diff --git a/python/src/com/jetbrains/python/testing/pytest/PyTestConfigurationProducer.java b/python/src/com/jetbrains/python/testing/pytest/PyTestConfigurationProducer.java
index f988dbd315e6..8dc27d4fd995 100644
--- a/python/src/com/jetbrains/python/testing/pytest/PyTestConfigurationProducer.java
+++ b/python/src/com/jetbrains/python/testing/pytest/PyTestConfigurationProducer.java
@@ -31,7 +31,6 @@ import com.intellij.webcore.packaging.PackageVersionComparator;
import com.jetbrains.python.packaging.PyExternalProcessException;
import com.jetbrains.python.packaging.PyPackage;
import com.jetbrains.python.packaging.PyPackageManager;
-import com.jetbrains.python.packaging.PyPackageManagerImpl;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.psi.PyFunction;
@@ -97,9 +96,9 @@ public class PyTestConfigurationProducer extends PythonTestConfigurationProducer
if (pyFunction != null) {
keywords = pyFunction.getName();
if (pyClass != null) {
- final PyPackageManagerImpl packageManager = (PyPackageManagerImpl)PyPackageManager.getInstance(sdk);
+ final PyPackageManager packageManager = PyPackageManager.getInstance(sdk);
try {
- final PyPackage pytestPackage = packageManager.findInstalledPackage("pytest");
+ final PyPackage pytestPackage = packageManager.findPackage("pytest", false);
if (pytestPackage != null && PackageVersionComparator.VERSION_COMPARATOR.compare(pytestPackage.getVersion(), "2.3.3") >= 0) {
keywords = pyClass.getName() + " and " + keywords;
}
diff --git a/python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java b/python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java
index a7faa6ebb98f..22bb1d308d2c 100644
--- a/python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java
+++ b/python/src/com/jetbrains/python/validation/Pep8ExternalAnnotator.java
@@ -15,6 +15,7 @@
*/
package com.jetbrains.python.validation;
+import com.google.common.collect.ImmutableMap;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInsight.intention.IntentionAction;
@@ -173,7 +174,7 @@ public class Pep8ExternalAnnotator extends ExternalAnnotator<Pep8ExternalAnnotat
options.add("-");
ProcessOutput output = PySdkUtil.getProcessOutput(new File(collectedInfo.interpreterPath).getParent(),
ArrayUtil.toStringArray(options),
- new String[] { "PYTHONUNBUFFERED=1" },
+ ImmutableMap.of("PYTHONBUFFERED", "1"),
10000,
collectedInfo.fileText.getBytes(), false);
diff --git a/python/src/com/jetbrains/python/validation/StringLiteralQuotesAnnotator.java b/python/src/com/jetbrains/python/validation/StringLiteralQuotesAnnotator.java
index 0666e8319caa..997123351d36 100644
--- a/python/src/com/jetbrains/python/validation/StringLiteralQuotesAnnotator.java
+++ b/python/src/com/jetbrains/python/validation/StringLiteralQuotesAnnotator.java
@@ -18,8 +18,10 @@ package com.jetbrains.python.validation;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
+import com.jetbrains.python.PyBundle;
import com.jetbrains.python.psi.PyStringLiteralExpression;
import com.jetbrains.python.psi.impl.PyStringLiteralExpressionImpl;
+import org.jetbrains.annotations.NotNull;
import java.util.List;
@@ -29,17 +31,16 @@ import java.util.List;
* @author dcheryasov
*/
public class StringLiteralQuotesAnnotator extends PyAnnotator {
- public static final String MISSING_Q = "Missing closing quote";
private static final String TRIPLE_QUOTES = "\"\"\"";
private static final String TRIPLE_APOS = "'''";
public void visitPyStringLiteralExpression(final PyStringLiteralExpression node) {
- List<ASTNode> stringNodes = node.getStringNodes();
+ final List<ASTNode> stringNodes = node.getStringNodes();
for (ASTNode stringNode : stringNodes) {
- boolean foundError;
- String nodeText = stringNode.getText();
- int index = PyStringLiteralExpressionImpl.getPrefixLength(nodeText);
- String unprefixed = nodeText.substring(index);
+ final String nodeText = stringNode.getText();
+ final int index = PyStringLiteralExpressionImpl.getPrefixLength(nodeText);
+ final String unprefixed = nodeText.substring(index);
+ final boolean foundError;
if (StringUtil.startsWith(unprefixed, TRIPLE_QUOTES)) {
foundError = checkTripleQuotedString(stringNode, unprefixed, TRIPLE_QUOTES);
}
@@ -49,23 +50,33 @@ public class StringLiteralQuotesAnnotator extends PyAnnotator {
else {
foundError = checkQuotedString(stringNode, unprefixed);
}
- if (foundError) break;
+ if (foundError) {
+ break;
+ }
}
}
- private boolean checkQuotedString(ASTNode stringNode, String nodeText) {
- char firstQuote = nodeText.charAt(0);
- int lastChar = nodeText.length()-1;
- if (lastChar == 0 || nodeText.charAt(lastChar) != firstQuote ||
- (nodeText.charAt(lastChar-1) == '\\' && (lastChar == 1 || nodeText.charAt(lastChar-2) != '\\'))) {
- getHolder().createErrorAnnotation(stringNode, MISSING_Q + " [" + firstQuote + "]");
+ private boolean checkQuotedString(@NotNull ASTNode stringNode, @NotNull String nodeText) {
+ final char firstQuote = nodeText.charAt(0);
+ final char lastChar = nodeText.charAt(nodeText.length() - 1);
+ int precedingBackslashCount = 0;
+ for (int i = nodeText.length() - 2; i >= 0; i--) {
+ if (nodeText.charAt(i) == '\\') {
+ precedingBackslashCount++;
+ }
+ else {
+ break;
+ }
+ }
+ if (nodeText.length() == 1 || lastChar != firstQuote || precedingBackslashCount % 2 != 0) {
+ getHolder().createErrorAnnotation(stringNode, PyBundle.message("ANN.missing.closing.quote", firstQuote));
return true;
}
return false;
}
- private boolean checkTripleQuotedString(ASTNode stringNode, String text, final String quotes) {
- if (text.length() < 6 || !text.endsWith(quotes)) {
+ private boolean checkTripleQuotedString(@NotNull ASTNode stringNode, @NotNull String text, @NotNull String quotes) {
+ if (text.length() < 6 || !text.endsWith(quotes)) {
int startOffset = StringUtil.trimTrailing(stringNode.getText()).lastIndexOf('\n');
if (startOffset < 0) {
startOffset = stringNode.getTextRange().getStartOffset();
@@ -73,8 +84,8 @@ public class StringLiteralQuotesAnnotator extends PyAnnotator {
else {
startOffset = stringNode.getTextRange().getStartOffset() + startOffset + 1;
}
- TextRange highlightRange = new TextRange(startOffset, stringNode.getTextRange().getEndOffset());
- getHolder().createErrorAnnotation(highlightRange, "Missing closing triple quotes");
+ final TextRange highlightRange = new TextRange(startOffset, stringNode.getTextRange().getEndOffset());
+ getHolder().createErrorAnnotation(highlightRange, PyBundle.message("ANN.missing.closing.triple.quotes"));
return true;
}
return false;