17db96d56Sopenharmony_ci#!/usr/bin/env python3 27db96d56Sopenharmony_ci# -*- coding: utf-8 -*- 37db96d56Sopenharmony_ci 47db96d56Sopenharmony_ci# Check for stylistic and formal issues in .rst and .py 57db96d56Sopenharmony_ci# files included in the documentation. 67db96d56Sopenharmony_ci# 77db96d56Sopenharmony_ci# 01/2009, Georg Brandl 87db96d56Sopenharmony_ci 97db96d56Sopenharmony_ci# TODO: - wrong versions in versionadded/changed 107db96d56Sopenharmony_ci# - wrong markup after versionchanged directive 117db96d56Sopenharmony_ci 127db96d56Sopenharmony_ciimport os 137db96d56Sopenharmony_ciimport re 147db96d56Sopenharmony_ciimport sys 157db96d56Sopenharmony_ciimport getopt 167db96d56Sopenharmony_cifrom string import ascii_letters 177db96d56Sopenharmony_cifrom os.path import join, splitext, abspath, exists 187db96d56Sopenharmony_cifrom collections import defaultdict 197db96d56Sopenharmony_ci 207db96d56Sopenharmony_cidirectives = [ 217db96d56Sopenharmony_ci # standard docutils ones 227db96d56Sopenharmony_ci 'admonition', 'attention', 'caution', 'class', 'compound', 'container', 237db96d56Sopenharmony_ci 'contents', 'csv-table', 'danger', 'date', 'default-role', 'epigraph', 247db96d56Sopenharmony_ci 'error', 'figure', 'footer', 'header', 'highlights', 'hint', 'image', 257db96d56Sopenharmony_ci 'important', 'include', 'line-block', 'list-table', 'meta', 'note', 267db96d56Sopenharmony_ci 'parsed-literal', 'pull-quote', 'raw', 'replace', 277db96d56Sopenharmony_ci 'restructuredtext-test-directive', 'role', 'rubric', 'sectnum', 'sidebar', 287db96d56Sopenharmony_ci 'table', 'target-notes', 'tip', 'title', 'topic', 'unicode', 'warning', 297db96d56Sopenharmony_ci # Sphinx and Python docs custom ones 307db96d56Sopenharmony_ci 'acks', 'attribute', 'autoattribute', 'autoclass', 'autodata', 317db96d56Sopenharmony_ci 'autoexception', 'autofunction', 'automethod', 'automodule', 327db96d56Sopenharmony_ci 'availability', 'centered', 'cfunction', 'class', 'classmethod', 'cmacro', 337db96d56Sopenharmony_ci 'cmdoption', 'cmember', 'code-block', 'confval', 'cssclass', 'ctype', 347db96d56Sopenharmony_ci 'currentmodule', 'cvar', 'data', 'decorator', 'decoratormethod', 357db96d56Sopenharmony_ci 'deprecated-removed', 'deprecated(?!-removed)', 'describe', 'directive', 367db96d56Sopenharmony_ci 'doctest', 'envvar', 'event', 'exception', 'function', 'glossary', 377db96d56Sopenharmony_ci 'highlight', 'highlightlang', 'impl-detail', 'index', 'literalinclude', 387db96d56Sopenharmony_ci 'method', 'miscnews', 'module', 'moduleauthor', 'opcode', 'pdbcommand', 397db96d56Sopenharmony_ci 'productionlist', 'program', 'role', 'sectionauthor', 'seealso', 407db96d56Sopenharmony_ci 'sourcecode', 'staticmethod', 'tabularcolumns', 'testcode', 'testoutput', 417db96d56Sopenharmony_ci 'testsetup', 'toctree', 'todo', 'todolist', 'versionadded', 427db96d56Sopenharmony_ci 'versionchanged' 437db96d56Sopenharmony_ci] 447db96d56Sopenharmony_ci 457db96d56Sopenharmony_ciroles = [ 467db96d56Sopenharmony_ci "(?<!py):class:", 477db96d56Sopenharmony_ci "(?<!:c|py):func:", 487db96d56Sopenharmony_ci "(?<!py):meth:", 497db96d56Sopenharmony_ci "(?<!:py):mod:", 507db96d56Sopenharmony_ci ":exc:", 517db96d56Sopenharmony_ci ":issue:", 527db96d56Sopenharmony_ci ":attr:", 537db96d56Sopenharmony_ci ":c:func:", 547db96d56Sopenharmony_ci ":ref:", 557db96d56Sopenharmony_ci ":const:", 567db96d56Sopenharmony_ci ":term:", 577db96d56Sopenharmony_ci "(?<!:c|py):data:", 587db96d56Sopenharmony_ci ":keyword:", 597db96d56Sopenharmony_ci ":file:", 607db96d56Sopenharmony_ci ":pep:", 617db96d56Sopenharmony_ci ":c:type:", 627db96d56Sopenharmony_ci ":c:member:", 637db96d56Sopenharmony_ci ":option:", 647db96d56Sopenharmony_ci ":rfc:", 657db96d56Sopenharmony_ci ":envvar:", 667db96d56Sopenharmony_ci ":c:data:", 677db96d56Sopenharmony_ci ":source:", 687db96d56Sopenharmony_ci ":mailheader:", 697db96d56Sopenharmony_ci ":program:", 707db96d56Sopenharmony_ci ":c:macro:", 717db96d56Sopenharmony_ci ":dfn:", 727db96d56Sopenharmony_ci ":kbd:", 737db96d56Sopenharmony_ci ":command:", 747db96d56Sopenharmony_ci ":mimetype:", 757db96d56Sopenharmony_ci ":opcode:", 767db96d56Sopenharmony_ci ":manpage:", 777db96d56Sopenharmony_ci ":py:data:", 787db96d56Sopenharmony_ci ":RFC:", 797db96d56Sopenharmony_ci ":pdbcmd:", 807db96d56Sopenharmony_ci ":abbr:", 817db96d56Sopenharmony_ci ":samp:", 827db96d56Sopenharmony_ci ":token:", 837db96d56Sopenharmony_ci ":PEP:", 847db96d56Sopenharmony_ci ":sup:", 857db96d56Sopenharmony_ci ":py:class:", 867db96d56Sopenharmony_ci ":menuselection:", 877db96d56Sopenharmony_ci ":doc:", 887db96d56Sopenharmony_ci ":sub:", 897db96d56Sopenharmony_ci ":py:meth:", 907db96d56Sopenharmony_ci ":newsgroup:", 917db96d56Sopenharmony_ci ":code:", 927db96d56Sopenharmony_ci ":py:func:", 937db96d56Sopenharmony_ci ":makevar:", 947db96d56Sopenharmony_ci ":guilabel:", 957db96d56Sopenharmony_ci ":title-reference:", 967db96d56Sopenharmony_ci ":py:mod:", 977db96d56Sopenharmony_ci ":download:", 987db96d56Sopenharmony_ci ":2to3fixer:", 997db96d56Sopenharmony_ci] 1007db96d56Sopenharmony_ci 1017db96d56Sopenharmony_ciall_directives = "(" + "|".join(directives) + ")" 1027db96d56Sopenharmony_ciall_roles = "(" + "|".join(roles) + ")" 1037db96d56Sopenharmony_ci 1047db96d56Sopenharmony_ci# Find comments that looks like a directive, like: 1057db96d56Sopenharmony_ci# .. versionchanged 3.6 1067db96d56Sopenharmony_ci# or 1077db96d56Sopenharmony_ci# .. versionchanged: 3.6 1087db96d56Sopenharmony_ci# as it should be: 1097db96d56Sopenharmony_ci# .. versionchanged:: 3.6 1107db96d56Sopenharmony_ciseems_directive_re = re.compile(r"(?<!\.)\.\. %s([^a-z:]|:(?!:))" % all_directives) 1117db96d56Sopenharmony_ci 1127db96d56Sopenharmony_ci# Find directive prefixed with three dots instead of two, like: 1137db96d56Sopenharmony_ci# ... versionchanged:: 3.6 1147db96d56Sopenharmony_ci# instead of: 1157db96d56Sopenharmony_ci# .. versionchanged:: 3.6 1167db96d56Sopenharmony_cithree_dot_directive_re = re.compile(r"\.\.\. %s::" % all_directives) 1177db96d56Sopenharmony_ci 1187db96d56Sopenharmony_ci# Find role used with double backticks instead of simple backticks like: 1197db96d56Sopenharmony_ci# :const:``None`` 1207db96d56Sopenharmony_ci# instead of: 1217db96d56Sopenharmony_ci# :const:`None` 1227db96d56Sopenharmony_cidouble_backtick_role = re.compile(r"(?<!``)%s``" % all_roles) 1237db96d56Sopenharmony_ci 1247db96d56Sopenharmony_ci 1257db96d56Sopenharmony_ci# Find role used with no backticks instead of simple backticks like: 1267db96d56Sopenharmony_ci# :const:None 1277db96d56Sopenharmony_ci# instead of: 1287db96d56Sopenharmony_ci# :const:`None` 1297db96d56Sopenharmony_cirole_with_no_backticks = re.compile(r"%s[^` ]" % all_roles) 1307db96d56Sopenharmony_ci 1317db96d56Sopenharmony_ci# Find role glued with another word like: 1327db96d56Sopenharmony_ci# the:c:func:`PyThreadState_LeaveTracing` function. 1337db96d56Sopenharmony_ci# instead of: 1347db96d56Sopenharmony_ci# the :c:func:`PyThreadState_LeaveTracing` function. 1357db96d56Sopenharmony_cirole_glued_with_word = re.compile(r"[a-zA-Z]%s" % all_roles) 1367db96d56Sopenharmony_ci 1377db96d56Sopenharmony_cidefault_role_re = re.compile(r"(^| )`\w([^`]*?\w)?`($| )") 1387db96d56Sopenharmony_cileaked_markup_re = re.compile(r"[a-z]::\s|`|\.\.\s*\w+:") 1397db96d56Sopenharmony_ci 1407db96d56Sopenharmony_ci 1417db96d56Sopenharmony_cicheckers = {} 1427db96d56Sopenharmony_ci 1437db96d56Sopenharmony_cichecker_props = {'severity': 1, 'falsepositives': False} 1447db96d56Sopenharmony_ci 1457db96d56Sopenharmony_ci 1467db96d56Sopenharmony_cidef checker(*suffixes, **kwds): 1477db96d56Sopenharmony_ci """Decorator to register a function as a checker.""" 1487db96d56Sopenharmony_ci def deco(func): 1497db96d56Sopenharmony_ci for suffix in suffixes: 1507db96d56Sopenharmony_ci checkers.setdefault(suffix, []).append(func) 1517db96d56Sopenharmony_ci for prop in checker_props: 1527db96d56Sopenharmony_ci setattr(func, prop, kwds.get(prop, checker_props[prop])) 1537db96d56Sopenharmony_ci return func 1547db96d56Sopenharmony_ci return deco 1557db96d56Sopenharmony_ci 1567db96d56Sopenharmony_ci 1577db96d56Sopenharmony_ci@checker('.py', severity=4) 1587db96d56Sopenharmony_cidef check_syntax(fn, lines): 1597db96d56Sopenharmony_ci """Check Python examples for valid syntax.""" 1607db96d56Sopenharmony_ci code = ''.join(lines) 1617db96d56Sopenharmony_ci if '\r' in code: 1627db96d56Sopenharmony_ci if os.name != 'nt': 1637db96d56Sopenharmony_ci yield 0, '\\r in code file' 1647db96d56Sopenharmony_ci code = code.replace('\r', '') 1657db96d56Sopenharmony_ci try: 1667db96d56Sopenharmony_ci compile(code, fn, 'exec') 1677db96d56Sopenharmony_ci except SyntaxError as err: 1687db96d56Sopenharmony_ci yield err.lineno, 'not compilable: %s' % err 1697db96d56Sopenharmony_ci 1707db96d56Sopenharmony_ci 1717db96d56Sopenharmony_ci@checker('.rst', severity=2) 1727db96d56Sopenharmony_cidef check_suspicious_constructs(fn, lines): 1737db96d56Sopenharmony_ci """Check for suspicious reST constructs.""" 1747db96d56Sopenharmony_ci inprod = False 1757db96d56Sopenharmony_ci for lno, line in enumerate(lines, start=1): 1767db96d56Sopenharmony_ci if seems_directive_re.search(line): 1777db96d56Sopenharmony_ci yield lno, "comment seems to be intended as a directive" 1787db96d56Sopenharmony_ci if three_dot_directive_re.search(line): 1797db96d56Sopenharmony_ci yield lno, "directive should start with two dots, not three." 1807db96d56Sopenharmony_ci if double_backtick_role.search(line): 1817db96d56Sopenharmony_ci yield lno, "role use a single backtick, double backtick found." 1827db96d56Sopenharmony_ci if role_with_no_backticks.search(line): 1837db96d56Sopenharmony_ci yield lno, "role use a single backtick, no backtick found." 1847db96d56Sopenharmony_ci if role_glued_with_word.search(line): 1857db96d56Sopenharmony_ci yield lno, "missing space before role" 1867db96d56Sopenharmony_ci if ".. productionlist::" in line: 1877db96d56Sopenharmony_ci inprod = True 1887db96d56Sopenharmony_ci elif not inprod and default_role_re.search(line): 1897db96d56Sopenharmony_ci yield lno, "default role used" 1907db96d56Sopenharmony_ci elif inprod and not line.strip(): 1917db96d56Sopenharmony_ci inprod = False 1927db96d56Sopenharmony_ci 1937db96d56Sopenharmony_ci 1947db96d56Sopenharmony_ci@checker('.py', '.rst') 1957db96d56Sopenharmony_cidef check_whitespace(fn, lines): 1967db96d56Sopenharmony_ci """Check for whitespace and line length issues.""" 1977db96d56Sopenharmony_ci for lno, line in enumerate(lines): 1987db96d56Sopenharmony_ci if '\r' in line: 1997db96d56Sopenharmony_ci yield lno+1, '\\r in line' 2007db96d56Sopenharmony_ci if '\t' in line: 2017db96d56Sopenharmony_ci yield lno+1, 'OMG TABS!!!1' 2027db96d56Sopenharmony_ci if line[:-1].rstrip(' \t') != line[:-1]: 2037db96d56Sopenharmony_ci yield lno+1, 'trailing whitespace' 2047db96d56Sopenharmony_ci 2057db96d56Sopenharmony_ci 2067db96d56Sopenharmony_ci@checker('.rst', severity=0) 2077db96d56Sopenharmony_cidef check_line_length(fn, lines): 2087db96d56Sopenharmony_ci """Check for line length; this checker is not run by default.""" 2097db96d56Sopenharmony_ci for lno, line in enumerate(lines): 2107db96d56Sopenharmony_ci if len(line) > 81: 2117db96d56Sopenharmony_ci # don't complain about tables, links and function signatures 2127db96d56Sopenharmony_ci if line.lstrip()[0] not in '+|' and \ 2137db96d56Sopenharmony_ci 'http://' not in line and \ 2147db96d56Sopenharmony_ci not line.lstrip().startswith(('.. function', 2157db96d56Sopenharmony_ci '.. method', 2167db96d56Sopenharmony_ci '.. cfunction')): 2177db96d56Sopenharmony_ci yield lno+1, "line too long" 2187db96d56Sopenharmony_ci 2197db96d56Sopenharmony_ci 2207db96d56Sopenharmony_ci@checker('.html', severity=2, falsepositives=True) 2217db96d56Sopenharmony_cidef check_leaked_markup(fn, lines): 2227db96d56Sopenharmony_ci """Check HTML files for leaked reST markup; this only works if 2237db96d56Sopenharmony_ci the HTML files have been built. 2247db96d56Sopenharmony_ci """ 2257db96d56Sopenharmony_ci for lno, line in enumerate(lines): 2267db96d56Sopenharmony_ci if leaked_markup_re.search(line): 2277db96d56Sopenharmony_ci yield lno+1, 'possibly leaked markup: %r' % line 2287db96d56Sopenharmony_ci 2297db96d56Sopenharmony_ci 2307db96d56Sopenharmony_cidef hide_literal_blocks(lines): 2317db96d56Sopenharmony_ci """Tool to remove literal blocks from given lines. 2327db96d56Sopenharmony_ci 2337db96d56Sopenharmony_ci It yields empty lines in place of blocks, so line numbers are 2347db96d56Sopenharmony_ci still meaningful. 2357db96d56Sopenharmony_ci """ 2367db96d56Sopenharmony_ci in_block = False 2377db96d56Sopenharmony_ci for line in lines: 2387db96d56Sopenharmony_ci if line.endswith("::\n"): 2397db96d56Sopenharmony_ci in_block = True 2407db96d56Sopenharmony_ci elif in_block: 2417db96d56Sopenharmony_ci if line == "\n" or line.startswith(" "): 2427db96d56Sopenharmony_ci line = "\n" 2437db96d56Sopenharmony_ci else: 2447db96d56Sopenharmony_ci in_block = False 2457db96d56Sopenharmony_ci yield line 2467db96d56Sopenharmony_ci 2477db96d56Sopenharmony_ci 2487db96d56Sopenharmony_cidef type_of_explicit_markup(line): 2497db96d56Sopenharmony_ci if re.match(fr'\.\. {all_directives}::', line): 2507db96d56Sopenharmony_ci return 'directive' 2517db96d56Sopenharmony_ci if re.match(r'\.\. \[[0-9]+\] ', line): 2527db96d56Sopenharmony_ci return 'footnote' 2537db96d56Sopenharmony_ci if re.match(r'\.\. \[[^\]]+\] ', line): 2547db96d56Sopenharmony_ci return 'citation' 2557db96d56Sopenharmony_ci if re.match(r'\.\. _.*[^_]: ', line): 2567db96d56Sopenharmony_ci return 'target' 2577db96d56Sopenharmony_ci if re.match(r'\.\. \|[^\|]*\| ', line): 2587db96d56Sopenharmony_ci return 'substitution_definition' 2597db96d56Sopenharmony_ci return 'comment' 2607db96d56Sopenharmony_ci 2617db96d56Sopenharmony_ci 2627db96d56Sopenharmony_cidef hide_comments(lines): 2637db96d56Sopenharmony_ci """Tool to remove comments from given lines. 2647db96d56Sopenharmony_ci 2657db96d56Sopenharmony_ci It yields empty lines in place of comments, so line numbers are 2667db96d56Sopenharmony_ci still meaningful. 2677db96d56Sopenharmony_ci """ 2687db96d56Sopenharmony_ci in_multiline_comment = False 2697db96d56Sopenharmony_ci for line in lines: 2707db96d56Sopenharmony_ci if line == "..\n": 2717db96d56Sopenharmony_ci in_multiline_comment = True 2727db96d56Sopenharmony_ci elif in_multiline_comment: 2737db96d56Sopenharmony_ci if line == "\n" or line.startswith(" "): 2747db96d56Sopenharmony_ci line = "\n" 2757db96d56Sopenharmony_ci else: 2767db96d56Sopenharmony_ci in_multiline_comment = False 2777db96d56Sopenharmony_ci if line.startswith(".. ") and type_of_explicit_markup(line) == 'comment': 2787db96d56Sopenharmony_ci line = "\n" 2797db96d56Sopenharmony_ci yield line 2807db96d56Sopenharmony_ci 2817db96d56Sopenharmony_ci 2827db96d56Sopenharmony_ci 2837db96d56Sopenharmony_ci@checker(".rst", severity=2) 2847db96d56Sopenharmony_cidef check_missing_surrogate_space_on_plural(fn, lines): 2857db96d56Sopenharmony_ci r"""Check for missing 'backslash-space' between a code sample a letter. 2867db96d56Sopenharmony_ci 2877db96d56Sopenharmony_ci Good: ``Point``\ s 2887db96d56Sopenharmony_ci Bad: ``Point``s 2897db96d56Sopenharmony_ci """ 2907db96d56Sopenharmony_ci in_code_sample = False 2917db96d56Sopenharmony_ci check_next_one = False 2927db96d56Sopenharmony_ci for lno, line in enumerate(hide_comments(hide_literal_blocks(lines))): 2937db96d56Sopenharmony_ci tokens = line.split("``") 2947db96d56Sopenharmony_ci for token_no, token in enumerate(tokens): 2957db96d56Sopenharmony_ci if check_next_one: 2967db96d56Sopenharmony_ci if token[0] in ascii_letters: 2977db96d56Sopenharmony_ci yield lno + 1, f"Missing backslash-space between code sample and {token!r}." 2987db96d56Sopenharmony_ci check_next_one = False 2997db96d56Sopenharmony_ci if token_no == len(tokens) - 1: 3007db96d56Sopenharmony_ci continue 3017db96d56Sopenharmony_ci if in_code_sample: 3027db96d56Sopenharmony_ci check_next_one = True 3037db96d56Sopenharmony_ci in_code_sample = not in_code_sample 3047db96d56Sopenharmony_ci 3057db96d56Sopenharmony_cidef main(argv): 3067db96d56Sopenharmony_ci usage = '''\ 3077db96d56Sopenharmony_ciUsage: %s [-v] [-f] [-s sev] [-i path]* [path] 3087db96d56Sopenharmony_ci 3097db96d56Sopenharmony_ciOptions: -v verbose (print all checked file names) 3107db96d56Sopenharmony_ci -f enable checkers that yield many false positives 3117db96d56Sopenharmony_ci -s sev only show problems with severity >= sev 3127db96d56Sopenharmony_ci -i path ignore subdir or file path 3137db96d56Sopenharmony_ci''' % argv[0] 3147db96d56Sopenharmony_ci try: 3157db96d56Sopenharmony_ci gopts, args = getopt.getopt(argv[1:], 'vfs:i:') 3167db96d56Sopenharmony_ci except getopt.GetoptError: 3177db96d56Sopenharmony_ci print(usage) 3187db96d56Sopenharmony_ci return 2 3197db96d56Sopenharmony_ci 3207db96d56Sopenharmony_ci verbose = False 3217db96d56Sopenharmony_ci severity = 1 3227db96d56Sopenharmony_ci ignore = [] 3237db96d56Sopenharmony_ci falsepos = False 3247db96d56Sopenharmony_ci for opt, val in gopts: 3257db96d56Sopenharmony_ci if opt == '-v': 3267db96d56Sopenharmony_ci verbose = True 3277db96d56Sopenharmony_ci elif opt == '-f': 3287db96d56Sopenharmony_ci falsepos = True 3297db96d56Sopenharmony_ci elif opt == '-s': 3307db96d56Sopenharmony_ci severity = int(val) 3317db96d56Sopenharmony_ci elif opt == '-i': 3327db96d56Sopenharmony_ci ignore.append(abspath(val)) 3337db96d56Sopenharmony_ci 3347db96d56Sopenharmony_ci if len(args) == 0: 3357db96d56Sopenharmony_ci path = '.' 3367db96d56Sopenharmony_ci elif len(args) == 1: 3377db96d56Sopenharmony_ci path = args[0] 3387db96d56Sopenharmony_ci else: 3397db96d56Sopenharmony_ci print(usage) 3407db96d56Sopenharmony_ci return 2 3417db96d56Sopenharmony_ci 3427db96d56Sopenharmony_ci if not exists(path): 3437db96d56Sopenharmony_ci print('Error: path %s does not exist' % path) 3447db96d56Sopenharmony_ci return 2 3457db96d56Sopenharmony_ci 3467db96d56Sopenharmony_ci count = defaultdict(int) 3477db96d56Sopenharmony_ci 3487db96d56Sopenharmony_ci print("""⚠ rstlint.py is no longer maintained here and will be removed 3497db96d56Sopenharmony_ci⚠ in a future release. 3507db96d56Sopenharmony_ci⚠ Please use https://pypi.org/p/sphinx-lint instead. 3517db96d56Sopenharmony_ci""") 3527db96d56Sopenharmony_ci 3537db96d56Sopenharmony_ci for root, dirs, files in os.walk(path): 3547db96d56Sopenharmony_ci # ignore subdirs in ignore list 3557db96d56Sopenharmony_ci if abspath(root) in ignore: 3567db96d56Sopenharmony_ci del dirs[:] 3577db96d56Sopenharmony_ci continue 3587db96d56Sopenharmony_ci 3597db96d56Sopenharmony_ci for fn in files: 3607db96d56Sopenharmony_ci fn = join(root, fn) 3617db96d56Sopenharmony_ci if fn[:2] == './': 3627db96d56Sopenharmony_ci fn = fn[2:] 3637db96d56Sopenharmony_ci 3647db96d56Sopenharmony_ci # ignore files in ignore list 3657db96d56Sopenharmony_ci if abspath(fn) in ignore: 3667db96d56Sopenharmony_ci continue 3677db96d56Sopenharmony_ci 3687db96d56Sopenharmony_ci ext = splitext(fn)[1] 3697db96d56Sopenharmony_ci checkerlist = checkers.get(ext, None) 3707db96d56Sopenharmony_ci if not checkerlist: 3717db96d56Sopenharmony_ci continue 3727db96d56Sopenharmony_ci 3737db96d56Sopenharmony_ci if verbose: 3747db96d56Sopenharmony_ci print('Checking %s...' % fn) 3757db96d56Sopenharmony_ci 3767db96d56Sopenharmony_ci try: 3777db96d56Sopenharmony_ci with open(fn, 'r', encoding='utf-8') as f: 3787db96d56Sopenharmony_ci lines = list(f) 3797db96d56Sopenharmony_ci except (IOError, OSError) as err: 3807db96d56Sopenharmony_ci print('%s: cannot open: %s' % (fn, err)) 3817db96d56Sopenharmony_ci count[4] += 1 3827db96d56Sopenharmony_ci continue 3837db96d56Sopenharmony_ci 3847db96d56Sopenharmony_ci for checker in checkerlist: 3857db96d56Sopenharmony_ci if checker.falsepositives and not falsepos: 3867db96d56Sopenharmony_ci continue 3877db96d56Sopenharmony_ci csev = checker.severity 3887db96d56Sopenharmony_ci if csev >= severity: 3897db96d56Sopenharmony_ci for lno, msg in checker(fn, lines): 3907db96d56Sopenharmony_ci print('[%d] %s:%d: %s' % (csev, fn, lno, msg)) 3917db96d56Sopenharmony_ci count[csev] += 1 3927db96d56Sopenharmony_ci if verbose: 3937db96d56Sopenharmony_ci print() 3947db96d56Sopenharmony_ci if not count: 3957db96d56Sopenharmony_ci if severity > 1: 3967db96d56Sopenharmony_ci print('No problems with severity >= %d found.' % severity) 3977db96d56Sopenharmony_ci else: 3987db96d56Sopenharmony_ci print('No problems found.') 3997db96d56Sopenharmony_ci else: 4007db96d56Sopenharmony_ci for severity in sorted(count): 4017db96d56Sopenharmony_ci number = count[severity] 4027db96d56Sopenharmony_ci print('%d problem%s with severity %d found.' % 4037db96d56Sopenharmony_ci (number, number > 1 and 's' or '', severity)) 4047db96d56Sopenharmony_ci return int(bool(count)) 4057db96d56Sopenharmony_ci 4067db96d56Sopenharmony_ci 4077db96d56Sopenharmony_ciif __name__ == '__main__': 4087db96d56Sopenharmony_ci sys.exit(main(sys.argv)) 409