• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org)
2#
3# Redistribution and use in source and binary forms, with or without
4# modification, are permitted provided that the following conditions
5# are met:
6# 1.  Redistributions of source code must retain the above copyright
7#     notice, this list of conditions and the following disclaimer.
8# 2.  Redistributions in binary form must reproduce the above copyright
9#     notice, this list of conditions and the following disclaimer in the
10#     documentation and/or other materials provided with the distribution.
11#
12# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
13# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
16# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
19# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
20# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
21# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
23"""Supports checking WebKit style in Python files."""
24
25import os
26import re
27import sys
28
29from StringIO import StringIO
30
31from webkitpy.common.system.filesystem import FileSystem
32from webkitpy.common.system.executive import Executive
33from webkitpy.common.webkit_finder import WebKitFinder
34from webkitpy.thirdparty import pep8
35
36
37class PythonChecker(object):
38    """Processes text lines for checking style."""
39    def __init__(self, file_path, handle_style_error):
40        self._file_path = file_path
41        self._handle_style_error = handle_style_error
42
43    def check(self, lines):
44        self._check_pep8(lines)
45        self._check_pylint(lines)
46
47    def _check_pep8(self, lines):
48        # Initialize pep8.options, which is necessary for
49        # Checker.check_all() to execute.
50        pep8.process_options(arglist=[self._file_path])
51
52        pep8_checker = pep8.Checker(self._file_path)
53
54        def _pep8_handle_error(line_number, offset, text, check):
55            # FIXME: Incorporate the character offset into the error output.
56            #        This will require updating the error handler __call__
57            #        signature to include an optional "offset" parameter.
58            pep8_code = text[:4]
59            pep8_message = text[5:]
60
61            category = "pep8/" + pep8_code
62
63            self._handle_style_error(line_number, category, 5, pep8_message)
64
65        pep8_checker.report_error = _pep8_handle_error
66        pep8_errors = pep8_checker.check_all()
67
68    def _check_pylint(self, lines):
69        output = self._run_pylint(self._file_path)
70        errors = self._parse_pylint_output(output)
71        for line_number, category, message in errors:
72            self._handle_style_error(line_number, category, 5, message)
73
74    def _run_pylint(self, path):
75        wkf = WebKitFinder(FileSystem())
76        executive = Executive()
77        env = os.environ.copy()
78        env['PYTHONPATH'] = ('%s%s%s%s%s' % (wkf.path_from_webkit_base('Tools', 'Scripts'),
79                                         os.pathsep,
80                                         wkf.path_from_webkit_base('Source', 'build', 'scripts'),
81                                         os.pathsep,
82                                         wkf.path_from_webkit_base('Tools', 'Scripts', 'webkitpy', 'thirdparty')))
83        return executive.run_command([sys.executable, wkf.path_from_depot_tools_base('pylint.py'),
84                                      '--output-format=parseable',
85                                      '--errors-only',
86                                      '--rcfile=' + wkf.path_from_webkit_base('Tools', 'Scripts', 'webkitpy', 'pylintrc'),
87                                      path],
88                                     env=env,
89                                     error_handler=executive.ignore_error)
90
91    def _parse_pylint_output(self, output):
92        # We filter out these messages because they are bugs in pylint that produce false positives.
93        # FIXME: Does it make sense to combine these rules with the rules in style/checker.py somehow?
94        FALSE_POSITIVES = [
95            # possibly http://www.logilab.org/ticket/98613 ?
96            "Instance of 'Popen' has no 'poll' member",
97            "Instance of 'Popen' has no 'returncode' member",
98            "Instance of 'Popen' has no 'stdin' member",
99            "Instance of 'Popen' has no 'stdout' member",
100            "Instance of 'Popen' has no 'stderr' member",
101            "Instance of 'Popen' has no 'wait' member",
102        ]
103
104        lint_regex = re.compile('([^:]+):([^:]+): \[([^]]+)\] (.*)')
105        errors = []
106        for line in output.splitlines():
107            if any(msg in line for msg in FALSE_POSITIVES):
108                continue
109
110            match_obj = lint_regex.match(line)
111            if not match_obj:
112                continue
113
114            line_number = int(match_obj.group(2))
115            category_and_method = match_obj.group(3).split(', ')
116            category = 'pylint/' + (category_and_method[0])
117            if len(category_and_method) > 1:
118                message = '[%s] %s' % (category_and_method[1], match_obj.group(4))
119            else:
120                message = match_obj.group(4)
121            errors.append((line_number, category, message))
122        return errors
123