159aec869SMax Reitz#!/usr/bin/env python3 29dd003a9SVladimir Sementsov-Ogievskiy# group: meta 319b7868eSKevin Wolf# 419b7868eSKevin Wolf# Copyright (C) 2020 Red Hat, Inc. 519b7868eSKevin Wolf# 619b7868eSKevin Wolf# This program is free software; you can redistribute it and/or modify 719b7868eSKevin Wolf# it under the terms of the GNU General Public License as published by 819b7868eSKevin Wolf# the Free Software Foundation; either version 2 of the License, or 919b7868eSKevin Wolf# (at your option) any later version. 1019b7868eSKevin Wolf# 1119b7868eSKevin Wolf# This program is distributed in the hope that it will be useful, 1219b7868eSKevin Wolf# but WITHOUT ANY WARRANTY; without even the implied warranty of 1319b7868eSKevin Wolf# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1419b7868eSKevin Wolf# GNU General Public License for more details. 1519b7868eSKevin Wolf# 1619b7868eSKevin Wolf# You should have received a copy of the GNU General Public License 1719b7868eSKevin Wolf# along with this program. If not, see <http://www.gnu.org/licenses/>. 1819b7868eSKevin Wolf 1959aec869SMax Reitzimport os 2059aec869SMax Reitzimport re 2159aec869SMax Reitzimport subprocess 2259aec869SMax Reitzimport sys 232d804f55SJohn Snowfrom typing import List, Mapping, Optional 2419b7868eSKevin Wolf 2559aec869SMax Reitzimport iotests 2619b7868eSKevin Wolf 2719b7868eSKevin Wolf 2859aec869SMax Reitz# TODO: Empty this list! 2959aec869SMax ReitzSKIP_FILES = ( 3059aec869SMax Reitz '030', '040', '041', '044', '045', '055', '056', '057', '065', '093', 31636aa64dSMax Reitz '096', '118', '124', '132', '136', '139', '147', '148', '149', 3226db7b23SHanna Reitz '151', '152', '155', '163', '165', '194', '196', '202', 3359aec869SMax Reitz '203', '205', '206', '207', '208', '210', '211', '212', '213', '216', 34f08ef043SVladimir Sementsov-Ogievskiy '218', '219', '224', '228', '234', '235', '236', '237', '238', 3559aec869SMax Reitz '240', '242', '245', '246', '248', '255', '256', '257', '258', '260', 3659aec869SMax Reitz '262', '264', '266', '274', '277', '280', '281', '295', '296', '298', 3759c9466dSMax Reitz '299', '302', '303', '304', '307', 3859aec869SMax Reitz 'nbd-fault-injector.py', 'qcow2.py', 'qcow2_format.py', 'qed.py' 3959aec869SMax Reitz) 4019b7868eSKevin Wolf 4119b7868eSKevin Wolf 4259aec869SMax Reitzdef is_python_file(filename): 4359aec869SMax Reitz if not os.path.isfile(filename): 4459aec869SMax Reitz return False 4519b7868eSKevin Wolf 4659aec869SMax Reitz if filename.endswith('.py'): 4759aec869SMax Reitz return True 4859aec869SMax Reitz 4981dcb9caSHanna Reitz with open(filename, encoding='utf-8') as f: 5059aec869SMax Reitz try: 5159aec869SMax Reitz first_line = f.readline() 5259aec869SMax Reitz return re.match('^#!.*python', first_line) is not None 5359aec869SMax Reitz except UnicodeDecodeError: # Ignore binary files 5459aec869SMax Reitz return False 5559aec869SMax Reitz 5659aec869SMax Reitz 573c1d5012SJohn Snowdef get_test_files() -> List[str]: 58098d983eSHanna Reitz named_tests = [f'tests/{entry}' for entry in os.listdir('tests')] 59098d983eSHanna Reitz check_tests = set(os.listdir('.') + named_tests) - set(SKIP_FILES) 603c1d5012SJohn Snow return list(filter(is_python_file, check_tests)) 613c1d5012SJohn Snow 623c1d5012SJohn Snow 63a4bde736SJohn Snowdef run_linter( 64a4bde736SJohn Snow tool: str, 65a4bde736SJohn Snow args: List[str], 662d804f55SJohn Snow env: Optional[Mapping[str, str]] = None, 67a4bde736SJohn Snow suppress_output: bool = False, 682d804f55SJohn Snow) -> None: 69a4bde736SJohn Snow """ 70a4bde736SJohn Snow Run a python-based linting tool. 7159aec869SMax Reitz 72752f425dSJohn Snow :param suppress_output: If True, suppress all stdout/stderr output. 73752f425dSJohn Snow :raise CalledProcessError: If the linter process exits with failure. 74a4bde736SJohn Snow """ 75752f425dSJohn Snow subprocess.run( 76a4bde736SJohn Snow ('python3', '-m', tool, *args), 7759aec869SMax Reitz env=env, 78752f425dSJohn Snow check=True, 79a4bde736SJohn Snow stdout=subprocess.PIPE if suppress_output else None, 80a4bde736SJohn Snow stderr=subprocess.STDOUT if suppress_output else None, 81a4bde736SJohn Snow universal_newlines=True, 82a4bde736SJohn Snow ) 8359aec869SMax Reitz 8459aec869SMax Reitz 85447aebdaSJohn Snowdef main() -> None: 86*7a90bcc2SJohn Snow for linter in ('pylint', 'mypy'): 87*7a90bcc2SJohn Snow try: 88*7a90bcc2SJohn Snow run_linter(linter, ['--version'], suppress_output=True) 89*7a90bcc2SJohn Snow except subprocess.CalledProcessError: 90*7a90bcc2SJohn Snow iotests.notrun(f"'{linter}' not found") 9159aec869SMax Reitz 922d804f55SJohn Snow files = get_test_files() 932d804f55SJohn Snow 942d804f55SJohn Snow iotests.logger.debug('Files to be checked:') 952d804f55SJohn Snow iotests.logger.debug(', '.join(sorted(files))) 962d804f55SJohn Snow 972d804f55SJohn Snow env = os.environ.copy() 982d804f55SJohn Snow env['MYPYPATH'] = env['PYTHONPATH'] 992d804f55SJohn Snow 1002d804f55SJohn Snow print('=== pylint ===') 1012d804f55SJohn Snow sys.stdout.flush() 102752f425dSJohn Snow try: 103a4bde736SJohn Snow run_linter('pylint', files, env=env) 104752f425dSJohn Snow except subprocess.CalledProcessError: 105752f425dSJohn Snow # pylint failure will be caught by diffing the IO. 106752f425dSJohn Snow pass 1072d804f55SJohn Snow 1082d804f55SJohn Snow print('=== mypy ===') 1092d804f55SJohn Snow sys.stdout.flush() 110752f425dSJohn Snow try: 111a4bde736SJohn Snow run_linter('mypy', files, env=env, suppress_output=True) 112752f425dSJohn Snow except subprocess.CalledProcessError as exc: 113752f425dSJohn Snow if exc.output: 114752f425dSJohn Snow print(exc.output) 115447aebdaSJohn Snow 116447aebdaSJohn Snow 117447aebdaSJohn Snowiotests.script_main(main) 118