Fix 159: check-docstring-first shall ignore attribute docstring at module top level

Now attribute docstrings no longer complain.
https://peps.python.org/pep-0257/#what-is-a-docstring
This commit is contained in:
João Eiras 2022-10-03 16:40:12 +02:00 committed by João Eiras
parent d970d5bf10
commit 009ed91552

View file

@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
import argparse import argparse
import ast
import io import io
import tokenize import tokenize
from tokenize import tokenize as tokenize_tokenize from tokenize import tokenize as tokenize_tokenize
@ -12,6 +13,14 @@ NON_CODE_TOKENS = frozenset((
)) ))
def _push_code(seen_code: io.StringIO, tok_type: int, text: str):
if tok_type == tokenize.ENCODING:
return
seen_code.write(text)
if text and not text.isspace():
seen_code.write(" ")
def check_docstring_first(src: bytes, filename: str = '<unknown>') -> int: def check_docstring_first(src: bytes, filename: str = '<unknown>') -> 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.
@ -21,12 +30,18 @@ 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
seen_code = io.StringIO()
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, _, (sline, scol), _, _ in tok_gen:
# 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:
tree = ast.parse(seen_code.getvalue())
assignments = ast.AnnAssign, ast.Assign, ast.AugAssign
if tree.body and isinstance(tree.body[-1], assignments):
return 0
print( print(
f'{filename}:{sline}: Multiple module docstrings ' f'{filename}:{sline}: Multiple module docstrings '
f'(first docstring on line {found_docstring_line}).', f'(first docstring on line {found_docstring_line}).',
@ -41,7 +56,10 @@ def check_docstring_first(src: bytes, filename: str = '<unknown>') -> int:
else: else:
found_docstring_line = sline found_docstring_line = sline
elif tok_type not in NON_CODE_TOKENS and found_code_line is None: elif tok_type not in NON_CODE_TOKENS and found_code_line is None:
_push_code(seen_code, tok_type, text)
found_code_line = sline found_code_line = sline
else:
_push_code(seen_code, tok_type, text)
return 0 return 0