def check_file(self): """Perform all necessary checks on our file (including rulific ones). Raise FileCheckerError if an error is detected. :return: None. :rtype: None """ log_info("Checking style of `%s' (%s)" % (self.filename, self.file_type)) # Build the error message into a list. And then, at the end of # this function, if it turns out we got at least one issue, # raise it via FileCheckerError. err_msgs = [] # First, run the external checker... external_checker_err_msg = self.run_external_checker() if external_checker_err_msg is not None: # Stop immediately if the external checker detected some # errors. We could continue, and run the rest of the # tests, but cvs_check, the previous style checker, # wasn't doing that, so we don't either so as to be # consistent with the traditional behavior. raise FileCheckerError(external_checker_err_msg) # First, feed line-by-line the contents of the file to each # rulific checker. with open(self.filename) as f: for lineno, line in enumerate(f, 1): eol = get_eol(line) line = line[:-len(eol or '')] for rulific_checker in self.my_rulific_checkers: rulific_checker.process_line(lineno, line, eol) # Merge all the result of the line-by-line checking, currently # stored in each rulific checker, into a combined dictionary, # where keys are still line numbers, but the value is a list # of error messages applying to that line number. all_linenos = sorted( set().union(*(rulific_checker.errors_found.keys() for rulific_checker in self.my_rulific_checkers))) for lineno in all_linenos: for rulific_checker in self.my_rulific_checkers: if lineno in rulific_checker.errors_found: err_msgs.append('%s:%d: %s' % (self.filename, lineno, rulific_checker.errors_found[lineno])) for rulific_checker in self.my_rulific_checkers: global_check_err_msg = rulific_checker.global_check() if global_check_err_msg: err_msgs.append(global_check_err_msg) if err_msgs: raise FileCheckerError(*err_msgs)
def get_file_type(filename): """Run the "file" command on filename and return its output. :param filename: The name of the file on which to run the "file" command. :type filename: str """ try: p = Run(["file", filename]) if p.status != 0: raise FileCheckerError( "%s returned nonzero (%d):" % (p.command_line_image(), p.status), p.out) except OSError as e: raise FileCheckerError("Failed to run `file %s': %s" % (filename, e)) return p.out
def run_external_checker(self): """Run an external program to check the contents of the file. This is typically a tool which will perform some kind of language-specific check, making sure the file compiles, follows the proper coding style, etc. :return: A string with the corresponding error message if the checker discovered some issues, None otherwise. :rtype: str | None """ raise FileCheckerError( 'abstract TypificChecker.run_external_checker method' ' unexpectedly called.')
def file_type(self): """Return a string describing the kind of file this checker applies to. NOTE: This was turned into a property so as to allow checkers to handle multiple kinds of files. This can be useful when there are only very slight variations on how the set of files is checked (Eg: with Ada, the compiler units typically have some extra checks). As such, a class attribute would not have worked in this context. :rtype: str """ raise FileCheckerError( 'abstract TypificChecker.file_type property unexpectedly called.')
def style_checker(argv=None): """Run the style checker with the given command-line arguments. :param argv: Same as in parse_cmdline. :type argv: list[str] | None """ args = parse_cmdline(argv) asclib.logging.logging_level = args.verbose_level if not args.filenames: # No filename provided, which means the user wants us to # read the list of filesnames from standard input. args.filenames = [ filename for filename in sys.stdin.read().splitlines() if filename ] config = Config( args.system_config, args.module_name, args.module_config, args.forced_year ) n_files_with_errors = 0 for filename in args.filenames: try: # Before even trying to launch the style checker, first verify # that the file exists, and if it doesn't then report the error, # and look at the next file to check... if not os.path.isfile(filename): raise FileCheckerError( "Error: `%s' is not a valid filename." % filename ) checker = get_file_checker(filename, config) if checker is None: # No checks for this kind of file. continue checker.check_file() except FileCheckerError as e: n_files_with_errors += 1 if n_files_with_errors > args.max_files_with_errors: log_error("[other files with style violations were found]") break else: log_error(e.args) return n_files_with_errors == 0
def check_rule(self, lineno, line, eol): """Report an error the given line contains a style violation. This is an abstract method which we expect child classes to override. :param lineno: Same as self.process_line. :type lineno: int :param line: Same as self.process_line. :type line: str :param eol: Same as self.process_line. :type eol: str | None :return: A string with the error message if a violation is detected. None otherwise. :rtype: str | None """ raise FileCheckerError("abstract RuleChecker.check_rule method called")