1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
|
import math
def check(app, what, name, obj, options, lines):
ctx = DocstringCheckContext(app, what, name, obj, options, lines)
if what in ('function', 'method'):
_docstring_check_returns_indent(ctx)
class DocstringCheckContext(object):
def __init__(self, app, what, name, obj, options, lines):
self.app = app
self.what = what
self.name = name
self.obj = obj
self.options = options
self.lines = lines
self.iline = 0
def nextline(self):
if self.iline >= len(self.lines):
raise StopIteration
line = self.lines[self.iline]
self.iline += 1
return line
def error(self, msg, include_line=True, include_source=True):
lines = self.lines
iline = self.iline - 1
msg = ('{}\n\n'
'on {}'.format(msg, self.name))
if include_line and 0 <= iline < len(lines):
line = lines[iline]
msg += '\n' + 'at line {}: "{}"\n'.format(iline, line)
if include_source:
msg += '\n'
msg += 'docstring:\n'
digits = int(math.floor(math.log10(len(lines)))) + 1
linum_fmt = '{{:0{}d}} '.format(digits)
for i, line in enumerate(lines):
msg += linum_fmt.format(i) + line + '\n'
raise InvalidDocstringError(msg, self, iline)
class InvalidDocstringError(Exception):
def __init__(self, msg, ctx, iline):
super(InvalidDocstringError, self).__init__(self, msg)
self.msg = msg
self.ctx = ctx
self.iline = iline
def __str__(self):
return self.msg
def _docstring_check_returns_indent(ctx):
# Seek the :returns: header
try:
line = ctx.nextline()
while line != ':returns:':
line = ctx.nextline()
except StopIteration:
return # No `Returns` section
# Skip empty lines and seek the first line of the content
try:
line = ctx.nextline()
while not line:
line = ctx.nextline()
except StopIteration:
ctx.error('`Returns` section has no content')
# Find the indentation of the first line
# (note: line should have at least one non-space character)
nindent = next(i for i, c in enumerate(line) if c != ' ')
# Check the indentation of the following lines
try:
line = ctx.nextline()
while line.startswith(' '):
if (not line.startswith(' ' * nindent) or
line[nindent:].startswith(' ')):
ctx.error('Invalid indentation of `Returns` section')
line = ctx.nextline()
except StopIteration:
pass
|