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 shutil 2259aec869SMax Reitzimport subprocess 2359aec869SMax Reitzimport sys 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', 3259aec869SMax Reitz '151', '152', '155', '163', '165', '169', '194', '196', '199', '202', 3359aec869SMax Reitz '203', '205', '206', '207', '208', '210', '211', '212', '213', '216', 34*f08ef043SVladimir 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 4959aec869SMax Reitz with open(filename) 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 5759aec869SMax Reitzdef run_linters(): 5859aec869SMax Reitz files = [filename for filename in (set(os.listdir('.')) - set(SKIP_FILES)) 5959aec869SMax Reitz if is_python_file(filename)] 6059aec869SMax Reitz 6159aec869SMax Reitz iotests.logger.debug('Files to be checked:') 6259aec869SMax Reitz iotests.logger.debug(', '.join(sorted(files))) 6359aec869SMax Reitz 6459aec869SMax Reitz print('=== pylint ===') 6559aec869SMax Reitz sys.stdout.flush() 6659aec869SMax Reitz 6759aec869SMax Reitz # Todo notes are fine, but fixme's or xxx's should probably just be 6859aec869SMax Reitz # fixed (in tests, at least) 6959aec869SMax Reitz env = os.environ.copy() 7059aec869SMax Reitz qemu_module_path = os.path.join(os.path.dirname(__file__), 7159aec869SMax Reitz '..', '..', 'python') 7259aec869SMax Reitz try: 7359aec869SMax Reitz env['PYTHONPATH'] += os.pathsep + qemu_module_path 7459aec869SMax Reitz except KeyError: 7559aec869SMax Reitz env['PYTHONPATH'] = qemu_module_path 7659aec869SMax Reitz subprocess.run(('pylint-3', '--score=n', '--notes=FIXME,XXX', *files), 7759aec869SMax Reitz env=env, check=False) 7859aec869SMax Reitz 7959aec869SMax Reitz print('=== mypy ===') 8059aec869SMax Reitz sys.stdout.flush() 8159aec869SMax Reitz 8259aec869SMax Reitz # We have to call mypy separately for each file. Otherwise, it 8359aec869SMax Reitz # will interpret all given files as belonging together (i.e., they 8459aec869SMax Reitz # may not both define the same classes, etc.; most notably, they 8559aec869SMax Reitz # must not both define the __main__ module). 8659aec869SMax Reitz env['MYPYPATH'] = env['PYTHONPATH'] 8759aec869SMax Reitz for filename in files: 8859aec869SMax Reitz p = subprocess.run(('mypy', 8959aec869SMax Reitz '--warn-unused-configs', 9059aec869SMax Reitz '--disallow-subclassing-any', 9159aec869SMax Reitz '--disallow-any-generics', 9259aec869SMax Reitz '--disallow-incomplete-defs', 9359aec869SMax Reitz '--disallow-untyped-decorators', 9459aec869SMax Reitz '--no-implicit-optional', 9559aec869SMax Reitz '--warn-redundant-casts', 9659aec869SMax Reitz '--warn-unused-ignores', 9759aec869SMax Reitz '--no-implicit-reexport', 987f0a143bSJohn Snow '--namespace-packages', 9959aec869SMax Reitz filename), 10059aec869SMax Reitz env=env, 10159aec869SMax Reitz check=False, 10259aec869SMax Reitz stdout=subprocess.PIPE, 10359aec869SMax Reitz stderr=subprocess.STDOUT, 10459aec869SMax Reitz universal_newlines=True) 10559aec869SMax Reitz 10659aec869SMax Reitz if p.returncode != 0: 10759aec869SMax Reitz print(p.stdout) 10859aec869SMax Reitz 10959aec869SMax Reitz 11059aec869SMax Reitzfor linter in ('pylint-3', 'mypy'): 11159aec869SMax Reitz if shutil.which(linter) is None: 11259aec869SMax Reitz iotests.notrun(f'{linter} not found') 11359aec869SMax Reitz 11459aec869SMax Reitziotests.script_main(run_linters) 115