src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/DocStyle/DocStyleChecker.py

branch
eric7
changeset 11150
73d80859079c
parent 11148
15e30f0c76a8
equal deleted inserted replaced
11149:fc45672fae42 11150:73d80859079c
11 # The routines of the checker class are modeled after the ones found in 11 # The routines of the checker class are modeled after the ones found in
12 # pep257.py (version 0.2.4). 12 # pep257.py (version 0.2.4).
13 # 13 #
14 14
15 import ast 15 import ast
16 import collections
16 import contextlib 17 import contextlib
17 import tokenize 18 import tokenize
18 19
19 from io import StringIO 20 from io import StringIO
20 21
21 try: 22 try:
22 ast.AsyncFunctionDef # __IGNORE_EXCEPTION__ 23 ast.AsyncFunctionDef # __IGNORE_EXCEPTION__
23 except AttributeError: 24 except AttributeError:
24 ast.AsyncFunctionDef = ast.FunctionDef 25 ast.AsyncFunctionDef = ast.FunctionDef
26
27 from CodeStyleTopicChecker import CodeStyleTopicChecker
25 28
26 29
27 class DocStyleContext: 30 class DocStyleContext:
28 """ 31 """
29 Class implementing the source context. 32 Class implementing the source context.
125 @rtype str 128 @rtype str
126 """ 129 """
127 return self.__special 130 return self.__special
128 131
129 132
130 class DocStyleChecker: 133 class DocStyleChecker(CodeStyleTopicChecker):
131 """ 134 """
132 Class implementing a checker for documentation string conventions. 135 Class implementing a checker for documentation string conventions.
133 """ 136 """
134 137
135 Codes = [ 138 Codes = [
187 "D-270", 190 "D-270",
188 "D-271", 191 "D-271",
189 "D-272", 192 "D-272",
190 "D-273", 193 "D-273",
191 ] 194 ]
195 Category = "D"
192 196
193 def __init__( 197 def __init__(
194 self, 198 self,
195 source, 199 source,
196 filename, 200 filename,
219 @param maxLineLength allowed line length 223 @param maxLineLength allowed line length
220 @type int 224 @type int
221 @param docType type of the documentation strings (one of 'eric' or 'pep257') 225 @param docType type of the documentation strings (one of 'eric' or 'pep257')
222 @type str 226 @type str
223 """ 227 """
224 self.__select = tuple(select) 228 super().__init__(
225 self.__ignore = tuple(ignore) 229 DocStyleChecker.Category,
226 self.__expected = expected[:] 230 source,
227 self.__repeat = repeat 231 filename,
232 None,
233 select,
234 ignore,
235 expected,
236 repeat,
237 [],
238 )
239
228 self.__maxLineLength = maxLineLength 240 self.__maxLineLength = maxLineLength
229 self.__docType = docType 241 self.__docType = docType
230 self.__filename = filename
231 self.__source = source[:]
232
233 # statistics counters
234 self.counters = {}
235
236 # collection of detected errors
237 self.errors = []
238
239 self.__lineNumber = 0 242 self.__lineNumber = 0
240 243
241 # caches 244 # caches
242 self.__functionsCache = None 245 self.__functionsCache = None
243 self.__classesCache = None 246 self.__classesCache = None
329 (self.__checkEricNBlankAfterLastParagraph, ("D-247",)), 332 (self.__checkEricNBlankAfterLastParagraph, ("D-247",)),
330 (self.__checkEricQuotesOnSeparateLines, ("D-222", "D-223")), 333 (self.__checkEricQuotesOnSeparateLines, ("D-222", "D-223")),
331 ], 334 ],
332 } 335 }
333 336
334 self.__checkers = {} 337 self.__checkers = collections.defaultdict(list)
335 for key, checkers in checkersWithCodes.items(): 338 for key, checkers in checkersWithCodes.items():
336 for checker, codes in checkers: 339 for checker, codes in checkers:
337 if any(not (code and self.__ignoreCode(code)) for code in codes): 340 if any(
338 if key not in self.__checkers: 341 not (msgCode and self._ignoreCode(msgCode)) for msgCode in codes
339 self.__checkers[key] = [] 342 ):
340 self.__checkers[key].append(checker) 343 self.__checkers[key].append(checker)
341 344
342 def __ignoreCode(self, code): 345 def addError(self, lineNumber, offset, msgCode, *args):
343 """ 346 """
344 Private method to check if the error code should be ignored. 347 Public method to record an issue.
345 348
346 @param code message code to check for 349 @param lineNumber line number of the issue (zero based)
347 @type str
348 @return flag indicating to ignore the given code
349 @rtype bool
350 """
351 return code in self.__ignore or (
352 code.startswith(self.__ignore) and not code.startswith(self.__select)
353 )
354
355 def __error(self, lineNumber, offset, code, *args):
356 """
357 Private method to record an issue.
358
359 @param lineNumber line number of the issue
360 @type int 350 @type int
361 @param offset position within line of the issue 351 @param offset position within line of the issue
362 @type int 352 @type int
363 @param code message code 353 @param msgCode message code
364 @type str 354 @type str
365 @param args arguments for the message 355 @param args arguments for the message
366 @type list 356 @type list
367 """ 357 """
368 if self.__ignoreCode(code): 358 # call super class method with one based line number
369 return 359 super().addError(lineNumber + 1, offset, msgCode, *args)
370
371 if code in self.counters:
372 self.counters[code] += 1
373 else:
374 self.counters[code] = 1
375
376 # Don't care about expected codes
377 if code in self.__expected:
378 return
379
380 if code and (self.counters[code] == 1 or self.__repeat):
381 # record the issue with one based line number
382 self.errors.append(
383 {
384 "file": self.__filename,
385 "line": lineNumber + 1,
386 "offset": offset,
387 "code": code,
388 "args": args,
389 }
390 )
391 360
392 def __resetReadline(self): 361 def __resetReadline(self):
393 """ 362 """
394 Private method to reset the internal readline function. 363 Private method to reset the internal readline function.
395 """ 364 """
401 370
402 @return next line of source 371 @return next line of source
403 @rtype str 372 @rtype str
404 """ 373 """
405 self.__lineNumber += 1 374 self.__lineNumber += 1
406 if self.__lineNumber > len(self.__source): 375 if self.__lineNumber > len(self.source):
407 return "" 376 return ""
408 return self.__source[self.__lineNumber - 1] 377 return self.source[self.__lineNumber - 1]
409 378
410 def run(self): 379 def run(self):
411 """ 380 """
412 Public method to check the given source for violations of doc string 381 Public method to check the given source for violations of doc string
413 conventions. 382 conventions.
414 """ 383 """
415 if not self.__filename: 384 if not self.filename:
416 # don't do anything, if essential data is missing 385 # don't do anything, if essential data is missing
417 return 386 return
418 387
419 if not self.__checkers: 388 if not self.__checkers:
420 # don't do anything, if no codes were selected 389 # don't do anything, if no codes were selected
421 return 390 return
422 391
423 for keyword in self.__keywords: 392 for key in self.__keywords:
424 if keyword in self.__checkers: 393 if key in self.__checkers:
425 for check in self.__checkers[keyword]: 394 for check in self.__checkers[key]:
426 for context in self.__parseContexts(keyword): 395 for context in self.__parseContexts(key):
427 docstring = self.__parseDocstring(context, keyword) 396 docstring = self.__parseDocstring(context, key)
428 check(docstring, context) 397 check(docstring, context)
429 398
430 def __getSummaryLine(self, docstringContext): 399 def __getSummaryLine(self, docstringContext):
431 """ 400 """
432 Private method to extract the summary line. 401 Private method to extract the summary line.
597 start = line - 1, char 566 start = line - 1, char
598 while not (kind == tokenize.DEDENT and value == "" and char == 0): 567 while not (kind == tokenize.DEDENT and value == "" and char == 0):
599 kind, value, (line, char), _, _ = next(tokenGenerator) 568 kind, value, (line, char), _, _ = next(tokenGenerator)
600 end = line - 1, char 569 end = line - 1, char
601 contexts.append( 570 contexts.append(
602 DocStyleContext(self.__source[start[0] : end[0]], start[0], keyword) 571 DocStyleContext(self.source[start[0] : end[0]], start[0], keyword)
603 ) 572 )
604 except StopIteration: 573 except StopIteration:
605 return contexts 574 return contexts
606 575
607 def __parseFunctions(self): 576 def __parseFunctions(self):
674 ) 643 )
675 end = line - 1, char 644 end = line - 1, char
676 startLine = classContext.start() + start[0] 645 startLine = classContext.start() + start[0]
677 endLine = classContext.start() + end[0] 646 endLine = classContext.start() + end[0]
678 context = DocStyleContext( 647 context = DocStyleContext(
679 self.__source[startLine:endLine], startLine, "def" 648 self.source[startLine:endLine], startLine, "def"
680 ) 649 )
681 if startLine > 0: 650 if startLine > 0:
682 if self.__source[startLine - 1].strip() == "@staticmethod": 651 if self.source[startLine - 1].strip() == "@staticmethod":
683 context.setSpecial("staticmethod") 652 context.setSpecial("staticmethod")
684 elif self.__source[startLine - 1].strip() == "@classmethod": 653 elif self.source[startLine - 1].strip() == "@classmethod":
685 context.setSpecial("classmethod") 654 context.setSpecial("classmethod")
686 contexts.append(context) 655 contexts.append(context)
687 self.__methodsCache = contexts 656 self.__methodsCache = contexts
688 657
689 return self.__methodsCache 658 return self.__methodsCache
696 @type str 665 @type str
697 @return requested contexts 666 @return requested contexts
698 @rtype list of DocStyleContext 667 @rtype list of DocStyleContext
699 """ 668 """
700 if kind == "moduleDocstring": 669 if kind == "moduleDocstring":
701 return [DocStyleContext(self.__source, 0, "module")] 670 return [DocStyleContext(self.source, 0, "module")]
702 if kind == "functionDocstring": 671 if kind == "functionDocstring":
703 return self.__parseFunctions() 672 return self.__parseFunctions()
704 if kind == "classDocstring": 673 if kind == "classDocstring":
705 return self.__parseClasses() 674 return self.__parseClasses()
706 if kind == "methodDocstring": 675 if kind == "methodDocstring":
707 return self.__parseMethods() 676 return self.__parseMethods()
708 if kind == "defDocstring": 677 if kind == "defDocstring":
709 return self.__parseFunctions() + self.__parseMethods() 678 return self.__parseFunctions() + self.__parseMethods()
710 if kind == "docstring": 679 if kind == "docstring":
711 return ( 680 return (
712 [DocStyleContext(self.__source, 0, "module")] 681 [DocStyleContext(self.source, 0, "module")]
713 + self.__parseFunctions() 682 + self.__parseFunctions()
714 + self.__parseClasses() 683 + self.__parseClasses()
715 + self.__parseMethods() 684 + self.__parseMethods()
716 ) 685 )
717 return [] # fall back 686 return [] # fall back
728 @type DocStyleContext 697 @type DocStyleContext
729 @param context context of the docstring 698 @param context context of the docstring
730 @type DocStyleContext 699 @type DocStyleContext
731 """ 700 """
732 if docstringContext is None: 701 if docstringContext is None:
733 self.__error(context.start(), 0, "D-101") 702 self.addError(context.start(), 0, "D-101")
734 return 703 return
735 704
736 docstring = docstringContext.ssource() 705 docstring = docstringContext.ssource()
737 if not docstring or not docstring.strip() or not docstring.strip("'\""): 706 if not docstring or not docstring.strip() or not docstring.strip("'\""):
738 self.__error(context.start(), 0, "D-101") 707 self.addError(context.start(), 0, "D-101")
739 708
740 if ( 709 if (
741 self.__docType == "eric" 710 self.__docType == "eric"
742 and docstring.strip("'\"").strip() == "Module documentation goes here." 711 and docstring.strip("'\"").strip() == "Module documentation goes here."
743 ): 712 ):
744 self.__error(docstringContext.end(), 0, "D-201") 713 self.addError(docstringContext.end(), 0, "D-201")
745 return 714 return
746 715
747 def __checkFunctionDocstring(self, docstringContext, context): 716 def __checkFunctionDocstring(self, docstringContext, context):
748 """ 717 """
749 Private method to check, that all public functions and methods 718 Private method to check, that all public functions and methods
762 code = "D-103" 731 code = "D-103"
763 else: 732 else:
764 code = "D-102" 733 code = "D-102"
765 734
766 if docstringContext is None: 735 if docstringContext is None:
767 self.__error(context.start(), 0, code) 736 self.addError(context.start(), 0, code)
768 return 737 return
769 738
770 docstring = docstringContext.ssource() 739 docstring = docstringContext.ssource()
771 if not docstring or not docstring.strip() or not docstring.strip("'\""): 740 if not docstring or not docstring.strip() or not docstring.strip("'\""):
772 self.__error(context.start(), 0, code) 741 self.addError(context.start(), 0, code)
773 742
774 if self.__docType == "eric": 743 if self.__docType == "eric":
775 if docstring.strip("'\"").strip() == "Function documentation goes here.": 744 if docstring.strip("'\"").strip() == "Function documentation goes here.":
776 self.__error(docstringContext.end(), 0, "D-202.1") 745 self.addError(docstringContext.end(), 0, "D-202.1")
777 return 746 return
778 747
779 if "DESCRIPTION" in docstring or "TYPE" in docstring: 748 if "DESCRIPTION" in docstring or "TYPE" in docstring:
780 self.__error(docstringContext.end(), 0, "D-202.2") 749 self.addError(docstringContext.end(), 0, "D-202.2")
781 return 750 return
782 751
783 def __checkClassDocstring(self, docstringContext, context): 752 def __checkClassDocstring(self, docstringContext, context):
784 """ 753 """
785 Private method to check, that all public functions and methods 754 Private method to check, that all public functions and methods
798 code = "D-105" 767 code = "D-105"
799 else: 768 else:
800 code = "D-104" 769 code = "D-104"
801 770
802 if docstringContext is None: 771 if docstringContext is None:
803 self.__error(context.start(), 0, code) 772 self.addError(context.start(), 0, code)
804 return 773 return
805 774
806 docstring = docstringContext.ssource() 775 docstring = docstringContext.ssource()
807 if not docstring or not docstring.strip() or not docstring.strip("'\""): 776 if not docstring or not docstring.strip() or not docstring.strip("'\""):
808 self.__error(context.start(), 0, code) 777 self.addError(context.start(), 0, code)
809 return 778 return
810 779
811 if ( 780 if (
812 self.__docType == "eric" 781 self.__docType == "eric"
813 and docstring.strip("'\"").strip() == "Class documentation goes here." 782 and docstring.strip("'\"").strip() == "Class documentation goes here."
814 ): 783 ):
815 self.__error(docstringContext.end(), 0, "D-206") 784 self.addError(docstringContext.end(), 0, "D-206")
816 return 785 return
817 786
818 def __checkTripleDoubleQuotes(self, docstringContext, _context): 787 def __checkTripleDoubleQuotes(self, docstringContext, _context):
819 """ 788 """
820 Private method to check, that all docstrings are surrounded 789 Private method to check, that all docstrings are surrounded
828 if docstringContext is None: 797 if docstringContext is None:
829 return 798 return
830 799
831 docstring = docstringContext.ssource().strip() 800 docstring = docstringContext.ssource().strip()
832 if not docstring.startswith(('"""', 'r"""', 'u"""')): 801 if not docstring.startswith(('"""', 'r"""', 'u"""')):
833 self.__error(docstringContext.start(), 0, "D-111") 802 self.addError(docstringContext.start(), 0, "D-111")
834 803
835 def __checkBackslashes(self, docstringContext, _context): 804 def __checkBackslashes(self, docstringContext, _context):
836 """ 805 """
837 Private method to check, that all docstrings containing 806 Private method to check, that all docstrings containing
838 backslashes are surrounded by raw triple double quotes. 807 backslashes are surrounded by raw triple double quotes.
845 if docstringContext is None: 814 if docstringContext is None:
846 return 815 return
847 816
848 docstring = docstringContext.ssource().strip() 817 docstring = docstringContext.ssource().strip()
849 if "\\" in docstring and not docstring.startswith('r"""'): 818 if "\\" in docstring and not docstring.startswith('r"""'):
850 self.__error(docstringContext.start(), 0, "D-112") 819 self.addError(docstringContext.start(), 0, "D-112")
851 820
852 def __checkOneLiner(self, docstringContext, context): 821 def __checkOneLiner(self, docstringContext, context):
853 """ 822 """
854 Private method to check, that one-liner docstrings fit on 823 Private method to check, that one-liner docstrings fit on
855 one line with quotes. 824 one line with quotes.
873 modLen += 4 842 modLen += 4
874 if not nonEmptyLines[0].strip().endswith("."): 843 if not nonEmptyLines[0].strip().endswith("."):
875 # account for a trailing dot 844 # account for a trailing dot
876 modLen += 1 845 modLen += 1
877 if modLen <= self.__maxLineLength: 846 if modLen <= self.__maxLineLength:
878 self.__error(docstringContext.start(), 0, "D-121") 847 self.addError(docstringContext.start(), 0, "D-121")
879 848
880 def __checkIndent(self, docstringContext, context): 849 def __checkIndent(self, docstringContext, context):
881 """ 850 """
882 Private method to check, that docstrings are properly indented. 851 Private method to check, that docstrings are properly indented.
883 852
900 indent = min(len(line) - len(line.strip()) for line in nonEmptyLines) 869 indent = min(len(line) - len(line.strip()) for line in nonEmptyLines)
901 expectedIndent = ( 870 expectedIndent = (
902 0 if context.contextType() == "module" else len(context.indent()) + 4 871 0 if context.contextType() == "module" else len(context.indent()) + 4
903 ) 872 )
904 if indent != expectedIndent: 873 if indent != expectedIndent:
905 self.__error(docstringContext.start(), 0, "D-122") 874 self.addError(docstringContext.start(), 0, "D-122")
906 875
907 def __checkSummary(self, docstringContext, _context): 876 def __checkSummary(self, docstringContext, _context):
908 """ 877 """
909 Private method to check, that docstring summaries contain some text. 878 Private method to check, that docstring summaries contain some text.
910 879
916 if docstringContext is None: 885 if docstringContext is None:
917 return 886 return
918 887
919 summary, lineNumber = self.__getSummaryLine(docstringContext) 888 summary, lineNumber = self.__getSummaryLine(docstringContext)
920 if summary == "": 889 if summary == "":
921 self.__error(docstringContext.start() + lineNumber, 0, "D-130") 890 self.addError(docstringContext.start() + lineNumber, 0, "D-130")
922 891
923 def __checkEndsWithPeriod(self, docstringContext, _context): 892 def __checkEndsWithPeriod(self, docstringContext, _context):
924 """ 893 """
925 Private method to check, that docstring summaries end with a period. 894 Private method to check, that docstring summaries end with a period.
926 895
932 if docstringContext is None: 901 if docstringContext is None:
933 return 902 return
934 903
935 summary, lineNumber = self.__getSummaryLine(docstringContext) 904 summary, lineNumber = self.__getSummaryLine(docstringContext)
936 if not summary.endswith("."): 905 if not summary.endswith("."):
937 self.__error(docstringContext.start() + lineNumber, 0, "D-131") 906 self.addError(docstringContext.start() + lineNumber, 0, "D-131")
938 907
939 def __checkImperativeMood(self, docstringContext, _context): 908 def __checkImperativeMood(self, docstringContext, _context):
940 """ 909 """
941 Private method to check, that docstring summaries are in 910 Private method to check, that docstring summaries are in
942 imperative mood. 911 imperative mood.
951 920
952 summary, lineNumber = self.__getSummaryLine(docstringContext) 921 summary, lineNumber = self.__getSummaryLine(docstringContext)
953 if summary: 922 if summary:
954 firstWord = summary.strip().split()[0] 923 firstWord = summary.strip().split()[0]
955 if firstWord.endswith("s") and not firstWord.endswith("ss"): 924 if firstWord.endswith("s") and not firstWord.endswith("ss"):
956 self.__error(docstringContext.start() + lineNumber, 0, "D-132") 925 self.addError(docstringContext.start() + lineNumber, 0, "D-132")
957 926
958 def __checkNoSignature(self, docstringContext, context): 927 def __checkNoSignature(self, docstringContext, context):
959 """ 928 """
960 Private method to check, that docstring summaries don't repeat 929 Private method to check, that docstring summaries don't repeat
961 the function's signature. 930 the function's signature.
972 summary, lineNumber = self.__getSummaryLine(docstringContext) 941 summary, lineNumber = self.__getSummaryLine(docstringContext)
973 if functionName + "(" in summary.replace( 942 if functionName + "(" in summary.replace(
974 " ", "" 943 " ", ""
975 ) and functionName + "()" not in summary.replace(" ", ""): 944 ) and functionName + "()" not in summary.replace(" ", ""):
976 # report only, if it is not an abbreviated form (i.e. function() ) 945 # report only, if it is not an abbreviated form (i.e. function() )
977 self.__error(docstringContext.start() + lineNumber, 0, "D-133") 946 self.addError(docstringContext.start() + lineNumber, 0, "D-133")
978 947
979 def __checkReturnType(self, docstringContext, context): 948 def __checkReturnType(self, docstringContext, context):
980 """ 949 """
981 Private method to check, that docstrings mention the return value type. 950 Private method to check, that docstrings mention the return value type.
982 951
999 ] 968 ]
1000 if ( 969 if (
1001 set(return_) - {tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE} 970 set(return_) - {tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE}
1002 != set() 971 != set()
1003 ): 972 ):
1004 self.__error(docstringContext.end(), 0, "D-134") 973 self.addError(docstringContext.end(), 0, "D-134")
1005 974
1006 def __checkNoBlankLineBefore(self, docstringContext, context): 975 def __checkNoBlankLineBefore(self, docstringContext, context):
1007 """ 976 """
1008 Private method to check, that function/method docstrings are not 977 Private method to check, that function/method docstrings are not
1009 preceded by a blank line. 978 preceded by a blank line.
1024 cti += 1 993 cti += 1
1025 if cti == len(contextLines): 994 if cti == len(contextLines):
1026 return 995 return
1027 996
1028 if not contextLines[cti - 1].strip(): 997 if not contextLines[cti - 1].strip():
1029 self.__error(docstringContext.start(), 0, "D-141") 998 self.addError(docstringContext.start(), 0, "D-141")
1030 999
1031 def __checkBlankBeforeAndAfterClass(self, docstringContext, context): 1000 def __checkBlankBeforeAndAfterClass(self, docstringContext, context):
1032 """ 1001 """
1033 Private method to check, that class docstrings have one 1002 Private method to check, that class docstrings have one
1034 blank line around them. 1003 blank line around them.
1062 end = cti 1031 end = cti
1063 if cti >= len(contextLines) - 1: 1032 if cti >= len(contextLines) - 1:
1064 return 1033 return
1065 1034
1066 if contextLines[start - 1].strip(): 1035 if contextLines[start - 1].strip():
1067 self.__error(docstringContext.start(), 0, "D-142") 1036 self.addError(docstringContext.start(), 0, "D-142")
1068 if contextLines[end + 1].strip(): 1037 if contextLines[end + 1].strip():
1069 self.__error(docstringContext.end(), 0, "D-143") 1038 self.addError(docstringContext.end(), 0, "D-143")
1070 1039
1071 def __checkBlankAfterSummary(self, docstringContext, _context): 1040 def __checkBlankAfterSummary(self, docstringContext, _context):
1072 """ 1041 """
1073 Private method to check, that docstring summaries are followed 1042 Private method to check, that docstring summaries are followed
1074 by a blank line. 1043 by a blank line.
1086 # correct/invalid one-liner 1055 # correct/invalid one-liner
1087 return 1056 return
1088 1057
1089 summary, lineNumber = self.__getSummaryLine(docstringContext) 1058 summary, lineNumber = self.__getSummaryLine(docstringContext)
1090 if len(docstrings) > 2 and docstrings[lineNumber + 1].strip(): 1059 if len(docstrings) > 2 and docstrings[lineNumber + 1].strip():
1091 self.__error(docstringContext.start() + lineNumber, 0, "D-144") 1060 self.addError(docstringContext.start() + lineNumber, 0, "D-144")
1092 1061
1093 def __checkBlankAfterLastParagraph(self, docstringContext, _context): 1062 def __checkBlankAfterLastParagraph(self, docstringContext, _context):
1094 """ 1063 """
1095 Private method to check, that the last paragraph of docstrings is 1064 Private method to check, that the last paragraph of docstrings is
1096 followed by a blank line. 1065 followed by a blank line.
1107 if len(docstrings) <= 3: 1076 if len(docstrings) <= 3:
1108 # correct/invalid one-liner 1077 # correct/invalid one-liner
1109 return 1078 return
1110 1079
1111 if docstrings[-2].strip(): 1080 if docstrings[-2].strip():
1112 self.__error(docstringContext.end(), 0, "D-145") 1081 self.addError(docstringContext.end(), 0, "D-145")
1113 1082
1114 ################################################################## 1083 ##################################################################
1115 ## Checking functionality below (eric specific ones) 1084 ## Checking functionality below (eric specific ones)
1116 ################################################################## 1085 ##################################################################
1117 1086
1128 if docstringContext is None: 1097 if docstringContext is None:
1129 return 1098 return
1130 1099
1131 lines = docstringContext.source() 1100 lines = docstringContext.source()
1132 if lines[0].strip().strip("ru\"'"): 1101 if lines[0].strip().strip("ru\"'"):
1133 self.__error(docstringContext.start(), 0, "D-221") 1102 self.addError(docstringContext.start(), 0, "D-221")
1134 if lines[-1].strip().strip("\"'"): 1103 if lines[-1].strip().strip("\"'"):
1135 self.__error(docstringContext.end(), 0, "D-222") 1104 self.addError(docstringContext.end(), 0, "D-222")
1136 1105
1137 def __checkEricEndsWithPeriod(self, docstringContext, _context): 1106 def __checkEricEndsWithPeriod(self, docstringContext, _context):
1138 """ 1107 """
1139 Private method to check, that docstring summaries end with a period. 1108 Private method to check, that docstring summaries end with a period.
1140 1109
1154 if ( 1123 if (
1155 summary 1124 summary
1156 and not summary.endswith(".") 1125 and not summary.endswith(".")
1157 and summary.split(None, 1)[0].lower() != "constructor" 1126 and summary.split(None, 1)[0].lower() != "constructor"
1158 ): 1127 ):
1159 self.__error( 1128 self.addError(
1160 docstringContext.start() + lineNumber + len(summaryLines) - 1, 1129 docstringContext.start() + lineNumber + len(summaryLines) - 1,
1161 0, 1130 0,
1162 "D-231", 1131 "D-231",
1163 ) 1132 )
1164 1133
1182 if "@return" not in docstringContext.ssource(): 1151 if "@return" not in docstringContext.ssource():
1183 if ( 1152 if (
1184 set(return_) - {tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE} 1153 set(return_) - {tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE}
1185 != set() 1154 != set()
1186 ): 1155 ):
1187 self.__error(docstringContext.end(), 0, "D-234r") 1156 self.addError(docstringContext.end(), 0, "D-234r")
1188 else: 1157 else:
1189 if ( 1158 if (
1190 set(return_) - {tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE} 1159 set(return_) - {tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE}
1191 == set() 1160 == set()
1192 ): 1161 ):
1193 self.__error(docstringContext.end(), 0, "D-235r") 1162 self.addError(docstringContext.end(), 0, "D-235r")
1194 1163
1195 def __checkEricYield(self, docstringContext, context): 1164 def __checkEricYield(self, docstringContext, context):
1196 """ 1165 """
1197 Private method to check, that docstrings contain an &#64;yield line 1166 Private method to check, that docstrings contain an &#64;yield line
1198 if they return anything and don't otherwise. 1167 if they return anything and don't otherwise.
1209 yield_ = [ 1178 yield_ = [
1210 tokens[i + 1][0] for i, token in enumerate(tokens) if token[1] == "yield" 1179 tokens[i + 1][0] for i, token in enumerate(tokens) if token[1] == "yield"
1211 ] 1180 ]
1212 if "@yield" not in docstringContext.ssource(): 1181 if "@yield" not in docstringContext.ssource():
1213 if set(yield_) - {tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE} != set(): 1182 if set(yield_) - {tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE} != set():
1214 self.__error(docstringContext.end(), 0, "D-234y") 1183 self.addError(docstringContext.end(), 0, "D-234y")
1215 else: 1184 else:
1216 if set(yield_) - {tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE} == set(): 1185 if set(yield_) - {tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE} == set():
1217 self.__error(docstringContext.end(), 0, "D-235y") 1186 self.addError(docstringContext.end(), 0, "D-235y")
1218 1187
1219 def __checkEricFunctionArguments(self, docstringContext, context): 1188 def __checkEricFunctionArguments(self, docstringContext, context):
1220 """ 1189 """
1221 Private method to check, that docstrings contain an &#64;param and/or 1190 Private method to check, that docstrings contain an &#64;param and/or
1222 &#64;keyparam line for each argument. 1191 &#64;keyparam line for each argument.
1251 if line.lstrip().startswith("@") 1220 if line.lstrip().startswith("@")
1252 ) 1221 )
1253 if tagstring.count("@param") + tagstring.count("@keyparam") < len( 1222 if tagstring.count("@param") + tagstring.count("@keyparam") < len(
1254 argNames + kwNames 1223 argNames + kwNames
1255 ): 1224 ):
1256 self.__error(docstringContext.end(), 0, "D-236") 1225 self.addError(docstringContext.end(), 0, "D-236")
1257 elif tagstring.count("@param") + tagstring.count("@keyparam") > len( 1226 elif tagstring.count("@param") + tagstring.count("@keyparam") > len(
1258 argNames + kwNames 1227 argNames + kwNames
1259 ): 1228 ):
1260 self.__error(docstringContext.end(), 0, "D-237") 1229 self.addError(docstringContext.end(), 0, "D-237")
1261 else: 1230 else:
1262 # extract @param and @keyparam from docstring 1231 # extract @param and @keyparam from docstring
1263 args = [] 1232 args = []
1264 kwargs = [] 1233 kwargs = []
1265 for line in docstringContext.source(): 1234 for line in docstringContext.source():
1272 args.append(name.lstrip("*")) 1241 args.append(name.lstrip("*"))
1273 1242
1274 # do the checks 1243 # do the checks
1275 for name in kwNames: 1244 for name in kwNames:
1276 if name not in kwargs: 1245 if name not in kwargs:
1277 self.__error(docstringContext.end(), 0, "D-238") 1246 self.addError(docstringContext.end(), 0, "D-238")
1278 return 1247 return
1279 if argNames + kwNames != args: 1248 if argNames + kwNames != args:
1280 self.__error(docstringContext.end(), 0, "D-239") 1249 self.addError(docstringContext.end(), 0, "D-239")
1281 1250
1282 def __checkEricException(self, docstringContext, context): 1251 def __checkEricException(self, docstringContext, context):
1283 """ 1252 """
1284 Private method to check, that docstrings contain an &#64;exception line 1253 Private method to check, that docstrings contain an &#64;exception line
1285 if they raise an exception and don't otherwise. 1254 if they raise an exception and don't otherwise.
1315 "@exception" not in docstringContext.ssource() 1284 "@exception" not in docstringContext.ssource()
1316 and "@throws" not in docstringContext.ssource() 1285 and "@throws" not in docstringContext.ssource()
1317 and "@raise" not in docstringContext.ssource() 1286 and "@raise" not in docstringContext.ssource()
1318 ): 1287 ):
1319 if exceptions - {tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE} != set(): 1288 if exceptions - {tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE} != set():
1320 self.__error(docstringContext.end(), 0, "D-250") 1289 self.addError(docstringContext.end(), 0, "D-250")
1321 else: 1290 else:
1322 if exceptions - {tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE} == set(): 1291 if exceptions - {tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE} == set():
1323 self.__error(docstringContext.end(), 0, "D-251") 1292 self.addError(docstringContext.end(), 0, "D-251")
1324 else: 1293 else:
1325 # step 1: extract documented exceptions 1294 # step 1: extract documented exceptions
1326 documentedExceptions = set() 1295 documentedExceptions = set()
1327 for line in docstringContext.source(): 1296 for line in docstringContext.source():
1328 line = line.strip() 1297 line = line.strip()
1332 documentedExceptions.add(exceptionTokens[1]) 1301 documentedExceptions.add(exceptionTokens[1])
1333 1302
1334 # step 2: report undocumented exceptions 1303 # step 2: report undocumented exceptions
1335 for exception in raisedExceptions: 1304 for exception in raisedExceptions:
1336 if exception not in documentedExceptions: 1305 if exception not in documentedExceptions:
1337 self.__error(docstringContext.end(), 0, "D-252", exception) 1306 self.addError(docstringContext.end(), 0, "D-252", exception)
1338 1307
1339 # step 3: report undefined signals 1308 # step 3: report undefined signals
1340 for exception in documentedExceptions: 1309 for exception in documentedExceptions:
1341 if exception not in raisedExceptions: 1310 if exception not in raisedExceptions:
1342 self.__error(docstringContext.end(), 0, "D-253", exception) 1311 self.addError(docstringContext.end(), 0, "D-253", exception)
1343 1312
1344 def __checkEricSignal(self, docstringContext, context): 1313 def __checkEricSignal(self, docstringContext, context):
1345 """ 1314 """
1346 Private method to check, that docstrings contain an &#64;signal line 1315 Private method to check, that docstrings contain an &#64;signal line
1347 if they define signals and don't otherwise. 1316 if they define signals and don't otherwise.
1366 definedSignals.add(tokens[i - 4][1]) 1335 definedSignals.add(tokens[i - 4][1])
1367 elif tokens[i - 1][1] == "=": 1336 elif tokens[i - 1][1] == "=":
1368 definedSignals.add(tokens[i - 2][1]) 1337 definedSignals.add(tokens[i - 2][1])
1369 1338
1370 if "@signal" not in docstringContext.ssource() and definedSignals: 1339 if "@signal" not in docstringContext.ssource() and definedSignals:
1371 self.__error(docstringContext.end(), 0, "D-260") 1340 self.addError(docstringContext.end(), 0, "D-260")
1372 elif "@signal" in docstringContext.ssource(): 1341 elif "@signal" in docstringContext.ssource():
1373 if not definedSignals: 1342 if not definedSignals:
1374 self.__error(docstringContext.end(), 0, "D-261") 1343 self.addError(docstringContext.end(), 0, "D-261")
1375 else: 1344 else:
1376 # step 1: extract documented signals 1345 # step 1: extract documented signals
1377 documentedSignals = set() 1346 documentedSignals = set()
1378 for line in docstringContext.source(): 1347 for line in docstringContext.source():
1379 line = line.strip() 1348 line = line.strip()
1386 documentedSignals.add(signal) 1355 documentedSignals.add(signal)
1387 1356
1388 # step 2: report undocumented signals 1357 # step 2: report undocumented signals
1389 for signal in definedSignals: 1358 for signal in definedSignals:
1390 if signal not in documentedSignals: 1359 if signal not in documentedSignals:
1391 self.__error(docstringContext.end(), 0, "D-262", signal) 1360 self.addError(docstringContext.end(), 0, "D-262", signal)
1392 1361
1393 # step 3: report undefined signals 1362 # step 3: report undefined signals
1394 for signal in documentedSignals: 1363 for signal in documentedSignals:
1395 if signal not in definedSignals: 1364 if signal not in definedSignals:
1396 self.__error(docstringContext.end(), 0, "D-263", signal) 1365 self.addError(docstringContext.end(), 0, "D-263", signal)
1397 1366
1398 def __checkEricBlankAfterSummary(self, docstringContext, _context): 1367 def __checkEricBlankAfterSummary(self, docstringContext, _context):
1399 """ 1368 """
1400 Private method to check, that docstring summaries are followed 1369 Private method to check, that docstring summaries are followed
1401 by a blank line. 1370 by a blank line.
1416 summaryLines, lineNumber = self.__getSummaryLines(docstringContext) 1385 summaryLines, lineNumber = self.__getSummaryLines(docstringContext)
1417 if ( 1386 if (
1418 len(docstrings) - 2 > lineNumber + len(summaryLines) - 1 1387 len(docstrings) - 2 > lineNumber + len(summaryLines) - 1
1419 and docstrings[lineNumber + len(summaryLines)].strip() 1388 and docstrings[lineNumber + len(summaryLines)].strip()
1420 ): 1389 ):
1421 self.__error(docstringContext.start() + lineNumber, 0, "D-246") 1390 self.addError(docstringContext.start() + lineNumber, 0, "D-246")
1422 1391
1423 def __checkEricNoBlankBeforeAndAfterClassOrFunction( 1392 def __checkEricNoBlankBeforeAndAfterClassOrFunction(
1424 self, docstringContext, context 1393 self, docstringContext, context
1425 ): 1394 ):
1426 """ 1395 """
1458 if cti >= len(contextLines) - 1: 1427 if cti >= len(contextLines) - 1:
1459 return 1428 return
1460 1429
1461 if isClassContext: 1430 if isClassContext:
1462 if not contextLines[start - 1].strip(): 1431 if not contextLines[start - 1].strip():
1463 self.__error(docstringContext.start(), 0, "D-242") 1432 self.addError(docstringContext.start(), 0, "D-242")
1464 if not contextLines[end + 1].strip() and self.__docType == "eric": 1433 if not contextLines[end + 1].strip() and self.__docType == "eric":
1465 self.__error(docstringContext.end(), 0, "D-243") 1434 self.addError(docstringContext.end(), 0, "D-243")
1466 elif contextLines[end + 1].strip() and self.__docType == "eric_black": 1435 elif contextLines[end + 1].strip() and self.__docType == "eric_black":
1467 self.__error(docstringContext.end(), 0, "D-143") 1436 self.addError(docstringContext.end(), 0, "D-143")
1468 else: 1437 else:
1469 if not contextLines[start - 1].strip(): 1438 if not contextLines[start - 1].strip():
1470 self.__error(docstringContext.start(), 0, "D-244") 1439 self.addError(docstringContext.start(), 0, "D-244")
1471 if not contextLines[end + 1].strip(): 1440 if not contextLines[end + 1].strip():
1472 if ( 1441 if (
1473 self.__docType == "eric_black" 1442 self.__docType == "eric_black"
1474 and len(contextLines) > end + 2 1443 and len(contextLines) > end + 2
1475 and contextLines[end + 2].strip().startswith("def ") 1444 and contextLines[end + 2].strip().startswith("def ")
1476 ): 1445 ):
1477 return 1446 return
1478 1447
1479 self.__error(docstringContext.end(), 0, "D-245") 1448 self.addError(docstringContext.end(), 0, "D-245")
1480 1449
1481 def __checkEricNBlankAfterLastParagraph(self, docstringContext, _context): 1450 def __checkEricNBlankAfterLastParagraph(self, docstringContext, _context):
1482 """ 1451 """
1483 Private method to check, that the last paragraph of docstrings is 1452 Private method to check, that the last paragraph of docstrings is
1484 not followed by a blank line. 1453 not followed by a blank line.
1495 if len(docstrings) <= 3: 1464 if len(docstrings) <= 3:
1496 # correct/invalid one-liner 1465 # correct/invalid one-liner
1497 return 1466 return
1498 1467
1499 if not docstrings[-2].strip(): 1468 if not docstrings[-2].strip():
1500 self.__error(docstringContext.end(), 0, "D-247") 1469 self.addError(docstringContext.end(), 0, "D-247")
1501 1470
1502 def __checkEricSummary(self, docstringContext, context): 1471 def __checkEricSummary(self, docstringContext, context):
1503 """ 1472 """
1504 Private method to check, that method docstring summaries start with 1473 Private method to check, that method docstring summaries start with
1505 specific words. 1474 specific words.
1520 context.source()[0].lstrip().split()[1].split("(", 1) 1489 context.source()[0].lstrip().split()[1].split("(", 1)
1521 ) 1490 )
1522 firstWord = summary.strip().split(None, 1)[0].lower() 1491 firstWord = summary.strip().split(None, 1)[0].lower()
1523 if functionName == "__init__": 1492 if functionName == "__init__":
1524 if firstWord != "constructor": 1493 if firstWord != "constructor":
1525 self.__error( 1494 self.addError(
1526 docstringContext.start() + lineNumber, 0, "D-232", "constructor" 1495 docstringContext.start() + lineNumber, 0, "D-232", "constructor"
1527 ) 1496 )
1528 elif functionName.startswith("__") and functionName.endswith("__"): 1497 elif functionName.startswith("__") and functionName.endswith("__"):
1529 if firstWord != "special": 1498 if firstWord != "special":
1530 self.__error( 1499 self.addError(
1531 docstringContext.start() + lineNumber, 0, "D-232", "special" 1500 docstringContext.start() + lineNumber, 0, "D-232", "special"
1532 ) 1501 )
1533 elif context.special() == "staticmethod": 1502 elif context.special() == "staticmethod":
1534 secondWord = summary.strip().split(None, 2)[1].lower() 1503 secondWord = summary.strip().split(None, 2)[1].lower()
1535 if firstWord != "static" and secondWord != "static": 1504 if firstWord != "static" and secondWord != "static":
1536 self.__error( 1505 self.addError(
1537 docstringContext.start() + lineNumber, 0, "D-232", "static" 1506 docstringContext.start() + lineNumber, 0, "D-232", "static"
1538 ) 1507 )
1539 elif secondWord == "static": 1508 elif secondWord == "static":
1540 if functionName.startswith(("__", "on_")): 1509 if functionName.startswith(("__", "on_")):
1541 if firstWord != "private": 1510 if firstWord != "private":
1542 self.__error( 1511 self.addError(
1543 docstringContext.start() + lineNumber, 1512 docstringContext.start() + lineNumber,
1544 0, 1513 0,
1545 "D-232", 1514 "D-232",
1546 "private static", 1515 "private static",
1547 ) 1516 )
1548 elif functionName.startswith("_") or functionName.endswith("Event"): 1517 elif functionName.startswith("_") or functionName.endswith("Event"):
1549 if firstWord != "protected": 1518 if firstWord != "protected":
1550 self.__error( 1519 self.addError(
1551 docstringContext.start() + lineNumber, 1520 docstringContext.start() + lineNumber,
1552 0, 1521 0,
1553 "D-232", 1522 "D-232",
1554 "protected static", 1523 "protected static",
1555 ) 1524 )
1556 else: 1525 else:
1557 if firstWord != "public": 1526 if firstWord != "public":
1558 self.__error( 1527 self.addError(
1559 docstringContext.start() + lineNumber, 1528 docstringContext.start() + lineNumber,
1560 0, 1529 0,
1561 "D-232", 1530 "D-232",
1562 "public static", 1531 "public static",
1563 ) 1532 )
1565 arguments.startswith(("cls,", "cls)")) 1534 arguments.startswith(("cls,", "cls)"))
1566 or context.special() == "classmethod" 1535 or context.special() == "classmethod"
1567 ): 1536 ):
1568 secondWord = summary.strip().split(None, 2)[1].lower() 1537 secondWord = summary.strip().split(None, 2)[1].lower()
1569 if firstWord != "class" and secondWord != "class": 1538 if firstWord != "class" and secondWord != "class":
1570 self.__error( 1539 self.addError(
1571 docstringContext.start() + lineNumber, 0, "D-232", "class" 1540 docstringContext.start() + lineNumber, 0, "D-232", "class"
1572 ) 1541 )
1573 elif secondWord == "class": 1542 elif secondWord == "class":
1574 if functionName.startswith(("__", "on_")): 1543 if functionName.startswith(("__", "on_")):
1575 if firstWord != "private": 1544 if firstWord != "private":
1576 self.__error( 1545 self.addError(
1577 docstringContext.start() + lineNumber, 1546 docstringContext.start() + lineNumber,
1578 0, 1547 0,
1579 "D-232", 1548 "D-232",
1580 "private class", 1549 "private class",
1581 ) 1550 )
1582 elif functionName.startswith("_") or functionName.endswith("Event"): 1551 elif functionName.startswith("_") or functionName.endswith("Event"):
1583 if firstWord != "protected": 1552 if firstWord != "protected":
1584 self.__error( 1553 self.addError(
1585 docstringContext.start() + lineNumber, 1554 docstringContext.start() + lineNumber,
1586 0, 1555 0,
1587 "D-232", 1556 "D-232",
1588 "protected class", 1557 "protected class",
1589 ) 1558 )
1590 else: 1559 else:
1591 if firstWord != "public": 1560 if firstWord != "public":
1592 self.__error( 1561 self.addError(
1593 docstringContext.start() + lineNumber, 1562 docstringContext.start() + lineNumber,
1594 0, 1563 0,
1595 "D-232", 1564 "D-232",
1596 "public class", 1565 "public class",
1597 ) 1566 )
1598 elif functionName.startswith(("__", "on_")): 1567 elif functionName.startswith(("__", "on_")):
1599 if firstWord != "private": 1568 if firstWord != "private":
1600 self.__error( 1569 self.addError(
1601 docstringContext.start() + lineNumber, 0, "D-232", "private" 1570 docstringContext.start() + lineNumber, 0, "D-232", "private"
1602 ) 1571 )
1603 elif functionName.startswith("_") or functionName.endswith("Event"): 1572 elif functionName.startswith("_") or functionName.endswith("Event"):
1604 if firstWord != "protected": 1573 if firstWord != "protected":
1605 self.__error( 1574 self.addError(
1606 docstringContext.start() + lineNumber, 0, "D-232", "protected" 1575 docstringContext.start() + lineNumber, 0, "D-232", "protected"
1607 ) 1576 )
1608 else: 1577 else:
1609 if firstWord != "public": 1578 if firstWord != "public":
1610 self.__error( 1579 self.addError(
1611 docstringContext.start() + lineNumber, 0, "D-232", "public" 1580 docstringContext.start() + lineNumber, 0, "D-232", "public"
1612 ) 1581 )
1613 1582
1614 def __checkEricDocumentationSequence( 1583 def __checkEricDocumentationSequence(
1615 self, 1584 self,
1640 if ( 1609 if (
1641 docToken in ("@type", "@rtype", "@ytype") 1610 docToken in ("@type", "@rtype", "@ytype")
1642 and lineno > 0 1611 and lineno > 0
1643 and lines[lineno - 1].strip() == "" 1612 and lines[lineno - 1].strip() == ""
1644 ): 1613 ):
1645 self.__error( 1614 self.addError(
1646 docstringContext.start() + lineno, 0, "D-271", docToken 1615 docstringContext.start() + lineno, 0, "D-271", docToken
1647 ) 1616 )
1648 1617
1649 # check the correct sequence of @param/@return/@yield and their accompanying 1618 # check the correct sequence of @param/@return/@yield and their accompanying
1650 # type tag 1619 # type tag
1654 docToken2, _ = docTokens[index + 1] 1623 docToken2, _ = docTokens[index + 1]
1655 except IndexError: 1624 except IndexError:
1656 docToken2 = "" 1625 docToken2 = ""
1657 1626
1658 if docToken in ("@param", "@keyparam") and docToken2 != "@type": 1627 if docToken in ("@param", "@keyparam") and docToken2 != "@type":
1659 self.__error( 1628 self.addError(
1660 docstringContext.start() + lineno, 0, "D-270", docToken, "@type" 1629 docstringContext.start() + lineno, 0, "D-270", docToken, "@type"
1661 ) 1630 )
1662 elif docToken == "@return" and docToken2 != "@rtype": 1631 elif docToken == "@return" and docToken2 != "@rtype":
1663 self.__error( 1632 self.addError(
1664 docstringContext.start() + lineno, 0, "D-270", docToken, "@rtype" 1633 docstringContext.start() + lineno, 0, "D-270", docToken, "@rtype"
1665 ) 1634 )
1666 elif docToken == "@yield" and docToken2 != "@ytype": 1635 elif docToken == "@yield" and docToken2 != "@ytype":
1667 self.__error( 1636 self.addError(
1668 docstringContext.start() + lineno, 0, "D-270", docToken, "@ytype" 1637 docstringContext.start() + lineno, 0, "D-270", docToken, "@ytype"
1669 ) 1638 )
1670 1639
1671 def __checkEricDocumentationDeprecatedTags( 1640 def __checkEricDocumentationDeprecatedTags(
1672 self, 1641 self,
1696 strippedLine = line.lstrip() 1665 strippedLine = line.lstrip()
1697 if strippedLine.startswith("@"): 1666 if strippedLine.startswith("@"):
1698 # it is a tag line 1667 # it is a tag line
1699 tag = strippedLine.split(None, 1)[0] 1668 tag = strippedLine.split(None, 1)[0]
1700 with contextlib.suppress(KeyError): 1669 with contextlib.suppress(KeyError):
1701 self.__error( 1670 self.addError(
1702 docstringContext.start() + lineno, 1671 docstringContext.start() + lineno,
1703 0, 1672 0,
1704 "D-272", 1673 "D-272",
1705 tag, 1674 tag,
1706 deprecationsList[tag], 1675 deprecationsList[tag],
1735 strippedLine = line.lstrip() 1704 strippedLine = line.lstrip()
1736 if strippedLine.startswith("@"): 1705 if strippedLine.startswith("@"):
1737 tag = strippedLine.split(None, 1)[0] 1706 tag = strippedLine.split(None, 1)[0]
1738 currentIndentation = len(line) - len(strippedLine) 1707 currentIndentation = len(line) - len(strippedLine)
1739 if currentIndentation != indentationLength: 1708 if currentIndentation != indentationLength:
1740 self.__error(docstringContext.start() + lineno, 0, "D-273", tag) 1709 self.addError(docstringContext.start() + lineno, 0, "D-273", tag)

eric ide

mercurial