eric6/Plugins/CheckerPlugins/CodeStyleChecker/DocStyleChecker.py

changeset 7256
4ef3b78ebb4e
parent 7243
cfeea029d8e1
child 7360
9190402e4505
equal deleted inserted replaced
7255:d595f6f9cbf8 7256:4ef3b78ebb4e
632 kind, value, char = None, None, None 632 kind, value, char = None, None, None
633 try: 633 try:
634 while True: 634 while True:
635 start, end = None, None 635 start, end = None, None
636 while not (kind == tokenize.NAME and value == 'def'): 636 while not (kind == tokenize.NAME and value == 'def'):
637 kind, value, (line, char), _, _ = \ 637 kind, value, (line, char), _, _ = (
638 next(tokenGenerator) 638 next(tokenGenerator)
639 )
639 start = line - 1, char 640 start = line - 1, char
640 kind, value, (line, char), _, _ = \ 641 kind, value, (line, char), _, _ = (
641 self.__skipIndentedBlock(tokenGenerator) 642 self.__skipIndentedBlock(tokenGenerator)
643 )
642 end = line - 1, char 644 end = line - 1, char
643 startLine = classContext.start() + start[0] 645 startLine = classContext.start() + start[0]
644 endLine = classContext.start() + end[0] 646 endLine = classContext.start() + end[0]
645 context = DocStyleContext( 647 context = DocStyleContext(
646 self.__source[startLine:endLine], 648 self.__source[startLine:endLine],
647 startLine, "def") 649 startLine, "def")
648 if startLine > 0: 650 if startLine > 0:
649 if self.__source[startLine - 1].strip() == \ 651 if (
650 "@staticmethod": 652 self.__source[startLine - 1].strip() ==
653 "@staticmethod"
654 ):
651 context.setSpecial("staticmethod") 655 context.setSpecial("staticmethod")
652 elif self.__source[startLine - 1].strip() == \ 656 elif (
653 "@classmethod": 657 self.__source[startLine - 1].strip() ==
658 "@classmethod"
659 ):
654 context.setSpecial("classmethod") 660 context.setSpecial("classmethod")
655 contexts.append(context) 661 contexts.append(context)
656 except StopIteration: 662 except StopIteration:
657 pass 663 pass
658 self.__methodsCache = contexts 664 self.__methodsCache = contexts
701 docstring = docstringContext.ssource() 707 docstring = docstringContext.ssource()
702 if (not docstring or not docstring.strip() or 708 if (not docstring or not docstring.strip() or
703 not docstring.strip('\'"')): 709 not docstring.strip('\'"')):
704 self.__error(context.start(), 0, "D101") 710 self.__error(context.start(), 0, "D101")
705 711
706 if self.__docType == "eric" and \ 712 if (
707 docstring.strip('\'"').strip() == "Module documentation goes here.": 713 self.__docType == "eric" and
714 docstring.strip('\'"').strip() ==
715 "Module documentation goes here."
716 ):
708 self.__error(docstringContext.end(), 0, "D201") 717 self.__error(docstringContext.end(), 0, "D201")
709 return 718 return
710 719
711 def __checkFunctionDocstring(self, docstringContext, context): 720 def __checkFunctionDocstring(self, docstringContext, context):
712 """ 721 """
732 docstring = docstringContext.ssource() 741 docstring = docstringContext.ssource()
733 if (not docstring or not docstring.strip() or 742 if (not docstring or not docstring.strip() or
734 not docstring.strip('\'"')): 743 not docstring.strip('\'"')):
735 self.__error(context.start(), 0, code) 744 self.__error(context.start(), 0, code)
736 745
737 if self.__docType == "eric" and \ 746 if (
738 docstring.strip('\'"').strip() == \ 747 self.__docType == "eric" and
739 "Function documentation goes here.": 748 docstring.strip('\'"').strip() ==
749 "Function documentation goes here."
750 ):
740 self.__error(docstringContext.end(), 0, "D202") 751 self.__error(docstringContext.end(), 0, "D202")
741 return 752 return
742 753
743 def __checkClassDocstring(self, docstringContext, context): 754 def __checkClassDocstring(self, docstringContext, context):
744 """ 755 """
765 if (not docstring or not docstring.strip() or 776 if (not docstring or not docstring.strip() or
766 not docstring.strip('\'"')): 777 not docstring.strip('\'"')):
767 self.__error(context.start(), 0, code) 778 self.__error(context.start(), 0, code)
768 return 779 return
769 780
770 if self.__docType == "eric" and \ 781 if (
771 docstring.strip('\'"').strip() == "Class documentation goes here.": 782 self.__docType == "eric" and
783 docstring.strip('\'"').strip() == "Class documentation goes here."
784 ):
772 self.__error(docstringContext.end(), 0, "D206") 785 self.__error(docstringContext.end(), 0, "D206")
773 return 786 return
774 787
775 def __checkTripleDoubleQuotes(self, docstringContext, context): 788 def __checkTripleDoubleQuotes(self, docstringContext, context):
776 """ 789 """
812 """ 825 """
813 if docstringContext is None: 826 if docstringContext is None:
814 return 827 return
815 828
816 docstring = docstringContext.ssource().strip() 829 docstring = docstringContext.ssource().strip()
817 if not docstring.startswith('u"""') and \ 830 if (
818 any(ord(char) > 127 for char in docstring): 831 not docstring.startswith('u"""') and
832 any(ord(char) > 127 for char in docstring)
833 ):
819 self.__error(docstringContext.start(), 0, "D113") 834 self.__error(docstringContext.start(), 0, "D113")
820 835
821 def __checkOneLiner(self, docstringContext, context): 836 def __checkOneLiner(self, docstringContext, context):
822 """ 837 """
823 Private method to check, that one-liner docstrings fit on 838 Private method to check, that one-liner docstrings fit on
925 if docstringContext is None: 940 if docstringContext is None:
926 return 941 return
927 942
928 functionName = context.source()[0].lstrip().split()[1].split("(")[0] 943 functionName = context.source()[0].lstrip().split()[1].split("(")[0]
929 summary, lineNumber = self.__getSummaryLine(docstringContext) 944 summary, lineNumber = self.__getSummaryLine(docstringContext)
930 if functionName + "(" in summary.replace(" ", "") and \ 945 if (
931 not functionName + "()" in summary.replace(" ", ""): 946 functionName + "(" in summary.replace(" ", "") and
947 not functionName + "()" in summary.replace(" ", "")
948 ):
932 # report only, if it is not an abbreviated form (i.e. function() ) 949 # report only, if it is not an abbreviated form (i.e. function() )
933 self.__error(docstringContext.start() + lineNumber, 0, "D133") 950 self.__error(docstringContext.start() + lineNumber, 0, "D133")
934 951
935 def __checkReturnType(self, docstringContext, context): 952 def __checkReturnType(self, docstringContext, context):
936 """ 953 """
963 if docstringContext is None: 980 if docstringContext is None:
964 return 981 return
965 982
966 contextLines = context.source() 983 contextLines = context.source()
967 cti = 0 984 cti = 0
968 while cti < len(contextLines) and \ 985 while (
969 not contextLines[cti].strip().startswith( 986 cti < len(contextLines) and
970 ('"""', 'r"""', 'u"""', "'''", "r'''", "u'''")): 987 not contextLines[cti].strip().startswith(
988 ('"""', 'r"""', 'u"""', "'''", "r'''", "u'''"))
989 ):
971 cti += 1 990 cti += 1
972 if cti == len(contextLines): 991 if cti == len(contextLines):
973 return 992 return
974 993
975 if not contextLines[cti - 1].strip(): 994 if not contextLines[cti - 1].strip():
986 if docstringContext is None: 1005 if docstringContext is None:
987 return 1006 return
988 1007
989 contextLines = context.source() 1008 contextLines = context.source()
990 cti = 0 1009 cti = 0
991 while cti < len(contextLines) and \ 1010 while (
1011 cti < len(contextLines) and
992 not contextLines[cti].strip().startswith( 1012 not contextLines[cti].strip().startswith(
993 ('"""', 'r"""', 'u"""', "'''", "r'''", "u'''")): 1013 ('"""', 'r"""', 'u"""', "'''", "r'''", "u'''"))
1014 ):
994 cti += 1 1015 cti += 1
995 if cti == len(contextLines): 1016 if cti == len(contextLines):
996 return 1017 return
997 1018
998 start = cti 1019 start = cti
999 if contextLines[cti].strip() in ( 1020 if contextLines[cti].strip() in (
1000 '"""', 'r"""', 'u"""', "'''", "r'''", "u'''"): 1021 '"""', 'r"""', 'u"""', "'''", "r'''", "u'''"):
1001 # it is a multi line docstring 1022 # it is a multi line docstring
1002 cti += 1 1023 cti += 1
1003 1024
1004 while cti < len(contextLines) and \ 1025 while (
1005 not contextLines[cti].strip().endswith(('"""', "'''")): 1026 cti < len(contextLines) and
1027 not contextLines[cti].strip().endswith(('"""', "'''"))
1028 ):
1006 cti += 1 1029 cti += 1
1007 end = cti 1030 end = cti
1008 if cti >= len(contextLines) - 1: 1031 if cti >= len(contextLines) - 1:
1009 return 1032 return
1010 1033
1087 summaryLines, lineNumber = self.__getSummaryLines(docstringContext) 1110 summaryLines, lineNumber = self.__getSummaryLines(docstringContext)
1088 if summaryLines: 1111 if summaryLines:
1089 if summaryLines[-1].lstrip().startswith("@"): 1112 if summaryLines[-1].lstrip().startswith("@"):
1090 summaryLines.pop(-1) 1113 summaryLines.pop(-1)
1091 summary = " ".join([s.strip() for s in summaryLines if s]) 1114 summary = " ".join([s.strip() for s in summaryLines if s])
1092 if summary and not summary.endswith(".") and \ 1115 if (
1093 not summary.split(None, 1)[0].lower() == "constructor": 1116 summary and
1117 not summary.endswith(".") and
1118 not summary.split(None, 1)[0].lower() == "constructor"
1119 ):
1094 self.__error( 1120 self.__error(
1095 docstringContext.start() + lineNumber + 1121 docstringContext.start() + lineNumber +
1096 len(summaryLines) - 1, 1122 len(summaryLines) - 1,
1097 0, "D231") 1123 0, "D231")
1098 1124
1197 tokensLen = len(tokens) 1223 tokensLen = len(tokens)
1198 for i, token in enumerate(tokens): 1224 for i, token in enumerate(tokens):
1199 if token[1] == "raise": 1225 if token[1] == "raise":
1200 exceptions.add(tokens[i + 1][0]) 1226 exceptions.add(tokens[i + 1][0])
1201 if tokens[i + 1][0] == tokenize.NAME: 1227 if tokens[i + 1][0] == tokenize.NAME:
1202 if tokensLen > (i + 2) and \ 1228 if (
1203 tokens[i + 2][1] == ".": 1229 tokensLen > (i + 2) and
1230 tokens[i + 2][1] == "."
1231 ):
1204 raisedExceptions.add("{0}.{1}".format( 1232 raisedExceptions.add("{0}.{1}".format(
1205 tokens[i + 1][1], tokens[i + 3][1])) 1233 tokens[i + 1][1], tokens[i + 3][1]))
1206 else: 1234 else:
1207 raisedExceptions.add(tokens[i + 1][1]) 1235 raisedExceptions.add(tokens[i + 1][1])
1208 1236
1209 if "@exception" not in docstringContext.ssource() and \ 1237 if (
1210 "@throws" not in docstringContext.ssource() and \ 1238 "@exception" not in docstringContext.ssource() and
1211 "@raise" not in docstringContext.ssource(): 1239 "@throws" not in docstringContext.ssource() and
1240 "@raise" not in docstringContext.ssource()
1241 ):
1212 if (exceptions - 1242 if (exceptions -
1213 {tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE} != 1243 {tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE} !=
1214 set()): 1244 set()):
1215 self.__error(docstringContext.end(), 0, "D250") 1245 self.__error(docstringContext.end(), 0, "D250")
1216 else: 1246 else:
1327 return 1357 return
1328 1358
1329 contextLines = context.source() 1359 contextLines = context.source()
1330 isClassContext = contextLines[0].lstrip().startswith("class ") 1360 isClassContext = contextLines[0].lstrip().startswith("class ")
1331 cti = 0 1361 cti = 0
1332 while cti < len(contextLines) and \ 1362 while (
1363 cti < len(contextLines) and
1333 not contextLines[cti].strip().startswith( 1364 not contextLines[cti].strip().startswith(
1334 ('"""', 'r"""', 'u"""', "'''", "r'''", "u'''")): 1365 ('"""', 'r"""', 'u"""', "'''", "r'''", "u'''"))
1366 ):
1335 cti += 1 1367 cti += 1
1336 if cti == len(contextLines): 1368 if cti == len(contextLines):
1337 return 1369 return
1338 1370
1339 start = cti 1371 start = cti
1340 if contextLines[cti].strip() in ( 1372 if contextLines[cti].strip() in (
1341 '"""', 'r"""', 'u"""', "'''", "r'''", "u'''"): 1373 '"""', 'r"""', 'u"""', "'''", "r'''", "u'''"):
1342 # it is a multi line docstring 1374 # it is a multi line docstring
1343 cti += 1 1375 cti += 1
1344 1376
1345 while cti < len(contextLines) and \ 1377 while (
1346 not contextLines[cti].strip().endswith(('"""', "'''")): 1378 cti < len(contextLines) and
1379 not contextLines[cti].strip().endswith(('"""', "'''"))
1380 ):
1347 cti += 1 1381 cti += 1
1348 end = cti 1382 end = cti
1349 if cti >= len(contextLines) - 1: 1383 if cti >= len(contextLines) - 1:
1350 return 1384 return
1351 1385
1392 1426
1393 summary, lineNumber = self.__getSummaryLine(docstringContext) 1427 summary, lineNumber = self.__getSummaryLine(docstringContext)
1394 if summary: 1428 if summary:
1395 # check, if the first word is 'Constructor', 'Public', 1429 # check, if the first word is 'Constructor', 'Public',
1396 # 'Protected' or 'Private' 1430 # 'Protected' or 'Private'
1397 functionName, arguments = context.source()[0].lstrip()\ 1431 functionName, arguments = (
1398 .split()[1].split("(", 1) 1432 context.source()[0].lstrip().split()[1].split("(", 1)
1433 )
1399 firstWord = summary.strip().split(None, 1)[0].lower() 1434 firstWord = summary.strip().split(None, 1)[0].lower()
1400 if functionName == '__init__': 1435 if functionName == '__init__':
1401 if firstWord != 'constructor': 1436 if firstWord != 'constructor':
1402 self.__error(docstringContext.start() + lineNumber, 0, 1437 self.__error(docstringContext.start() + lineNumber, 0,
1403 "D232", 'constructor') 1438 "D232", 'constructor')
1404 elif functionName.startswith('__') and \ 1439 elif (
1405 functionName.endswith('__'): 1440 functionName.startswith('__') and
1441 functionName.endswith('__')
1442 ):
1406 if firstWord != 'special': 1443 if firstWord != 'special':
1407 self.__error(docstringContext.start() + lineNumber, 0, 1444 self.__error(docstringContext.start() + lineNumber, 0,
1408 "D232", 'special') 1445 "D232", 'special')
1409 elif context.special() == "staticmethod": 1446 elif context.special() == "staticmethod":
1410 secondWord = summary.strip().split(None, 2)[1].lower() 1447 secondWord = summary.strip().split(None, 2)[1].lower()
1414 elif secondWord == 'static': 1451 elif secondWord == 'static':
1415 if functionName.startswith(('__', 'on_')): 1452 if functionName.startswith(('__', 'on_')):
1416 if firstWord != 'private': 1453 if firstWord != 'private':
1417 self.__error(docstringContext.start() + lineNumber, 1454 self.__error(docstringContext.start() + lineNumber,
1418 0, "D232", 'private static') 1455 0, "D232", 'private static')
1419 elif functionName.startswith('_') or \ 1456 elif (
1420 functionName.endswith('Event'): 1457 functionName.startswith('_') or
1458 functionName.endswith('Event')
1459 ):
1421 if firstWord != 'protected': 1460 if firstWord != 'protected':
1422 self.__error(docstringContext.start() + lineNumber, 1461 self.__error(docstringContext.start() + lineNumber,
1423 0, "D232", 'protected static') 1462 0, "D232", 'protected static')
1424 else: 1463 else:
1425 if firstWord != 'public': 1464 if firstWord != 'public':
1426 self.__error(docstringContext.start() + lineNumber, 1465 self.__error(docstringContext.start() + lineNumber,
1427 0, "D232", 'public static') 1466 0, "D232", 'public static')
1428 elif arguments.startswith(('cls,', 'cls)')) or \ 1467 elif (
1429 context.special() == "classmethod": 1468 arguments.startswith(('cls,', 'cls)')) or
1469 context.special() == "classmethod"
1470 ):
1430 secondWord = summary.strip().split(None, 2)[1].lower() 1471 secondWord = summary.strip().split(None, 2)[1].lower()
1431 if firstWord != 'class' and secondWord != 'class': 1472 if firstWord != 'class' and secondWord != 'class':
1432 self.__error(docstringContext.start() + lineNumber, 0, 1473 self.__error(docstringContext.start() + lineNumber, 0,
1433 "D232", 'class') 1474 "D232", 'class')
1434 elif secondWord == 'class': 1475 elif secondWord == 'class':
1435 if functionName.startswith(('__', 'on_')): 1476 if functionName.startswith(('__', 'on_')):
1436 if firstWord != 'private': 1477 if firstWord != 'private':
1437 self.__error(docstringContext.start() + lineNumber, 1478 self.__error(docstringContext.start() + lineNumber,
1438 0, "D232", 'private class') 1479 0, "D232", 'private class')
1439 elif functionName.startswith('_') or \ 1480 elif (
1440 functionName.endswith('Event'): 1481 functionName.startswith('_') or
1482 functionName.endswith('Event')
1483 ):
1441 if firstWord != 'protected': 1484 if firstWord != 'protected':
1442 self.__error(docstringContext.start() + lineNumber, 1485 self.__error(docstringContext.start() + lineNumber,
1443 0, "D232", 'protected class') 1486 0, "D232", 'protected class')
1444 else: 1487 else:
1445 if firstWord != 'public': 1488 if firstWord != 'public':
1447 0, "D232", 'public class') 1490 0, "D232", 'public class')
1448 elif functionName.startswith(('__', 'on_')): 1491 elif functionName.startswith(('__', 'on_')):
1449 if firstWord != 'private': 1492 if firstWord != 'private':
1450 self.__error(docstringContext.start() + lineNumber, 0, 1493 self.__error(docstringContext.start() + lineNumber, 0,
1451 "D232", 'private') 1494 "D232", 'private')
1452 elif functionName.startswith('_') or \ 1495 elif (
1453 functionName.endswith('Event'): 1496 functionName.startswith('_') or
1497 functionName.endswith('Event')
1498 ):
1454 if firstWord != 'protected': 1499 if firstWord != 'protected':
1455 self.__error(docstringContext.start() + lineNumber, 0, 1500 self.__error(docstringContext.start() + lineNumber, 0,
1456 "D232", 'protected') 1501 "D232", 'protected')
1457 else: 1502 else:
1458 if firstWord != 'public': 1503 if firstWord != 'public':

eric ide

mercurial