1#!/usr/bin/env python3 2# Copyright 2016 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16"""Wrapper to run pylint with the right settings.""" 17 18import argparse 19import errno 20import os 21import shutil 22import sys 23import subprocess 24 25 26assert (sys.version_info.major, sys.version_info.minor) >= (3, 6), ( 27 'Python 3.6 or newer is required; found %s' % (sys.version,)) 28 29 30DEFAULT_PYLINTRC_PATH = os.path.join( 31 os.path.dirname(os.path.realpath(__file__)), 'pylintrc') 32 33 34def is_pylint3(pylint): 35 """See whether |pylint| supports Python 3.""" 36 # Make sure pylint is using Python 3. 37 result = subprocess.run([pylint, '--version'], stdout=subprocess.PIPE, 38 check=True) 39 if b'Python 3' not in result.stdout: 40 print('%s: unable to locate a Python 3 version of pylint; Python 3 ' 41 'support cannot be guaranteed' % (__file__,), file=sys.stderr) 42 return False 43 44 return True 45 46 47def find_pylint3(): 48 """Figure out the name of the pylint tool for Python 3. 49 50 It keeps changing with Python 2->3 migrations. Fun. 51 """ 52 # Prefer pylint3 as that's what we want. 53 if shutil.which('pylint3'): 54 return 'pylint3' 55 56 # If there's no pylint, give up. 57 if not shutil.which('pylint'): 58 print('%s: unable to locate pylint; please install:\n' 59 'sudo apt-get install pylint' % (__file__,), file=sys.stderr) 60 sys.exit(1) 61 62 return 'pylint' 63 64 65def get_parser(): 66 """Return a command line parser.""" 67 parser = argparse.ArgumentParser(description=__doc__) 68 parser.add_argument('--init-hook', help='Init hook commands to run.') 69 parser.add_argument('--py3', action='store_true', 70 help='Force Python 3 mode') 71 parser.add_argument('--executable-path', 72 help='The path of the pylint executable.') 73 parser.add_argument('--no-rcfile', 74 help='Specify to use the executable\'s default ' 75 'configuration.', 76 action='store_true') 77 parser.add_argument('files', nargs='+') 78 return parser 79 80 81def main(argv): 82 """The main entry.""" 83 parser = get_parser() 84 opts, unknown = parser.parse_known_args(argv) 85 86 pylint = opts.executable_path 87 if pylint is None: 88 if opts.py3: 89 pylint = find_pylint3() 90 else: 91 pylint = 'pylint' 92 93 # Make sure pylint is using Python 3. 94 if opts.py3: 95 is_pylint3(pylint) 96 97 cmd = [pylint] 98 if not opts.no_rcfile: 99 # We assume pylint is running in the top directory of the project, 100 # so load the pylintrc file from there if it's available. 101 pylintrc = os.path.abspath('pylintrc') 102 if not os.path.exists(pylintrc): 103 pylintrc = DEFAULT_PYLINTRC_PATH 104 # If we pass a non-existent rcfile to pylint, it'll happily ignore 105 # it. 106 assert os.path.exists(pylintrc), 'Could not find %s' % pylintrc 107 cmd += ['--rcfile', pylintrc] 108 109 cmd += unknown + opts.files 110 111 if opts.init_hook: 112 cmd += ['--init-hook', opts.init_hook] 113 114 try: 115 os.execvp(cmd[0], cmd) 116 return 0 117 except OSError as e: 118 if e.errno == errno.ENOENT: 119 print('%s: unable to run `%s`: %s' % (__file__, cmd[0], e), 120 file=sys.stderr) 121 print('%s: Try installing pylint: sudo apt-get install %s' % 122 (__file__, os.path.basename(cmd[0])), file=sys.stderr) 123 return 1 124 125 raise 126 127 128if __name__ == '__main__': 129 sys.exit(main(sys.argv[1:])) 130