From 1510c25ea34523149cbca23e0ba5d7c98890842a Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Mon, 13 Dec 2010 18:10:07 +0300 Subject: [PATCH 1/9] partly fixed "No tests found " in doctests --- python/helpers/pycharm/docrunner.py | 11 +++++++++++ .../doctest/PythonDocTestCommandLineState.java | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/python/helpers/pycharm/docrunner.py b/python/helpers/pycharm/docrunner.py index e50f3f48ae44..ffbb923cd85f 100644 --- a/python/helpers/pycharm/docrunner.py +++ b/python/helpers/pycharm/docrunner.py @@ -7,6 +7,7 @@ import traceback import datetime from tcunittest import TeamcityTestResult +from tcmessages import TeamcityServiceMessages class TeamcityDocTestResult(TeamcityTestResult): """ @@ -182,6 +183,8 @@ def testfile(filename): parser = doctest.DocTestParser() # Read the file, convert it to a test, and run it. test = parser.get_doctest(text, globs, name, filename, 0) + debug("/ Loaded " + str(len(test.examples)) + " tests") + TeamcityServiceMessages(sys.stdout).testCount(len(test.examples)) if test.examples: runner.run(test) @@ -263,6 +266,8 @@ if __name__ == "__main__": # for doctests for module in modules: tests = finder.find(module, module.__name__) + debug("/ Loaded " + str(len(tests)) + " tests") + TeamcityServiceMessages(sys.stdout).testCount(len(tests)) for test in tests: if test.examples: runner.run(test) @@ -277,6 +282,8 @@ if __name__ == "__main__": if hasattr(module, a[1]): testcase = getattr(module, a[1]) tests = finder.find(testcase, testcase.__name__) + debug("/ Loaded " + str(len(tests)) + " tests") + TeamcityServiceMessages(sys.stdout).testCount(len(tests)) for test in tests: runner.run(test) else: @@ -293,6 +300,8 @@ if __name__ == "__main__": if hasattr(module, a[2]): testcase = getattr(module, a[2]) tests = finder.find(testcase, testcase.__name__) + debug("/ Loaded " + str(len(tests)) + " tests") + TeamcityServiceMessages(sys.stdout).testCount(len(tests)) for test in tests: runner.run(test) else: @@ -304,6 +313,8 @@ if __name__ == "__main__": if hasattr(testCaseClass, a[2]): testcase = getattr(testCaseClass, a[2]) tests = finder.find(testcase, testcase.__name__) + debug("/ Loaded " + str(len(tests)) + " tests") + TeamcityServiceMessages(sys.stdout).testCount(len(tests)) for test in tests: runner.run(test) else: diff --git a/python/src/com/jetbrains/python/testing/doctest/PythonDocTestCommandLineState.java b/python/src/com/jetbrains/python/testing/doctest/PythonDocTestCommandLineState.java index 6b47f626b7fb..83c6d960257e 100644 --- a/python/src/com/jetbrains/python/testing/doctest/PythonDocTestCommandLineState.java +++ b/python/src/com/jetbrains/python/testing/doctest/PythonDocTestCommandLineState.java @@ -58,7 +58,7 @@ public class PythonDocTestCommandLineState extends PythonTestCommandLineStateBas break; case TEST_FOLDER: if (!myConfig.getPattern().isEmpty()) - specs.add(myConfig.getFolderName() + "/" + ";" + myConfig.getPattern() + "$"); + specs.add(myConfig.getFolderName() + "/" + ";" + myConfig.getPattern()); else specs.add(myConfig.getFolderName() + "/"); // TODO[kate]:think about delimiter between folderName and Pattern From 50754613f0049ff1995c578221b00053d63748cf Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Tue, 14 Dec 2010 16:51:37 +0300 Subject: [PATCH 2/9] fixed (PY-2467) PyCharm's own unittest / unittest2 helpers for PyDev get into the way while debugging a test suite which is run as a script --- python/helpers/pycharm/docrunner.py | 3 +- python/helpers/pycharm/utrunner.py | 7 ---- python/helpers/pydev/unittest2.py | 63 ----------------------------- 3 files changed, 1 insertion(+), 72 deletions(-) delete mode 100644 python/helpers/pydev/unittest2.py diff --git a/python/helpers/pycharm/docrunner.py b/python/helpers/pycharm/docrunner.py index ffbb923cd85f..616192ea8df7 100644 --- a/python/helpers/pycharm/docrunner.py +++ b/python/helpers/pycharm/docrunner.py @@ -176,8 +176,7 @@ def testfile(filename): text, filename = doctest._load_testfile(filename, None, False) name = os.path.basename(filename) - globs = {} - globs['__name__'] = '__main__' + globs = {'__name__': '__main__'} runner = DocTestRunner() parser = doctest.DocTestParser() diff --git a/python/helpers/pycharm/utrunner.py b/python/helpers/pycharm/utrunner.py index 8d7ac8967193..0eee3919c2d8 100644 --- a/python/helpers/pycharm/utrunner.py +++ b/python/helpers/pycharm/utrunner.py @@ -1,4 +1,3 @@ -import unittest import os import imp import sys @@ -7,12 +6,6 @@ import re from tcmessages import TeamcityServiceMessages from tcunittest import TeamcityTestRunner -try: - import unittest2 as unittest -except: - # python version doesn't have unittest2 - pass - from nose_helper import TestLoader, ContextSuite PYTHON_VERSION_MAJOR = sys.version_info[0] diff --git a/python/helpers/pydev/unittest2.py b/python/helpers/pydev/unittest2.py deleted file mode 100644 index 98b28268ec2f..000000000000 --- a/python/helpers/pydev/unittest2.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python -from unittest import TestResult - -class TestListener: - """ Simulate a Java interface by providing an abstract class - All methods need to be implemented by classes extending TestListener - """ - - # Test was successful. - def addSuccess(self, test): - raise NotImplementedError, "TestListener.addSuccess()" - - # An error occured - def addError(self, test, err): - raise NotImplementedError, "TestListener.addError()" - - # A failure occurred. - def addFailure(self, test, err): - raise NotImplementedError, "TestListener.addFailure()" - - # A test started. - def startTest(self, test): - raise NotImplementedError, "TestListener.startTest()" - - # A test ended. - def endTest(self, test): - raise NotImplementedError, "TestListener.endTest()" - -class TestResultWithListeners(TestResult): - def __init__(self): - TestResult.__init__(self) - self.listeners = [] - - def startTest(self, test): - TestResult.startTest(self, test) - for listener in self.listeners: - listener.startTest(test) - - def endTest(self, test): - for listener in self.listeners: - listener.endTest(test) - - def addSuccess(self, test): - TestResult.addSuccess(self, test) - for listener in self.listeners: - listener.addSuccess(test) - - def addError(self, test, err): - TestResult.addError(self, test, err) - for listener in self.listeners: - listener.addError(test, err) - - def addFailure(self, test, err): - TestResult.addFailure(self, test, err) - for listener in self.listeners: - listener.addFailure(test, err) - - def addListener(self, listener): - self.listeners.append(listener) - - def removeListener(self, listener): - self.listeners.remove(listener) - From 7f9205ce281783019e7c1143d0cd781670a52f2a Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Tue, 14 Dec 2010 17:14:45 +0300 Subject: [PATCH 3/9] fixed bugs, connected to lambda - PY-2480 Intention: Convert lambda to function: create function on the module level - PY-2478 PyReturnStatementImpl at com.intellij.extapi.psi.PsiElementBase.getContainingFile - PY-2479 Intention: Convert lambda to function: function should be created on the next line when lambda is defined on the same line with the colon --- .../PyConvertLambdaToFunctionIntention.java | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PyConvertLambdaToFunctionIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/PyConvertLambdaToFunctionIntention.java index 3f025d113936..921b09624481 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/PyConvertLambdaToFunctionIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/PyConvertLambdaToFunctionIntention.java @@ -58,17 +58,36 @@ public class PyConvertLambdaToFunctionIntention extends BaseIntentionAction { } functionBuilder.statement("return " + body.getText()); PyFunction function = functionBuilder.buildFunction(project); - PyStatement statement = PsiTreeUtil.getParentOfType(lambdaExpression, PyStatement.class); - if (statement != null) { - PsiElement parentOfStatement = statement.getParent(); - if (parentOfStatement != null) - function = (PyFunction)parentOfStatement.addBefore(function, statement); + + + + PyFunction parentFunction = PsiTreeUtil.getTopmostParentOfType(lambdaExpression, PyFunction.class); + if (parentFunction != null ) { + PyClass parentClass = PsiTreeUtil.getTopmostParentOfType(parentFunction, PyClass.class); + if (parentClass != null) { + PsiElement classParent = parentClass.getParent(); + function = (PyFunction)classParent.addBefore(function, parentClass); + } else { + PsiElement funcParent = parentFunction.getParent(); + function = (PyFunction)funcParent.addBefore(function, parentFunction); + } + } else { + PyStatement statement = PsiTreeUtil.getTopmostParentOfType(lambdaExpression, + PyStatement.class); + if (statement != null) { + PsiElement statementParent = statement.getParent(); + if (statementParent != null) + function = (PyFunction)statementParent.addBefore(function, statement); + } } + function = CodeInsightUtilBase + .forcePsiPostprocessAndRestoreElement(function); + if (parent instanceof PyAssignmentStatement) { parent.delete(); } else { - PyElement parentScope = PsiTreeUtil.getParentOfType(lambdaExpression, PyFunction.class, PyClass.class, PyFile.class); + PyElement parentScope = PsiTreeUtil.getParentOfType(lambdaExpression, PyClass.class, PyFile.class); final TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(parentScope); PsiElement functionName = function.getNameIdentifier(); functionName = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(functionName); From a2e8f5fa0bcd6b6cc9e74c5b10cea9e9c2af5f91 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 14 Dec 2010 17:16:33 +0300 Subject: [PATCH 4/9] DMG background for PyCharm --- python/build/DMG_background.png | Bin 0 -> 28048 bytes python/build/pycharm_build.gant | 1 + 2 files changed, 1 insertion(+) create mode 100644 python/build/DMG_background.png diff --git a/python/build/DMG_background.png b/python/build/DMG_background.png new file mode 100644 index 0000000000000000000000000000000000000000..132428137a48a0bd615843274aa0365420d2403d GIT binary patch literal 28048 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 Vm@dNfal{&gjD(_iwWwj>{{oxz!^Qvr literal 0 HcmV?d00001 diff --git a/python/build/pycharm_build.gant b/python/build/pycharm_build.gant index bcc539250fd7..255179da8936 100644 --- a/python/build/pycharm_build.gant +++ b/python/build/pycharm_build.gant @@ -257,6 +257,7 @@ target('default': "Build artifacts") { } } executeExternalAnt("file.name": "pycharm", + "dmg.image.name" : "${home}/python/build/DMG_background.png", "$home/build/buildDmg.xml") buildTarGz() From dc9d6f7850d1efaa7745c9f46a4a880b3ef0f0f1 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Tue, 14 Dec 2010 18:40:32 +0300 Subject: [PATCH 5/9] added py.test to tes runner settings --- python/src/com/jetbrains/python/PyBundle.properties | 5 +++++ .../python/testing/PythonTestConfigurationsModel.java | 1 + .../python/testing/PythonUnitTestConfigurationType.java | 2 +- .../jetbrains/python/testing/TestRunnerConfigurable.java | 8 ++++++++ .../nosetest/PythonNoseTestRunConfigurationType.java | 2 +- .../testing/pytest/PyTestConfigurationProducer.java | 6 ++++++ .../python/testing/pytest/PyTestRunConfiguration.java | 4 ++++ .../python/testing/pytest/PyTestRunConfigurationType.java | 8 ++++++-- 8 files changed, 32 insertions(+), 4 deletions(-) diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index d9e0084e93dd..65ff273bb142 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -462,6 +462,11 @@ runcfg.doctest.description=Python's doctests run configuration runcfg.nosetests.display_name=Python's nosetests runcfg.nosetests.description=Python's nosetests run configuration +### pytest run conf +runcfg.pytest.display_name=py.test +runcfg.pytest.description=py.test run configuration + + ### unittest run configuration runcfg.unittest.display_name=Python's unittest runcfg.unittest.description=Python's unittest run configuration diff --git a/python/src/com/jetbrains/python/testing/PythonTestConfigurationsModel.java b/python/src/com/jetbrains/python/testing/PythonTestConfigurationsModel.java index 1b230346b461..c0759397658f 100644 --- a/python/src/com/jetbrains/python/testing/PythonTestConfigurationsModel.java +++ b/python/src/com/jetbrains/python/testing/PythonTestConfigurationsModel.java @@ -31,6 +31,7 @@ import java.util.Set; public class PythonTestConfigurationsModel implements ComboBoxModel { public static final String PYTHONS_UNITTEST_NAME = PyBundle.message("runcfg.unittest.display_name"); public static final String PYTHONS_NOSETEST_NAME = PyBundle.message("runcfg.nosetests.display_name"); + public static final String PY_TEST_NAME = PyBundle.message("runcfg.pytest.display_name"); private static final PythonTestConfigurationsModel INSTANCE = new PythonTestConfigurationsModel(); private final List myConfigurationTypes = new ArrayList(); diff --git a/python/src/com/jetbrains/python/testing/PythonUnitTestConfigurationType.java b/python/src/com/jetbrains/python/testing/PythonUnitTestConfigurationType.java index e541fb88b91e..ce7785e7e4c6 100644 --- a/python/src/com/jetbrains/python/testing/PythonUnitTestConfigurationType.java +++ b/python/src/com/jetbrains/python/testing/PythonUnitTestConfigurationType.java @@ -33,7 +33,7 @@ public class PythonUnitTestConfigurationType extends ConfigurationTypeBase { } public RunConfiguration createTemplateConfiguration(Project project) { - myModel.addConfiguration(PyBundle.message("runcfg.unittest.display_name"), false); + myModel.addConfiguration(PythonTestConfigurationsModel.PYTHONS_UNITTEST_NAME, false); return new PythonUnitTestRunConfiguration(new RunConfigurationModule(project), this, ""); } } diff --git a/python/src/com/jetbrains/python/testing/TestRunnerConfigurable.java b/python/src/com/jetbrains/python/testing/TestRunnerConfigurable.java index 3d7f90c14816..f5b71b11e91e 100644 --- a/python/src/com/jetbrains/python/testing/TestRunnerConfigurable.java +++ b/python/src/com/jetbrains/python/testing/TestRunnerConfigurable.java @@ -4,6 +4,7 @@ import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.NonDefaultProjectConfigurable; import com.jetbrains.python.testing.nosetest.PythonNoseTestConfigurationProducer; +import com.jetbrains.python.testing.pytest.PyTestConfigurationProducer; import org.jetbrains.annotations.Nls; import javax.swing.*; @@ -51,10 +52,17 @@ public class TestRunnerConfigurable implements Configurable, NonDefaultProjectCo if (selectedItem.equals(PythonTestConfigurationsModel.PYTHONS_UNITTEST_NAME)) { PythonUnitTestConfigurationProducer.getInstance(PythonUnitTestConfigurationProducer.class).setActive(true); PythonNoseTestConfigurationProducer.getInstance(PythonNoseTestConfigurationProducer.class).setActive(false); + PyTestConfigurationProducer.getInstance(PyTestConfigurationProducer.class).setActive(false); } else if (selectedItem.equals(PythonTestConfigurationsModel.PYTHONS_NOSETEST_NAME)) { PythonNoseTestConfigurationProducer.getInstance(PythonNoseTestConfigurationProducer.class).setActive(true); PythonUnitTestConfigurationProducer.getInstance(PythonUnitTestConfigurationProducer.class).setActive(false); + PyTestConfigurationProducer.getInstance(PyTestConfigurationProducer.class).setActive(false); + } + else if (selectedItem.equals(PythonTestConfigurationsModel.PY_TEST_NAME)) { + PyTestConfigurationProducer.getInstance(PyTestConfigurationProducer.class).setActive(true); + PythonNoseTestConfigurationProducer.getInstance(PythonNoseTestConfigurationProducer.class).setActive(false); + PythonUnitTestConfigurationProducer.getInstance(PythonUnitTestConfigurationProducer.class).setActive(false); } } }); diff --git a/python/src/com/jetbrains/python/testing/nosetest/PythonNoseTestRunConfigurationType.java b/python/src/com/jetbrains/python/testing/nosetest/PythonNoseTestRunConfigurationType.java index 10d5b8ecff3b..241c23e63af5 100644 --- a/python/src/com/jetbrains/python/testing/nosetest/PythonNoseTestRunConfigurationType.java +++ b/python/src/com/jetbrains/python/testing/nosetest/PythonNoseTestRunConfigurationType.java @@ -33,7 +33,7 @@ public class PythonNoseTestRunConfigurationType extends ConfigurationTypeBase { } public RunConfiguration createTemplateConfiguration(Project project) { - myModel.addConfiguration(PyBundle.message("runcfg.nosetests.display_name"), false); + myModel.addConfiguration(PythonTestConfigurationsModel.PYTHONS_NOSETEST_NAME, false); return new PythonNoseTestRunConfiguration(new RunConfigurationModule(project), this, ""); } } diff --git a/python/src/com/jetbrains/python/testing/pytest/PyTestConfigurationProducer.java b/python/src/com/jetbrains/python/testing/pytest/PyTestConfigurationProducer.java index f1969ce4f640..312322225587 100644 --- a/python/src/com/jetbrains/python/testing/pytest/PyTestConfigurationProducer.java +++ b/python/src/com/jetbrains/python/testing/pytest/PyTestConfigurationProducer.java @@ -29,6 +29,7 @@ import java.util.List; public class PyTestConfigurationProducer extends RuntimeConfigurationProducer { private PsiElement myPsiElement; + private boolean isActive = false; public PyTestConfigurationProducer() { super(ConfigurationTypeUtil.findConfigurationType(PyTestRunConfigurationType.class)); @@ -41,6 +42,7 @@ public class PyTestConfigurationProducer extends RuntimeConfigurationProducer { @Override protected RunnerAndConfigurationSettings createConfigurationByElement(Location location, ConfigurationContext context) { + if (!isActive) return null; PsiElement element = location.getPsiElement(); PsiFileSystemItem file = element instanceof PsiDirectory ? (PsiDirectory)element : element.getContainingFile(); if (file == null) return null; @@ -73,6 +75,7 @@ public class PyTestConfigurationProducer extends RuntimeConfigurationProducer { configuration.setName(name + " in " + configuration.getName()); myPsiElement = pyFunction; } + configuration.setName(configuration.suggestedName()); return result; } @@ -116,4 +119,7 @@ public class PyTestConfigurationProducer extends RuntimeConfigurationProducer { } return PREFERED; } + public void setActive(boolean active) { + isActive = active; + } } \ No newline at end of file diff --git a/python/src/com/jetbrains/python/testing/pytest/PyTestRunConfiguration.java b/python/src/com/jetbrains/python/testing/pytest/PyTestRunConfiguration.java index b98f84c07fbf..f7f8af48618f 100644 --- a/python/src/com/jetbrains/python/testing/pytest/PyTestRunConfiguration.java +++ b/python/src/com/jetbrains/python/testing/pytest/PyTestRunConfiguration.java @@ -106,4 +106,8 @@ public class PyTestRunConfiguration extends AbstractPythonRunConfiguration { if (runner.exists()) return runner.getPath(); return null; } + @Override + public String suggestedName() { + return "py.test in " + getName(); + } } diff --git a/python/src/com/jetbrains/python/testing/pytest/PyTestRunConfigurationType.java b/python/src/com/jetbrains/python/testing/pytest/PyTestRunConfigurationType.java index 00e060dbe8eb..a8407f62ff9f 100644 --- a/python/src/com/jetbrains/python/testing/pytest/PyTestRunConfigurationType.java +++ b/python/src/com/jetbrains/python/testing/pytest/PyTestRunConfigurationType.java @@ -5,7 +5,9 @@ import com.intellij.execution.configurations.ConfigurationType; import com.intellij.execution.configurations.RunConfiguration; import com.intellij.execution.configurations.RunConfigurationModule; import com.intellij.openapi.project.Project; +import com.jetbrains.python.PyBundle; import com.jetbrains.python.PythonFileType; +import com.jetbrains.python.testing.PythonTestConfigurationsModel; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -17,11 +19,11 @@ public class PyTestRunConfigurationType implements ConfigurationType { private final ConfigurationFactory myPyTestConfigurationFactory = new PyTestRunConfigurationFactory(this); public String getDisplayName() { - return "py.test"; + return PyBundle.message("runcfg.pytest.display_name"); } public String getConfigurationTypeDescription() { - return "py.test"; + return PyBundle.message("runcfg.pytest.description"); } public Icon getIcon() { @@ -39,11 +41,13 @@ public class PyTestRunConfigurationType implements ConfigurationType { private static class PyTestRunConfigurationFactory extends ConfigurationFactory { + final private PythonTestConfigurationsModel myModel = PythonTestConfigurationsModel.getInstance(); protected PyTestRunConfigurationFactory(ConfigurationType type) { super(type); } public RunConfiguration createTemplateConfiguration(Project project) { + myModel.addConfiguration(PythonTestConfigurationsModel.PY_TEST_NAME, false); return new PyTestRunConfiguration("", new RunConfigurationModule(project), this); } } From 49bc2408f216bd0a362f8bd88f0887057eeec192 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Tue, 14 Dec 2010 19:06:51 +0300 Subject: [PATCH 6/9] fixed test count in doctest runner --- python/helpers/pycharm/docrunner.py | 47 ++++++++++++++++------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/python/helpers/pycharm/docrunner.py b/python/helpers/pycharm/docrunner.py index 616192ea8df7..1bb78d0147d3 100644 --- a/python/helpers/pycharm/docrunner.py +++ b/python/helpers/pycharm/docrunner.py @@ -58,6 +58,20 @@ class DocTestRunner(doctest.DocTestRunner): doctest.DocTestRunner.__init__(self, verbose, optionflags) self.stream = sys.stdout self.result = TeamcityDocTestResult(self.stream) + self._tests = [] + + def addTests(self, tests): + self._tests.extend(tests) + + def addTest(self, test): + self._tests.append(test) + + def countTests(self): + return len(self._tests) + + def start(self): + for test in self._tests: + self.run(test) def __run(self, test, compileflags, out): failures = tries = 0 @@ -153,6 +167,8 @@ modules = {} from utrunner import debug, getModuleName, PYTHON_VERSION_MAJOR +runner = DocTestRunner() + def loadSource(fileName): """ loads source from fileName, @@ -178,14 +194,11 @@ def testfile(filename): name = os.path.basename(filename) globs = {'__name__': '__main__'} - runner = DocTestRunner() parser = doctest.DocTestParser() # Read the file, convert it to a test, and run it. test = parser.get_doctest(text, globs, name, filename, 0) - debug("/ Loaded " + str(len(test.examples)) + " tests") - TeamcityServiceMessages(sys.stdout).testCount(len(test.examples)) if test.examples: - runner.run(test) + runner.addTest(test) def walkModules(modules, dirname, names): walkModulesUsingPattern(modules, dirname, names) @@ -231,7 +244,6 @@ def testFilesInFolderUsingPattern(folder, pattern = ".*"): return result if __name__ == "__main__": - runner = DocTestRunner() finder = doctest.DocTestFinder() for arg in sys.argv[1:]: @@ -265,11 +277,9 @@ if __name__ == "__main__": # for doctests for module in modules: tests = finder.find(module, module.__name__) - debug("/ Loaded " + str(len(tests)) + " tests") - TeamcityServiceMessages(sys.stdout).testCount(len(tests)) for test in tests: if test.examples: - runner.run(test) + runner.addTest(test) elif len(a) == 2: # From testcase @@ -281,10 +291,7 @@ if __name__ == "__main__": if hasattr(module, a[1]): testcase = getattr(module, a[1]) tests = finder.find(testcase, testcase.__name__) - debug("/ Loaded " + str(len(tests)) + " tests") - TeamcityServiceMessages(sys.stdout).testCount(len(tests)) - for test in tests: - runner.run(test) + runner.addTests(tests) else: raise NameError('Module "%s" has no class "%s"' % (a[0], a[1])) else: @@ -299,10 +306,7 @@ if __name__ == "__main__": if hasattr(module, a[2]): testcase = getattr(module, a[2]) tests = finder.find(testcase, testcase.__name__) - debug("/ Loaded " + str(len(tests)) + " tests") - TeamcityServiceMessages(sys.stdout).testCount(len(tests)) - for test in tests: - runner.run(test) + runner.addTests(tests) else: raise NameError('Module "%s" has no method "%s"' % (a[0], a[2])) else: @@ -312,11 +316,12 @@ if __name__ == "__main__": if hasattr(testCaseClass, a[2]): testcase = getattr(testCaseClass, a[2]) tests = finder.find(testcase, testcase.__name__) - debug("/ Loaded " + str(len(tests)) + " tests") - TeamcityServiceMessages(sys.stdout).testCount(len(tests)) - for test in tests: - runner.run(test) + runner.addTests(tests) else: raise NameError('Class "%s" has no function "%s"' % (testCaseClass, a[2])) else: - raise NameError('Module "%s" has no class "%s"' % (module, a[1])) \ No newline at end of file + raise NameError('Module "%s" has no class "%s"' % (module, a[1])) + + debug("/ Loaded " + str(runner.countTests()) + " tests") + TeamcityServiceMessages(sys.stdout).testCount(runner.countTests()) + runner.start() \ No newline at end of file From bf210a865a4df43cb4992e70be9032b4e93f685a Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Tue, 14 Dec 2010 19:12:12 +0300 Subject: [PATCH 7/9] fixed (PY-2481) "Replace assigment with augmented assignment" break code --- .../jetbrains/python/actions/AugmentedAssignmentQuickFix.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/actions/AugmentedAssignmentQuickFix.java b/python/src/com/jetbrains/python/actions/AugmentedAssignmentQuickFix.java index aa45be55829a..714498dcc6f9 100644 --- a/python/src/com/jetbrains/python/actions/AugmentedAssignmentQuickFix.java +++ b/python/src/com/jetbrains/python/actions/AugmentedAssignmentQuickFix.java @@ -52,7 +52,7 @@ public class AugmentedAssignmentQuickFix implements LocalQuickFix { PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append(target.getName()).append(" "). + stringBuilder.append(target.getText()).append(" "). append(expression.getPsiOperator().getText()).append("= ").append(rightExpression.getText()); PyAugAssignmentStatementImpl augAssignment = elementGenerator.createFromText(LanguageLevel.getDefault(), PyAugAssignmentStatementImpl.class, stringBuilder.toString()); From 16c89724c610ae0d72fed9c49d304d3611972dd0 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Tue, 14 Dec 2010 19:18:09 +0300 Subject: [PATCH 8/9] fixed PY-2482 "Replace assignment with augmented assignment" - support communicativity --- .../actions/AugmentedAssignmentQuickFix.java | 18 +++++++++++++++--- .../PyAugmentAssignmentInspection.java | 13 ++++++++++++- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/python/src/com/jetbrains/python/actions/AugmentedAssignmentQuickFix.java b/python/src/com/jetbrains/python/actions/AugmentedAssignmentQuickFix.java index 714498dcc6f9..9a832cea0a17 100644 --- a/python/src/com/jetbrains/python/actions/AugmentedAssignmentQuickFix.java +++ b/python/src/com/jetbrains/python/actions/AugmentedAssignmentQuickFix.java @@ -9,9 +9,6 @@ import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyAugAssignmentStatementImpl; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.List; - /** * User: catherine * @@ -60,6 +57,21 @@ public class AugmentedAssignmentQuickFix implements LocalQuickFix { } } } + else if (rightExpression != null && rightExpression instanceof PyReferenceExpression) { + if (rightExpression.getName().equals(target.getName())) { + if (leftExpression instanceof PyNumericLiteralExpression || + leftExpression instanceof PyStringLiteralExpression) { + + PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(target.getText()).append(" "). + append(expression.getPsiOperator().getText()).append("= ").append(leftExpression.getText()); + PyAugAssignmentStatementImpl augAssignment = elementGenerator.createFromText(LanguageLevel.getDefault(), + PyAugAssignmentStatementImpl.class, stringBuilder.toString()); + statement.replace(augAssignment); + } + } + } } } } diff --git a/python/src/com/jetbrains/python/inspections/PyAugmentAssignmentInspection.java b/python/src/com/jetbrains/python/inspections/PyAugmentAssignmentInspection.java index 69563a657c72..3ffe2ef12142 100644 --- a/python/src/com/jetbrains/python/inspections/PyAugmentAssignmentInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyAugmentAssignmentInspection.java @@ -5,7 +5,6 @@ import com.intellij.psi.PsiElementVisitor; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.actions.AugmentedAssignmentQuickFix; -import com.jetbrains.python.actions.RedundantParenthesesQuickFix; import com.jetbrains.python.psi.*; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; @@ -57,6 +56,18 @@ public class PyAugmentAssignmentInspection extends PyInspection { } } } + else if (rightExpression != null && rightExpression instanceof PyReferenceExpression) { + if (rightExpression.getText().equals(target.getText())) { + if (leftExpression instanceof PyNumericLiteralExpression) { + PyElementType op = expression.getOperator(); + if (op == PyTokenTypes.PLUS || op == PyTokenTypes.MINUS || + op == PyTokenTypes.MULT || op == PyTokenTypes.DIV) { + AugmentedAssignmentQuickFix quickFix = new AugmentedAssignmentQuickFix(); + registerProblem(node, "Assignment can be replaced with augmented assignment", quickFix); + } + } + } + } } } } From d7d50e6a8954e4a42754ee2637fd82c2982eb3b1 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Tue, 14 Dec 2010 19:24:48 +0300 Subject: [PATCH 9/9] added tests for PY-2481, PY-2482 --- python/testData/inspections/AugmentAssignmentWithContext.py | 4 ++++ .../inspections/AugmentAssignmentWithContext_after.py | 4 ++++ .../inspections/PyAugmentAssignmentInspection/test.py | 4 +++- python/testSrc/com/jetbrains/python/PyQuickFixTest.java | 5 +++++ 4 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 python/testData/inspections/AugmentAssignmentWithContext.py create mode 100644 python/testData/inspections/AugmentAssignmentWithContext_after.py diff --git a/python/testData/inspections/AugmentAssignmentWithContext.py b/python/testData/inspections/AugmentAssignmentWithContext.py new file mode 100644 index 000000000000..e7a26bcccbb2 --- /dev/null +++ b/python/testData/inspections/AugmentAssignmentWithContext.py @@ -0,0 +1,4 @@ +class A: + x = 3 +a = A() +a.x = a.x + 1 \ No newline at end of file diff --git a/python/testData/inspections/AugmentAssignmentWithContext_after.py b/python/testData/inspections/AugmentAssignmentWithContext_after.py new file mode 100644 index 000000000000..56470da44af6 --- /dev/null +++ b/python/testData/inspections/AugmentAssignmentWithContext_after.py @@ -0,0 +1,4 @@ +class A: + x = 3 +a = A() +a.x += 1 \ No newline at end of file diff --git a/python/testData/inspections/PyAugmentAssignmentInspection/test.py b/python/testData/inspections/PyAugmentAssignmentInspection/test.py index bf91a1a9e638..9643d6d659ca 100644 --- a/python/testData/inspections/PyAugmentAssignmentInspection/test.py +++ b/python/testData/inspections/PyAugmentAssignmentInspection/test.py @@ -10,4 +10,6 @@ var_4 = 1 var_5 = var_5 + "string" var_6 = var_6 + var_4 -var_7 += 2 \ No newline at end of file +var_7 += 2 + +var = 2 + var # PY-2482 \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index bc9f47b6e55c..e28884bb3703 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -179,6 +179,11 @@ public class PyQuickFixTest extends PyLightFixtureTestCase { PyBundle.message("QFIX.augment.assignment"), true, true); } + public void testAugmentAssignmentWithContext() { // PY-2481 + doInspectionTest("AugmentAssignmentWithContext.py", PyAugmentAssignmentInspection.class, + PyBundle.message("QFIX.augment.assignment"), true, true); + } + public void testChainedComparisons() { // PY-1020 doInspectionTest("ChainedComparisons.py", PyChainedComparisonsInspection.class, PyBundle.message("QFIX.chained.comparison"), true, true);