diff --git a/python/build/build-plugin.xml b/python/build/build-plugin.xml index bd723494de05..b4c6d4d2988b 100644 --- a/python/build/build-plugin.xml +++ b/python/build/build-plugin.xml @@ -1,197 +1,196 @@ - - + + - - + + - - + + - + - - - - - - + + + + + + - - - + + + - - - - - + + + + + - - + + - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - - + + + + + + + + + - - + + - - - + + + - - - - + + + + - - - - + + + + - - + - + - + - - - - + - + + + + + + \ No newline at end of file diff --git a/python/build/pycharm.sh b/python/build/pycharm.sh index 44f1a9e86824..04415d96db96 100644 --- a/python/build/pycharm.sh +++ b/python/build/pycharm.sh @@ -83,7 +83,7 @@ if [ -z "$PYCHARM_VM_OPTIONS" ]; then fi # isEap -if ["@@isEap@@" -eq "true" ] +if [ "@@isEap@@" -eq "true" ]; then $AGENT="-agentlib:yjpagent$BITS=disablej2ee,sessionname=pycharm" fi diff --git a/python/build/pycharm_build.gant b/python/build/pycharm_build.gant index 3cf89800e612..0e4f7c529900 100644 --- a/python/build/pycharm_build.gant +++ b/python/build/pycharm_build.gant @@ -486,10 +486,15 @@ def layoutMac(Map args, String target) { layout(target) { dir("bin") { fileset(dir: "$home/community/bin/mac") - fileset(dir: "$home/community/bin/nix") + fileset(dir: "$home/community/bin/nix") { + exclude(name: "idea.sh") + } fileset(dir: "$home/bin") { include(name: "*.jnilib") } + fileset(dir: "$home/python/build") { + include(name: "pycharm.sh") + } } dir("lib") { fileset(dir: "$home/community/build/conf/mac/lib") diff --git a/python/helpers/pycharm/django_manage.py b/python/helpers/pycharm/django_manage.py index 951ff61099a3..1602c37b1055 100644 --- a/python/helpers/pycharm/django_manage.py +++ b/python/helpers/pycharm/django_manage.py @@ -1,8 +1,13 @@ #!/usr/bin/env python from pycharm.fix_getpass import fixGetpass -from django.core.management import execute_manager from pycharm import django_settings +import os +from runpy import run_module + +manage_file = os.getenv('PYCHARM_DJANGO_MANAGE_MODULE') +if not manage_file: + manage_file = 'manage' if __name__ == "__main__": fixGetpass() - execute_manager(django_settings) + run_module(manage_file) diff --git a/python/helpers/pycharm/utrunner.py b/python/helpers/pycharm/utrunner.py index bf42c8291b90..82980c27c80a 100644 --- a/python/helpers/pycharm/utrunner.py +++ b/python/helpers/pycharm/utrunner.py @@ -3,9 +3,19 @@ import os import imp import sys import types +import re from pycharm.tcmessages import TeamcityServiceMessages from pycharm.tcunittest import TeamcityTestRunner +try: + import unittest2 as unittest +except: + # python version doesn't have unittest2 + pass + +PYTHON_VERSION_MAJOR = sys.version_info[0] +PYTHON_VERSION_MINOR = sys.version_info[1] + ENABLE_DEBUG_LOGGING = False if os.getenv("UTRUNNER_ENABLE_DEBUG_LOGGING"): ENABLE_DEBUG_LOGGING = True @@ -39,9 +49,28 @@ def walkModules(modules, dirname, names): def loadModulesFromFolderRec(folder): modules = [] - os.path.walk(folder, walkModules, modules) + if PYTHON_VERSION_MAJOR == 3: + for root, dirs, files in os.walk(folder, walkModules, modules): + for name in files: + if name.endswith(".py"): + modules.append(loadSource(os.path.join(root, name))) + else: + os.path.walk(folder, walkModules, modules) + return modules +def loadModulesFromFolderUsingPattern(folder, pattern): + ''' loads modules from folder , + check if module name matches given pattern''' + modules = loadModulesFromFolderRec(folder) + result = [] + prog = re.compile(pattern) + + for module in modules: + if prog.match(module.__name__): + result.append(module) + return result + testLoader = unittest.TestLoader() all = unittest.TestSuite() @@ -53,12 +82,19 @@ for arg in sys.argv[1:]: a = arg.split("::") if len(a) == 1: # From module or folder - if a[0].endswith("/"): - debug("/ from folder " + a[0]) - modules = loadModulesFromFolderRec(a[0]) + a_splitted = a[0].split(";") + if len(a_splitted) != 1: + # means we have pattern to match against + if a_splitted[0].endswith("/"): + debug("/ from folder " + a_splitted[0] + ". Use pattern: " + a_splitted[1]) + modules = loadModulesFromFolderUsingPattern(a_splitted[0], a_splitted[1]) else: - debug("/ from module " + a[0]) - modules = [loadSource(a[0])] + if a[0].endswith("/"): + debug("/ from folder " + a[0]) + modules = loadModulesFromFolderRec(a[0]) + else: + debug("/ from module " + a[0]) + modules = [loadSource(a[0])] for module in modules: all.addTests(testLoader.loadTestsFromModule(module)._tests) diff --git a/python/helpers/pydev/pydevd_comm.py b/python/helpers/pydev/pydevd_comm.py index 34e1b219b96d..31b448993cff 100644 --- a/python/helpers/pydev/pydevd_comm.py +++ b/python/helpers/pydev/pydevd_comm.py @@ -48,11 +48,11 @@ each command has a format: 120 CMD_GET_COMPLETIONS JAVA 500 series diagnostics/ok - 901 VERSION either Version string (1.0) Currently just used at startup - 902 RETURN either Depends on caller - + 501 VERSION either Version string (1.0) Currently just used at startup + 502 RETURN either Depends on caller - 900 series: errors - 501 ERROR either - This is reserved for unexpected errors. + 901 ERROR either - This is reserved for unexpected errors. * JAVA - remote debugger, the java end * PYDB - pydevd, the python end @@ -755,7 +755,7 @@ class InternalGetCompletions(InternalThreadCommand): except: exc = GetExceptionTracebackStr() sys.stderr.write('%s\n' % (exc,)) - cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error evaluating expression " + exc) + cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error getting completion " + exc) dbg.writer.addCommand(cmd) diff --git a/python/helpers/pydev/pydevd_vars.py b/python/helpers/pydev/pydevd_vars.py index e4bef21924e2..164cf1613a8b 100644 --- a/python/helpers/pydev/pydevd_vars.py +++ b/python/helpers/pydev/pydevd_vars.py @@ -325,6 +325,11 @@ def resolveCompoundVariable(thread_id, frame_id, scope, attrs): del attrList[0] # globals are special, and they get a single dummy unused attribute else: var = frame.f_locals + type, _typeName, resolver = getType(var) + try: + resolver.resolve(var, attrList[0]) + except: + var = frame.f_globals for k in attrList: type, _typeName, resolver = getType(var) @@ -423,7 +428,7 @@ def changeAttrExpression(thread_id, frame_id, attr, expression): #default way (only works for changing it in the topmost frame) result = eval(expression, frame.f_globals, frame.f_locals) exec('%s=%s' % (attr, expression), frame.f_globals, frame.f_locals) - return result; + return result except Exception: diff --git a/python/pluginSrc/META-INF/plugin.xml b/python/pluginSrc/META-INF/plugin.xml index c69ad04b367c..b88d07d36e14 100644 --- a/python/pluginSrc/META-INF/plugin.xml +++ b/python/pluginSrc/META-INF/plugin.xml @@ -4,10 +4,11 @@ Pythonid Python - + Smart editing for Python scripts - 2.6.2 + 2.6.3 com.intellij.modules.ultimate + com.intellij.modules.java org.jetbrains.plugins.yaml JetBrains, Keith Lea @@ -22,6 +23,8 @@ + @@ -42,4 +45,6 @@ + + diff --git a/python/pluginSrc/com/jetbrains/python/module/PythonSdkChooserPanel.java b/python/pluginSrc/com/jetbrains/python/module/PythonSdkChooserPanel.java index 579492811e38..c97115b6a8b1 100644 --- a/python/pluginSrc/com/jetbrains/python/module/PythonSdkChooserPanel.java +++ b/python/pluginSrc/com/jetbrains/python/module/PythonSdkChooserPanel.java @@ -57,7 +57,7 @@ public class PythonSdkChooserPanel extends JComponent { myJdkChooser.setAllowedJdkTypes(new SdkType[]{PythonSdkType.getInstance()}); - final Sdk selectedJdk = project == null ? null : ProjectRootManager.getInstance(project).getProjectJdk(); + final Sdk selectedJdk = project == null ? null : ProjectRootManager.getInstance(project).getProjectSdk(); myJdkChooser.updateList(selectedJdk, null); } diff --git a/python/python-ide.iml b/python/python-ide.iml index 7216109fe347..40af0d8cc13f 100644 --- a/python/python-ide.iml +++ b/python/python-ide.iml @@ -17,6 +17,7 @@ + diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index 93f9a92322eb..b37d64a92fe9 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -41,7 +41,7 @@ serviceImplementation="com.jetbrains.python.psi.impl.PyElementGeneratorImpl"/> - + @@ -150,6 +150,8 @@ + @@ -274,6 +276,8 @@ + + @@ -363,6 +367,7 @@ id="Devmode.DumpPsi" class="com.jetbrains.python.devmode.DumpPsiToClipboardAction" text="Dump PSI to clipboard" + internal="true" > diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index 0d28e940bd9d..55fa59a81113 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -437,6 +437,7 @@ runcfg.captions.script_parameters_dialog=Enter script parameters runcfg.captions.interpreter_options_dialog=Enter interpreter options sdk.error.invalid.interpreter.name.$0=Invalid Python interpeter name '{0}'! sdk.select.path=Select Python Interpreter +runcfg.unittest.dlg.pattern=Pattern: # Consoles messages python.console=Python Console diff --git a/python/src/com/jetbrains/python/PythonModuleTypeBase.java b/python/src/com/jetbrains/python/PythonModuleTypeBase.java index c9500db06d15..4199f4b36aa5 100644 --- a/python/src/com/jetbrains/python/PythonModuleTypeBase.java +++ b/python/src/com/jetbrains/python/PythonModuleTypeBase.java @@ -25,7 +25,7 @@ public abstract class PythonModuleTypeBase extends Modu } public String getDescription() { - return "Provides facilities for developing Python applications"; + return "Provides facilities for developing Python and Django applications"; } public Icon getBigIcon() { diff --git a/python/src/com/jetbrains/python/buildout/BuildoutConfigurable.java b/python/src/com/jetbrains/python/buildout/BuildoutConfigurable.java index 657d6dc0d5eb..e4c6626a8321 100644 --- a/python/src/com/jetbrains/python/buildout/BuildoutConfigurable.java +++ b/python/src/com/jetbrains/python/buildout/BuildoutConfigurable.java @@ -80,7 +80,7 @@ public class BuildoutConfigurable implements Configurable, NonDefaultProjectConf @Override public Icon getIcon() { - return BuildoutFacetType.ourIcon; + return BuildoutFacetType.BUILDOUT_ICON; } @Override diff --git a/python/src/com/jetbrains/python/buildout/BuildoutFacetType.java b/python/src/com/jetbrains/python/buildout/BuildoutFacetType.java index 19eafd93a224..d999e0fa539b 100644 --- a/python/src/com/jetbrains/python/buildout/BuildoutFacetType.java +++ b/python/src/com/jetbrains/python/buildout/BuildoutFacetType.java @@ -4,11 +4,8 @@ import com.intellij.facet.Facet; import com.intellij.facet.FacetType; import com.intellij.facet.FacetTypeId; import com.intellij.facet.FacetTypeRegistry; -import com.intellij.ide.DataManager; -import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleType; -import com.intellij.openapi.project.Project; import com.intellij.openapi.util.IconLoader; import com.jetbrains.python.PythonModuleTypeBase; import org.jetbrains.annotations.NotNull; @@ -51,10 +48,10 @@ public class BuildoutFacetType extends FacetType"; + } return element.getName(); } diff --git a/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java b/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java index 130502b1d72c..213b96b78550 100644 --- a/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java @@ -169,24 +169,29 @@ public class PyOverrideImplementUtil { }, ArrayUtil.EMPTY_STRING_ARRAY); int startIndex = 0; - if (baseFunction.getReturnType() != PyNoneType.INSTANCE) { - statementBody.append("return "); - } - if (baseClass.isNewStyleClass()) { - statementBody.append(PyNames.SUPER); - statementBody.append("("); - final LanguageLevel langLevel = ((PyFile)pyClass.getContainingFile()).getLanguageLevel(); - if (!langLevel.isPy3K()) { - statementBody.append(pyClass.getName()).append(", ").append(PyUtil.getFirstParameterName(baseFunction)); - } - statementBody.append(").").append(baseFunction.getName()).append("("); - startIndex = 1; + if (PyNames.FAKE_OLD_BASE.equals(baseFunction.getContainingClass().getName())) { + statementBody.append("pass"); } else { - statementBody.append(baseClass.getName()).append(".").append(baseFunction.getName()).append("("); + if (baseFunction.getReturnType() != PyNoneType.INSTANCE) { + statementBody.append("return "); + } + if (baseClass.isNewStyleClass()) { + statementBody.append(PyNames.SUPER); + statementBody.append("("); + final LanguageLevel langLevel = ((PyFile)pyClass.getContainingFile()).getLanguageLevel(); + if (!langLevel.isPy3K()) { + statementBody.append(pyClass.getName()).append(", ").append(PyUtil.getFirstParameterName(baseFunction)); + } + statementBody.append(").").append(baseFunction.getName()).append("("); + startIndex = 1; + } + else { + statementBody.append(baseClass.getName()).append(".").append(baseFunction.getName()).append("("); + } + statementBody.append(StringUtil.join(paramTexts, startIndex, paramTexts.length, ", ")); + statementBody.append(")"); } - statementBody.append(StringUtil.join(paramTexts, startIndex, paramTexts.length, ", ")); - statementBody.append(")"); pyFunctionBuilder.statement(statementBody.toString()); return pyFunctionBuilder; diff --git a/python/src/com/jetbrains/python/console/PydevCompletionAutopopupBlockingHandler.java b/python/src/com/jetbrains/python/console/PydevCompletionAutopopupBlockingHandler.java new file mode 100644 index 000000000000..d5393f33f5b3 --- /dev/null +++ b/python/src/com/jetbrains/python/console/PydevCompletionAutopopupBlockingHandler.java @@ -0,0 +1,24 @@ +package com.jetbrains.python.console; + +import com.intellij.codeInsight.editorActions.TypedHandlerDelegate; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Key; +import com.intellij.psi.PsiFile; + +/** + * @author oleg + * @date 10/26/10 + */ +public class PydevCompletionAutopopupBlockingHandler extends TypedHandlerDelegate { + + public static final Key REPL_KEY = new Key("python.repl.console.editor"); + + @Override + public Result checkAutoPopup(final char charTyped, final Project project, final Editor editor, final PsiFile file) { + if (editor.getUserData(REPL_KEY) != null){ + return Result.DEFAULT; + } + return Result.CONTINUE; + } +} diff --git a/python/src/com/jetbrains/python/console/PydevLanguageConsole.java b/python/src/com/jetbrains/python/console/PydevLanguageConsole.java index 53a218b82e25..c9e67b27f6f9 100644 --- a/python/src/com/jetbrains/python/console/PydevLanguageConsole.java +++ b/python/src/com/jetbrains/python/console/PydevLanguageConsole.java @@ -11,6 +11,8 @@ import com.jetbrains.python.console.pydev.PydevConsoleCommunication; public class PydevLanguageConsole extends LanguageConsoleImpl { public PydevLanguageConsole(final Project project, final String title) { super(project, title, PythonLanguage.getInstance(), false); + // Mark editor as console one, to prevent autopopup completion + getConsoleEditor().putUserData(PydevCompletionAutopopupBlockingHandler.REPL_KEY, new Object()); } public void setPydevConsoleCommunication(final PydevConsoleCommunication communication) { diff --git a/python/src/com/jetbrains/python/debugger/PyDebuggerEvaluator.java b/python/src/com/jetbrains/python/debugger/PyDebuggerEvaluator.java index d095b0ae7e91..f05ed85bb308 100644 --- a/python/src/com/jetbrains/python/debugger/PyDebuggerEvaluator.java +++ b/python/src/com/jetbrains/python/debugger/PyDebuggerEvaluator.java @@ -3,8 +3,10 @@ package com.jetbrains.python.debugger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; +import com.intellij.xdebugger.XSourcePosition; import com.intellij.xdebugger.evaluation.XDebuggerEvaluator; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class PyDebuggerEvaluator extends XDebuggerEvaluator { @@ -18,7 +20,7 @@ public class PyDebuggerEvaluator extends XDebuggerEvaluator { } @Override - public void evaluate(@NotNull String expression, final XEvaluationCallback callback) { + public void evaluate(@NotNull String expression, XEvaluationCallback callback, @Nullable XSourcePosition expressionPosition) { expression = expression.trim(); if ("".equals(expression)) { callback.evaluated(NONE); diff --git a/python/src/com/jetbrains/python/editor/PythonEnterHandler.java b/python/src/com/jetbrains/python/editor/PythonEnterHandler.java index 28c5405b9954..ebe513774863 100644 --- a/python/src/com/jetbrains/python/editor/PythonEnterHandler.java +++ b/python/src/com/jetbrains/python/editor/PythonEnterHandler.java @@ -31,7 +31,8 @@ public class PythonEnterHandler implements EnterHandlerDelegate { PyArgumentList.class, PyParameterList.class, PyFunction.class, - PySliceExpression.class + PySliceExpression.class, + PySubscriptionExpression.class }; @Override diff --git a/python/src/com/jetbrains/python/formatter/PyBlock.java b/python/src/com/jetbrains/python/formatter/PyBlock.java index 38d213b37880..9d1bf5fb3f6e 100644 --- a/python/src/com/jetbrains/python/formatter/PyBlock.java +++ b/python/src/com/jetbrains/python/formatter/PyBlock.java @@ -46,7 +46,8 @@ public class PyBlock implements ASTBlock { PyElementTypes.PARAMETER_LIST, PyElementTypes.TUPLE_EXPRESSION, PyElementTypes.PARENTHESIZED_EXPRESSION, - PyElementTypes.SLICE_EXPRESSION); + PyElementTypes.SLICE_EXPRESSION, + PyElementTypes.SUBSCRIPTION_EXPRESSION); private static final TokenSet ourBrackets = TokenSet.create(PyTokenTypes.LPAR, PyTokenTypes.RPAR, PyTokenTypes.LBRACE, PyTokenTypes.RBRACE, diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index 857162d1ff13..21cc4dd80bf1 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -74,9 +74,6 @@ public class PyUtil { } seeker = seeker.getPrevSibling(); } - if (seeker instanceof PyExpressionStatement && seeker.getFirstChild() instanceof PyStringLiteralExpression) { - comments.add(seeker); - } return comments; } diff --git a/python/src/com/jetbrains/python/psi/impl/PyBuiltinCache.java b/python/src/com/jetbrains/python/psi/impl/PyBuiltinCache.java index a0116c89bd81..143fa5879dcf 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyBuiltinCache.java +++ b/python/src/com/jetbrains/python/psi/impl/PyBuiltinCache.java @@ -85,7 +85,7 @@ public class PyBuiltinCache { final VirtualFile vfile = psifile.getVirtualFile(); if (vfile != null) { // reality final ProjectRootManager projectRootManager = ProjectRootManager.getInstance(project); - sdk = projectRootManager.getProjectJdk(); + sdk = projectRootManager.getProjectSdk(); if (sdk == null) { final List orderEntries = projectRootManager.getFileIndex().getOrderEntriesForFile(vfile); for (OrderEntry orderEntry : orderEntries) { diff --git a/python/src/com/jetbrains/python/psi/impl/PyReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/PyReferenceImpl.java index 2256563bba43..dcf765ce4dbd 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyReferenceImpl.java @@ -1,5 +1,6 @@ package com.jetbrains.python.psi.impl; +import com.google.common.collect.Lists; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.lang.ASTNode; @@ -15,11 +16,11 @@ import com.intellij.util.Icons; import com.intellij.util.IncorrectOperationException; import com.intellij.util.ProcessingContext; import com.intellij.util.containers.SortedList; +import com.jetbrains.django.util.PythonDataflowUtil; import com.jetbrains.python.PyNames; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.resolve.*; -import com.jetbrains.python.psi.search.PySuperMethodsSearch; import com.jetbrains.python.psi.types.PyModuleType; import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.psi.types.TypeEvalContext; @@ -351,7 +352,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference @NotNull public Object[] getVariants() { - final List ret = new ArrayList(); + final List ret = Lists.newArrayList(); // Use real context here to enable correct completion and resolve in case of PyExpressionCodeFragment!!! final PsiElement realContext = PyPsiUtils.getRealContext(myElement); @@ -364,24 +365,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference PyResolveUtil.scanOuterContext(processor, realContext); // possible names from around us at call time // in a call, include function's arg names - PyCallExpression call_expr = PsiTreeUtil.getParentOfType(myElement, PyCallExpression.class); - if (call_expr != null) { - PyExpression callee = call_expr.getCallee(); - if (callee instanceof PyReferenceExpression) { - if (PsiTreeUtil.getParentOfType(myElement, PyKeywordArgument.class) == null) { - PsiElement def = ((PyReferenceExpression)callee).getReference().resolve(); - if (def instanceof PyFunction) { - addKeywordArgumentVariants((PyFunction)def, ret); - } - else if (def instanceof PyClass) { - PyFunction init = ((PyClass)def).findMethodByName(PyNames.INIT, true); // search in superclasses - if (init != null) { - addKeywordArgumentVariants(init, ret); - } - } - } - } - } + PythonDataflowUtil.collectFunctionArgNames(myElement, ret); // scan all "import *" and include names provided by them CollectProcessor collect_proc = new CollectProcessor(IS_STAR_IMPORT); @@ -428,155 +412,6 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference return ret.toArray(); } - private static void addKeywordArgumentVariants(PyFunction def, final List ret) { - addKeywordArgumentVariants(def, ret, new HashSet()); - } - - private static void addKeywordArgumentVariants(PyFunction def, List ret, Collection visited) { - if (visited.contains(def)) { - return; - } - visited.add(def); - final Set flags = def.getContainingClass() != null ? PyUtil.detectDecorationsAndWrappersOf(def) : null; - final KwArgParameterCollector collector = new KwArgParameterCollector(flags, ret); - def.getParameterList().acceptChildren(collector); - if (collector.hasKwArgs()) { - KwArgFromStatementCallCollector fromStatementCallCollector = new KwArgFromStatementCallCollector(ret, collector.getKwArgs()); - def.getStatementList().acceptChildren(fromStatementCallCollector); - - //if (collector.hasOnlySelfAndKwArgs()) { - // nothing interesting besides self and **kwargs, let's look at superclass (PY-778) - if (fromStatementCallCollector.isKwArgsTransit()) { - - final PsiElement superMethod = PySuperMethodsSearch.search(def).findFirst(); - if (superMethod instanceof PyFunction) { - addKeywordArgumentVariants((PyFunction)superMethod, ret, visited); - } - } - } -//} - } - - private static class KwArgParameterCollector extends PyElementVisitor { - private int myCount; - private final Set myFlags; - private final List myRet; - private boolean myHasSelf = false; - private boolean myHasKwArgs = false; - private PyParameter kwArgsParam = null; - - public KwArgParameterCollector(Set flags, List ret) { - myFlags = flags; - myRet = ret; - } - - @Override - public void visitPyParameter(PyParameter par) { - myCount++; - if (myCount == 1 && myFlags != null && !myFlags.contains(PyFunction.Flag.STATICMETHOD)) { - myHasSelf = true; - return; - } - PyNamedParameter namedParam = par.getAsNamed(); - if (namedParam != null) { - if (!namedParam.isKeywordContainer() && !namedParam.isPositionalContainer()) { - final LookupElement item = PyUtil.createNamedParameterLookup(namedParam.getName()); - myRet.add(item); - } - else if (namedParam.isKeywordContainer()) { - myHasKwArgs = true; - kwArgsParam = namedParam; - } - } - else { - PyTupleParameter nestedTupleParam = par.getAsTuple(); - if (nestedTupleParam != null) { - nestedTupleParam.acceptChildren(this); - } - // else it's a lone star parameter, it can't contribute to completion - } - } - - public PyParameter getKwArgs() { - return kwArgsParam; - } - - public boolean hasKwArgs() { - return myHasKwArgs; - } - - public boolean hasOnlySelfAndKwArgs() { - return myCount == 2 && myHasSelf && myHasKwArgs; - } - } - - private static class KwArgFromStatementCallCollector extends PyElementVisitor { - private final List myRet; - private final PyParameter myKwArgs; - private boolean kwArgsTransit = true; - - public KwArgFromStatementCallCollector(List ret, @NotNull PyParameter kwArgs) { - myRet = ret; - this.myKwArgs = kwArgs; - } - - @Override - public void visitPyElement(PyElement node) { - node.acceptChildren(this); - } - - @Override - public void visitPySubscriptionExpression(PySubscriptionExpression node) { - String operandName = node.getOperand().getName(); - processGet(operandName, node.getIndexExpression()); - } - - @Override - public void visitPyCallExpression(PyCallExpression node) { - if (node.isCalleeText("pop", "get", "getattr")) { - PyReferenceExpression child = PsiTreeUtil.getChildOfType(node.getCallee(), PyReferenceExpression.class); - if (child != null) { - String operandName = child.getName(); - if (node.getArguments().length > 0) { - PyExpression argument = node.getArguments()[0]; - processGet(operandName, argument); - } - } - } - else if (node.isCalleeText("__init__")) { - kwArgsTransit = false; - for (PyExpression e : node.getArguments()) { - if (e instanceof PyStarArgument) { - PyStarArgument kw = (PyStarArgument)e; - if (Comparing.equal(myKwArgs.getName(), kw.getFirstChild().getNextSibling().getText())) { - kwArgsTransit = true; - break; - } - } - } - } - super.visitPyCallExpression(node); - } - - private void processGet(String operandName, PyExpression argument) { - if (Comparing.equal(myKwArgs.getName(), operandName) && - argument instanceof PyStringLiteralExpression) { - String name = ((PyStringLiteralExpression)argument).getStringValue(); - if (PyUtil.isPythonIdentifier(name)) { - myRet.add(PyUtil.createNamedParameterLookup(name)); - } - } - } - - /** - * is name of kwargs parameter the same as transmitted to __init__ call - * @return - */ - public boolean isKwArgsTransit() { - return kwArgsTransit; - } - } - public boolean isSoft() { return false; } diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java index a6bca3ecc8f1..40d575a7b5d0 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java @@ -1,5 +1,6 @@ package com.jetbrains.python.psi.resolve; +import com.google.common.collect.Sets; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtil; @@ -23,9 +24,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; -import static com.jetbrains.python.psi.resolve.ResolveImportUtil.PointInImport.ROLE.AS_MODULE; -import static com.jetbrains.python.psi.resolve.ResolveImportUtil.PointInImport.ROLE.AS_NAME; -import static com.jetbrains.python.psi.resolve.ResolveImportUtil.PointInImport.ROLE.NONE; +import static com.jetbrains.python.psi.resolve.ResolveImportUtil.PointInImport.ROLE.*; /** * @author dcheryasov @@ -96,7 +95,7 @@ public class ResolveImportUtil { for (PsiElement element : psiElements) { final PsiElement init = PyUtil.turnDirIntoInit(element); if (init instanceof PsiFile) { - VirtualFile vFile = ((PsiFile) init).getVirtualFile(); + VirtualFile vFile = ((PsiFile)init).getVirtualFile(); if (vFile != null && vFile.getLength() > 0) { return element; } @@ -299,6 +298,32 @@ public class ResolveImportUtil { return visitor.results; } + @NotNull + public static List resolveModulesInRoots(@NotNull Module module, @NotNull PyQualifiedName moduleQualifiedName) { + PythonPathCache cache = PythonModulePathCache.getInstance(module); + final List cachedResults = cache.get(moduleQualifiedName); + if (cachedResults != null) { + return cachedResults; + } + ResolveInRootVisitor visitor = new ResolveInRootVisitor(moduleQualifiedName, PsiManager.getInstance(module.getProject()), null); + visitRoots(module, visitor); + cache.put(moduleQualifiedName, visitor.results); + return visitor.results; + } + + @NotNull + public static List resolveModulesInRootProvider(@NotNull RootProvider rootProvider, + @NotNull Module module, + @NotNull PyQualifiedName moduleQualifiedName) { + ResolveInRootVisitor visitor = new ResolveInRootVisitor(moduleQualifiedName, PsiManager.getInstance(module.getProject()), null); + if (!visitModuleContentEntries(module, visitor)) { + for (VirtualFile file : rootProvider.getFiles(OrderRootType.CLASSES)) { + visitor.visitRoot(file); + } + } + return visitor.results; + } + // TODO: rewrite using resolveImportReference /** @@ -375,7 +400,6 @@ public class ResolveImportUtil { } else { visitSdkRoots(elt, visitor); - } } @@ -395,26 +419,36 @@ public class ResolveImportUtil { private static void visitRoots(Module module, RootVisitor visitor) { // TODO: implement a proper module-like approach in PyCharm for "project's dirs on pythonpath", minding proper search order // Module-based approach works only in the IDEA plugin. + if (visitModuleContentEntries(module, visitor)) return; + // else look in SDK roots + visitModuleSdkRoots(visitor, module); + } + + private static boolean visitModuleContentEntries(Module module, RootVisitor visitor) { ModuleRootManager rootManager = ModuleRootManager.getInstance(module); // look in module sources boolean sourceEntriesMissing = true; - Set contentRoots = new java.util.HashSet(); + Set contentRoots = Sets.newHashSet(); for (ContentEntry entry : rootManager.getContentEntries()) { VirtualFile rootFile = entry.getFile(); - if (rootFile != null && !visitor.visitRoot(rootFile)) return; + if (rootFile != null && !visitor.visitRoot(rootFile)) return true; contentRoots.add(rootFile); for (VirtualFile folder : entry.getSourceFolderFiles()) { sourceEntriesMissing = false; - if (!visitor.visitRoot(folder)) return; + if (!visitor.visitRoot(folder)) return true; } } if (sourceEntriesMissing) { // fallback for a case without any source entries: use project root VirtualFile projectRoot = module.getProject().getBaseDir(); - if (projectRoot != null && !contentRoots.contains(projectRoot) && !visitor.visitRoot(projectRoot)) return; + if (projectRoot != null && !contentRoots.contains(projectRoot) && !visitor.visitRoot(projectRoot)) return true; } - // else look in SDK roots + return false; + } + + private static void visitModuleSdkRoots(@NotNull RootVisitor visitor, @NotNull Module module) { + ModuleRootManager rootManager = ModuleRootManager.getInstance(module); rootManager.orderEntries().process(new SdkRootVisitingPolicy(visitor), null); } @@ -478,22 +512,22 @@ public class ResolveImportUtil { } static class ResolveInRootVisitor implements RootVisitor { - final PsiFile foothold_file; + final PsiFile myFootholdFile; final @NotNull PyQualifiedName qualifiedName; final @NotNull PsiManager psiManager; final List results = new ArrayList(); - public ResolveInRootVisitor(@NotNull PyQualifiedName qName, @NotNull PsiManager psiManager, PsiFile foothold_file) { + public ResolveInRootVisitor(@NotNull PyQualifiedName qName, @NotNull PsiManager psiManager, @Nullable PsiFile foothold_file) { this.qualifiedName = qName; this.psiManager = psiManager; - this.foothold_file = foothold_file; + myFootholdFile = foothold_file; } public boolean visitRoot(final VirtualFile root) { if (!root.isValid()) { return true; } - PsiElement module = resolveInRoot(root, qualifiedName, psiManager, foothold_file); + PsiElement module = resolveInRoot(root, qualifiedName, psiManager, myFootholdFile); if (module != null) { results.add(module); } @@ -502,7 +536,10 @@ public class ResolveImportUtil { } @Nullable - private static PsiElement resolveInRoot(VirtualFile root, PyQualifiedName qualifiedName, PsiManager psiManager, PsiFile foothold_file) { + private static PsiElement resolveInRoot(VirtualFile root, + PyQualifiedName qualifiedName, + PsiManager psiManager, + @Nullable PsiFile foothold_file) { PsiElement module = root.isDirectory() ? psiManager.findDirectory(root) : psiManager.findFile(root); for (String component : qualifiedName.getComponents()) { if (component == null) { @@ -530,7 +567,7 @@ public class ResolveImportUtil { */ @Nullable public static PsiElement resolveChild(@Nullable final PsiElement parent, @NotNull final String referencedName, - final PsiFile containingFile, @Nullable VirtualFile root, boolean fileOnly) { + @Nullable final PsiFile containingFile, @Nullable VirtualFile root, boolean fileOnly) { PsiDirectory dir = null; PsiElement ret = null; PsiElement possible_ret = null; @@ -569,7 +606,7 @@ public class ResolveImportUtil { } @Nullable - private static PsiElement resolveInDirectory(final String referencedName, final PsiFile containingFile, + private static PsiElement resolveInDirectory(final String referencedName, @Nullable final PsiFile containingFile, final PsiDirectory dir, @Nullable VirtualFile root, boolean isFileOnly) { if (referencedName == null) return null; final PsiElement module = findModuleInDir(dir, referencedName); @@ -635,7 +672,8 @@ public class ResolveImportUtil { } VirtualFile skeletonsRoot = findSkeletonsRoot(dir); if (skeletonsRoot != null && relativeName != null) { - VirtualFile skeletonsVFile = relativeName.length() == 0 ? skeletonsRoot : skeletonsRoot.findFileByRelativePath(relativeName.replace(".", "/")); + VirtualFile skeletonsVFile = + relativeName.length() == 0 ? skeletonsRoot : skeletonsRoot.findFileByRelativePath(relativeName.replace(".", "/")); if (skeletonsVFile != null) { return dir.getManager().findDirectory(skeletonsVFile); } @@ -675,7 +713,7 @@ public class ResolveImportUtil { List result = StringUtil.split(relativePath, "/"); if (myResult == null || result.size() < myResult.size()) { if (result.size() > 0) { - result.set(result.size()-1, FileUtil.getNameWithoutExtension(result.get(result.size()-1))); + result.set(result.size() - 1, FileUtil.getNameWithoutExtension(result.get(result.size() - 1))); } myResult = result; } @@ -750,8 +788,9 @@ public class ResolveImportUtil { /** * When a name is imported from a module, tries to find the definition of that name inside the module, * as opposed to looking for submodules. + * * @param where an element related to the name, presumably inside import - * @param name the name to find + * @param name the name to find * @return found element, or null. */ @Nullable @@ -815,7 +854,6 @@ public class ResolveImportUtil { */ AS_NAME } - } /** diff --git a/python/src/com/jetbrains/python/psi/resolve/VariantsProcessor.java b/python/src/com/jetbrains/python/psi/resolve/VariantsProcessor.java index a7b494dab34b..9045d0cc0c6e 100644 --- a/python/src/com/jetbrains/python/psi/resolve/VariantsProcessor.java +++ b/python/src/com/jetbrains/python/psi/resolve/VariantsProcessor.java @@ -96,7 +96,7 @@ public class VariantsProcessor implements PsiScopeProcessor { PyReferenceExpression expr = (PyReferenceExpression)element; String referencedName = expr.getReferencedName(); if (nameIsAcceptable(referencedName)) { - myVariants.put(referencedName, setupItem(LookupElementBuilder.create(referencedName))); + myVariants.put(referencedName, setupItem(LookupElementBuilder.create(expr, referencedName))); } } else if (element instanceof NameDefiner) { @@ -123,7 +123,7 @@ public class VariantsProcessor implements PsiScopeProcessor { // things like PyTargetExpression cannot have a general icon, but here we only have variables if (icon == null) icon = Icons.VARIABLE_ICON; if (referencedName != null && nameIsAcceptable(referencedName)) { - LookupElementBuilder lookupItem = setupItem(LookupElementBuilder.create(referencedName).setIcon(icon)); + LookupElementBuilder lookupItem = setupItem(LookupElementBuilder.create(expr, referencedName).setIcon(icon)); if (definer instanceof PyImportElement) { // set notice to imported module name if needed PsiElement maybeFromImport = definer.getParent(); if (maybeFromImport instanceof PyFromImportStatement) { diff --git a/python/src/com/jetbrains/python/psi/stubs/PyClassNameIndex.java b/python/src/com/jetbrains/python/psi/stubs/PyClassNameIndex.java index c35af3f7768b..3e0609a6db02 100644 --- a/python/src/com/jetbrains/python/psi/stubs/PyClassNameIndex.java +++ b/python/src/com/jetbrains/python/psi/stubs/PyClassNameIndex.java @@ -49,7 +49,7 @@ public class PyClassNameIndex extends StringStubIndexExtension { * @return the resulting scope */ public static GlobalSearchScope projectWithLibrariesScope(Project project) { - final Sdk sdk = ProjectRootManager.getInstance(project).getProjectJdk(); + final Sdk sdk = ProjectRootManager.getInstance(project).getProjectSdk(); // TODO cache the scope in project userdata (update when SDK paths change or different project SDK is selected) if (sdk != null && sdk.getSdkType() instanceof PythonSdkType) { VirtualFile libDir = findLibDir(sdk.getRootProvider().getFiles(OrderRootType.CLASSES)); diff --git a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassDialog.java b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassDialog.java index e5a7b5ae6b29..17533957438f 100644 --- a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassDialog.java +++ b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassDialog.java @@ -8,6 +8,7 @@ import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.ui.TextComponentAccessor; import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.refactoring.RefactoringBundle; import com.intellij.refactoring.classMembers.AbstractUsesDependencyMemberInfoModel; @@ -69,9 +70,9 @@ public class PyExtractSuperclassDialog extends UpDirectedMembersMovingDialog { } boolean found_root = false; try { - String target_dir = new File(myTargetDirField.getText()).getCanonicalPath(); + String target_dir = FileUtil.toSystemIndependentName(new File(myTargetDirField.getText()).getCanonicalPath()); for (VirtualFile file : ProjectRootManager.getInstance(myClass.getProject()).getContentRoots()) { - if (target_dir.startsWith(file.getPath())) { + if (StringUtil.startsWithIgnoreCase(target_dir, file.getPath())) { found_root = true; break; } diff --git a/python/src/com/jetbrains/python/run/AbstractPythonRunConfiguration.java b/python/src/com/jetbrains/python/run/AbstractPythonRunConfiguration.java index dab282046cf6..508a449ba5eb 100644 --- a/python/src/com/jetbrains/python/run/AbstractPythonRunConfiguration.java +++ b/python/src/com/jetbrains/python/run/AbstractPythonRunConfiguration.java @@ -1,5 +1,6 @@ package com.jetbrains.python.run; +import com.google.common.collect.Maps; import com.intellij.execution.configuration.EnvironmentVariablesComponent; import com.intellij.execution.configurations.*; import com.intellij.openapi.module.Module; @@ -33,7 +34,7 @@ public abstract class AbstractPythonRunConfiguration extends ModuleBasedConfigur private String myWorkingDirectory = ""; private String mySdkHome = ""; private boolean myPassParentEnvs = true; - private Map myEnvs = new HashMap(); + private Map myEnvs = Maps.newHashMap(); private boolean myUseModuleSdk; public AbstractPythonRunConfiguration(final String name, final RunConfigurationModule module, final ConfigurationFactory factory) { @@ -65,7 +66,7 @@ public abstract class AbstractPythonRunConfiguration extends ModuleBasedConfigur else { if (!myUseModuleSdk) { if (StringUtil.isEmptyOrSpaces(getSdkHome())) { - final Sdk projectSdk = ProjectRootManager.getInstance(getProject()).getProjectJdk(); + final Sdk projectSdk = ProjectRootManager.getInstance(getProject()).getProjectSdk(); if (projectSdk == null || !(projectSdk.getSdkType() instanceof PythonSdkType)) { throw new RuntimeConfigurationError(PyBundle.message("runcfg.unittest.no_sdk")); } diff --git a/python/src/com/jetbrains/python/run/PythonCommandLineState.java b/python/src/com/jetbrains/python/run/PythonCommandLineState.java index 7613d58095fa..3f49b8b38152 100644 --- a/python/src/com/jetbrains/python/run/PythonCommandLineState.java +++ b/python/src/com/jetbrains/python/run/PythonCommandLineState.java @@ -1,5 +1,6 @@ package com.jetbrains.python.run; +import com.google.common.collect.Lists; import com.intellij.execution.DefaultExecutionResult; import com.intellij.execution.ExecutionException; import com.intellij.execution.ExecutionResult; @@ -17,9 +18,14 @@ import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ProgramRunner; import com.intellij.execution.ui.ConsoleView; import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.OrderRootType; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.HashMap; import com.jetbrains.python.sdk.PythonEnvUtil; import com.jetbrains.python.sdk.PythonSdkFlavor; +import com.jetbrains.python.sdk.PythonSdkType; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -61,12 +67,13 @@ public abstract class PythonCommandLineState extends CommandLineState { } @NotNull - protected ConsoleView createAndAttachConsole(Project project, ProcessHandler processHandler, Executor executor) throws ExecutionException { + protected ConsoleView createAndAttachConsole(Project project, ProcessHandler processHandler, Executor executor) + throws ExecutionException { final TextConsoleBuilder consoleBuilder = TextConsoleBuilderFactory.getInstance().createBuilder(project); for (Filter filter : myFilters) { consoleBuilder.addFilter(filter); } - + final ConsoleView consoleView = consoleBuilder.getConsole(); consoleView.attachToProcess(processHandler); return consoleView; @@ -78,6 +85,7 @@ public abstract class PythonCommandLineState extends CommandLineState { /** * Patches the command line parameters applying patchers from first to last, and then runs it. + * * @param patchers any number of patchers; any patcher may be null, and the whole argument may be null. * @return handler of the started process * @throws ExecutionException @@ -93,7 +101,7 @@ public abstract class PythonCommandLineState extends CommandLineState { public GeneralCommandLine generateCommandLine(CommandLinePatcher[] patchers) throws ExecutionException { GeneralCommandLine commandLine = generateCommandLine(); if (patchers != null) { - for (CommandLinePatcher patcher: patchers) { + for (CommandLinePatcher patcher : patchers) { if (patcher != null) patcher.patchCommandLine(commandLine); } } @@ -121,7 +129,8 @@ public abstract class PythonCommandLineState extends CommandLineState { /** * Creates a number of parameter groups in the command line: * GROUP_EXE_OPTIONS, GROUP_DEBUGGER, GROUP_SCRIPT. - * These are necessary for command line patchers to work properly. + * These are necessary for command line patchers to work properly. + * * @param commandLine */ public static void createStandardGroupsIn(GeneralCommandLine commandLine) { @@ -133,10 +142,12 @@ public abstract class PythonCommandLineState extends CommandLineState { protected void initEnvironment(GeneralCommandLine commandLine) { Map envs = myConfig.getEnvs(); - if (envs == null) + if (envs == null) { envs = new HashMap(); - else + } + else { envs = new HashMap(envs); + } addPredefinedEnvironmentVariables(envs, myConfig.isPassParentEnvs()); addCommonEnvironmentVariables(envs); @@ -154,6 +165,14 @@ public abstract class PythonCommandLineState extends CommandLineState { if (flavor != null) { flavor.addPredefinedEnvironmentVariables(envs); } + + Sdk pythonSdk = PythonSdkType.findSdkByPath(myConfig.getSdkHome()); + VirtualFile[] paths = pythonSdk.getRootProvider().getFiles(OrderRootType.CLASSES); + List pathList = Lists.newArrayList(); + for (VirtualFile file : paths) { + pathList.add(FileUtil.toSystemDependentName(file.getPath())); + } + PythonSdkFlavor.initPythonPath(envs, passParentEnvs, pathList); } protected void setRunnerPath(GeneralCommandLine commandLine) throws ExecutionException { diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkAdditionalData.java b/python/src/com/jetbrains/python/sdk/PythonSdkAdditionalData.java new file mode 100644 index 000000000000..68f436cf35c8 --- /dev/null +++ b/python/src/com/jetbrains/python/sdk/PythonSdkAdditionalData.java @@ -0,0 +1,107 @@ +package com.jetbrains.python.sdk; + +import com.google.common.collect.Sets; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.projectRoots.SdkAdditionalData; +import com.intellij.openapi.projectRoots.SdkModel; +import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.django.util.VirtualFileUtil; +import org.jdom.Element; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +/** +* @author traff +*/ +public class PythonSdkAdditionalData implements SdkAdditionalData { + @NonNls private static final String PATHS_ADDED_BY_USER_ROOT = "PATHS_ADDED_BY_USER_ROOT"; + @NonNls private static final String PATH_ADDED_BY_USER = "PATH_ADDED_BY_USER"; + @NonNls private static final String PATHS_REMOVED_BY_USER_ROOT = "PATHS_REMOVED_BY_USER_ROOT"; + @NonNls private static final String PATH_REMOVED_BY_USER = "PATH_REMOVED_BY_USER"; + + + private Set myAddedPaths = Sets.newHashSet(); + private Set myExcludedPaths = Sets.newHashSet(); + + public Object clone() throws CloneNotSupportedException { + try { + final PythonSdkAdditionalData copy = (PythonSdkAdditionalData)super.clone(); + copy.setAddedPaths(getAddedPaths()); + copy.setExcludedPaths(getExcludedPaths()); + return copy; + } + catch (CloneNotSupportedException e) { + return null; + } + } + + public Set getAddedPaths() { + return myAddedPaths; + } + + public void setAddedPaths(Set addedPaths) { + myAddedPaths = Sets.newHashSet(addedPaths); + } + + public Set getExcludedPaths() { + return myExcludedPaths; + } + + public void setExcludedPaths(Set excludedPaths) { + myExcludedPaths = Sets.newHashSet(excludedPaths); + } + + @Override + public void checkValid(SdkModel sdkModel) throws ConfigurationException { + + } + + public void save(@NotNull final Element rootElement) { + for (VirtualFile addedPath : myAddedPaths) { + final Element child = new Element(PATHS_ADDED_BY_USER_ROOT); + child.setAttribute(PATH_ADDED_BY_USER, addedPath.getPath()); + rootElement.addContent(child); + } + + for (VirtualFile removed : myExcludedPaths) { + final Element child = new Element(PATHS_REMOVED_BY_USER_ROOT); + child.setAttribute(PATH_REMOVED_BY_USER, removed.getPath()); + rootElement.addContent(child); + } + } + + @NotNull + public static PythonSdkAdditionalData load(@Nullable Element element) { + final PythonSdkAdditionalData data = new PythonSdkAdditionalData(); + + data.setAddedPaths(loadStringList(element, PATHS_ADDED_BY_USER_ROOT, PATH_ADDED_BY_USER)); + data.setExcludedPaths(loadStringList(element, PATHS_REMOVED_BY_USER_ROOT, PATH_REMOVED_BY_USER)); + + return data; + } + + private static Set loadStringList(Element element, String rootName, String attrName) { + final List paths = new LinkedList(); + if (element != null) { + final List list = element.getChildren(rootName); + if (list != null) { + for (Object o : list) { + paths.add(((Element)o).getAttribute(attrName).getValue()); + } + } + } + final Set files = Sets.newHashSet(); + for (String path : paths) { + VirtualFile vf = VirtualFileUtil.findFile(path); + if (vf != null) { + files.add(vf); + } + } + return files; + } +} diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java index 8092324946a2..88ae00d3fe78 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkFlavor.java @@ -4,6 +4,7 @@ import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.process.ProcessOutput; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.Nullable; import java.io.File; @@ -22,6 +23,14 @@ public abstract class PythonSdkFlavor { return pythonPath; } + public static void initPythonPath(Map envs, boolean passParentEnvs, List pythonPathList) { + String pythonPath = StringUtil.join(pythonPathList, File.pathSeparator); + if (passParentEnvs && !envs.containsKey(PYTHONPATH)) { + pythonPath = appendSystemPythonPath(pythonPath); + } + addToPythonPath(envs, pythonPath); + } + public Collection suggestHomePaths() { return Collections.emptyList(); } diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkType.java b/python/src/com/jetbrains/python/sdk/PythonSdkType.java index 6d5f1c11ed35..24e622798b1d 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkType.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkType.java @@ -78,6 +78,12 @@ public class PythonSdkType extends SdkType { return PythonFileType.INSTANCE.getIcon(); } + @NotNull + @Override + public String getHelpTopic() { + return "reference.project.structure.sdk.python"; + } + public Icon getIconForAddAction() { return PythonFileType.INSTANCE.getIcon(); } @@ -281,6 +287,9 @@ public class PythonSdkType extends SdkType { } public void saveAdditionalData(final SdkAdditionalData additionalData, final Element additional) { + if (additionalData instanceof PythonSdkAdditionalData ) { + ((PythonSdkAdditionalData)additionalData).save(additional); + } } @Override @@ -315,7 +324,7 @@ public class PythonSdkType extends SdkType { // Don't fix skeletons here, PythonSdkUpdater will take care of that (see PY-1226 - no progress will be displayed if skeletons // generation is invoked from here - return null; + return PythonSdkAdditionalData.load(additional); } private boolean switchPathToInterpreter(Sdk currentSdk, String... variants) { @@ -396,7 +405,7 @@ public class PythonSdkType extends SdkType { final ProgressManager progman = ProgressManager.getInstance(); final Ref success = new Ref(); success.set(true); - final Task.Modal setup_task = new Task.Modal(project, "Setting up library files for " + sdk.getName(), false) { + final Task.Modal setupTask = new Task.Modal(project, "Setting up library files for " + sdk.getName(), false) { public void run(@NotNull final ProgressIndicator indicator) { try { @@ -412,7 +421,7 @@ public class PythonSdkType extends SdkType { } } }; - progman.run(setup_task); + progman.run(setupTask); return success.get(); } diff --git a/python/src/com/jetbrains/python/structureView/PyStructureViewModel.java b/python/src/com/jetbrains/python/structureView/PyStructureViewModel.java index 32b05f99117d..85a03ed28fb4 100644 --- a/python/src/com/jetbrains/python/structureView/PyStructureViewModel.java +++ b/python/src/com/jetbrains/python/structureView/PyStructureViewModel.java @@ -28,4 +28,9 @@ public class PyStructureViewModel extends StructureViewModelBase implements Stru public boolean isAlwaysLeaf(StructureViewTreeElement element) { return element.getValue() instanceof PyTargetExpression; } + + @Override + public boolean shouldEnterElement(Object element) { + return element instanceof PyClass; + } } diff --git a/python/src/com/jetbrains/python/testing/PythonUnitTestCommandLineState.java b/python/src/com/jetbrains/python/testing/PythonUnitTestCommandLineState.java index b773baf87b52..4d1853505a4d 100644 --- a/python/src/com/jetbrains/python/testing/PythonUnitTestCommandLineState.java +++ b/python/src/com/jetbrains/python/testing/PythonUnitTestCommandLineState.java @@ -56,7 +56,11 @@ public class PythonUnitTestCommandLineState extends PythonTestCommandLineStateBa specs.add(myConfig.getScriptName() + "::" + myConfig.getClassName() + "::" + myConfig.getMethodName()); break; case TEST_FOLDER: - specs.add(myConfig.getFolderName() + "/"); + if (!myConfig.getPattern().isEmpty()) + specs.add(myConfig.getFolderName() + "/" + ";" + myConfig.getPattern() + "$"); + else + specs.add(myConfig.getFolderName() + "/"); + // TODO[kate]:think about delimiter between folderName and Pattern break; default: throw new IllegalArgumentException("Unknown test type: " + myConfig.getTestType()); diff --git a/python/src/com/jetbrains/python/testing/PythonUnitTestRunConfiguration.java b/python/src/com/jetbrains/python/testing/PythonUnitTestRunConfiguration.java index ed0e617ce783..50ce980ece99 100644 --- a/python/src/com/jetbrains/python/testing/PythonUnitTestRunConfiguration.java +++ b/python/src/com/jetbrains/python/testing/PythonUnitTestRunConfiguration.java @@ -25,6 +25,7 @@ public class PythonUnitTestRunConfiguration extends AbstractPythonRunConfigurati private String myScriptName = ""; private String myMethodName = ""; private String myFolderName = ""; + private String myPattern = ""; // pattern for modules in folder to match against private TestType myTestType = TestType.TEST_SCRIPT; protected PythonUnitTestRunConfiguration(RunConfigurationModule module, ConfigurationFactory configurationFactory, String name) { @@ -42,6 +43,7 @@ public class PythonUnitTestRunConfiguration extends AbstractPythonRunConfigurati myClassName = JDOMExternalizerUtil.readField(element, "CLASS_NAME"); myMethodName = JDOMExternalizerUtil.readField(element, "METHOD_NAME"); myFolderName = JDOMExternalizerUtil.readField(element, "FOLDER_NAME"); + myPattern = JDOMExternalizerUtil.readField(element, "PATTERN"); try { myTestType = TestType.valueOf(JDOMExternalizerUtil.readField(element, "TEST_TYPE")); @@ -54,10 +56,12 @@ public class PythonUnitTestRunConfiguration extends AbstractPythonRunConfigurati @Override public void writeExternal(Element element) throws WriteExternalException { super.writeExternal(element); + JDOMExternalizerUtil.writeField(element, "SCRIPT_NAME", myScriptName); JDOMExternalizerUtil.writeField(element, "CLASS_NAME", myClassName); JDOMExternalizerUtil.writeField(element, "METHOD_NAME", myMethodName); JDOMExternalizerUtil.writeField(element, "FOLDER_NAME", myFolderName); + JDOMExternalizerUtil.writeField(element, "PATTERN", myPattern); JDOMExternalizerUtil.writeField(element, "TEST_TYPE", myTestType.toString()); } @@ -77,6 +81,14 @@ public class PythonUnitTestRunConfiguration extends AbstractPythonRunConfigurati myClassName = className; } + public String getPattern() { + return myPattern; + } + + public void setPattern(String pattern) { + myPattern = pattern; + } + public String getFolderName() { return myFolderName; } @@ -188,5 +200,6 @@ public class PythonUnitTestRunConfiguration extends AbstractPythonRunConfigurati target.setFolderName(source.getFolderName()); target.setMethodName(source.getMethodName()); target.setTestType(source.getTestType()); + target.setPattern(source.getPattern()); } } diff --git a/python/src/com/jetbrains/python/testing/PythonUnitTestRunConfigurationForm.form b/python/src/com/jetbrains/python/testing/PythonUnitTestRunConfigurationForm.form index 3f7c024c076e..11d36d0baf88 100644 --- a/python/src/com/jetbrains/python/testing/PythonUnitTestRunConfigurationForm.form +++ b/python/src/com/jetbrains/python/testing/PythonUnitTestRunConfigurationForm.form @@ -8,7 +8,7 @@ - + @@ -18,7 +18,7 @@ - + @@ -26,7 +26,7 @@ - + @@ -42,12 +42,20 @@ - + + + + + + + + + diff --git a/python/src/com/jetbrains/python/testing/PythonUnitTestRunConfigurationForm.java b/python/src/com/jetbrains/python/testing/PythonUnitTestRunConfigurationForm.java index 7d4f4577d397..0f02254c4657 100644 --- a/python/src/com/jetbrains/python/testing/PythonUnitTestRunConfigurationForm.java +++ b/python/src/com/jetbrains/python/testing/PythonUnitTestRunConfigurationForm.java @@ -30,11 +30,13 @@ public class PythonUnitTestRunConfigurationForm implements PythonUnitTestRunConf private JRadioButton myTestClassRB; private JRadioButton myTestMethodRB; private JPanel myCommonOptionsPlaceholder; + private LabeledComponent myPatternComponent; private TextFieldWithBrowseButton myTestFolderTextField; private TextFieldWithBrowseButton myTestScriptTextField; private JTextField myTestMethodTextField; private JTextField myTestClassTextField; + private JTextField myPatternTextField; private final Project myProject; private final AbstractPyCommonOptionsForm myCommonOptionsForm; @@ -74,6 +76,14 @@ public class PythonUnitTestRunConfigurationForm implements PythonUnitTestRunConf myTestClassTextField.setText(className); } + public String getPattern() { + return myPatternTextField.getText().trim(); + } + + public void setPattern(String pattern) { + myPatternTextField.setText(pattern); + } + public String getFolderName() { return toSystemIndependentName(myTestFolderTextField.getText().trim()); } @@ -129,6 +139,7 @@ public class PythonUnitTestRunConfigurationForm implements PythonUnitTestRunConf myTestScriptComponent.setEnabled(testType != TestType.TEST_FOLDER); myTestClassComponent.setEnabled(testType == TestType.TEST_CLASS || testType == TestType.TEST_METHOD); myTestMethodComponent.setEnabled(testType == TestType.TEST_METHOD); + myPatternComponent.setEnabled(testType == TestType.TEST_FOLDER); } public JComponent getPanel() { @@ -182,6 +193,7 @@ public class PythonUnitTestRunConfigurationForm implements PythonUnitTestRunConf private void createUIComponents() { myTestClassComponent = createTestClassComponent(); myTestMethodComponent = createTestMethodComponent(); + myPatternComponent = createPatternComponent(); final Ref testsFolderTextFieldWrapper = new Ref(); myTestFolderComponent = createTestFolderComponent(testsFolderTextFieldWrapper); @@ -195,6 +207,16 @@ public class PythonUnitTestRunConfigurationForm implements PythonUnitTestRunConf title = PyBundle.message("runcfg.unittest.dlg.select.script.path"); PythonRunConfigurationFormUtil.addFileChooser(title, myTestScriptTextField, myProject); } + + private LabeledComponent createPatternComponent() { + myPatternTextField = new JTextField(); + + LabeledComponent myComponent = new LabeledComponent(); + myComponent.setComponent(myPatternTextField); + myComponent.setText(PyBundle.message("runcfg.unittest.dlg.pattern")); + + return myComponent; + } } diff --git a/python/src/com/jetbrains/python/testing/PythonUnitTestRunConfigurationParams.java b/python/src/com/jetbrains/python/testing/PythonUnitTestRunConfigurationParams.java index 5d38cc7758b3..adf3730fbc5f 100644 --- a/python/src/com/jetbrains/python/testing/PythonUnitTestRunConfigurationParams.java +++ b/python/src/com/jetbrains/python/testing/PythonUnitTestRunConfigurationParams.java @@ -22,4 +22,8 @@ public interface PythonUnitTestRunConfigurationParams { PythonUnitTestRunConfiguration.TestType getTestType(); void setTestType(PythonUnitTestRunConfiguration.TestType testType); + + String getPattern(); + void setPattern(String pattern); + } diff --git a/python/testData/codeInsight/codefragment/raise2102.test b/python/testData/codeInsight/codefragment/raise2102.test new file mode 100644 index 000000000000..a9f49a6fdb23 --- /dev/null +++ b/python/testData/codeInsight/codefragment/raise2102.test @@ -0,0 +1,17 @@ +foo() + +for i in range(l): + if foo: + if tag is None: + if bar: + do + else: + if baz: + raise ValueError(i) + else: + bzzzz + +bar() + +In: +Out: \ No newline at end of file diff --git a/python/testData/refactoring/pullup/withMultilineComments.after.py b/python/testData/refactoring/pullup/withMultilineComments.after.py index e1a0f1a49780..3780dec0eb33 100644 --- a/python/testData/refactoring/pullup/withMultilineComments.after.py +++ b/python/testData/refactoring/pullup/withMultilineComments.after.py @@ -2,13 +2,12 @@ class Foo: def foo(self): print("a") - ''' - this is boo - very long boo - ''' - def boo(self): print "rrrrr" class Boo(Foo): - pass + ''' + this is boo + very long boo + ''' + diff --git a/python/testSrc/com/jetbrains/python/PyEditingTest.java b/python/testSrc/com/jetbrains/python/PyEditingTest.java index 60556d11e405..23ae43a15ad5 100644 --- a/python/testSrc/com/jetbrains/python/PyEditingTest.java +++ b/python/testSrc/com/jetbrains/python/PyEditingTest.java @@ -146,6 +146,10 @@ public class PyEditingTest extends PyLightFixtureTestCase { doTestEnter("a = some_list[slice_start:slice_end]", "a = some_list[\n slice_start:slice_end]"); } + public void testEnterInSubscriptionExpression() { // PY-1992 + doTestEnter("a = some_list[slice_start]", "a = some_list[\n slice_start]"); + } + private void doTestEnter(String before, final String after) { int pos = before.indexOf(""); before = before.replace("", ""); diff --git a/python/testSrc/com/jetbrains/python/PyIndentTest.java b/python/testSrc/com/jetbrains/python/PyIndentTest.java index 5ae348eca1d0..aae064bc44c0 100644 --- a/python/testSrc/com/jetbrains/python/PyIndentTest.java +++ b/python/testSrc/com/jetbrains/python/PyIndentTest.java @@ -12,12 +12,7 @@ public class PyIndentTest extends PyLightFixtureTestCase { private void doTest(final String before, String after) { final String name = getTestName(false); - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - myFixture.configureByText(name + ".py", before); - } - }); + myFixture.configureByText(name + ".py", before); CommandProcessor.getInstance().executeCommand(myFixture.getProject(), new Runnable() { @Override public void run() { diff --git a/python/testSrc/com/jetbrains/python/PyTypeTest.java b/python/testSrc/com/jetbrains/python/PyTypeTest.java index 52b792086515..48a855dda208 100644 --- a/python/testSrc/com/jetbrains/python/PyTypeTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypeTest.java @@ -107,12 +107,7 @@ public class PyTypeTest extends PyLightFixtureTestCase { } private PyType doTest(final String text) { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - myFixture.configureByText(PythonFileType.INSTANCE, text); - } - }); + myFixture.configureByText(PythonFileType.INSTANCE, text); PyExpression expr = myFixture.findElementByText("expr", PyExpression.class); return expr.getType(TypeEvalContext.slow()); } diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index af7786863f13..e3244ccd3e5b 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -196,12 +196,7 @@ public class PythonCompletionTest extends PyLightFixtureTestCase { } public void testEmptyFile() { // PY-1845 - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - myFixture.configureByText(PythonFileType.INSTANCE, ""); - } - }); + myFixture.configureByText(PythonFileType.INSTANCE, ""); myFixture.completeBasic(); final List elements = myFixture.getLookupElementStrings(); assertTrue(elements.contains("import")); @@ -229,4 +224,4 @@ public class PythonCompletionTest extends PyLightFixtureTestCase { myFixture.completeBasic(); myFixture.checkResultByFile(dirname + "importedModule.after.py"); } -} \ No newline at end of file +} diff --git a/python/testSrc/com/jetbrains/python/fixtures/LightMarkedTestCase.java b/python/testSrc/com/jetbrains/python/fixtures/LightMarkedTestCase.java index 6871d2befc61..b5dcd11bac63 100644 --- a/python/testSrc/com/jetbrains/python/fixtures/LightMarkedTestCase.java +++ b/python/testSrc/com/jetbrains/python/fixtures/LightMarkedTestCase.java @@ -94,13 +94,8 @@ public abstract class LightMarkedTestCase extends PyLightFixtureTestCase { // create a file and map marks to PSI elements Map result = new HashMap(); - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - myFile = myFixture.addFileToProject(fileName, text.toString()); - myFixture.configureFromExistingVirtualFile(myFile.getVirtualFile()); - } - }); + myFile = myFixture.addFileToProject(fileName, text.toString()); + myFixture.configureFromExistingVirtualFile(myFile.getVirtualFile()); for (Map.Entry entry : offsets.entrySet()) { result.put(entry.getKey(), myFile.findElementAt(entry.getValue())); } @@ -111,4 +106,4 @@ public abstract class LightMarkedTestCase extends PyLightFixtureTestCase { String fname = getTestName(false) + ".py"; return configureByFile(fname); } -} \ No newline at end of file +} diff --git a/python/testSrc/com/jetbrains/python/fixtures/PyResolveTestCase.java b/python/testSrc/com/jetbrains/python/fixtures/PyResolveTestCase.java index ef93d9aee077..85d391024845 100644 --- a/python/testSrc/com/jetbrains/python/fixtures/PyResolveTestCase.java +++ b/python/testSrc/com/jetbrains/python/fixtures/PyResolveTestCase.java @@ -38,12 +38,7 @@ public abstract class PyResolveTestCase extends PyLightFixtureTestCase { assertTrue(offset >= 0); fileText = fileText.substring(0, offset) + fileText.substring(offset + MARKER.length()); final String finalFileText = fileText; - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - myFixture.configureByText(new File(filePath).getName(), finalFileText); - } - }); + myFixture.configureByText(new File(filePath).getName(), finalFileText); final PsiReference reference = myFixture.getFile().findReferenceAt(offset); return reference; } diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyCodeFragmentTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyCodeFragmentTest.java index 353069f54939..8f46ff540429 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyCodeFragmentTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyCodeFragmentTest.java @@ -159,5 +159,10 @@ public class PyCodeFragmentTest extends LightMarkedTestCase { public void testForIfReturn() throws Exception { doTest(); } + + public void testRaise2102() throws Exception { + doTest(); + } + }