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