Plugins/CheckerPlugins/Pep8/Pep257Checker.py

changeset 2916
a8628dfdfe04
parent 2915
9da653363d07
child 2917
fe82710d02cb
--- a/Plugins/CheckerPlugins/Pep8/Pep257Checker.py	Sun Sep 15 19:55:39 2013 +0200
+++ b/Plugins/CheckerPlugins/Pep8/Pep257Checker.py	Mon Sep 16 19:12:49 2013 +0200
@@ -107,7 +107,7 @@
         "D111", "D112", "D113",
         "D121", "D122",
         "D131", "D132", "D133", "D134",
-        "D141"
+        "D141", "D142", "D143", "D144", "D145",
     ]
     
     Messages = {
@@ -146,10 +146,24 @@
             "Pep257Checker",
             "docstring does not mention the return value type"),
         "D141": QT_TRANSLATE_NOOP(
-            "Pep257Checker", "docstring is separated by a blank line"),
+            "Pep257Checker",
+            "function/method docstring is separated by a blank line"),
+        "D142": QT_TRANSLATE_NOOP(
+            "Pep257Checker",
+            "class docstring is not preceded by a blank line"),
+        "D143": QT_TRANSLATE_NOOP(
+            "Pep257Checker",
+            "class docstring is not followed by a blank line"),
+        "D144": QT_TRANSLATE_NOOP(
+            "Pep257Checker",
+            "docstring summary is not followed by a blank line"),
+        "D145": QT_TRANSLATE_NOOP(
+            "Pep257Checker",
+            "last paragraph of docstring is not followed by a blank line"),
     }
     
-    def __init__(self, source, filename, select, ignore, expected, repeat):
+    def __init__(self, source, filename, select, ignore, expected, repeat,
+                 maxLineLength=79):
         """
         Constructor (according to 'extended' pep8.py API)
         
@@ -160,11 +174,13 @@
         @param expected list of expected codes (list of string)
         @param repeat flag indicating to report each occurrence of a code
             (boolean)
+        @param maxLineLength allowed line length (integer)
         """
         self.__select = tuple(select)
         self.__ignore = tuple(ignore)
         self.__expected = expected[:]
         self.__repeat = repeat
+        self.__maxLineLength = maxLineLength
         self.__filename = filename
         self.__source = source[:]
         self.__isScript = self.__source[0].startswith('#!')
@@ -195,6 +211,7 @@
             ],
             "classDocstring": [
                 (self.__checkClassDocstring, ("D104", "D105")),
+                (self.__checkBlankBeforeAndAfterClass, ("D142", "D143")),
             ],
             "methodDocstring": [
             ],
@@ -212,6 +229,8 @@
                 (self.__checkOneLiner, ("D121",)),
                 (self.__checkIndent, ("D122",)),
                 (self.__checkEndsWithPeriod, ("D131",)),
+                (self.__checkBlankAfterSummary, ("D144",)),
+                (self.__checkBlankAfterLastParagraph, ("D145",)),
             ],
         }
         
@@ -323,16 +342,14 @@
                 .replace('r"""', "", 1)
                 .replace('u"""', "", 1)
                 .replace('"""', "")
+                .replace("r'''", "", 1)
+                .replace("u'''", "", 1)
+                .replace("'''", "")
                 .strip())
         
         if len(lines) == 1 or len(line) > 0:
             return line, 0
         return lines[1].strip(), 1
-        
-        first_line = lines[0].strip()
-        if len(lines) == 1 or len(first_line) > 0:
-            return first_line, 0
-        return lines[1].strip(), 1
     
     ##################################################################
     ## Parsing functionality below
@@ -362,7 +379,7 @@
         @return context of extracted docstring (Pep257Context)
         """
         moduleDocstring = self.__parseModuleDocstring(context.source())
-        if what.startswith('module'):
+        if what.startswith('module') or context.contextType() == "module":
             return moduleDocstring
         if moduleDocstring:
             return moduleDocstring
@@ -642,7 +659,12 @@
         if len(lines) > 1:
             nonEmptyLines = [l for l in lines if l.strip().strip('\'"')]
             if len(nonEmptyLines) == 1:
-                self.__error(docstringContext.start(), 0, "D121")
+                modLen = len(context.indent() + '"""' +
+                             nonEmptyLines[0].strip() + '"""')
+                if context.contextType() != "module":
+                    modLen += 4
+                if modLen <= self.__maxLineLength:
+                    self.__error(docstringContext.start(), 0, "D121")
     
     def __checkIndent(self, docstringContext, context):
         """
@@ -731,7 +753,7 @@
                 tokenize.generate_tokens(StringIO(context.ssource()).readline))
             return_ = [tokens[i + 1][0] for i,  token in enumerate(tokens)
                        if token[1] == "return"]
-            if (set(return_) - 
+            if (set(return_) -
                     set([tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE]) !=
                     set([])):
                 self.__error(docstringContext.end(), 0, "D134")
@@ -744,69 +766,94 @@
         @param docstringContext docstring context (Pep257Context)
         @param context context of the docstring (Pep257Context)
         """
