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 ANY 13# 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 ANY 16# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 23"""Defines style error handler classes. 24 25A style error handler is a function to call when a style error is 26found. Style error handlers can also have state. A class that represents 27a style error handler should implement the following methods. 28 29Methods: 30 31 __call__(self, line_number, category, confidence, message): 32 33 Handle the occurrence of a style error. 34 35 Check whether the error is reportable. If so, increment the total 36 error count and report the details. Note that error reporting can 37 be suppressed after reaching a certain number of reports. 38 39 Args: 40 line_number: The integer line number of the line containing the error. 41 category: The name of the category of the error, for example 42 "whitespace/newline". 43 confidence: An integer between 1 and 5 inclusive that represents the 44 application's level of confidence in the error. The value 45 5 means that we are certain of the problem, and the 46 value 1 means that it could be a legitimate construct. 47 message: The error message to report. 48 49""" 50 51 52import sys 53 54 55class DefaultStyleErrorHandler(object): 56 57 """The default style error handler.""" 58 59 def __init__(self, file_path, configuration, increment_error_count, 60 line_numbers=None): 61 """Create a default style error handler. 62 63 Args: 64 file_path: The path to the file containing the error. This 65 is used for reporting to the user. 66 configuration: A StyleProcessorConfiguration instance. 67 increment_error_count: A function that takes no arguments and 68 increments the total count of reportable 69 errors. 70 line_numbers: An array of line numbers of the lines for which 71 style errors should be reported, or None if errors 72 for all lines should be reported. When it is not 73 None, this array normally contains the line numbers 74 corresponding to the modified lines of a patch. 75 76 """ 77 if line_numbers is not None: 78 line_numbers = set(line_numbers) 79 80 self._file_path = file_path 81 self._configuration = configuration 82 self._increment_error_count = increment_error_count 83 self._line_numbers = line_numbers 84 85 # A string to integer dictionary cache of the number of reportable 86 # errors per category passed to this instance. 87 self._category_totals = {} 88 89 # Useful for unit testing. 90 def __eq__(self, other): 91 """Return whether this instance is equal to another.""" 92 if self._configuration != other._configuration: 93 return False 94 if self._file_path != other._file_path: 95 return False 96 if self._increment_error_count != other._increment_error_count: 97 return False 98 if self._line_numbers != other._line_numbers: 99 return False 100 101 return True 102 103 # Useful for unit testing. 104 def __ne__(self, other): 105 # Python does not automatically deduce __ne__ from __eq__. 106 return not self.__eq__(other) 107 108 def _add_reportable_error(self, category): 109 """Increment the error count and return the new category total.""" 110 self._increment_error_count() # Increment the total. 111 112 # Increment the category total. 113 if not category in self._category_totals: 114 self._category_totals[category] = 1 115 else: 116 self._category_totals[category] += 1 117 118 return self._category_totals[category] 119 120 def _max_reports(self, category): 121 """Return the maximum number of errors to report.""" 122 if not category in self._configuration.max_reports_per_category: 123 return None 124 return self._configuration.max_reports_per_category[category] 125 126 def should_line_be_checked(self, line_number): 127 "Returns if a particular line should be checked" 128 # Was the line that was modified? 129 return self._line_numbers is None or line_number in self._line_numbers 130 131 def __call__(self, line_number, category, confidence, message): 132 """Handle the occurrence of a style error. 133 134 See the docstring of this module for more information. 135 136 """ 137 if not self.should_line_be_checked(line_number): 138 return 139 140 if not self._configuration.is_reportable(category=category, 141 confidence_in_error=confidence, 142 file_path=self._file_path): 143 return 144 145 category_total = self._add_reportable_error(category) 146 147 max_reports = self._max_reports(category) 148 149 if (max_reports is not None) and (category_total > max_reports): 150 # Then suppress displaying the error. 151 return 152 153 self._configuration.write_style_error(category=category, 154 confidence_in_error=confidence, 155 file_path=self._file_path, 156 line_number=line_number, 157 message=message) 158 159 if category_total == max_reports: 160 self._configuration.stderr_write("Suppressing further [%s] reports " 161 "for this file.\n" % category) 162