diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties
index 95f792d0fde3..78322e0e29f1 100644
--- a/python/src/com/jetbrains/python/PyBundle.properties
+++ b/python/src/com/jetbrains/python/PyBundle.properties
@@ -69,6 +69,9 @@ QFIX.unresolved.reference.add.future=Add 'from __future__ import with_statement'
# RemoveUnnecessaryBackslashQuickFix
QFIX.remove.unnecessary.backslash=Remove unnecessary backslash in expression
+# ConvertDocstringQuickFix
+QFIX.convert.single.quoted.docstring=Convert docstring to the triple double-quoted string form
+
# Intentions: INTN
INTN.Family.convert.import.unqualify=Convert 'import module' to 'from module import'
INTN.Family.convert.import.qualify=Convert 'from module import' to 'import module'
@@ -357,6 +360,10 @@ INSP.NAME.compatibility=Code compatibility inspection
# PyUnnecessaryBackslashInspection
INSP.NAME.unnecessary.backslash=Unnecessary backslash
+# PySingleQuotedDocstringInspection
+INSP.NAME.single.quoted.docstring=Single quoted docstring
+
+
# Refactoring
# introduce
refactoring.introduce.name.error=Incorrect name
diff --git a/python/src/com/jetbrains/python/actions/ConvertDocstringQuickFix.java b/python/src/com/jetbrains/python/actions/ConvertDocstringQuickFix.java
new file mode 100644
index 000000000000..614b0422e2ba
--- /dev/null
+++ b/python/src/com/jetbrains/python/actions/ConvertDocstringQuickFix.java
@@ -0,0 +1,53 @@
+package com.jetbrains.python.actions;
+
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.psi.LanguageLevel;
+import com.jetbrains.python.psi.PyElementGenerator;
+import com.jetbrains.python.psi.PyExpressionStatement;
+import com.jetbrains.python.psi.PyStringLiteralExpression;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * User: catherine
+ *
+ * QuickFix to convert docstrings to the common form according to PEP-257
+ * For consistency, always use """triple double quotes""" around docstrings.
+ */
+public class ConvertDocstringQuickFix implements LocalQuickFix {
+
+ public ConvertDocstringQuickFix() {
+ }
+
+ @NotNull
+ public String getName() {
+ return PyBundle.message("QFIX.convert.single.quoted.docstring");
+ }
+
+ @NotNull
+ public String getFamilyName() {
+ return PyBundle.message("INSP.GROUP.python");
+ }
+
+ public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
+ PsiElement expression = descriptor.getPsiElement();
+ if (expression instanceof PyStringLiteralExpression) {
+ PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project);
+
+ String content = expression.getText();
+ if (content.startsWith("'''") ) {
+ content = content.substring(3, content.length()-3);
+ } else {
+ content = content.substring(1, content.length()-1);
+ }
+
+ PyStringLiteralExpression newString = (PyStringLiteralExpression)elementGenerator.createFromText(LanguageLevel.forElement(expression),
+ PyExpressionStatement.class,"\"\"\"" + content + "\"\"\"").getExpression();
+ expression.replace(newString);
+ }
+ }
+
+}
diff --git a/python/src/com/jetbrains/python/inspections/PySingleQuotedDocstringInspection.java b/python/src/com/jetbrains/python/inspections/PySingleQuotedDocstringInspection.java
new file mode 100644
index 000000000000..5f78a9b4a366
--- /dev/null
+++ b/python/src/com/jetbrains/python/inspections/PySingleQuotedDocstringInspection.java
@@ -0,0 +1,50 @@
+package com.jetbrains.python.inspections;
+
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.psi.PsiElementVisitor;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.actions.ConvertDocstringQuickFix;
+import com.jetbrains.python.psi.PyDocStringOwner;
+import com.jetbrains.python.psi.PyStringLiteralExpression;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * User: catherine
+ *
+ * Inspection to detect docstrings not using triple double-quoted string
+ */
+public class PySingleQuotedDocstringInspection extends PyInspection {
+
+ @Nls
+ @NotNull
+ @Override
+ public String getDisplayName() {
+ return PyBundle.message("INSP.NAME.single.quoted.docstring");
+ }
+
+ @NotNull
+ @Override
+ public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
+ return new Visitor(holder);
+ }
+
+ private static class Visitor extends PyInspectionVisitor {
+ public Visitor(final ProblemsHolder holder) {
+ super(holder);
+ }
+
+ @Override
+ public void visitPyStringLiteralExpression(final PyStringLiteralExpression string) {
+ String stringText = string.getText();
+ final PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(string, PyDocStringOwner.class);
+ if (docStringOwner != null) {
+ if (docStringOwner.getDocStringExpression() == string) {
+ if (!stringText.startsWith("\"\"\"") && !stringText.endsWith("\"\"\""))
+ registerProblem(string, "Triple double-quoted strings should be used for docstrings.", new ConvertDocstringQuickFix());
+ }
+ }
+ }
+ }
+}
diff --git a/python/src/com/jetbrains/python/inspections/PythonInspectionToolProvider.java b/python/src/com/jetbrains/python/inspections/PythonInspectionToolProvider.java
index ac783d929b70..a36ce50df6a2 100644
--- a/python/src/com/jetbrains/python/inspections/PythonInspectionToolProvider.java
+++ b/python/src/com/jetbrains/python/inspections/PythonInspectionToolProvider.java
@@ -55,6 +55,7 @@ public class PythonInspectionToolProvider implements InspectionToolProvider {
PyCompatibilityInspection.class,
PyListCreationInspection.class,
PyUnnecessaryBackslashInspection.class,
+ PySingleQuotedDocstringInspection.class,
};
}
}
diff --git a/python/testData/inspections/ConvertSingleQuotedDocstring.py b/python/testData/inspections/ConvertSingleQuotedDocstring.py
new file mode 100644
index 000000000000..d8904c3301a1
--- /dev/null
+++ b/python/testData/inspections/ConvertSingleQuotedDocstring.py
@@ -0,0 +1,4 @@
+def foo():
+ '''foo first line docstring
+ second line of docstring'''
+ pass
\ No newline at end of file
diff --git a/python/testData/inspections/ConvertSingleQuotedDocstring_after.py b/python/testData/inspections/ConvertSingleQuotedDocstring_after.py
new file mode 100644
index 000000000000..5d84b37d7ef2
--- /dev/null
+++ b/python/testData/inspections/ConvertSingleQuotedDocstring_after.py
@@ -0,0 +1,4 @@
+def foo():
+ """foo first line docstring
+ second line of docstring"""
+ pass
\ No newline at end of file
diff --git a/python/testData/inspections/PySingleQuotedDocstringInspection/test.py b/python/testData/inspections/PySingleQuotedDocstringInspection/test.py
new file mode 100644
index 000000000000..948d9023140e
--- /dev/null
+++ b/python/testData/inspections/PySingleQuotedDocstringInspection/test.py
@@ -0,0 +1,17 @@
+'''package docstring'''
+
+def foo():
+ "foo docstring"
+ pass
+
+class Klass:
+ 'class docstring\
+ second line'
+ pass
+
+def bar():
+ """ bar docstring """
+ pass
+
+a = '''some string'''
+'''another string'''
\ 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 2f1a567e31eb..3088d67afe3a 100644
--- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java
+++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java
@@ -220,6 +220,11 @@ public class PyQuickFixTest extends PyLightFixtureTestCase {
PyBundle.message("QFIX.list.creation"), true, true);
}
+ public void testConvertSingleQuotedDocstring() { //PY-1445
+ doInspectionTest("ConvertSingleQuotedDocstring.py", PySingleQuotedDocstringInspection.class,
+ PyBundle.message("QFIX.convert.single.quoted.docstring"), true, true);
+ }
+
public void testUnnecessaryBackslash() {
String[] testFiles = new String[]{"UnnecessaryBackslash.py"};
myFixture.enableInspections(PyUnnecessaryBackslashInspection.class);
diff --git a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java
index f136436a3e8d..8c13b1f54e11 100644
--- a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java
+++ b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java
@@ -308,4 +308,8 @@ public class PythonInspectionsTest extends PyLightFixtureTestCase {
setLanguageLevel(LanguageLevel.PYTHON27);
doHighlightingTest(PyUnnecessaryBackslashInspection.class);
}
+
+ public void testPySingleQuotedDocstringInspection() { //PY-1445
+ doHighlightingTest(PySingleQuotedDocstringInspection.class);
+ }
}