Plugins/CheckerPlugins/Pep8/Pep257Checker.py

changeset 2929
28ab0bc63d69
parent 2928
4f74d3f595ce
child 2934
82811ddafea2
equal deleted inserted replaced
2928:4f74d3f595ce 2929:28ab0bc63d69
18 except ImportError: 18 except ImportError:
19 # Python 3 19 # Python 3
20 from io import StringIO # __IGNORE_WARNING__ 20 from io import StringIO # __IGNORE_WARNING__
21 import tokenize 21 import tokenize
22 import ast 22 import ast
23 import sys
23 24
24 from PyQt4.QtCore import QT_TRANSLATE_NOOP, QCoreApplication 25 from PyQt4.QtCore import QT_TRANSLATE_NOOP, QCoreApplication
25 26
26 27
27 class Pep257Context(object): 28 class Pep257Context(object):
110 "D131", "D132", "D133", "D134", 111 "D131", "D132", "D133", "D134",
111 "D141", "D142", "D143", "D144", "D145", 112 "D141", "D142", "D143", "D144", "D145",
112 113
113 "D203", "D205", 114 "D203", "D205",
114 "D221", 115 "D221",
115 "D231", "D234", "D235", "D236", "D237", 116 "D231", "D234", "D235", "D236", "D237", "D238",
116 "D242", "D243", "D244", "D245", 117 "D242", "D243", "D244", "D245",
117 ] 118 ]
118 119
119 Messages = { 120 Messages = {
120 "D101": QT_TRANSLATE_NOOP( 121 "D101": QT_TRANSLATE_NOOP(
182 "docstring does not contain enough @param/@keyparam lines"), 183 "docstring does not contain enough @param/@keyparam lines"),
183 "D236": QT_TRANSLATE_NOOP( 184 "D236": QT_TRANSLATE_NOOP(
184 "Pep257Checker", 185 "Pep257Checker",
185 "docstring contains too many @param/@keyparam lines"), 186 "docstring contains too many @param/@keyparam lines"),
186 "D237": QT_TRANSLATE_NOOP( 187 "D237": QT_TRANSLATE_NOOP(
188 "Pep257Checker",
189 "keyword only arguments must be documented with @keyparam lines"),
190 "D238": QT_TRANSLATE_NOOP(
187 "Pep257Checker", "order of @param/@keyparam lines does" 191 "Pep257Checker", "order of @param/@keyparam lines does"
188 " not match the function/method signature"), 192 " not match the function/method signature"),
189 "D242": QT_TRANSLATE_NOOP( 193 "D242": QT_TRANSLATE_NOOP(
190 "Pep257Checker", "class docstring is preceded by a blank line"), 194 "Pep257Checker", "class docstring is preceded by a blank line"),
191 "D243": QT_TRANSLATE_NOOP( 195 "D243": QT_TRANSLATE_NOOP(
293 (self.__checkFunctionDocstring, ("D102", "D203")), 297 (self.__checkFunctionDocstring, ("D102", "D203")),
294 (self.__checkImperativeMood, ("D132",)), 298 (self.__checkImperativeMood, ("D132",)),
295 (self.__checkNoSignature, ("D133",)), 299 (self.__checkNoSignature, ("D133",)),
296 (self.__checkEricReturn, ("D234",)), 300 (self.__checkEricReturn, ("D234",)),
297 (self.__checkEricFunctionArguments, 301 (self.__checkEricFunctionArguments,
298 ("D235", "D236", "D237")), 302 ("D235", "D236", "D237", "D238")),
299 (self.__checkNoBlankLineBefore, ("D141",)), 303 (self.__checkNoBlankLineBefore, ("D141",)),
300 ], 304 ],
301 "docstring": [ 305 "docstring": [
302 (self.__checkTripleDoubleQuotes, ("D111",)), 306 (self.__checkTripleDoubleQuotes, ("D111",)),
303 (self.__checkBackslashes, ("D112",)), 307 (self.__checkBackslashes, ("D112",)),
357 text = code + " " + QCoreApplication.translate("Pep257Checker", 361 text = code + " " + QCoreApplication.translate("Pep257Checker",
358 "no message for this code defined") 362 "no message for this code defined")
359 # record the issue with one based line number 363 # record the issue with one based line number
360 self.errors.append((self.__filename, lineNumber + 1, offset, text)) 364 self.errors.append((self.__filename, lineNumber + 1, offset, text))
361 365
362 def getMessage(self, code, *args): 366 @classmethod
363 """ 367 def getMessage(cls, code, *args):
364 Public method to get a translated and formatted message for a 368 """
369 Class method to get a translated and formatted message for a
365 given code. 370 given code.
366 371
367 @param code message code (string) 372 @param code message code (string)
368 @param args arguments for a formatted message (list) 373 @param args arguments for a formatted message (list)
369 @return translated and formatted message (string) 374 @return translated and formatted message (string)
472 else: 477 else:
473 lineno = 2 478 lineno = 2
474 summaries.append(line2) 479 summaries.append(line2)
475 return summaries, lineno 480 return summaries, lineno
476 481
477 def __getArgNames(self, node): 482 if sys.version_info[0] < 3:
478 """ 483 def __getArgNames(self, node):
479 Private method to get the argument names of a function node. 484 """
480 485 Private method to get the argument names of a function node.
481 @param node AST node to extract arguments names from 486
482 @return list of argument names (list of string) 487 @param node AST node to extract arguments names from
483 """ 488 @return tuple of two list of argument names, one for arguments
484 arguments = [] 489 and one for keyword arguments (tuple of list of string)
485 arguments.extend([arg.arg for arg in node.args.args]) 490 """
486 if node.args.vararg is not None: 491 def unpackArgs(args):
487 arguments.append(node.args.vararg) 492 """
488 arguments.extend([arg.arg for arg in node.args.kwonlyargs]) 493 Local helper function to unpack function argument names.
489 if node.args.kwarg is not None: 494
490 arguments.append(node.args.kwarg) 495 @param args list of AST node arguments
491 return arguments 496 @return list of argument names (list of string)
497 """
498 ret = []
499 for arg in args:
500 if isinstance(arg, ast.Tuple):
501 ret.extend(unpackArgs(arg.elts))
502 else:
503 ret.append(arg.id)
504 return ret
505
506 arguments = unpackArgs(node.args.args)
507 if node.args.vararg is not None:
508 arguments.append(node.args.vararg)
509 kwarguments = []
510 if node.args.kwarg is not None:
511 kwarguments.append(node.args.kwarg)
512 return arguments, kwarguments
513 else:
514 def __getArgNames(self, node): # __IGNORE_WARNING__
515 """
516 Private method to get the argument names of a function node.
517
518 @param node AST node to extract arguments names from
519 @return tuple of two list of argument names, one for arguments
520 and one for keyword arguments (tuple of list of string)
521 """
522 arguments = []
523 arguments.extend([arg.arg for arg in node.args.args])
524 if node.args.vararg is not None:
525 arguments.append(node.args.vararg)
526
527 kwarguments = []
528 kwarguments.extend([arg.arg for arg in node.args.kwonlyargs])
529 if node.args.kwarg is not None:
530 kwarguments.append(node.args.kwarg)
531 return arguments, kwarguments
492 532
493 ################################################################## 533 ##################################################################
494 ## Parsing functionality below 534 ## Parsing functionality below
495 ################################################################## 535 ##################################################################
496 536
1082 except (SyntaxError, TypeError): 1122 except (SyntaxError, TypeError):
1083 return 1123 return
1084 if (isinstance(tree, ast.Module) and len(tree.body) == 1 and 1124 if (isinstance(tree, ast.Module) and len(tree.body) == 1 and
1085 isinstance(tree.body[0], ast.FunctionDef)): 1125 isinstance(tree.body[0], ast.FunctionDef)):
1086 functionDef = tree.body[0] 1126 functionDef = tree.body[0]
1087 argNames = self.__getArgNames(functionDef) 1127 argNames, kwNames = self.__getArgNames(functionDef)
1088 if "self" in argNames: 1128 if "self" in argNames:
1089 argNames.remove("self") 1129 argNames.remove("self")
1090 if "cls" in argNames: 1130 if "cls" in argNames:
1091 argNames.remove("cls") 1131 argNames.remove("cls")
1092 1132
1093 docstring = docstringContext.ssource() 1133 docstring = docstringContext.ssource()
1094 if (docstring.count("@param") + docstring.count("@keyparam") < 1134 if (docstring.count("@param") + docstring.count("@keyparam") <
1095 len(argNames)): 1135 len(argNames + kwNames)):
1096 self.__error(docstringContext.end(), 0, "D235") 1136 self.__error(docstringContext.end(), 0, "D235")
1097 elif (docstring.count("@param") + docstring.count("@keyparam") > 1137 elif (docstring.count("@param") + docstring.count("@keyparam") >
1098 len(argNames)): 1138 len(argNames + kwNames)):
1099 self.__error(docstringContext.end(), 0, "D236") 1139 self.__error(docstringContext.end(), 0, "D236")
1100 # TODO: check order (args, vararg, kwonlyargs, kwarg 1140 else:
1141 # extract @param and @keyparam from docstring
1142 args = []
1143 kwargs = []
1144 for line in docstringContext.source():
1145 if line.strip().startswith(("@param", "@keyparam")):
1146 at, name, _ = line.strip().split(None, 2)
1147 if at == "@keyparam":
1148 kwargs.append(name)
1149 args.append(name)
1150
1151 # do the checks
1152 for name in kwNames:
1153 if name not in kwargs:
1154 self.__error(docstringContext.end(), 0, "D237")
1155 return
1156 if argNames + kwNames != args:
1157 self.__error(docstringContext.end(), 0, "D238")
1101 1158
1102 def __checkEricBlankAfterSummary(self, docstringContext, context): 1159 def __checkEricBlankAfterSummary(self, docstringContext, context):
1103 """ 1160 """
1104 Private method to check, that docstring summaries are followed 1161 Private method to check, that docstring summaries are followed
1105 by a blank line. 1162 by a blank line.

eric ide

mercurial