Plugins/CheckerPlugins/Pep8/Pep257Checker.py

changeset 2915
9da653363d07
child 2916
a8628dfdfe04
equal deleted inserted replaced
2914:6b30a602e404 2915:9da653363d07
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2013 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a checker for PEP-257 documentation string conventions.
8 """
9
10 #
11 # The routines of the checker class are modeled after the ones found in
12 # pep257.py (version 0.2.4).
13 #
14
15 try:
16 # Python 2
17 from StringIO import StringIO # __IGNORE_EXCEPTION__
18 except ImportError:
19 # Python 3
20 from io import StringIO # __IGNORE_WARNING__
21 import tokenize
22
23 from PyQt4.QtCore import QT_TRANSLATE_NOOP, QCoreApplication
24
25
26 class Pep257Context(object):
27 """
28 Class implementing the source context.
29 """
30 def __init__(self, source, startLine, contextType):
31 """
32 Constructor
33
34 @param source source code of the context (list of string or string)
35 @param startLine line number the context starts in the source (integer)
36 @param contextType type of the context object (string)
37 """
38 if isinstance(source, str):
39 self.__source = source.splitlines(True)
40 else:
41 self.__source = source[:]
42 self.__start = startLine
43 self.__indent = ""
44 self.__type = contextType
45
46 # ensure first line is left justified
47 if self.__source:
48 self.__indent = self.__source[0].replace(
49 self.__source[0].lstrip(), "")
50 self.__source[0] = self.__source[0].lstrip()
51
52 def source(self):
53 """
54 Public method to get the source.
55
56 @return source (list of string)
57 """
58 return self.__source
59
60 def ssource(self):
61 """
62 Public method to get the joined source lines.
63
64 @return source (string)
65 """
66 return "".join(self.__source)
67
68 def start(self):
69 """
70 Public method to get the start line number.
71
72 @return start line number (integer)
73 """
74 return self.__start
75
76 def end(self):
77 """
78 Public method to get the end line number.
79
80 @return end line number (integer)
81 """
82 return self.__start + len(self.__source) - 1
83
84 def indent(self):
85 """
86 Public method to get the indentation of the first line.
87
88 @return indentation string (string)
89 """
90 return self.__indent
91
92 def contextType(self):
93 """
94 Public method to get the context type.
95
96 @return context type (string)
97 """
98 return self.__type
99
100
101 class Pep257Checker(object):
102 """
103 Class implementing a checker for PEP-257 documentation string conventions.
104 """
105 Codes = [
106 "D101", "D102", "D103", "D104", "D105",
107 "D111", "D112", "D113",
108 "D121", "D122",
109 "D131", "D132", "D133", "D134",
110 "D141"
111 ]
112
113 Messages = {
114 "D101": QT_TRANSLATE_NOOP(
115 "Pep257Checker", "module is missing a docstring"),
116 "D102": QT_TRANSLATE_NOOP(
117 "Pep257Checker", "public function/method is missing a docstring"),
118 "D103": QT_TRANSLATE_NOOP(
119 "Pep257Checker",
120 "private function/method may be missing a docstring"),
121 "D104": QT_TRANSLATE_NOOP(
122 "Pep257Checker", "public class is missing a docstring"),
123 "D105": QT_TRANSLATE_NOOP(
124 "Pep257Checker", "private class may be missing a docstring"),
125 "D111": QT_TRANSLATE_NOOP(
126 "Pep257Checker", 'docstring not surrounded by """'),
127 "D112": QT_TRANSLATE_NOOP(
128 "Pep257Checker", 'docstring containing \\ not surrounded by r"""'),
129 "D113": QT_TRANSLATE_NOOP(
130 "Pep257Checker",
131 'docstring containing unicode character not surrounded by u"""'),
132 "D121": QT_TRANSLATE_NOOP(
133 "Pep257Checker", "one-liner docstring on multiple lines"),
134 "D122": QT_TRANSLATE_NOOP(
135 "Pep257Checker", "docstring has wrong indentation"),
136 "D131": QT_TRANSLATE_NOOP(
137 "Pep257Checker", "docstring summary does not end with a period"),
138 "D132": QT_TRANSLATE_NOOP(
139 "Pep257Checker",
140 "docstring summary is not in imperative mood"
141 " (Does instead of Do)"),
142 "D133": QT_TRANSLATE_NOOP(
143 "Pep257Checker",
144 "docstring summary looks like a function's/method's signature"),
145 "D134": QT_TRANSLATE_NOOP(
146 "Pep257Checker",
147 "docstring does not mention the return value type"),
148 "D141": QT_TRANSLATE_NOOP(
149 "Pep257Checker", "docstring is separated by a blank line"),
150 }
151
152 def __init__(self, source, filename, select, ignore, expected, repeat):
153 """
154 Constructor (according to 'extended' pep8.py API)
155
156 @param source source code to be checked (list of string)
157 @param filename name of the source file (string)
158 @param select list of selected codes (list of string)
159 @param ignore list of codes to be ignored (list of string)
160 @param expected list of expected codes (list of string)
161 @param repeat flag indicating to report each occurrence of a code
162 (boolean)
163 """
164 self.__select = tuple(select)
165 self.__ignore = tuple(ignore)
166 self.__expected = expected[:]
167 self.__repeat = repeat
168 self.__filename = filename
169 self.__source = source[:]
170 self.__isScript = self.__source[0].startswith('#!')
171
172 # statistics counters
173 self.counters = {}
174
175 # collection of detected errors
176 self.errors = []
177
178 self.__lineNumber = 0
179
180 # caches
181 self.__functionsCache = None
182 self.__classesCache = None
183 self.__methodsCache = None
184
185 self.__keywords = [
186 'moduleDocstring', 'functionDocstring',
187 'classDocstring', 'methodDocstring',
188 'defDocstring', 'docstring'
189 ]
190 self.__checkersWithCodes = {
191 "moduleDocstring": [
192 (self.__checkModulesDocstrings, ("D101",)),
193 ],
194 "functionDocstring": [
195 ],
196 "classDocstring": [
197 (self.__checkClassDocstring, ("D104", "D105")),
198 ],
199 "methodDocstring": [
200 ],
201 "defDocstring": [
202 (self.__checkFunctionDocstring, ("D102", "D103")),
203 (self.__checkImperativeMood, ("D132",)),
204 (self.__checkNoSignature, ("D133",)),
205 (self.__checkReturnType, ("D134",)),
206 (self.__checkNoBlankLineBefore, ("D141",)),
207 ],
208 "docstring": [
209 (self.__checkTripleDoubleQuotes, ("D111",)),
210 (self.__checkBackslashes, ("D112",)),
211 (self.__checkUnicode, ("D113",)),
212 (self.__checkOneLiner, ("D121",)),
213 (self.__checkIndent, ("D122",)),
214 (self.__checkEndsWithPeriod, ("D131",)),
215 ],
216 }
217
218 self.__checkers = {}
219 for key, checkers in self.__checkersWithCodes.items():
220 for checker, codes in checkers:
221 if any(not (code and self.__ignoreCode(code))
222 for code in codes):
223 if key not in self.__checkers:
224 self.__checkers[key] = []
225 self.__checkers[key].append(checker)
226
227 def __ignoreCode(self, code):
228 """
229 Private method to check if the error code should be ignored.
230
231 @param code message code to check for (string)
232 @return flag indicating to ignore the given code (boolean)
233 """
234 return (code.startswith(self.__ignore) and
235 not code.startswith(self.__select))
236
237 def __error(self, lineNumber, offset, code, *args):
238 """
239 Private method to record an issue.
240
241 @param lineNumber line number of the issue (integer)
242 @param offset position within line of the issue (integer)
243 @param code message code (string)
244 @param args arguments for the message (list)
245 """
246 if self.__ignoreCode(code):
247 return
248
249 if code in self.counters:
250 self.counters[code] += 1
251 else:
252 self.counters[code] = 1
253
254 # Don't care about expected codes
255 if code in self.__expected:
256 return
257
258 if code and (self.counters[code] == 1 or self.__repeat):
259 if code in Pep257Checker.Codes:
260 text = self.__getMessage(code, *args)
261 # record the issue with one based line number
262 self.errors.append((self.__filename, lineNumber + 1, offset, text))
263
264 def __getMessage(self, code, *args):
265 """
266 Private method to get a translated and formatted message for a
267 given code.
268
269 @param code message code (string)
270 @param args arguments for a formatted message (list)
271 @return translated and formatted message (string)
272 """
273 if code in Pep257Checker.Messages:
274 return code + " " + QCoreApplication.translate(
275 "Pep257Checker", Pep257Checker.Messages[code]).format(*args)
276 else:
277 return code + " " + QCoreApplication.translate(
278 "Pep257Checker", "no message for this code defined")
279
280 def __resetReadline(self):
281 """
282 Private method to reset the internal readline function.
283 """
284 self.__lineNumber = 0
285
286 def __readline(self):
287 """
288 Private method to get the next line from the source.
289
290 @return next line of source (string)
291 """
292 self.__lineNumber += 1
293 if self.__lineNumber > len(self.__source):
294 return ''
295 return self.__source[self.__lineNumber - 1]
296
297 def run(self):
298 """
299 Public method to check the given source for violations of doc string
300 conventions according to PEP-257.
301 """
302 if not self.__source or not self.__filename:
303 # don't do anything, if essential data is missing
304 return
305
306 for keyword in self.__keywords:
307 if keyword in self.__checkers:
308 for check in self.__checkers[keyword]:
309 for context in self.__parseContexts(keyword):
310 docstring = self.__parseDocstring(context, keyword)
311 check(docstring, context)
312
313 def __getSummaryLine(self, docstringContext):
314 """
315 Private method to extract the summary line.
316
317 @param docstringContext docstring context (Pep257Context)
318 @return summary line (string) and the line it was found on (integer)
319 """
320 lines = docstringContext.source()
321
322 line = (lines[0]
323 .replace('r"""', "", 1)
324 .replace('u"""', "", 1)
325 .replace('"""', "")
326 .strip())
327
328 if len(lines) == 1 or len(line) > 0:
329 return line, 0
330 return lines[1].strip(), 1
331
332 first_line = lines[0].strip()
333 if len(lines) == 1 or len(first_line) > 0:
334 return first_line, 0
335 return lines[1].strip(), 1
336
337 ##################################################################
338 ## Parsing functionality below
339 ##################################################################
340
341 def __parseModuleDocstring(self, source):
342 """
343 Private method to extract a docstring given a module source.
344
345 @param source source to parse (list of string)
346 @return context of extracted docstring (Pep257Context)
347 """
348 for kind, value, (line, char), _, _ in tokenize.generate_tokens(
349 StringIO("".join(source)).readline):
350 if kind in [tokenize.COMMENT, tokenize.NEWLINE, tokenize.NL]:
351 continue
352 elif kind == tokenize.STRING: # first STRING should be docstring
353 return Pep257Context(value, line - 1, "docstring")
354 else:
355 return None
356
357 def __parseDocstring(self, context, what=''):
358 """
359 Private method to extract a docstring given `def` or `class` source.
360
361 @param context context data to get the docstring from (Pep257Context)
362 @return context of extracted docstring (Pep257Context)
363 """
364 moduleDocstring = self.__parseModuleDocstring(context.source())
365 if what.startswith('module'):
366 return moduleDocstring
367 if moduleDocstring:
368 return moduleDocstring
369
370 tokenGenerator = tokenize.generate_tokens(
371 StringIO(context.ssource()).readline)
372 try:
373 kind = None
374 while kind != tokenize.INDENT:
375 kind, _, _, _, _ = next(tokenGenerator)
376 kind, value, (line, char), _, _ = next(tokenGenerator)
377 if kind == tokenize.STRING: # STRING after INDENT is a docstring
378 return Pep257Context(
379 value, context.start() + line - 1, "docstring")
380 except StopIteration:
381 pass
382
383 return None
384
385 def __parseTopLevel(self, keyword):
386 """
387 Private method to extract top-level functions or classes.
388
389 @param keyword keyword signaling what to extract (string)
390 @return extracted function or class contexts (list of Pep257Context)
391 """
392 self.__resetReadline()
393 tokenGenerator = tokenize.generate_tokens(self.__readline)
394 kind, value, char = None, None, None
395 contexts = []
396 try:
397 while True:
398 start, end = None, None
399 while not (kind == tokenize.NAME and
400 value == keyword and
401 char == 0):
402 kind, value, (line, char), _, _ = next(tokenGenerator)
403 start = line - 1, char
404 while not (kind == tokenize.DEDENT and
405 value == '' and
406 char == 0):
407 kind, value, (line, char), _, _ = next(tokenGenerator)
408 end = line - 1, char
409 contexts.append(Pep257Context(
410 self.__source[start[0]:end[0]], start[0], keyword))
411 except StopIteration:
412 return contexts
413
414 def __parseFunctions(self):
415 """
416 Private method to extract top-level functions.
417
418 @return extracted function contexts (list of Pep257Context)
419 """
420 if not self.__functionsCache:
421 self.__functionsCache = self.__parseTopLevel('def')
422 return self.__functionsCache
423
424 def __parseClasses(self):
425 """
426 Private method to extract top-level classes.
427
428 @return extracted class contexts (list of Pep257Context)
429 """
430 if not self.__classesCache:
431 self.__classesCache = self.__parseTopLevel('class')
432 return self.__classesCache
433
434 def __skipIndentedBlock(self, tokenGenerator):
435 """
436 Private method to skip over an indented block of source code.
437
438 @param tokenGenerator token generator
439 @return last token of the indented block
440 """
441 kind, value, start, end, raw = next(tokenGenerator)
442 while kind != tokenize.INDENT:
443 kind, value, start, end, raw = next(tokenGenerator)
444 indent = 1
445 for kind, value, start, end, raw in tokenGenerator:
446 if kind == tokenize.INDENT:
447 indent += 1
448 elif kind == tokenize.DEDENT:
449 indent -= 1
450 if indent == 0:
451 return kind, value, start, end, raw
452
453 def __parseMethods(self):
454 """
455 Private method to extract methods of all classes.
456
457 @return extracted method contexts (list of Pep257Context)
458 """
459 if not self.__methodsCache:
460 contexts = []
461 for classContext in self.__parseClasses():
462 tokenGenerator = tokenize.generate_tokens(
463 StringIO(classContext.ssource()).readline)
464 kind, value, char = None, None, None
465 try:
466 while True:
467 start, end = None, None
468 while not (kind == tokenize.NAME and value == 'def'):
469 kind, value, (line, char), _, _ = \
470 next(tokenGenerator)
471 start = line - 1, char
472 kind, value, (line, char), _, _ = \
473 self.__skipIndentedBlock(tokenGenerator)
474 end = line - 1, char
475 startLine = classContext.start() + start[0]
476 endLine = classContext.start() + end[0]
477 contexts.append(
478 Pep257Context(self.__source[startLine:endLine],
479 startLine, "def"))
480 except StopIteration:
481 pass
482 self.__methodsCache = contexts
483
484 return self.__methodsCache
485
486 def __parseContexts(self, kind):
487 """
488 Private method to extract a context from the source.
489
490 @param kind kind of context to extract (string)
491 @return requested contexts (list of Pep257Context)
492 """
493 if kind == 'moduleDocstring':
494 return [Pep257Context(self.__source, 0, "module")]
495 if kind == 'functionDocstring':
496 return self.__parseFunctions()
497 if kind == 'classDocstring':
498 return self.__parseClasses()
499 if kind == 'methodDocstring':
500 return self.__parseMethods()
501 if kind == 'defDocstring':
502 return self.__parseFunctions() + self.__parseMethods()
503 if kind == 'docstring':
504 return ([Pep257Context(self.__source, 0, "module")] +
505 self.__parseFunctions() +
506 self.__parseClasses() +
507 self.__parseMethods())
508 return [] # fall back
509
510 ##################################################################
511 ## Checking functionality below
512 ##################################################################
513
514 def __checkModulesDocstrings(self, docstringContext, context):
515 """
516 Private method to check, if the module has a docstring.
517
518 @param docstringContext docstring context (Pep257Context)
519 @param context context of the docstring (Pep257Context)
520 """
521 if docstringContext is None:
522 self.__error(context.start(), 0, "D101")
523 return
524
525 docstring = docstringContext.ssource()
526 if (not docstring or not docstring.strip() or
527 not docstring.strip('\'"')):
528 self.__error(context.start(), 0, "D101")
529
530 def __checkFunctionDocstring(self, docstringContext, context):
531 """
532 Private method to check, that all public functions and methods
533 have a docstring.
534
535 @param docstringContext docstring context (Pep257Context)
536 @param context context of the docstring (Pep257Context)
537 """
538 if self.__isScript:
539 # assume nothing is exported
540 return
541
542 functionName = context.source()[0].lstrip().split()[1].split("(")[0]
543 if functionName.startswith('_') and not functionName.endswith('__'):
544 code = "D103"
545 else:
546 code = "D102"
547
548 if docstringContext is None:
549 self.__error(context.start(), 0, code)
550 return
551
552 docstring = docstringContext.ssource()
553 if (not docstring or not docstring.strip() or
554 not docstring.strip('\'"')):
555 self.__error(context.start(), 0, code)
556
557 def __checkClassDocstring(self, docstringContext, context):
558 """
559 Private method to check, that all public functions and methods
560 have a docstring.
561
562 @param docstringContext docstring context (Pep257Context)
563 @param context context of the docstring (Pep257Context)
564 """
565 if self.__isScript:
566 # assume nothing is exported
567 return
568
569 className = context.source()[0].lstrip().split()[1].split("(")[0]
570 if className.startswith('_'):
571 code = "D105"
572 else:
573 code = "D104"
574
575 if docstringContext is None:
576 self.__error(context.start(), 0, code)
577 return
578
579 docstring = docstringContext.ssource()
580 if (not docstring or not docstring.strip() or
581 not docstring.strip('\'"')):
582 self.__error(context.start(), 0, code)
583
584 def __checkTripleDoubleQuotes(self, docstringContext, context):
585 """
586 Private method to check, that all docstrings are surrounded
587 by triple double quotes.
588
589 @param docstringContext docstring context (Pep257Context)
590 @param context context of the docstring (Pep257Context)
591 """
592 if docstringContext is None:
593 return
594
595 docstring = docstringContext.ssource().strip()
596 if not docstring.startswith(('"""', 'r"""', 'u"""')):
597 self.__error(docstringContext.start(), 0, "D111")
598
599 def __checkBackslashes(self, docstringContext, context):
600 """
601 Private method to check, that all docstrings containing
602 backslashes are surrounded by raw triple double quotes.
603
604 @param docstringContext docstring context (Pep257Context)
605 @param context context of the docstring (Pep257Context)
606 """
607 if docstringContext is None:
608 return
609
610 docstring = docstringContext.ssource().strip()
611 if "\\" in docstring and not docstring.startswith('r"""'):
612 self.__error(docstringContext.start(), 0, "D112")
613
614 def __checkUnicode(self, docstringContext, context):
615 """
616 Private method to check, that all docstrings containing unicode
617 characters are surrounded by unicode triple double quotes.
618
619 @param docstringContext docstring context (Pep257Context)
620 @param context context of the docstring (Pep257Context)
621 """
622 if docstringContext is None:
623 return
624
625 docstring = docstringContext.ssource().strip()
626 if not docstring.startswith('u"""') and \
627 any(ord(char) > 127 for char in docstring):
628 self.__error(docstringContext.start(), 0, "D113")
629
630 def __checkOneLiner(self, docstringContext, context):
631 """
632 Private method to check, that one-liner docstrings fit on
633 one line with quotes.
634
635 @param docstringContext docstring context (Pep257Context)
636 @param context context of the docstring (Pep257Context)
637 """
638 if docstringContext is None:
639 return
640
641 lines = docstringContext.source()
642 if len(lines) > 1:
643 nonEmptyLines = [l for l in lines if l.strip().strip('\'"')]
644 if len(nonEmptyLines) == 1:
645 self.__error(docstringContext.start(), 0, "D121")
646
647 def __checkIndent(self, docstringContext, context):
648 """
649 Private method to check, that docstrings are properly indented.
650
651 @param docstringContext docstring context (Pep257Context)
652 @param context context of the docstring (Pep257Context)
653 """
654 if docstringContext is None:
655 return
656
657 lines = docstringContext.source()
658 if len(lines) == 1:
659 return
660
661 nonEmptyLines = [l.rstrip() for l in lines[1:] if l.strip()]
662 if not nonEmptyLines:
663 return
664
665 indent = min([len(l) - len(l.strip()) for l in nonEmptyLines])
666 if context.contextType() == "module":
667 expectedIndent = 0
668 else:
669 expectedIndent = len(context.indent()) + 4
670 if indent != expectedIndent:
671 self.__error(docstringContext.start(), 0, "D122")
672
673 def __checkEndsWithPeriod(self, docstringContext, context):
674 """
675 Private method to check, that docstring summaries end with a period.
676
677 @param docstringContext docstring context (Pep257Context)
678 @param context context of the docstring (Pep257Context)
679 """
680 if docstringContext is None:
681 return
682
683 summary, lineNumber = self.__getSummaryLine(docstringContext)
684 if not summary.endswith("."):
685 self.__error(docstringContext.start() + lineNumber, 0, "D131")
686
687 def __checkImperativeMood(self, docstringContext, context):
688 """
689 Private method to check, that docstring summaries are in
690 imperative mood.
691
692 @param docstringContext docstring context (Pep257Context)
693 @param context context of the docstring (Pep257Context)
694 """
695 if docstringContext is None:
696 return
697
698 summary, lineNumber = self.__getSummaryLine(docstringContext)
699 firstWord = summary.strip().split()[0]
700 if firstWord.endswith("s") and not firstWord.endswith("ss"):
701 self.__error(docstringContext.start() + lineNumber, 0, "D132")
702
703 def __checkNoSignature(self, docstringContext, context):
704 """
705 Private method to check, that docstring summaries don't repeat
706 the function's signature.
707
708 @param docstringContext docstring context (Pep257Context)
709 @param context context of the docstring (Pep257Context)
710 """
711 if docstringContext is None:
712 return
713
714 functionName = context.source()[0].lstrip().split()[1].split("(")[0]
715 summary, lineNumber = self.__getSummaryLine(docstringContext)
716 if functionName + "(" in summary.replace(" ", ""):
717 self.__error(docstringContext.start() + lineNumber, 0, "D133")
718
719 def __checkReturnType(self, docstringContext, context):
720 """
721 Private method to check, that docstrings mention the return value type.
722
723 @param docstringContext docstring context (Pep257Context)
724 @param context context of the docstring (Pep257Context)
725 """
726 if docstringContext is None or self.__isScript:
727 return
728
729 if "return" not in docstringContext.ssource().lower():
730 tokens = list(
731 tokenize.generate_tokens(StringIO(context.ssource()).readline))
732 return_ = [tokens[i + 1][0] for i, token in enumerate(tokens)
733 if token[1] == "return"]
734 if (set(return_) -
735 set([tokenize.COMMENT, tokenize.NL, tokenize.NEWLINE]) !=
736 set([])):
737 self.__error(docstringContext.end(), 0, "D134")
738
739 def __checkNoBlankLineBefore(self, docstringContext, context):
740 """
741 Private method to check, that function/method docstrings are not
742 preceded by a blank line.
743
744 @param docstringContext docstring context (Pep257Context)
745 @param context context of the docstring (Pep257Context)
746 """
747 if docstringContext is None or self.__isScript:
748 return
749
750 contextLines = context.source()
751 cti = 0
752 while not contextLines[cti].strip().startswith(
753 ('"""', 'r"""', 'u"""')):
754 cti += 1
755 if not contextLines[cti - 1].strip():
756 self.__error(docstringContext.start(), 0, "D141")
757
758 # D142: check_blank_before_after_class
759 ##def check_blank_before_after_class(class_docstring, context, is_script):
760 ## """Class docstring should have 1 blank line around them.
761 ##
762 ## Insert a blank line before and after all docstrings (one-line or
763 ## multi-line) that document a class -- generally speaking, the class's
764 ## methods are separated from each other by a single blank line, and the
765 ## docstring needs to be offset from the first method by a blank line;
766 ## for symmetry, put a blank line between the class header and the
767 ## docstring.
768 ##
769 ## """
770 ## if not class_docstring:
771 ## return
772 ## before, after = context.split(class_docstring)[:2]
773 ## before_blanks = [not line.strip() for line in before.split('\n')]
774 ## after_blanks = [not line.strip() for line in after.split('\n')]
775 ## if before_blanks[-3:] != [False, True, True]:
776 ## return True
777 ## if not all(after_blanks) and after_blanks[:3] != [True, True, False]:
778 ## return True
779
780 # D143: check_blank_after_summary
781 ##def check_blank_after_summary(docstring, context, is_script):
782 ## """Blank line missing after one-line summary.
783 ##
784 ## Multi-line docstrings consist of a summary line just like a one-line
785 ## docstring, followed by a blank line, followed by a more elaborate
786 ## description. The summary line may be used by automatic indexing tools;
787 ## it is important that it fits on one line and is separated from the
788 ## rest of the docstring by a blank line.
789 ##
790 ## """
791 ## if not docstring:
792 ## return
793 ## lines = eval(docstring).split('\n')
794 ## if len(lines) > 1:
795 ## (summary_line, line_number) = get_summary_line_info(docstring)
796 ## if len(lines) <= (line_number+1) or lines[line_number+1].strip() != '':
797 ## return True
798
799 # D144: check_blank_after_last_paragraph
800 ##def check_blank_after_last_paragraph(docstring, context, is_script):
801 ## """Multiline docstring should end with 1 blank line.
802 ##
803 ## The BDFL recommends inserting a blank line between the last
804 ## paragraph in a multi-line docstring and its closing quotes,
805 ## placing the closing quotes on a line by themselves.
806 ##
807 ## """
808 ## if (not docstring) or len(eval(docstring).split('\n')) == 1:
809 ## return
810 ## blanks = [not line.strip() for line in eval(docstring).split('\n')]
811 ## if blanks[-3:] != [False, True, True]:
812 ## return True

eric ide

mercurial