1 # Copyright (C) 2009 Google Inc. All rights reserved. 2 # Copyright (C) 2010 Chris Jerdonek (chris.jerdonek (at] gmail.com) 3 # Copyright (C) 2010 ProFUSION embedded systems 4 # 5 # Redistribution and use in source and binary forms, with or without 6 # modification, are permitted provided that the following conditions are 7 # met: 8 # 9 # * Redistributions of source code must retain the above copyright 10 # notice, this list of conditions and the following disclaimer. 11 # * Redistributions in binary form must reproduce the above 12 # copyright notice, this list of conditions and the following disclaimer 13 # in the documentation and/or other materials provided with the 14 # distribution. 15 # * Neither the name of Google Inc. nor the names of its 16 # contributors may be used to endorse or promote products derived from 17 # this software without specific prior written permission. 18 # 19 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 """Front end of some style-checker modules.""" 32 33 import logging 34 import os.path 35 import re 36 import sys 37 38 from checkers.common import categories as CommonCategories 39 from checkers.common import CarriageReturnChecker 40 from checkers.cpp import CppChecker 41 from checkers.jsonchecker import JSONChecker 42 from checkers.png import PNGChecker 43 from checkers.python import PythonChecker 44 from checkers.test_expectations import TestExpectationsChecker 45 from checkers.text import TextChecker 46 from checkers.xcodeproj import XcodeProjectFileChecker 47 from checkers.xml import XMLChecker 48 from error_handlers import DefaultStyleErrorHandler 49 from filter import FilterConfiguration 50 from optparser import ArgumentParser 51 from optparser import DefaultCommandOptionValues 52 from webkitpy.common.system.logutils import configure_logging as _configure_logging 53 54 55 _log = logging.getLogger(__name__) 56 57 58 # These are default option values for the command-line option parser. 59 _DEFAULT_MIN_CONFIDENCE = 1 60 _DEFAULT_OUTPUT_FORMAT = 'emacs' 61 62 63 # FIXME: For style categories we will never want to have, remove them. 64 # For categories for which we want to have similar functionality, 65 # modify the implementation and enable them. 66 # 67 # Throughout this module, we use "filter rule" rather than "filter" 68 # for an individual boolean filter flag like "+foo". This allows us to 69 # reserve "filter" for what one gets by collectively applying all of 70 # the filter rules. 71 # 72 # The base filter rules are the filter rules that begin the list of 73 # filter rules used to check style. For example, these rules precede 74 # any user-specified filter rules. Since by default all categories are 75 # checked, this list should normally include only rules that begin 76 # with a "-" sign. 77 _BASE_FILTER_RULES = [ 78 '-build/endif_comment', 79 '-build/include_what_you_use', # <string> for std::string 80 '-build/storage_class', # const static 81 '-legal/copyright', 82 '-readability/multiline_comment', 83 '-readability/braces', # int foo() {}; 84 '-readability/fn_size', 85 '-readability/casting', 86 '-readability/function', 87 '-runtime/arrays', # variable length array 88 '-runtime/casting', 89 '-runtime/sizeof', 90 '-runtime/explicit', # explicit 91 '-runtime/virtual', # virtual dtor 92 '-runtime/printf', 93 '-runtime/threadsafe_fn', 94 '-runtime/rtti', 95 '-whitespace/blank_line', 96 '-whitespace/end_of_line', 97 # List Python pep8 categories last. 98 # 99 # Because much of WebKit's Python code base does not abide by the 100 # PEP8 79 character limit, we ignore the 79-character-limit category 101 # pep8/E501 for now. 102 # 103 # FIXME: Consider bringing WebKit's Python code base into conformance 104 # with the 79 character limit, or some higher limit that is 105 # agreeable to the WebKit project. 106 '-pep8/E501', 107 108 # FIXME: Move the pylint rules from the pylintrc to here. This will 109 # also require us to re-work lint-webkitpy to produce the equivalent output. 110 ] 111 112 113 # The path-specific filter rules. 114 # 115 # This list is order sensitive. Only the first path substring match 116 # is used. See the FilterConfiguration documentation in filter.py 117 # for more information on this list. 118 # 119 # Each string appearing in this nested list should have at least 120 # one associated unit test assertion. These assertions are located, 121 # for example, in the test_path_rules_specifier() unit test method of 122 # checker_unittest.py. 123 _PATH_RULES_SPECIFIER = [ 124 # Files in these directories are consumers of the WebKit 125 # API and therefore do not follow the same header including 126 # discipline as WebCore. 127 128 ([# There is no clean way to avoid "yy_*" names used by flex. 129 "Source/core/css/CSSParser-in.cpp"], 130 ["-readability/naming"]), 131 132 # For third-party Python code, keep only the following checks-- 133 # 134 # No tabs: to avoid having to set the SVN allow-tabs property. 135 # No trailing white space: since this is easy to correct. 136 # No carriage-return line endings: since this is easy to correct. 137 # 138 (["webkitpy/thirdparty/"], 139 ["-", 140 "+pep8/W191", # Tabs 141 "+pep8/W291", # Trailing white space 142 "+whitespace/carriage_return"]), 143 144 ([# Jinja templates: files have .cpp or .h extensions, but contain 145 # template code, which can't be handled, so disable tests. 146 "Source/bindings/templates", 147 "Source/build/scripts/templates"], 148 ["-"]), 149 150 ([# IDL compiler reference output 151 # Conforming to style significantly increases the complexity of the code 152 # generator and decreases *its* readability, which is of more concern 153 # than style of the machine-generated code itself. 154 "Source/bindings/tests/results"], 155 ["-"]), 156 ] 157 158 159 _CPP_FILE_EXTENSIONS = [ 160 'c', 161 'cpp', 162 'h', 163 ] 164 165 _JSON_FILE_EXTENSION = 'json' 166 167 _PYTHON_FILE_EXTENSION = 'py' 168 169 _TEXT_FILE_EXTENSIONS = [ 170 'cc', 171 'cgi', 172 'css', 173 'gyp', 174 'gypi', 175 'html', 176 'idl', 177 'in', 178 'js', 179 'mm', 180 'php', 181 'pl', 182 'pm', 183 'rb', 184 'sh', 185 'txt', 186 'xhtml', 187 'y', 188 ] 189 190 _XCODEPROJ_FILE_EXTENSION = 'pbxproj' 191 192 _XML_FILE_EXTENSIONS = [ 193 'vcproj', 194 'vsprops', 195 ] 196 197 _PNG_FILE_EXTENSION = 'png' 198 199 # Files to skip that are less obvious. 200 # 201 # Some files should be skipped when checking style. For example, 202 # WebKit maintains some files in Mozilla style on purpose to ease 203 # future merges. 204 _SKIPPED_FILES_WITH_WARNING = [ 205 "Source/WebKit/gtk/tests/", 206 # All WebKit*.h files in Source/WebKit2/UIProcess/API/gtk, 207 # except those ending in ...Private.h are GTK+ API headers, 208 # which differ greatly from WebKit coding style. 209 re.compile(r'Source/WebKit2/UIProcess/API/gtk/WebKit(?!.*Private\.h).*\.h$'), 210 re.compile(r'Source/WebKit2/WebProcess/InjectedBundle/API/gtk/WebKit(?!.*Private\.h).*\.h$'), 211 'Source/WebKit2/UIProcess/API/gtk/webkit2.h', 212 'Source/WebKit2/WebProcess/InjectedBundle/API/gtk/webkit-web-extension.h'] 213 214 # Files to skip that are more common or obvious. 215 # 216 # This list should be in addition to files with FileType.NONE. Files 217 # with FileType.NONE are automatically skipped without warning. 218 _SKIPPED_FILES_WITHOUT_WARNING = [ 219 "LayoutTests" + os.path.sep, 220 "Source/ThirdParty/leveldb" + os.path.sep, 221 # Prevents this being recognized as a text file. 222 "Source/WebCore/GNUmakefile.features.am.in", 223 ] 224 225 # Extensions of files which are allowed to contain carriage returns. 226 _CARRIAGE_RETURN_ALLOWED_FILE_EXTENSIONS = [ 227 'png', 228 'vcproj', 229 'vsprops', 230 ] 231 232 # The maximum number of errors to report per file, per category. 233 # If a category is not a key, then it has no maximum. 234 _MAX_REPORTS_PER_CATEGORY = { 235 "whitespace/carriage_return": 1 236 } 237 238 239 def _all_categories(): 240 """Return the set of all categories used by check-webkit-style.""" 241 # Take the union across all checkers. 242 categories = CommonCategories.union(CppChecker.categories) 243 categories = categories.union(JSONChecker.categories) 244 categories = categories.union(TestExpectationsChecker.categories) 245 categories = categories.union(PNGChecker.categories) 246 247 # FIXME: Consider adding all of the pep8 categories. Since they 248 # are not too meaningful for documentation purposes, for 249 # now we add only the categories needed for the unit tests 250 # (which validate the consistency of the configuration 251 # settings against the known categories, etc). 252 categories = categories.union(["pep8/W191", "pep8/W291", "pep8/E501"]) 253 254 return categories 255 256 257 def _check_webkit_style_defaults(): 258 """Return the default command-line options for check-webkit-style.""" 259 return DefaultCommandOptionValues(min_confidence=_DEFAULT_MIN_CONFIDENCE, 260 output_format=_DEFAULT_OUTPUT_FORMAT) 261 262 263 # This function assists in optparser not having to import from checker. 264 def check_webkit_style_parser(): 265 all_categories = _all_categories() 266 default_options = _check_webkit_style_defaults() 267 return ArgumentParser(all_categories=all_categories, 268 base_filter_rules=_BASE_FILTER_RULES, 269 default_options=default_options) 270 271 272 def check_webkit_style_configuration(options): 273 """Return a StyleProcessorConfiguration instance for check-webkit-style. 274 275 Args: 276 options: A CommandOptionValues instance. 277 278 """ 279 filter_configuration = FilterConfiguration( 280 base_rules=_BASE_FILTER_RULES, 281 path_specific=_PATH_RULES_SPECIFIER, 282 user_rules=options.filter_rules) 283 284 return StyleProcessorConfiguration(filter_configuration=filter_configuration, 285 max_reports_per_category=_MAX_REPORTS_PER_CATEGORY, 286 min_confidence=options.min_confidence, 287 output_format=options.output_format, 288 stderr_write=sys.stderr.write) 289 290 291 def _create_log_handlers(stream): 292 """Create and return a default list of logging.Handler instances. 293 294 Format WARNING messages and above to display the logging level, and 295 messages strictly below WARNING not to display it. 296 297 Args: 298 stream: See the configure_logging() docstring. 299 300 """ 301 # Handles logging.WARNING and above. 302 error_handler = logging.StreamHandler(stream) 303 error_handler.setLevel(logging.WARNING) 304 formatter = logging.Formatter("%(levelname)s: %(message)s") 305 error_handler.setFormatter(formatter) 306 307 # Create a logging.Filter instance that only accepts messages 308 # below WARNING (i.e. filters out anything WARNING or above). 309 non_error_filter = logging.Filter() 310 # The filter method accepts a logging.LogRecord instance. 311 non_error_filter.filter = lambda record: record.levelno < logging.WARNING 312 313 non_error_handler = logging.StreamHandler(stream) 314 non_error_handler.addFilter(non_error_filter) 315 formatter = logging.Formatter("%(message)s") 316 non_error_handler.setFormatter(formatter) 317 318 return [error_handler, non_error_handler] 319 320 321 def _create_debug_log_handlers(stream): 322 """Create and return a list of logging.Handler instances for debugging. 323 324 Args: 325 stream: See the configure_logging() docstring. 326 327 """ 328 handler = logging.StreamHandler(stream) 329 formatter = logging.Formatter("%(name)s: %(levelname)-8s %(message)s") 330 handler.setFormatter(formatter) 331 332 return [handler] 333 334 335 def configure_logging(stream, logger=None, is_verbose=False): 336 """Configure logging, and return the list of handlers added. 337 338 Returns: 339 A list of references to the logging handlers added to the root 340 logger. This allows the caller to later remove the handlers 341 using logger.removeHandler. This is useful primarily during unit 342 testing where the caller may want to configure logging temporarily 343 and then undo the configuring. 344 345 Args: 346 stream: A file-like object to which to log. The stream must 347 define an "encoding" data attribute, or else logging 348 raises an error. 349 logger: A logging.logger instance to configure. This parameter 350 should be used only in unit tests. Defaults to the 351 root logger. 352 is_verbose: A boolean value of whether logging should be verbose. 353 354 """ 355 # If the stream does not define an "encoding" data attribute, the 356 # logging module can throw an error like the following: 357 # 358 # Traceback (most recent call last): 359 # File "/System/Library/Frameworks/Python.framework/Versions/2.6/... 360 # lib/python2.6/logging/__init__.py", line 761, in emit 361 # self.stream.write(fs % msg.encode(self.stream.encoding)) 362 # LookupError: unknown encoding: unknown 363 if logger is None: 364 logger = logging.getLogger() 365 366 if is_verbose: 367 logging_level = logging.DEBUG 368 handlers = _create_debug_log_handlers(stream) 369 else: 370 logging_level = logging.INFO 371 handlers = _create_log_handlers(stream) 372 373 handlers = _configure_logging(logging_level=logging_level, logger=logger, 374 handlers=handlers) 375 376 return handlers 377 378 379 # Enum-like idiom 380 class FileType: 381 382 NONE = 0 # FileType.NONE evaluates to False. 383 # Alphabetize remaining types 384 # CHANGELOG = 1 385 CPP = 2 386 JSON = 3 387 PNG = 4 388 PYTHON = 5 389 TEXT = 6 390 # WATCHLIST = 7 391 XML = 8 392 XCODEPROJ = 9 393 394 395 class CheckerDispatcher(object): 396 397 """Supports determining whether and how to check style, based on path.""" 398 399 def _file_extension(self, file_path): 400 """Return the file extension without the leading dot.""" 401 return os.path.splitext(file_path)[1].lstrip(".") 402 403 def _should_skip_file_path(self, file_path, skip_array_entry): 404 match = re.search("\s*png$", file_path) 405 if match: 406 return False 407 if isinstance(skip_array_entry, str): 408 if file_path.find(skip_array_entry) >= 0: 409 return True 410 elif skip_array_entry.match(file_path): 411 return True 412 return False 413 414 def should_skip_with_warning(self, file_path): 415 """Return whether the given file should be skipped with a warning.""" 416 for skipped_file in _SKIPPED_FILES_WITH_WARNING: 417 if self._should_skip_file_path(file_path, skipped_file): 418 return True 419 return False 420 421 def should_skip_without_warning(self, file_path): 422 """Return whether the given file should be skipped without a warning.""" 423 if not self._file_type(file_path): # FileType.NONE. 424 return True 425 # Since "LayoutTests" is in _SKIPPED_FILES_WITHOUT_WARNING, make 426 # an exception to prevent files like 'TestExpectations' from being skipped. 427 # 428 # FIXME: Figure out a good way to avoid having to add special logic 429 # for this special case. 430 basename = os.path.basename(file_path) 431 if basename == 'TestExpectations': 432 return False 433 for skipped_file in _SKIPPED_FILES_WITHOUT_WARNING: 434 if self._should_skip_file_path(file_path, skipped_file): 435 return True 436 return False 437 438 def should_check_and_strip_carriage_returns(self, file_path): 439 return self._file_extension(file_path) not in _CARRIAGE_RETURN_ALLOWED_FILE_EXTENSIONS 440 441 def _file_type(self, file_path): 442 """Return the file type corresponding to the given file.""" 443 file_extension = self._file_extension(file_path) 444 445 if (file_extension in _CPP_FILE_EXTENSIONS) or (file_path == '-'): 446 # FIXME: Do something about the comment below and the issue it 447 # raises since cpp_style already relies on the extension. 448 # 449 # Treat stdin as C++. Since the extension is unknown when 450 # reading from stdin, cpp_style tests should not rely on 451 # the extension. 452 return FileType.CPP 453 elif file_extension == _JSON_FILE_EXTENSION: 454 return FileType.JSON 455 elif file_extension == _PYTHON_FILE_EXTENSION: 456 return FileType.PYTHON 457 elif file_extension in _XML_FILE_EXTENSIONS: 458 return FileType.XML 459 elif file_extension == _XCODEPROJ_FILE_EXTENSION: 460 return FileType.XCODEPROJ 461 elif file_extension == _PNG_FILE_EXTENSION: 462 return FileType.PNG 463 elif ((not file_extension and os.path.join("Tools", "Scripts") in file_path) or 464 file_extension in _TEXT_FILE_EXTENSIONS or os.path.basename(file_path) == 'TestExpectations'): 465 return FileType.TEXT 466 else: 467 return FileType.NONE 468 469 def _create_checker(self, file_type, file_path, handle_style_error, 470 min_confidence): 471 """Instantiate and return a style checker based on file type.""" 472 if file_type == FileType.NONE: 473 checker = None 474 elif file_type == FileType.CPP: 475 file_extension = self._file_extension(file_path) 476 checker = CppChecker(file_path, file_extension, 477 handle_style_error, min_confidence) 478 elif file_type == FileType.JSON: 479 checker = JSONChecker(file_path, handle_style_error) 480 elif file_type == FileType.PYTHON: 481 checker = PythonChecker(file_path, handle_style_error) 482 elif file_type == FileType.XML: 483 checker = XMLChecker(file_path, handle_style_error) 484 elif file_type == FileType.XCODEPROJ: 485 checker = XcodeProjectFileChecker(file_path, handle_style_error) 486 elif file_type == FileType.PNG: 487 checker = PNGChecker(file_path, handle_style_error) 488 elif file_type == FileType.TEXT: 489 basename = os.path.basename(file_path) 490 if basename == 'TestExpectations': 491 checker = TestExpectationsChecker(file_path, handle_style_error) 492 else: 493 checker = TextChecker(file_path, handle_style_error) 494 else: 495 raise ValueError('Invalid file type "%(file_type)s": the only valid file types ' 496 "are %(NONE)s, %(CPP)s, and %(TEXT)s." 497 % {"file_type": file_type, 498 "NONE": FileType.NONE, 499 "CPP": FileType.CPP, 500 "TEXT": FileType.TEXT}) 501 502 return checker 503 504 def dispatch(self, file_path, handle_style_error, min_confidence): 505 """Instantiate and return a style checker based on file path.""" 506 file_type = self._file_type(file_path) 507 508 checker = self._create_checker(file_type, 509 file_path, 510 handle_style_error, 511 min_confidence) 512 return checker 513 514 515 # FIXME: Remove the stderr_write attribute from this class and replace 516 # its use with calls to a logging module logger. 517 class StyleProcessorConfiguration(object): 518 519 """Stores configuration values for the StyleProcessor class. 520 521 Attributes: 522 min_confidence: An integer between 1 and 5 inclusive that is the 523 minimum confidence level of style errors to report. 524 525 max_reports_per_category: The maximum number of errors to report 526 per category, per file. 527 528 stderr_write: A function that takes a string as a parameter and 529 serves as stderr.write. 530 531 """ 532 533 def __init__(self, 534 filter_configuration, 535 max_reports_per_category, 536 min_confidence, 537 output_format, 538 stderr_write): 539 """Create a StyleProcessorConfiguration instance. 540 541 Args: 542 filter_configuration: A FilterConfiguration instance. The default 543 is the "empty" filter configuration, which 544 means that all errors should be checked. 545 546 max_reports_per_category: The maximum number of errors to report 547 per category, per file. 548 549 min_confidence: An integer between 1 and 5 inclusive that is the 550 minimum confidence level of style errors to report. 551 The default is 1, which reports all style errors. 552 553 output_format: A string that is the output format. The supported 554 output formats are "emacs" which emacs can parse 555 and "vs7" which Microsoft Visual Studio 7 can parse. 556 557 stderr_write: A function that takes a string as a parameter and 558 serves as stderr.write. 559 560 """ 561 self._filter_configuration = filter_configuration 562 self._output_format = output_format 563 564 self.max_reports_per_category = max_reports_per_category 565 self.min_confidence = min_confidence 566 self.stderr_write = stderr_write 567 568 def is_reportable(self, category, confidence_in_error, file_path): 569 """Return whether an error is reportable. 570 571 An error is reportable if both the confidence in the error is 572 at least the minimum confidence level and the current filter 573 says the category should be checked for the given path. 574 575 Args: 576 category: A string that is a style category. 577 confidence_in_error: An integer between 1 and 5 inclusive that is 578 the application's confidence in the error. 579 A higher number means greater confidence. 580 file_path: The path of the file being checked 581 582 """ 583 if confidence_in_error < self.min_confidence: 584 return False 585 586 return self._filter_configuration.should_check(category, file_path) 587 588 def write_style_error(self, 589 category, 590 confidence_in_error, 591 file_path, 592 line_number, 593 message): 594 """Write a style error to the configured stderr.""" 595 if self._output_format == 'vs7': 596 format_string = "%s(%s): %s [%s] [%d]\n" 597 else: 598 format_string = "%s:%s: %s [%s] [%d]\n" 599 600 self.stderr_write(format_string % (file_path, 601 line_number, 602 message, 603 category, 604 confidence_in_error)) 605 606 607 class ProcessorBase(object): 608 609 """The base class for processors of lists of lines.""" 610 611 def should_process(self, file_path): 612 """Return whether the file at file_path should be processed. 613 614 The TextFileReader class calls this method prior to reading in 615 the lines of a file. Use this method, for example, to prevent 616 the style checker from reading binary files into memory. 617 618 """ 619 raise NotImplementedError('Subclasses should implement.') 620 621 def process(self, lines, file_path, **kwargs): 622 """Process lines of text read from a file. 623 624 Args: 625 lines: A list of lines of text to process. 626 file_path: The path from which the lines were read. 627 **kwargs: This argument signifies that the process() method of 628 subclasses of ProcessorBase may support additional 629 keyword arguments. 630 For example, a style checker's check() method 631 may support a "reportable_lines" parameter that represents 632 the line numbers of the lines for which style errors 633 should be reported. 634 635 """ 636 raise NotImplementedError('Subclasses should implement.') 637 638 639 class StyleProcessor(ProcessorBase): 640 641 """A ProcessorBase for checking style. 642 643 Attributes: 644 error_count: An integer that is the total number of reported 645 errors for the lifetime of this instance. 646 647 """ 648 649 def __init__(self, configuration, mock_dispatcher=None, 650 mock_increment_error_count=None, 651 mock_carriage_checker_class=None): 652 """Create an instance. 653 654 Args: 655 configuration: A StyleProcessorConfiguration instance. 656 mock_dispatcher: A mock CheckerDispatcher instance. This 657 parameter is for unit testing. Defaults to a 658 CheckerDispatcher instance. 659 mock_increment_error_count: A mock error-count incrementer. 660 mock_carriage_checker_class: A mock class for checking and 661 transforming carriage returns. 662 This parameter is for unit testing. 663 Defaults to CarriageReturnChecker. 664 665 """ 666 if mock_dispatcher is None: 667 dispatcher = CheckerDispatcher() 668 else: 669 dispatcher = mock_dispatcher 670 671 if mock_increment_error_count is None: 672 # The following blank line is present to avoid flagging by pep8.py. 673 674 def increment_error_count(): 675 """Increment the total count of reported errors.""" 676 self.error_count += 1 677 else: 678 increment_error_count = mock_increment_error_count 679 680 if mock_carriage_checker_class is None: 681 # This needs to be a class rather than an instance since the 682 # process() method instantiates one using parameters. 683 carriage_checker_class = CarriageReturnChecker 684 else: 685 carriage_checker_class = mock_carriage_checker_class 686 687 self.error_count = 0 688 689 self._carriage_checker_class = carriage_checker_class 690 self._configuration = configuration 691 self._dispatcher = dispatcher 692 self._increment_error_count = increment_error_count 693 694 def should_process(self, file_path): 695 """Return whether the file should be checked for style.""" 696 if self._dispatcher.should_skip_without_warning(file_path): 697 return False 698 if self._dispatcher.should_skip_with_warning(file_path): 699 _log.warn('File exempt from style guide. Skipping: "%s"' 700 % file_path) 701 return False 702 return True 703 704 def process(self, lines, file_path, line_numbers=None): 705 """Check the given lines for style. 706 707 Arguments: 708 lines: A list of all lines in the file to check. 709 file_path: The path of the file to process. If possible, the path 710 should be relative to the source root. Otherwise, 711 path-specific logic may not behave as expected. 712 line_numbers: A list of line numbers of the lines for which 713 style errors should be reported, or None if errors 714 for all lines should be reported. When not None, this 715 list normally contains the line numbers corresponding 716 to the modified lines of a patch. 717 718 """ 719 _log.debug("Checking style: " + file_path) 720 721 style_error_handler = DefaultStyleErrorHandler( 722 configuration=self._configuration, 723 file_path=file_path, 724 increment_error_count=self._increment_error_count, 725 line_numbers=line_numbers) 726 727 carriage_checker = self._carriage_checker_class(style_error_handler) 728 729 # Check for and remove trailing carriage returns ("\r"). 730 if self._dispatcher.should_check_and_strip_carriage_returns(file_path): 731 lines = carriage_checker.check(lines) 732 733 min_confidence = self._configuration.min_confidence 734 checker = self._dispatcher.dispatch(file_path, 735 style_error_handler, 736 min_confidence) 737 738 if checker is None: 739 raise AssertionError("File should not be checked: '%s'" % file_path) 740 741 _log.debug("Using class: " + checker.__class__.__name__) 742 743 checker.check(lines) 744