diff --git a/python/helpers/pycharm/tcunittest.py b/python/helpers/pycharm/tcunittest.py index 5b78c7f4205d..d8b37caad90f 100644 --- a/python/helpers/pycharm/tcunittest.py +++ b/python/helpers/pycharm/tcunittest.py @@ -40,6 +40,9 @@ class TeamcityTestResult(TestResult): self.messages.testFailed(self.getTestName(test), message='Failure', details=err) + def addSkip(self, test, reason): + self.messages.testIgnored(self.getTestName(test), message=reason) + def startTest(self, test): setattr(test, "startTime", datetime.datetime.now()) self.messages.testStarted(self.getTestName(test), location="python_uttestid://" + str(test.id())) diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index 6c53e1d844d7..e19d92a37b7f 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -33,9 +33,6 @@ - @@ -64,9 +61,6 @@ - - @@ -74,59 +68,50 @@ - - PyDemorganIntention - Python - PyDemorganIntention - com.jetbrains.python.codeInsight.intentions.ImportToImportFromIntention Python - ImportToImportFromIntention com.jetbrains.python.codeInsight.intentions.ImportFromToImportIntention Python - ImportFromToImportIntention com.jetbrains.python.codeInsight.intentions.ImportToggleAliasIntention Python - ImportToggleAliasIntention com.jetbrains.python.codeInsight.intentions.PySplitIfIntention Python - PySplitIfIntention com.jetbrains.python.codeInsight.intentions.PyNegateComparisonIntention Python - PyNegateComparisonIntention com.jetbrains.python.codeInsight.intentions.PyStringConcatenationToFormatIntention Python - PyStringConcatenationToFormatIntention com.jetbrains.python.codeInsight.intentions.ConvertFormatOperatorToMethodIntention Python - ConvertFormatOperatorToMethodIntention com.jetbrains.python.codeInsight.intentions.PyFlipComparisonIntention Python - PyFlipComparisonIntention + + + + com.jetbrains.python.codeInsight.intentions.PyDemorganIntention + Python diff --git a/python/src/com/jetbrains/python/JythonManager.java b/python/src/com/jetbrains/python/JythonManager.java deleted file mode 100644 index a9da91a3817e..000000000000 --- a/python/src/com/jetbrains/python/JythonManager.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.jetbrains.python; - -import com.intellij.openapi.components.ServiceManager; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.util.PathUtil; -import org.jetbrains.annotations.NonNls; -import org.python.core.*; -import org.python.util.PythonInterpreter; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.Properties; - -/** - * @author yole - */ -public class JythonManager { - private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.JythonManager"); - - public static JythonManager getInstance() { - return ServiceManager.getService(JythonManager.class); - } - - private final PythonInterpreter myInterpreter; - - public JythonManager() { - @NonNls Properties postProperties = new Properties(); - postProperties.put("python.cachedir.skip", "true"); - String pythonScriptPath = findPythonScriptPath(); - postProperties.put("python.path", pythonScriptPath); - LOG.info("Loading Python scripts from " + pythonScriptPath); - PySystemState.initialize(PySystemState.getBaseProperties(), postProperties, new String[] { "" }); - myInterpreter = new PythonInterpreter(); - } - - private String findPythonScriptPath() { - @NonNls String classRoot = PathUtil.getJarPathForClass(getClass()); - if (classRoot.endsWith(".jar")) { - return classRoot; - } - // compiled classes - File f = new File(new File(classRoot).getParent(), "python-py"); - if (f.exists()) { - return f.getPath(); - } - throw new RuntimeException("Can't figure out Python script path: " + classRoot); - } - - public void execScriptFromResource(@NonNls String resourcePath) { - final InputStream stream = getClass().getClassLoader().getResourceAsStream(resourcePath); - try { - myInterpreter.execfile(stream); - } - finally { - try { - stream.close(); - } - catch (IOException e) { - LOG.error(e); - } - } - } - - public PyObject eval(@NonNls final String code) { - return myInterpreter.eval(code); - } -} diff --git a/python/src/com/jetbrains/python/PyExtensionFactory.java b/python/src/com/jetbrains/python/PyExtensionFactory.java deleted file mode 100644 index 188b1f40cd25..000000000000 --- a/python/src/com/jetbrains/python/PyExtensionFactory.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.jetbrains.python; - -import com.intellij.openapi.extensions.ExtensionFactory; -import com.intellij.openapi.diagnostic.Logger; -import org.python.core.PyObject; - -/** - * @author yole - */ -public class PyExtensionFactory implements ExtensionFactory { - private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.PyExtensionFactory"); - - public Object createInstance(final String factoryArgument, final String implementationClass) { - LOG.info("Instantiating Jython class " + implementationClass + " from script " + factoryArgument); - JythonManager jythonManager = JythonManager.getInstance(); - jythonManager.execScriptFromResource(factoryArgument); - final PyObject object = jythonManager.eval(implementationClass + "()"); - return object.__tojava__(Object.class); - } -} diff --git a/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java b/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java index ca1c502f430b..241703071895 100644 --- a/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java @@ -92,9 +92,26 @@ public class PyCodeFragmentUtil { } // If we see more than 1 outer instruction, controlflow is interrupted - if (outerInstructions.size() > 1){ + if (outerInstructions.size() > 2){ throw new CannotCreateCodeFragmentException(PyBundle.message("refactoring.extract.method.error.cannot.perform.refactoring.when.execution.flow.is.interrupted")); } + if (outerInstructions.size() == 2){ + boolean errorFound = true; + for (Instruction outerInstruction : outerInstructions) { + // Here we check control flow when for statement content is beeing extracted + if (outerInstruction instanceof ReadWriteInstruction && + PyForStatementNavigator.getPyForStatementByIterable(outerInstruction.getElement())!=null) { + // In case when return instruction is inside + if (!returnInstructionInside){ + errorFound = false; + break; + } + } + } + if (errorFound){ + throw new CannotCreateCodeFragmentException(PyBundle.message("refactoring.extract.method.error.cannot.perform.refactoring.when.execution.flow.is.interrupted")); + } + } // Building code fragment final PyCodeFragmentBuilder builder = new PyCodeFragmentBuilder(owner, start, end); diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PyDemorganIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/PyDemorganIntention.java new file mode 100644 index 000000000000..da1eb8cc58dd --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/intentions/PyDemorganIntention.java @@ -0,0 +1,123 @@ +package com.jetbrains.python.codeInsight.intentions; + +import com.intellij.codeInsight.intention.impl.BaseIntentionAction; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import com.jetbrains.python.PyTokenTypes; +import com.jetbrains.python.psi.*; +import org.jetbrains.annotations.NotNull; + +/** + * @author yole + */ +public class PyDemorganIntention extends BaseIntentionAction { + @NotNull + @Override + public String getText() { + return "DeMorgan Law"; + } + + @NotNull + @Override + public String getFamilyName() { + return "DeMorgan Law"; + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + PyBinaryExpression expression = PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyBinaryExpression.class); + if (expression != null) { + PyElementType op = expression.getOperator(); + if (op == PyTokenTypes.AND_KEYWORD || op == PyTokenTypes.OR_KEYWORD) { + return true; + } + } + return false; + } + + @Override + public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + PyBinaryExpression expression = PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), + PyBinaryExpression.class); + PyElementType op = expression.getOperator(); + String converted = convertConjunctionExpression(expression, op); + replaceExpression(converted, expression); + } + + private static void replaceExpression(String newExpression, PyBinaryExpression expression) { + PsiElement expressionToReplace = expression; + String expString = "not(" + newExpression + ')'; + PsiElement parent = expression.getParent().getParent(); + if (isNegation(parent)) { + expressionToReplace = parent; + expString = newExpression; + } + PyElementGenerator generator = PyElementGenerator.getInstance(expression.getProject()); + PyExpression newCall = generator.createExpressionFromText(expString); + PsiElement insertedElement = expressionToReplace.replace(newCall); + // codeStyleManager = expression.getManager().getCodeStyleManager() + // TODO codeStyleManager.reformat(insertedElement) + } + + private static String convertConjunctionExpression(PyBinaryExpression exp, PyElementType tokenType) { + PyExpression lhs = exp.getLeftExpression(); + String lhsText, rhsText; + if (isConjunctionExpression(lhs, tokenType)) { + lhsText = convertConjunctionExpression((PyBinaryExpression)lhs, tokenType); + } + else { + lhsText = convertLeafExpression(lhs); + } + + PyExpression rhs = exp.getRightExpression(); + if (isConjunctionExpression(rhs, tokenType)) { + rhsText = convertConjunctionExpression((PyBinaryExpression)rhs, tokenType); + } + else { + rhsText = convertLeafExpression(rhs); + } + + String flippedConjunction = (tokenType == PyTokenTypes.AND_KEYWORD) ? " or " : " and "; + return lhsText + flippedConjunction + rhsText; + } + + private static String convertLeafExpression(PyExpression condition) { + if (isNegation(condition)) { + PyExpression negated = getNegated(condition); + if (negated == null) { + return ""; + } + return negated.getText(); + } + else { + return "not " + condition.getText(); + } + } + + private static PyExpression getNegated(PyExpression expression) { + PyExpression operand = ((PyPrefixExpression)expression).getOperand(); + return operand; // TODO strip () + } + + private static boolean isConjunctionExpression(PyExpression expression, PyElementType tokenType) { + if (expression instanceof PyBinaryExpression) { + PyElementType operator = ((PyBinaryExpression) expression).getOperator(); + return operator == tokenType; + } + return false; + } + + private static boolean isNegation(PsiElement expression) { + if (!(expression instanceof PyPrefixExpression)) { + return false; + } + PyElementType op = ((PyPrefixExpression)expression).getOperationSign(); + return op == PyTokenTypes.NOT_KEYWORD; + } + + +} diff --git a/python/src/com/jetbrains/python/debugger/PyDebugValue.java b/python/src/com/jetbrains/python/debugger/PyDebugValue.java index 63a1c590a426..47aec3a7fec5 100644 --- a/python/src/com/jetbrains/python/debugger/PyDebugValue.java +++ b/python/src/com/jetbrains/python/debugger/PyDebugValue.java @@ -74,18 +74,18 @@ public class PyDebugValue extends XValue { } // todo: pass StringBuilder to recursive calls - public String getQualifiedExpression() { + public String getEvaluationExpression() { if (myParent == null) { return getTempName(); } else if ("list".equals(myParent.getType()) || "tuple".equals(myParent.getType())) { - return new StringBuilder().append(myParent.getQualifiedExpression()).append('[').append(myName).append(']').toString(); + return new StringBuilder().append(myParent.getEvaluationExpression()).append('[').append(myName).append(']').toString(); } else if ("dict".equals(myParent.getType())) { - return new StringBuilder().append(myParent.getQualifiedExpression()).append("['").append(myName).append("']").toString(); + return new StringBuilder().append(myParent.getEvaluationExpression()).append("['").append(myName).append("']").toString(); } else { - return new StringBuilder().append(myParent.getQualifiedExpression()).append('.').append(myName).toString(); + return new StringBuilder().append(myParent.getEvaluationExpression()).append('.').append(myName).toString(); } } diff --git a/python/src/com/jetbrains/python/inspections/PythonPyInspectionToolProvider.java b/python/src/com/jetbrains/python/inspections/PythonPyInspectionToolProvider.java deleted file mode 100644 index e04ce9d71647..000000000000 --- a/python/src/com/jetbrains/python/inspections/PythonPyInspectionToolProvider.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.jetbrains.python.inspections; - -import com.intellij.codeInspection.InspectionProfileEntry; -import com.intellij.codeInspection.InspectionToolsFactory; -import com.intellij.codeInspection.LocalInspectionTool; -import com.intellij.codeInspection.ex.InspectionTool; -import com.intellij.codeInspection.ex.InspectionToolRegistrar; -import com.intellij.codeInspection.ex.LocalInspectionToolWrapper; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.util.Function; -import com.jetbrains.python.JythonManager; -import org.jetbrains.annotations.Nullable; -import org.python.core.PyList; -import org.python.core.PyObject; - -import java.util.HashSet; -import java.util.Set; - -/** - * @author yole - */ -public class PythonPyInspectionToolProvider implements InspectionToolsFactory { - private final JythonManager myManager; - - public static PythonPyInspectionToolProvider getInstance() { - return ApplicationManager.getApplication().getComponent(PythonPyInspectionToolProvider.class); - } - - private final InspectionToolRegistrar myRegistrar; - - private final Set myShortNames = new HashSet(); - - public PythonPyInspectionToolProvider(final InspectionToolRegistrar registrar, final JythonManager jythonManager) { - myRegistrar = registrar; - myManager = jythonManager; - } - - @Nullable - public LocalInspectionTool createLocalInspectionTool(final String inspectionToolName) { - try { - final PyObject object = myManager.eval(inspectionToolName + "()"); - final Object o = object.__tojava__(LocalInspectionTool.class); - return (LocalInspectionTool)o; - } - catch (Exception e) { - return null; - } - } - - public InspectionProfileEntry[] createTools() { - myRegistrar.registerInspectionToolProvider(new Function() { - @Nullable - public InspectionTool fun(String shortName) { - final LocalInspectionTool tool = myShortNames.contains(shortName) ? createLocalInspectionTool(shortName) : null; - return tool != null ? new LocalInspectionToolWrapper(tool) : null; - } - }); - - myManager.execScriptFromResource("inspections/inspections.py"); - - final PyList pyList = (PyList) myManager.eval("getAllInspections()"); - int len = pyList.__len__(); - final InspectionProfileEntry[] profileEntries = new InspectionProfileEntry[len]; - for(int i=0; iif hour >= start and hour < end: return name \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyCodeFragmentTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyCodeFragmentTest.java index 03fdb9bae802..db7a65fa4a33 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyCodeFragmentTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyCodeFragmentTest.java @@ -70,7 +70,7 @@ public class PyCodeFragmentTest extends LightMarkedTestCase { final StringBuffer buffer = new StringBuffer(); try { final CodeFragment fragment = PyCodeFragmentUtil.createCodeFragment((ScopeOwner)context, startElement, endElement); - if (fragment.isReturnInstructonInside()) { + if (fragment.isReturnInstructionInside()) { buffer.append("Return instruction inside found").append("\n"); } buffer.append("In:\n"); diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java index c04760dd7c97..6a0334228c5a 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java @@ -126,4 +126,9 @@ public class PyExtractMethodTest extends LightMarkedTestCase { public void testClass() throws Throwable { doTest("context/class.before.py", "bar", "context/class.after.py"); } + + public void testConditionalReturn() throws Throwable { + doTest("conditionalreturn.before.py", "bar", "Cannot perform refactoring when execution flow is interrupted"); + } + }