106 "D101", "D102", "D103", "D104", "D105", |
107 "D101", "D102", "D103", "D104", "D105", |
107 "D111", "D112", "D113", |
108 "D111", "D112", "D113", |
108 "D121", "D122", |
109 "D121", "D122", |
109 "D131", "D132", "D133", "D134", |
110 "D131", "D132", "D133", "D134", |
110 "D141", "D142", "D143", "D144", "D145", |
111 "D141", "D142", "D143", "D144", "D145", |
|
112 |
|
113 "D203", "D205", |
|
114 "D221", |
|
115 "D231", "D234", "D235", "D236", "D237", |
|
116 "D242", "D243", "D244", "D245", |
111 ] |
117 ] |
112 |
118 |
113 Messages = { |
119 Messages = { |
114 "D101": QT_TRANSLATE_NOOP( |
120 "D101": QT_TRANSLATE_NOOP( |
115 "Pep257Checker", "module is missing a docstring"), |
121 "Pep257Checker", "module is missing a docstring"), |
158 "Pep257Checker", |
164 "Pep257Checker", |
159 "docstring summary is not followed by a blank line"), |
165 "docstring summary is not followed by a blank line"), |
160 "D145": QT_TRANSLATE_NOOP( |
166 "D145": QT_TRANSLATE_NOOP( |
161 "Pep257Checker", |
167 "Pep257Checker", |
162 "last paragraph of docstring is not followed by a blank line"), |
168 "last paragraph of docstring is not followed by a blank line"), |
|
169 |
|
170 "D203": QT_TRANSLATE_NOOP( |
|
171 "Pep257Checker", "private function/method is missing a docstring"), |
|
172 "D205": QT_TRANSLATE_NOOP( |
|
173 "Pep257Checker", "private class is missing a docstring"), |
|
174 "D221": QT_TRANSLATE_NOOP( |
|
175 "Pep257Checker", "one-liner docstring not on three lines"), |
|
176 "D231": QT_TRANSLATE_NOOP( |
|
177 "Pep257Checker", "docstring summary does not end with a period"), |
|
178 "D234": QT_TRANSLATE_NOOP( |
|
179 "Pep257Checker", "docstring does not contain a @return line"), |
|
180 "D235": QT_TRANSLATE_NOOP( |
|
181 "Pep257Checker", |
|
182 "docstring does not contain enough @param/@keyparam lines"), |
|
183 "D236": QT_TRANSLATE_NOOP( |
|
184 "Pep257Checker", |
|
185 "docstring contains too many @param/@keyparam lines"), |
|
186 "D237": QT_TRANSLATE_NOOP( |
|
187 "Pep257Checker", "order of @param/@keyparam lines does" |
|
188 " not match the function/method signature"), |
|
189 "D242": QT_TRANSLATE_NOOP( |
|
190 "Pep257Checker", "class docstring is preceded by a blank line"), |
|
191 "D243": QT_TRANSLATE_NOOP( |
|
192 "Pep257Checker", "class docstring is followed by a blank line"), |
|
193 "D244": QT_TRANSLATE_NOOP( |
|
194 "Pep257Checker", |
|
195 "docstring summary is not followed by a blank line"), |
|
196 "D245": QT_TRANSLATE_NOOP( |
|
197 "Pep257Checker", |
|
198 "last paragraph of docstring is followed by a blank line"), |
163 } |
199 } |
164 |
200 |
165 def __init__(self, source, filename, select, ignore, expected, repeat, |
201 def __init__(self, source, filename, select, ignore, expected, repeat, |
166 maxLineLength=79): |
202 maxLineLength=79, docType="pep257"): |
167 """ |
203 """ |
168 Constructor (according to 'extended' pep8.py API) |
204 Constructor |
169 |
205 |
170 @param source source code to be checked (list of string) |
206 @param source source code to be checked (list of string) |
171 @param filename name of the source file (string) |
207 @param filename name of the source file (string) |
172 @param select list of selected codes (list of string) |
208 @param select list of selected codes (list of string) |
173 @param ignore list of codes to be ignored (list of string) |
209 @param ignore list of codes to be ignored (list of string) |
174 @param expected list of expected codes (list of string) |
210 @param expected list of expected codes (list of string) |
175 @param repeat flag indicating to report each occurrence of a code |
211 @param repeat flag indicating to report each occurrence of a code |
176 (boolean) |
212 (boolean) |
177 @param maxLineLength allowed line length (integer) |
213 @keyparam maxLineLength allowed line length (integer) |
178 """ |
214 @keyparam docType type of the documentation strings |
|
215 (string, one of 'eric' or 'pep257') |
|
216 """ |
|
217 assert docType in ("eric", "pep257") |
|
218 |
179 self.__select = tuple(select) |
219 self.__select = tuple(select) |
180 self.__ignore = tuple(ignore) |
220 self.__ignore = tuple(ignore) |
181 self.__expected = expected[:] |
221 self.__expected = expected[:] |
182 self.__repeat = repeat |
222 self.__repeat = repeat |
183 self.__maxLineLength = maxLineLength |
223 self.__maxLineLength = maxLineLength |
|
224 self.__docType = docType |
184 self.__filename = filename |
225 self.__filename = filename |
185 self.__source = source[:] |
226 self.__source = source[:] |
186 self.__isScript = self.__source[0].startswith('#!') |
227 self.__isScript = self.__source[0].startswith('#!') |
187 |
228 |
188 # statistics counters |
229 # statistics counters |
201 self.__keywords = [ |
242 self.__keywords = [ |
202 'moduleDocstring', 'functionDocstring', |
243 'moduleDocstring', 'functionDocstring', |
203 'classDocstring', 'methodDocstring', |
244 'classDocstring', 'methodDocstring', |
204 'defDocstring', 'docstring' |
245 'defDocstring', 'docstring' |
205 ] |
246 ] |
206 self.__checkersWithCodes = { |
247 if self.__docType == "pep257": |
207 "moduleDocstring": [ |
248 checkersWithCodes = { |
208 (self.__checkModulesDocstrings, ("D101",)), |
249 "moduleDocstring": [ |
209 ], |
250 (self.__checkModulesDocstrings, ("D101",)), |
210 "functionDocstring": [ |
251 ], |
211 ], |
252 "functionDocstring": [ |
212 "classDocstring": [ |
253 ], |
213 (self.__checkClassDocstring, ("D104", "D105")), |
254 "classDocstring": [ |
214 (self.__checkBlankBeforeAndAfterClass, ("D142", "D143")), |
255 (self.__checkClassDocstring, ("D104", "D105")), |
215 ], |
256 (self.__checkBlankBeforeAndAfterClass, ("D142", "D143")), |
216 "methodDocstring": [ |
257 ], |
217 ], |
258 "methodDocstring": [ |
218 "defDocstring": [ |
259 ], |
219 (self.__checkFunctionDocstring, ("D102", "D103")), |
260 "defDocstring": [ |
220 (self.__checkImperativeMood, ("D132",)), |
261 (self.__checkFunctionDocstring, ("D102", "D103")), |
221 (self.__checkNoSignature, ("D133",)), |
262 (self.__checkImperativeMood, ("D132",)), |
222 (self.__checkReturnType, ("D134",)), |
263 (self.__checkNoSignature, ("D133",)), |
223 (self.__checkNoBlankLineBefore, ("D141",)), |
264 (self.__checkReturnType, ("D134",)), |
224 ], |
265 (self.__checkNoBlankLineBefore, ("D141",)), |
225 "docstring": [ |
266 ], |
226 (self.__checkTripleDoubleQuotes, ("D111",)), |
267 "docstring": [ |
227 (self.__checkBackslashes, ("D112",)), |
268 (self.__checkTripleDoubleQuotes, ("D111",)), |
228 (self.__checkUnicode, ("D113",)), |
269 (self.__checkBackslashes, ("D112",)), |
229 (self.__checkOneLiner, ("D121",)), |
270 (self.__checkUnicode, ("D113",)), |
230 (self.__checkIndent, ("D122",)), |
271 (self.__checkOneLiner, ("D121",)), |
231 (self.__checkEndsWithPeriod, ("D131",)), |
272 (self.__checkIndent, ("D122",)), |
232 (self.__checkBlankAfterSummary, ("D144",)), |
273 (self.__checkEndsWithPeriod, ("D131",)), |
233 (self.__checkBlankAfterLastParagraph, ("D145",)), |
274 (self.__checkBlankAfterSummary, ("D144",)), |
234 ], |
275 (self.__checkBlankAfterLastParagraph, ("D145",)), |
235 } |
276 ], |
|
277 } |
|
278 elif self.__docType == "eric": |
|
279 checkersWithCodes = { |
|
280 "moduleDocstring": [ |
|
281 (self.__checkModulesDocstrings, ("D101",)), |
|
282 ], |
|
283 "functionDocstring": [ |
|
284 ], |
|
285 "classDocstring": [ |
|
286 (self.__checkClassDocstring, ("D104", "D205")), |
|
287 (self.__checkEricNoBlankBeforeAndAfterClass, |
|
288 ("D242", "D243")), |
|
289 ], |
|
290 "methodDocstring": [ |
|
291 ], |
|
292 "defDocstring": [ |
|
293 (self.__checkFunctionDocstring, ("D102", "D203")), |
|
294 (self.__checkImperativeMood, ("D132",)), |
|
295 (self.__checkNoSignature, ("D133",)), |
|
296 (self.__checkEricReturn, ("D234",)), |
|
297 (self.__checkEricFunctionArguments, |
|
298 ("D235", "D236", "D237")), |
|
299 (self.__checkNoBlankLineBefore, ("D141",)), |
|
300 ], |
|
301 "docstring": [ |
|
302 (self.__checkTripleDoubleQuotes, ("D111",)), |
|
303 (self.__checkBackslashes, ("D112",)), |
|
304 (self.__checkUnicode, ("D113",)), |
|
305 (self.__checkEricOneLiner, ("D221",)), |
|
306 (self.__checkIndent, ("D122",)), |
|
307 (self.__checkEricEndsWithPeriod, ("D231",)), |
|
308 (self.__checkEricBlankAfterSummary, ("D244",)), |
|
309 (self.__checkEricNBlankAfterLastParagraph, ("D245",)), |
|
310 ], |
|
311 } |
236 |
312 |
237 self.__checkers = {} |
313 self.__checkers = {} |
238 for key, checkers in self.__checkersWithCodes.items(): |
314 for key, checkers in checkersWithCodes.items(): |
239 for checker, codes in checkers: |
315 for checker, codes in checkers: |
240 if any(not (code and self.__ignoreCode(code)) |
316 if any(not (code and self.__ignoreCode(code)) |
241 for code in codes): |
317 for code in codes): |
242 if key not in self.__checkers: |
318 if key not in self.__checkers: |
243 self.__checkers[key] = [] |
319 self.__checkers[key] = [] |
348 .strip()) |
427 .strip()) |
349 |
428 |
350 if len(lines) == 1 or len(line) > 0: |
429 if len(lines) == 1 or len(line) > 0: |
351 return line, 0 |
430 return line, 0 |
352 return lines[1].strip().replace('"""', "").replace("'''", ""), 1 |
431 return lines[1].strip().replace('"""', "").replace("'''", ""), 1 |
|
432 |
|
433 def __getSummaryLines(self, docstringContext): |
|
434 """ |
|
435 Private method to extract the summary lines. |
|
436 |
|
437 @param docstringContext docstring context (Pep257Context) |
|
438 @return summary lines (list of string) and the line it was found on |
|
439 (integer) |
|
440 """ |
|
441 summaries = [] |
|
442 lines = docstringContext.source() |
|
443 |
|
444 line0 = (lines[0] |
|
445 .replace('r"""', "", 1) |
|
446 .replace('u"""', "", 1) |
|
447 .replace('"""', "") |
|
448 .replace("r'''", "", 1) |
|
449 .replace("u'''", "", 1) |
|
450 .replace("'''", "") |
|
451 .strip()) |
|
452 if len(lines) > 1: |
|
453 line1 = lines[1].strip().replace('"""', "").replace("'''", "") |
|
454 else: |
|
455 line1 = "" |
|
456 if len(lines) > 2: |
|
457 line2 = lines[2].strip().replace('"""', "").replace("'''", "") |
|
458 else: |
|
459 line2 = "" |
|
460 if line0: |
|
461 lineno = 0 |
|
462 summaries.append(line0) |
|
463 if not line0.endswith(".") and line1: |
|
464 # two line summary |
|
465 summaries.append(line1) |
|
466 elif line1: |
|
467 lineno = 1 |
|
468 summaries.append(line1) |
|
469 if not line1.endswith(".") and line2: |
|
470 # two line summary |
|
471 summaries.append(line2) |
|
472 else: |
|
473 lineno = 2 |
|
474 summaries.append(line2) |
|
475 return summaries, lineno |
|
476 |
|
477 def __getArgNames(self, node): |
|
478 """ |
|
479 Private method to get the argument names of a function node. |
|
480 |
|
481 @param node AST node to extract arguments names from |
|
482 @return list of argument names (list of string) |
|
483 """ |
|
484 arguments = [] |
|
485 arguments.extend([arg.arg for arg in node.args.args]) |
|
486 if node.args.vararg is not None: |
|
487 arguments.append(node.args.vararg) |
|
488 arguments.extend([arg.arg for arg in node.args.kwonlyargs]) |
|
489 if node.args.kwarg is not None: |
|
490 arguments.append(node.args.kwarg) |
|
491 return arguments |
353 |
492 |
354 ################################################################## |
493 ################################################################## |
355 ## Parsing functionality below |
494 ## Parsing functionality below |
356 ################################################################## |
495 ################################################################## |
357 |
496 |
859 # correct/invalid one-liner |
1004 # correct/invalid one-liner |
860 return |
1005 return |
861 |
1006 |
862 if docstrings[-2].strip(): |
1007 if docstrings[-2].strip(): |
863 self.__error(docstringContext.end(), 0, "D145") |
1008 self.__error(docstringContext.end(), 0, "D145") |
|
1009 |
|
1010 ################################################################## |
|
1011 ## Checking functionality below (eric specific ones) |
|
1012 ################################################################## |
|
1013 |
|
1014 def __checkEricOneLiner(self, docstringContext, context): |
|
1015 """ |
|
1016 Private method to check, that one-liner docstrings are on |
|
1017 three lines (quotes, docstring, quotes). |
|
1018 |
|
1019 @param docstringContext docstring context (Pep257Context) |
|
1020 @param context context of the docstring (Pep257Context) |
|
1021 """ |
|
1022 if docstringContext is None: |
|
1023 return |
|
1024 |
|
1025 lines = docstringContext.source() |
|
1026 if len(lines) != 3: |
|
1027 nonEmptyLines = [l for l in lines if l.strip().strip('\'"')] |
|
1028 if len(nonEmptyLines) == 1: |
|
1029 self.__error(docstringContext.start(), 0, "D221") |
|
1030 |
|
1031 def __checkEricEndsWithPeriod(self, docstringContext, context): |
|
1032 """ |
|
1033 Private method to check, that docstring summaries end with a period. |
|
1034 |
|
1035 @param docstringContext docstring context (Pep257Context) |
|
1036 @param context context of the docstring (Pep257Context) |
|
1037 """ |
|
1038 if docstringContext is None: |
|
1039 return |
|
1040 |
|
1041 summaryLines, lineNumber = self.__getSummaryLines(docstringContext) |
|
1042 summary = " ".join([s.strip() for s in summaryLines if s]) |
|
1043 if not summary.endswith(".") and \ |
|
1044 not summary.split(None, 1)[0].lower() == "constructor": |
|
1045 self.__error( |
|
1046 docstringContext.start() + lineNumber + len(summaryLines) - 1, |
|
1047 0, "D231") |
|
1048 |
|
1049 def __checkEricReturn(self, docstringContext, context): |
|
1050 """ |
|
1051 Private method to check, that docstrings contain an @return line. |
|
1052 |
|
1053 @param docstringContext docstring context (Pep257Context) |
|
1054 @param context context of the docstring (Pep257Context) |
|
1055 """ |
|
1056 if docstringContext is None or self.__isScript: |
|
1057 return |
|
1058 |
|
1059 if "@return" not in docstringContext.ssource(): |
|
1060 tokens = list( |
|
1061 tokenize.generate_tokens(StringIO(context.ssource()).readline)) |
|
1062 return_ = [tokens[i + 1][0] for i, token in enumerate(tokens) |
|
1063 if token[1] == "return"] |
|
1064 if (set(return_) - |
|
1065 set([tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE]) != |
|
1066 set([])): |
|
1067 self.__error(docstringContext.end(), 0, "D234") |
|
1068 |
|
1069 def __checkEricFunctionArguments(self, docstringContext, context): |
|
1070 """ |
|
1071 Private method to check, that docstrings contain an @param line |
|
1072 for each argument. |
|
1073 |
|
1074 @param docstringContext docstring context (Pep257Context) |
|
1075 @param context context of the docstring (Pep257Context) |
|
1076 """ |
|
1077 if docstringContext is None or self.__isScript: |
|
1078 return |
|
1079 |
|
1080 try: |
|
1081 tree = ast.parse(context.ssource()) |
|
1082 except (SyntaxError, TypeError): |
|
1083 return |
|
1084 if (isinstance(tree, ast.Module) and len(tree.body) == 1 and |
|
1085 isinstance(tree.body[0], ast.FunctionDef)): |
|
1086 functionDef = tree.body[0] |
|
1087 argNames = self.__getArgNames(functionDef) |
|
1088 if "self" in argNames: |
|
1089 argNames.remove("self") |
|
1090 if "cls" in argNames: |
|
1091 argNames.remove("cls") |
|
1092 |
|
1093 docstring = docstringContext.ssource() |
|
1094 if (docstring.count("@param") + docstring.count("@keyparam") < |
|
1095 len(argNames)): |
|
1096 self.__error(docstringContext.end(), 0, "D235") |
|
1097 elif (docstring.count("@param") + docstring.count("@keyparam") > |
|
1098 len(argNames)): |
|
1099 self.__error(docstringContext.end(), 0, "D236") |
|
1100 # TODO: check order (args, vararg, kwonlyargs, kwarg |
|
1101 |
|
1102 def __checkEricBlankAfterSummary(self, docstringContext, context): |
|
1103 """ |
|
1104 Private method to check, that docstring summaries are followed |
|
1105 by a blank line. |
|
1106 |
|
1107 @param docstringContext docstring context (Pep257Context) |
|
1108 @param context context of the docstring (Pep257Context) |
|
1109 """ |
|
1110 if docstringContext is None: |
|
1111 return |
|
1112 |
|
1113 docstrings = docstringContext.source() |
|
1114 if len(docstrings) <= 3: |
|
1115 # correct/invalid one-liner |
|
1116 return |
|
1117 |
|
1118 summaryLines, lineNumber = self.__getSummaryLines(docstringContext) |
|
1119 if len(docstrings) > lineNumber + len(summaryLines) - 1: |
|
1120 if docstrings[lineNumber + len(summaryLines)].strip(): |
|
1121 self.__error(docstringContext.start() + lineNumber, 0, "D244") |
|
1122 |
|
1123 def __checkEricNoBlankBeforeAndAfterClass(self, docstringContext, context): |
|
1124 """ |
|
1125 Private method to check, that class docstrings have no blank line |
|
1126 around them. |
|
1127 |
|
1128 @param docstringContext docstring context (Pep257Context) |
|
1129 @param context context of the docstring (Pep257Context) |
|
1130 """ |
|
1131 if docstringContext is None: |
|
1132 return |
|
1133 |
|
1134 contextLines = context.source() |
|
1135 cti = 0 |
|
1136 while cti < len(contextLines) and \ |
|
1137 not contextLines[cti].strip().startswith( |
|
1138 ('"""', 'r"""', 'u"""', "'''", "r'''", "u'''")): |
|
1139 cti += 1 |
|
1140 if cti == len(contextLines): |
|
1141 return |
|
1142 |
|
1143 start = cti |
|
1144 if contextLines[cti].strip() in ( |
|
1145 '"""', 'r"""', 'u"""', "'''", "r'''", "u'''"): |
|
1146 # it is a multi line docstring |
|
1147 cti += 1 |
|
1148 |
|
1149 while cti < len(contextLines) and \ |
|
1150 not contextLines[cti].strip().endswith(('"""', "'''")): |
|
1151 cti += 1 |
|
1152 end = cti |
|
1153 if cti == len(contextLines): |
|
1154 return |
|
1155 |
|
1156 if not contextLines[start - 1].strip(): |
|
1157 self.__error(docstringContext.start(), 0, "D242") |
|
1158 if not contextLines[end + 1].strip(): |
|
1159 self.__error(docstringContext.end(), 0, "D243") |
|
1160 |
|
1161 def __checkEricNBlankAfterLastParagraph(self, docstringContext, context): |
|
1162 """ |
|
1163 Private method to check, that the last paragraph of docstrings is |
|
1164 not followed by a blank line. |
|
1165 |
|
1166 @param docstringContext docstring context (Pep257Context) |
|
1167 @param context context of the docstring (Pep257Context) |
|
1168 """ |
|
1169 if docstringContext is None: |
|
1170 return |
|
1171 |
|
1172 docstrings = docstringContext.source() |
|
1173 if len(docstrings) <= 3: |
|
1174 # correct/invalid one-liner |
|
1175 return |
|
1176 |
|
1177 if not docstrings[-2].strip(): |
|
1178 self.__error(docstringContext.end(), 0, "D245") |