mirror of
https://github.com/pre-commit/pre-commit-hooks.git
synced 2026-04-06 20:16:53 +00:00
Support attribute docstrings
This commit is contained in:
parent
0e509ddcbc
commit
4a101bbf99
2 changed files with 20 additions and 5 deletions
|
|
@ -11,7 +11,7 @@ NON_CODE_TOKENS = frozenset((
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
def check_docstring_first(src: bytes, filename: str = '<unknown>') -> int:
|
def check_docstring_first(src: bytes, filename: str) -> int:
|
||||||
"""Returns nonzero if the source has what looks like a docstring that is
|
"""Returns nonzero if the source has what looks like a docstring that is
|
||||||
not at the beginning of the source.
|
not at the beginning of the source.
|
||||||
|
|
||||||
|
|
@ -20,9 +20,14 @@ def check_docstring_first(src: bytes, filename: str = '<unknown>') -> int:
|
||||||
"""
|
"""
|
||||||
found_docstring_line = None
|
found_docstring_line = None
|
||||||
found_code_line = None
|
found_code_line = None
|
||||||
|
assignment_lines = set()
|
||||||
|
|
||||||
tok_gen = tokenize_tokenize(io.BytesIO(src).readline)
|
tok_gen = tokenize_tokenize(io.BytesIO(src).readline)
|
||||||
for tok_type, _, (sline, scol), _, _ in tok_gen:
|
for tok_type, string, (sline, scol), _, _ in tok_gen:
|
||||||
|
# Save all lines with top-level attribute assignments
|
||||||
|
if scol == 2 and tok_type == tokenize.OP and string == '=':
|
||||||
|
assignment_lines.add(sline)
|
||||||
|
|
||||||
# Looks like a docstring!
|
# Looks like a docstring!
|
||||||
if tok_type == tokenize.STRING and scol == 0:
|
if tok_type == tokenize.STRING and scol == 0:
|
||||||
if found_docstring_line is not None:
|
if found_docstring_line is not None:
|
||||||
|
|
@ -31,7 +36,10 @@ def check_docstring_first(src: bytes, filename: str = '<unknown>') -> int:
|
||||||
f'(first docstring on line {found_docstring_line}).',
|
f'(first docstring on line {found_docstring_line}).',
|
||||||
)
|
)
|
||||||
return 1
|
return 1
|
||||||
elif found_code_line is not None:
|
elif (
|
||||||
|
found_code_line is not None and
|
||||||
|
sline > 0 and sline - 1 not in assignment_lines
|
||||||
|
):
|
||||||
print(
|
print(
|
||||||
f'{filename}:{sline} Module docstring appears after code '
|
f'{filename}:{sline} Module docstring appears after code '
|
||||||
f'(code seen on line {found_code_line}).',
|
f'(code seen on line {found_code_line}).',
|
||||||
|
|
@ -55,6 +63,6 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
||||||
for filename in args.filenames:
|
for filename in args.filenames:
|
||||||
with open(filename, 'rb') as f:
|
with open(filename, 'rb') as f:
|
||||||
contents = f.read()
|
contents = f.read()
|
||||||
retv |= check_docstring_first(contents, filename=filename)
|
retv |= check_docstring_first(contents, filename)
|
||||||
|
|
||||||
return retv
|
return retv
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,13 @@ TESTS = (
|
||||||
),
|
),
|
||||||
# String literals in expressions are ok.
|
# String literals in expressions are ok.
|
||||||
(b'x = "foo"\n', 0, ''),
|
(b'x = "foo"\n', 0, ''),
|
||||||
|
# Attribute docstrings are ok.
|
||||||
|
(
|
||||||
|
b'x = "foo"\n'
|
||||||
|
b'"""x holds the foo"""',
|
||||||
|
0,
|
||||||
|
'',
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -48,7 +55,7 @@ all_tests = pytest.mark.parametrize(
|
||||||
|
|
||||||
@all_tests
|
@all_tests
|
||||||
def test_unit(capsys, contents, expected, expected_out):
|
def test_unit(capsys, contents, expected, expected_out):
|
||||||
assert check_docstring_first(contents) == expected
|
assert check_docstring_first(contents, '<unknown>') == expected
|
||||||
assert capsys.readouterr()[0] == expected_out.format(filename='<unknown>')
|
assert capsys.readouterr()[0] == expected_out.format(filename='<unknown>')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue