xref: /openbmc/qemu/tests/qemu-iotests/linters.py (revision c624cb32)
1c293ba55SJohn Snow# Copyright (C) 2020 Red Hat, Inc.
2c293ba55SJohn Snow#
3c293ba55SJohn Snow# This program is free software; you can redistribute it and/or modify
4c293ba55SJohn Snow# it under the terms of the GNU General Public License as published by
5c293ba55SJohn Snow# the Free Software Foundation; either version 2 of the License, or
6c293ba55SJohn Snow# (at your option) any later version.
7c293ba55SJohn Snow#
8c293ba55SJohn Snow# This program is distributed in the hope that it will be useful,
9c293ba55SJohn Snow# but WITHOUT ANY WARRANTY; without even the implied warranty of
10c293ba55SJohn Snow# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11c293ba55SJohn Snow# GNU General Public License for more details.
12c293ba55SJohn Snow#
13c293ba55SJohn Snow# You should have received a copy of the GNU General Public License
14c293ba55SJohn Snow# along with this program.  If not, see <http://www.gnu.org/licenses/>.
15c293ba55SJohn Snow
16c293ba55SJohn Snowimport os
17c293ba55SJohn Snowimport re
18c293ba55SJohn Snowimport subprocess
19a4294435SJohn Snowimport sys
20c293ba55SJohn Snowfrom typing import List, Mapping, Optional
21c293ba55SJohn Snow
22c293ba55SJohn Snow
23c293ba55SJohn Snow# TODO: Empty this list!
24c293ba55SJohn SnowSKIP_FILES = (
25c293ba55SJohn Snow    '030', '040', '041', '044', '045', '055', '056', '057', '065', '093',
26c293ba55SJohn Snow    '096', '118', '124', '132', '136', '139', '147', '148', '149',
27c293ba55SJohn Snow    '151', '152', '155', '163', '165', '194', '196', '202',
28c293ba55SJohn Snow    '203', '205', '206', '207', '208', '210', '211', '212', '213', '216',
29c293ba55SJohn Snow    '218', '219', '224', '228', '234', '235', '236', '237', '238',
30c293ba55SJohn Snow    '240', '242', '245', '246', '248', '255', '256', '257', '258', '260',
31c293ba55SJohn Snow    '262', '264', '266', '274', '277', '280', '281', '295', '296', '298',
32c293ba55SJohn Snow    '299', '302', '303', '304', '307',
33c293ba55SJohn Snow    'nbd-fault-injector.py', 'qcow2.py', 'qcow2_format.py', 'qed.py'
34c293ba55SJohn Snow)
35c293ba55SJohn Snow
36c293ba55SJohn Snow
37c293ba55SJohn Snowdef is_python_file(filename):
38c293ba55SJohn Snow    if not os.path.isfile(filename):
39c293ba55SJohn Snow        return False
40c293ba55SJohn Snow
41c293ba55SJohn Snow    if filename.endswith('.py'):
42c293ba55SJohn Snow        return True
43c293ba55SJohn Snow
44c293ba55SJohn Snow    with open(filename, encoding='utf-8') as f:
45c293ba55SJohn Snow        try:
46c293ba55SJohn Snow            first_line = f.readline()
47c293ba55SJohn Snow            return re.match('^#!.*python', first_line) is not None
48c293ba55SJohn Snow        except UnicodeDecodeError:  # Ignore binary files
49c293ba55SJohn Snow            return False
50c293ba55SJohn Snow
51c293ba55SJohn Snow
52c293ba55SJohn Snowdef get_test_files() -> List[str]:
53c293ba55SJohn Snow    named_tests = [f'tests/{entry}' for entry in os.listdir('tests')]
54c293ba55SJohn Snow    check_tests = set(os.listdir('.') + named_tests) - set(SKIP_FILES)
55c293ba55SJohn Snow    return list(filter(is_python_file, check_tests))
56c293ba55SJohn Snow
57c293ba55SJohn Snow
58c293ba55SJohn Snowdef run_linter(
59c293ba55SJohn Snow        tool: str,
60c293ba55SJohn Snow        args: List[str],
61c293ba55SJohn Snow        env: Optional[Mapping[str, str]] = None,
62c293ba55SJohn Snow        suppress_output: bool = False,
63c293ba55SJohn Snow) -> None:
64c293ba55SJohn Snow    """
65c293ba55SJohn Snow    Run a python-based linting tool.
66c293ba55SJohn Snow
67c293ba55SJohn Snow    :param suppress_output: If True, suppress all stdout/stderr output.
68c293ba55SJohn Snow    :raise CalledProcessError: If the linter process exits with failure.
69c293ba55SJohn Snow    """
70c293ba55SJohn Snow    subprocess.run(
71*c624cb32SJohn Snow        (sys.executable, '-m', tool, *args),
72c293ba55SJohn Snow        env=env,
73c293ba55SJohn Snow        check=True,
74c293ba55SJohn Snow        stdout=subprocess.PIPE if suppress_output else None,
75c293ba55SJohn Snow        stderr=subprocess.STDOUT if suppress_output else None,
76c293ba55SJohn Snow        universal_newlines=True,
77c293ba55SJohn Snow    )
78a4294435SJohn Snow
79a4294435SJohn Snow
80a4294435SJohn Snowdef main() -> None:
81a4294435SJohn Snow    """
82a4294435SJohn Snow    Used by the Python CI system as an entry point to run these linters.
83a4294435SJohn Snow    """
84a4294435SJohn Snow    def show_usage() -> None:
85a4294435SJohn Snow        print(f"Usage: {sys.argv[0]} < --mypy | --pylint >", file=sys.stderr)
86a4294435SJohn Snow        sys.exit(1)
87a4294435SJohn Snow
88a4294435SJohn Snow    if len(sys.argv) != 2:
89a4294435SJohn Snow        show_usage()
90a4294435SJohn Snow
91a4294435SJohn Snow    files = get_test_files()
92a4294435SJohn Snow
93a4294435SJohn Snow    if sys.argv[1] == '--pylint':
94a4294435SJohn Snow        run_linter('pylint', files)
95a4294435SJohn Snow    elif sys.argv[1] == '--mypy':
96558dbe99SJohn Snow        # mypy bug #9852; disable incremental checking as a workaround.
97558dbe99SJohn Snow        args = ['--no-incremental'] + files
98558dbe99SJohn Snow        run_linter('mypy', args)
99a4294435SJohn Snow    else:
100a4294435SJohn Snow        print(f"Unrecognized argument: '{sys.argv[1]}'", file=sys.stderr)
101a4294435SJohn Snow        show_usage()
102a4294435SJohn Snow
103a4294435SJohn Snow
104a4294435SJohn Snowif __name__ == '__main__':
105a4294435SJohn Snow    main()
106