Source code for flake8.formatting.base

"""The base class and interface for all formatting plugins."""
from __future__ import print_function

import optparse
from typing import IO, List, Optional, Tuple

if False:  # `typing.TYPE_CHECKING` was introduced in 3.5.2
    from flake8.statistics import Statistics
    from flake8.style_guide import Violation


[docs]class BaseFormatter(object): """Class defining the formatter interface. .. attribute:: options The options parsed from both configuration files and the command-line. .. attribute:: filename If specified by the user, the path to store the results of the run. .. attribute:: output_fd Initialized when the :meth:`start` is called. This will be a file object opened for writing. .. attribute:: newline The string to add to the end of a line. This is only used when the output filename has been specified. """ def __init__(self, options): # type: (optparse.Values) -> None """Initialize with the options parsed from config and cli. This also calls a hook, :meth:`after_init`, so subclasses do not need to call super to call this method. :param optparse.Values options: User specified configuration parsed from both configuration files and the command-line interface. """ self.options = options self.filename = options.output_file self.output_fd = None # type: Optional[IO[str]] self.newline = "\n" self.after_init()
[docs] def after_init(self): # type: () -> None """Initialize the formatter further."""
[docs] def beginning(self, filename): # type: (str) -> None """Notify the formatter that we're starting to process a file. :param str filename: The name of the file that Flake8 is beginning to report results from. """
[docs] def finished(self, filename): # type: (str) -> None """Notify the formatter that we've finished processing a file. :param str filename: The name of the file that Flake8 has finished reporting results from. """
[docs] def start(self): # type: () -> None """Prepare the formatter to receive input. This defaults to initializing :attr:`output_fd` if :attr:`filename` """ if self.filename: self.output_fd = open(self.filename, "a")
[docs] def handle(self, error): # type: (Violation) -> None """Handle an error reported by Flake8. This defaults to calling :meth:`format`, :meth:`show_source`, and then :meth:`write`. To extend how errors are handled, override this method. :param error: This will be an instance of :class:`~flake8.style_guide.Violation`. :type error: flake8.style_guide.Violation """ line = self.format(error) source = self.show_source(error) self.write(line, source)
[docs] def format(self, error): # type: (Violation) -> Optional[str] """Format an error reported by Flake8. This method **must** be implemented by subclasses. :param error: This will be an instance of :class:`~flake8.style_guide.Violation`. :type error: flake8.style_guide.Violation :returns: The formatted error string. :rtype: str """ raise NotImplementedError( "Subclass of BaseFormatter did not implement" " format." )
[docs] def show_statistics(self, statistics): # type: (Statistics) -> None """Format and print the statistics.""" for error_code in statistics.error_codes(): stats_for_error_code = statistics.statistics_for(error_code) statistic = next(stats_for_error_code) count = statistic.count count += sum(stat.count for stat in stats_for_error_code) self._write( "{count:<5} {error_code} {message}".format( count=count, error_code=error_code, message=statistic.message, ) )
[docs] def show_benchmarks(self, benchmarks): # type: (List[Tuple[str, float]]) -> None """Format and print the benchmarks.""" # NOTE(sigmavirus24): The format strings are a little confusing, even # to me, so here's a quick explanation: # We specify the named value first followed by a ':' to indicate we're # formatting the value. # Next we use '<' to indicate we want the value left aligned. # Then '10' is the width of the area. # For floats, finally, we only want only want at most 3 digits after # the decimal point to be displayed. This is the precision and it # can not be specified for integers which is why we need two separate # format strings. float_format = "{value:<10.3} {statistic}".format int_format = "{value:<10} {statistic}".format for statistic, value in benchmarks: if isinstance(value, int): benchmark = int_format(statistic=statistic, value=value) else: benchmark = float_format(statistic=statistic, value=value) self._write(benchmark)
[docs] def show_source(self, error): # type: (Violation) -> Optional[str] """Show the physical line generating the error. This also adds an indicator for the particular part of the line that is reported as generating the problem. :param error: This will be an instance of :class:`~flake8.style_guide.Violation`. :type error: flake8.style_guide.Violation :returns: The formatted error string if the user wants to show the source. If the user does not want to show the source, this will return ``None``. :rtype: str """ if not self.options.show_source or error.physical_line is None: return "" # Because column numbers are 1-indexed, we need to remove one to get # the proper number of space characters. pointer = (" " * (error.column_number - 1)) + "^" # Physical lines have a newline at the end, no need to add an extra # one return error.physical_line + pointer
def _write(self, output): # type: (str) -> None """Handle logic of whether to use an output file or print().""" if self.output_fd is not None: self.output_fd.write(output + self.newline) if self.output_fd is None or self.options.tee: print(output, end=self.newline)
[docs] def write(self, line, source): # type: (Optional[str], Optional[str]) -> None """Write the line either to the output file or stdout. This handles deciding whether to write to a file or print to standard out for subclasses. Override this if you want behaviour that differs from the default. :param str line: The formatted string to print or write. :param str source: The source code that has been formatted and associated with the line of output. """ if line: self._write(line) if source: self._write(source)
[docs] def stop(self): # type: () -> None """Clean up after reporting is finished.""" if self.output_fd is not None: self.output_fd.close() self.output_fd = None