error_handlers.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  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. """Defines style error handler classes.
  23. A style error handler is a function to call when a style error is
  24. found. Style error handlers can also have state. A class that represents
  25. a style error handler should implement the following methods.
  26. Methods:
  27. __call__(self, line_number, category, confidence, message):
  28. Handle the occurrence of a style error.
  29. Check whether the error is reportable. If so, increment the total
  30. error count and report the details. Note that error reporting can
  31. be suppressed after reaching a certain number of reports.
  32. Args:
  33. line_number: The integer line number of the line containing the error.
  34. category: The name of the category of the error, for example
  35. "whitespace/newline".
  36. confidence: An integer between 1 and 5 inclusive that represents the
  37. application's level of confidence in the error. The value
  38. 5 means that we are certain of the problem, and the
  39. value 1 means that it could be a legitimate construct.
  40. message: The error message to report.
  41. """
  42. import sys
  43. class DefaultStyleErrorHandler(object):
  44. """The default style error handler."""
  45. def __init__(self, file_path, configuration, increment_error_count,
  46. line_numbers=None):
  47. """Create a default style error handler.
  48. Args:
  49. file_path: The path to the file containing the error. This
  50. is used for reporting to the user.
  51. configuration: A StyleProcessorConfiguration instance.
  52. increment_error_count: A function that takes no arguments and
  53. increments the total count of reportable
  54. errors.
  55. line_numbers: An array of line numbers of the lines for which
  56. style errors should be reported, or None if errors
  57. for all lines should be reported. When it is not
  58. None, this array normally contains the line numbers
  59. corresponding to the modified lines of a patch.
  60. """
  61. if line_numbers is not None:
  62. line_numbers = set(line_numbers)
  63. self._file_path = file_path
  64. self._configuration = configuration
  65. self._increment_error_count = increment_error_count
  66. self._line_numbers = line_numbers
  67. # A string to integer dictionary cache of the number of reportable
  68. # errors per category passed to this instance.
  69. self._category_totals = {}
  70. # Useful for unit testing.
  71. def __eq__(self, other):
  72. """Return whether this instance is equal to another."""
  73. if self._configuration != other._configuration:
  74. return False
  75. if self._file_path != other._file_path:
  76. return False
  77. if self._increment_error_count != other._increment_error_count:
  78. return False
  79. if self._line_numbers != other._line_numbers:
  80. return False
  81. return True
  82. # Useful for unit testing.
  83. def __ne__(self, other):
  84. # Python does not automatically deduce __ne__ from __eq__.
  85. return not self.__eq__(other)
  86. def _add_reportable_error(self, category):
  87. """Increment the error count and return the new category total."""
  88. self._increment_error_count() # Increment the total.
  89. # Increment the category total.
  90. if not category in self._category_totals:
  91. self._category_totals[category] = 1
  92. else:
  93. self._category_totals[category] += 1
  94. return self._category_totals[category]
  95. def _max_reports(self, category):
  96. """Return the maximum number of errors to report."""
  97. if not category in self._configuration.max_reports_per_category:
  98. return None
  99. return self._configuration.max_reports_per_category[category]
  100. def should_line_be_checked(self, line_number):
  101. "Returns if a particular line should be checked"
  102. # Was the line that was modified?
  103. return self._line_numbers is None or line_number in self._line_numbers
  104. def turn_off_line_filtering(self):
  105. self._line_numbers = None
  106. def __call__(self, line_number, category, confidence, message):
  107. """Handle the occurrence of a style error.
  108. See the docstring of this module for more information.
  109. """
  110. if not self.should_line_be_checked(line_number):
  111. return False
  112. if not self._configuration.is_reportable(category=category,
  113. confidence_in_error=confidence,
  114. file_path=self._file_path):
  115. return False
  116. category_total = self._add_reportable_error(category)
  117. max_reports = self._max_reports(category)
  118. if (max_reports is not None) and (category_total > max_reports):
  119. # Then suppress displaying the error.
  120. return False
  121. self._configuration.write_style_error(category=category,
  122. confidence_in_error=confidence,
  123. file_path=self._file_path,
  124. line_number=line_number,
  125. message=message)
  126. if category_total == max_reports:
  127. self._configuration.stderr_write("Suppressing further [%s] reports "
  128. "for this file.\n" % category)
  129. return True