1*59aec869SMax 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 19*59aec869SMax Reitzimport os 20*59aec869SMax Reitzimport re 21*59aec869SMax Reitzimport shutil 22*59aec869SMax Reitzimport subprocess 23*59aec869SMax Reitzimport sys 2419b7868eSKevin Wolf 25*59aec869SMax Reitzimport iotests 2619b7868eSKevin Wolf 2719b7868eSKevin Wolf 28*59aec869SMax Reitz# TODO: Empty this list! 29*59aec869SMax ReitzSKIP_FILES = ( 30*59aec869SMax Reitz '030', '040', '041', '044', '045', '055', '056', '057', '065', '093', 31*59aec869SMax Reitz '096', '118', '124', '129', '132', '136', '139', '147', '148', '149', 32*59aec869SMax Reitz '151', '152', '155', '163', '165', '169', '194', '196', '199', '202', 33*59aec869SMax Reitz '203', '205', '206', '207', '208', '210', '211', '212', '213', '216', 34*59aec869SMax Reitz '218', '219', '222', '224', '228', '234', '235', '236', '237', '238', 35*59aec869SMax Reitz '240', '242', '245', '246', '248', '255', '256', '257', '258', '260', 36*59aec869SMax Reitz '262', '264', '266', '274', '277', '280', '281', '295', '296', '298', 37*59aec869SMax Reitz '299', '300', '302', '303', '304', '307', 38*59aec869SMax Reitz 'nbd-fault-injector.py', 'qcow2.py', 'qcow2_format.py', 'qed.py' 39*59aec869SMax Reitz) 4019b7868eSKevin Wolf 4119b7868eSKevin Wolf 42*59aec869SMax Reitzdef is_python_file(filename): 43*59aec869SMax Reitz if not os.path.isfile(filename): 44*59aec869SMax Reitz return False 4519b7868eSKevin Wolf 46*59aec869SMax Reitz if filename.endswith('.py'): 47*59aec869SMax Reitz return True 48*59aec869SMax Reitz 49*59aec869SMax Reitz with open(filename) as f: 50*59aec869SMax Reitz try: 51*59aec869SMax Reitz first_line = f.readline() 52*59aec869SMax Reitz return re.match('^#!.*python', first_line) is not None 53*59aec869SMax Reitz except UnicodeDecodeError: # Ignore binary files 54*59aec869SMax Reitz return False 55*59aec869SMax Reitz 56*59aec869SMax Reitz 57*59aec869SMax Reitzdef run_linters(): 58*59aec869SMax Reitz files = [filename for filename in (set(os.listdir('.')) - set(SKIP_FILES)) 59*59aec869SMax Reitz if is_python_file(filename)] 60*59aec869SMax Reitz 61*59aec869SMax Reitz iotests.logger.debug('Files to be checked:') 62*59aec869SMax Reitz iotests.logger.debug(', '.join(sorted(files))) 63*59aec869SMax Reitz 64*59aec869SMax Reitz print('=== pylint ===') 65*59aec869SMax Reitz sys.stdout.flush() 66*59aec869SMax Reitz 67*59aec869SMax Reitz # Todo notes are fine, but fixme's or xxx's should probably just be 68*59aec869SMax Reitz # fixed (in tests, at least) 69*59aec869SMax Reitz env = os.environ.copy() 70*59aec869SMax Reitz qemu_module_path = os.path.join(os.path.dirname(__file__), 71*59aec869SMax Reitz '..', '..', 'python') 72*59aec869SMax Reitz try: 73*59aec869SMax Reitz env['PYTHONPATH'] += os.pathsep + qemu_module_path 74*59aec869SMax Reitz except KeyError: 75*59aec869SMax Reitz env['PYTHONPATH'] = qemu_module_path 76*59aec869SMax Reitz subprocess.run(('pylint-3', '--score=n', '--notes=FIXME,XXX', *files), 77*59aec869SMax Reitz env=env, check=False) 78*59aec869SMax Reitz 79*59aec869SMax Reitz print('=== mypy ===') 80*59aec869SMax Reitz sys.stdout.flush() 81*59aec869SMax Reitz 82*59aec869SMax Reitz # We have to call mypy separately for each file. Otherwise, it 83*59aec869SMax Reitz # will interpret all given files as belonging together (i.e., they 84*59aec869SMax Reitz # may not both define the same classes, etc.; most notably, they 85*59aec869SMax Reitz # must not both define the __main__ module). 86*59aec869SMax Reitz env['MYPYPATH'] = env['PYTHONPATH'] 87*59aec869SMax Reitz for filename in files: 88*59aec869SMax Reitz p = subprocess.run(('mypy', 89*59aec869SMax Reitz '--warn-unused-configs', 90*59aec869SMax Reitz '--disallow-subclassing-any', 91*59aec869SMax Reitz '--disallow-any-generics', 92*59aec869SMax Reitz '--disallow-incomplete-defs', 93*59aec869SMax Reitz '--disallow-untyped-decorators', 94*59aec869SMax Reitz '--no-implicit-optional', 95*59aec869SMax Reitz '--warn-redundant-casts', 96*59aec869SMax Reitz '--warn-unused-ignores', 97*59aec869SMax Reitz '--no-implicit-reexport', 98*59aec869SMax Reitz filename), 99*59aec869SMax Reitz env=env, 100*59aec869SMax Reitz check=False, 101*59aec869SMax Reitz stdout=subprocess.PIPE, 102*59aec869SMax Reitz stderr=subprocess.STDOUT, 103*59aec869SMax Reitz universal_newlines=True) 104*59aec869SMax Reitz 105*59aec869SMax Reitz if p.returncode != 0: 106*59aec869SMax Reitz print(p.stdout) 107*59aec869SMax Reitz 108*59aec869SMax Reitz 109*59aec869SMax Reitzfor linter in ('pylint-3', 'mypy'): 110*59aec869SMax Reitz if shutil.which(linter) is None: 111*59aec869SMax Reitz iotests.notrun(f'{linter} not found') 112*59aec869SMax Reitz 113*59aec869SMax Reitziotests.script_main(run_linters) 114