15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Copyright (C) 2010 Chris Jerdonek (cjerdonek@webkit.org) 25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Redistribution and use in source and binary forms, with or without 45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# modification, are permitted provided that the following conditions 55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# are met: 65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 1. Redistributions of source code must retain the above copyright 75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# notice, this list of conditions and the following disclaimer. 85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 2. Redistributions in binary form must reproduce the above copyright 95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# notice, this list of conditions and the following disclaimer in the 105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# documentation and/or other materials provided with the distribution. 115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND 135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR 165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)"""Supports checking WebKit style in Python files.""" 245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 258abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles)import os 26926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)import re 277757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochimport sys 287757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 29926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)from StringIO import StringIO 30926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 31926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)from webkitpy.common.system.filesystem import FileSystem 327757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdochfrom webkitpy.common.system.executive import Executive 33926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)from webkitpy.common.webkit_finder import WebKitFinder 340019e4eead4d990e4304c54a9028aca9122fb256Ben Murdochfrom webkitpy.thirdparty import pep8 355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)class PythonChecker(object): 385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) """Processes text lines for checking style.""" 395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) def __init__(self, file_path, handle_style_error): 405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) self._file_path = file_path 415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) self._handle_style_error = handle_style_error 425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) def check(self, lines): 44926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) self._check_pep8(lines) 45926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) self._check_pylint(lines) 46926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 47926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def _check_pep8(self, lines): 485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # Initialize pep8.options, which is necessary for 495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # Checker.check_all() to execute. 505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) pep8.process_options(arglist=[self._file_path]) 515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 52926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) pep8_checker = pep8.Checker(self._file_path) 535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) def _pep8_handle_error(line_number, offset, text, check): 555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # FIXME: Incorporate the character offset into the error output. 565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # This will require updating the error handler __call__ 575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) # signature to include an optional "offset" parameter. 585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) pep8_code = text[:4] 595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) pep8_message = text[5:] 605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) category = "pep8/" + pep8_code 625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) self._handle_style_error(line_number, category, 5, pep8_message) 645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 65926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) pep8_checker.report_error = _pep8_handle_error 66926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) pep8_errors = pep8_checker.check_all() 67926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 68926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) def _check_pylint(self, lines): 697757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch output = self._run_pylint(self._file_path) 707757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch errors = self._parse_pylint_output(output) 717757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch for line_number, category, message in errors: 727757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch self._handle_style_error(line_number, category, 5, message) 73926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 747757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def _run_pylint(self, path): 757757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch wkf = WebKitFinder(FileSystem()) 767757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch executive = Executive() 778abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles) env = os.environ.copy() 7851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) env['PYTHONPATH'] = ('%s%s%s%s%s' % (wkf.path_from_webkit_base('Tools', 'Scripts'), 7951b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) os.pathsep, 8051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) wkf.path_from_webkit_base('Source', 'build', 'scripts'), 818abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles) os.pathsep, 828abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles) wkf.path_from_webkit_base('Tools', 'Scripts', 'webkitpy', 'thirdparty'))) 837757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return executive.run_command([sys.executable, wkf.path_from_depot_tools_base('pylint.py'), 847757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch '--output-format=parseable', 857757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch '--errors-only', 867757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch '--rcfile=' + wkf.path_from_webkit_base('Tools', 'Scripts', 'webkitpy', 'pylintrc'), 877757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch path], 888abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles) env=env, 898abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles) error_handler=executive.ignore_error) 907757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 917757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch def _parse_pylint_output(self, output): 927757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # We filter out these messages because they are bugs in pylint that produce false positives. 937757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # FIXME: Does it make sense to combine these rules with the rules in style/checker.py somehow? 947757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch FALSE_POSITIVES = [ 957757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch # possibly http://www.logilab.org/ticket/98613 ? 967757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "Instance of 'Popen' has no 'poll' member", 977757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "Instance of 'Popen' has no 'returncode' member", 987757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "Instance of 'Popen' has no 'stdin' member", 997757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "Instance of 'Popen' has no 'stdout' member", 1007757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "Instance of 'Popen' has no 'stderr' member", 1017757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch "Instance of 'Popen' has no 'wait' member", 1027757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch ] 103926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) 104926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) lint_regex = re.compile('([^:]+):([^:]+): \[([^]]+)\] (.*)') 1057757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch errors = [] 1067757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch for line in output.splitlines(): 1077757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if any(msg in line for msg in FALSE_POSITIVES): 1087757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch continue 1097757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 1107757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch match_obj = lint_regex.match(line) 1117757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch if not match_obj: 1127757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch continue 1137757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch 114926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) line_number = int(match_obj.group(2)) 115926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) category_and_method = match_obj.group(3).split(', ') 116926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) category = 'pylint/' + (category_and_method[0]) 117926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) if len(category_and_method) > 1: 118926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) message = '[%s] %s' % (category_and_method[1], match_obj.group(4)) 119926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) else: 120926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles) message = match_obj.group(4) 1217757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch errors.append((line_number, category, message)) 1227757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch return errors 123