-        if docstringContext is None or self.__isScript:
+        if docstringContext is None:
+            return
+        
+        contextLines = context.source()
+        cti = 0
+        while cti < len(contextLines) and \
+                not contextLines[cti].strip().startswith(
+                ('"""', 'r"""', 'u"""', "'''", "r'''", "u'''")):
+            cti += 1
+        
+        if cti == len(contextLines):
+            return
+        
+        if not contextLines[cti - 1].strip():
+            self.__error(docstringContext.start(), 0, "D141")
+    
+    def __checkBlankBeforeAndAfterClass(self, docstringContext, context):
+        """
+        Private method to check, that class docstrings have one
+        blank line around them.
+        
+        @param docstringContext docstring context (Pep257Context)
+        @param context context of the docstring (Pep257Context)
+        """
+        if docstringContext is None:
             return
         
         contextLines = context.source()
         cti = 0
-        while not contextLines[cti].strip().startswith(
-                ('"""', 'r"""', 'u"""')):
+        while cti < len(contextLines) and \
+            not contextLines[cti].strip().startswith(
+                ('"""', 'r"""', 'u"""', "'''", "r'''", "u'''")):
             cti += 1
-        if not contextLines[cti - 1].strip():
-            self.__error(docstringContext.start(), 0, "D141")
-    
-    # D142: check_blank_before_after_class
-##def check_blank_before_after_class(class_docstring, context, is_script):
-##    """Class docstring should have 1 blank line around them.
-##
-##    Insert a blank line before and after all docstrings (one-line or
-##    multi-line) that document a class -- generally speaking, the class's
-##    methods are separated from each other by a single blank line, and the
-##    docstring needs to be offset from the first method by a blank line;
-##    for symmetry, put a blank line between the class header and the
-##    docstring.
-##
-##    """
-##    if not class_docstring:
-##        return
-##    before, after = context.split(class_docstring)[:2]
-##    before_blanks = [not line.strip() for line in before.split('\n')]
-##    after_blanks = [not line.strip() for line in after.split('\n')]
-##    if before_blanks[-3:] != [False, True, True]:
-##        return True
-##    if not all(after_blanks) and after_blanks[:3] != [True, True, False]:
-##        return True
+        
+        if cti == len(contextLines):
+            return
+        
+        start = cti
+        if contextLines[cti].strip() in (
+                '"""', 'r"""', 'u"""', "'''", "r'''", "u'''"):
+            # it is a multi line docstring
+            cti += 1
+        
+        while cti < len(contextLines) and \
+                not contextLines[cti].strip().endswith(('"""', "'''")):
+            cti += 1
+        end = cti
+        
+        if contextLines[start - 1].strip():
+            self.__error(docstringContext.start(), 0, "D142")
+        if contextLines[end + 1].strip():
+            self.__error(docstringContext.end(), 0, "D143")
     
-    # D143: check_blank_after_summary
-##def check_blank_after_summary(docstring, context, is_script):
-##    """Blank line missing after one-line summary.
-##
-##    Multi-line docstrings consist of a summary line just like a one-line
-##    docstring, followed by a blank line, followed by a more elaborate
-##    description. The summary line may be used by automatic indexing tools;
-##    it is important that it fits on one line and is separated from the
-##    rest of the docstring by a blank line.
-##
-##    """
-##    if not docstring:
-##        return
-##    lines = eval(docstring).split('\n')
-##    if len(lines) > 1:
-##        (summary_line, line_number) = get_summary_line_info(docstring)
-##        if len(lines) <= (line_number+1) or lines[line_number+1].strip() != '':
-##            return True
+    def __checkBlankAfterSummary(self, docstringContext, context):
+        """
+        Private method to check, that docstring summaries are followed
+        by a blank line.
+        
+        @param docstringContext docstring context (Pep257Context)
+        @param context context of the docstring (Pep257Context)
+        """
+        if docstringContext is None:
+            return
+        
+        docstrings = docstringContext.source()
+        if len(docstrings) in [1, 3]:
+            # correct/invalid one-liner
+            return
+        
+        summary, lineNumber = self.__getSummaryLine(docstringContext)
+        if docstrings[lineNumber + 1].strip():
+            self.__error(docstringContext.start() + lineNumber, 0, "D144")
     
-    # D144: check_blank_after_last_paragraph
-##def check_blank_after_last_paragraph(docstring, context, is_script):
-##    """Multiline docstring should end with 1 blank line.
-##
-##    The BDFL recommends inserting a blank line between the last
-##    paragraph in a multi-line docstring and its closing quotes,
-##    placing the closing quotes on a line by themselves.
-##
-##    """
-##    if (not docstring) or len(eval(docstring).split('\n')) == 1:
-##        return
-##    blanks = [not line.strip() for line in eval(docstring).split('\n')]
-##    if blanks[-3:] != [False, True, True]:
-##        return True
+    def __checkBlankAfterLastParagraph(self, docstringContext, context):
+        """
+        Private method to check, that docstring summaries are followed
+        by a blank line.
+        
+        @param docstringContext docstring context (Pep257Context)
+        @param context context of the docstring (Pep257Context)
+        """
+        if docstringContext is None:
+            return
+        
+        docstrings = docstringContext.source()
+        if len(docstrings) in [1, 3]:
+            # correct/invalid one-liner
+            return
+        
+        if docstrings[-2].strip():
+            self.__error(docstringContext.end(), 0, "D145")

eric ide

mercurial