1
"""The base class and interface for all formatting plugins."""
2
from __future__ import print_function
5
class BaseFormatter(object):
6
"""Class defining the formatter interface.
10
The options parsed from both configuration files and the command-line.
12
.. attribute:: filename
14
If specified by the user, the path to store the results of the run.
16
.. attribute:: output_fd
18
Initialized when the :meth:`start` is called. This will be a file
19
object opened for writing.
21
.. attribute:: newline
23
The string to add to the end of a line. This is only used when the
24
output filename has been specified.
27
def __init__(self, options):
28
"""Initialize with the options parsed from config and cli.
30
This also calls a hook, :meth:`after_init`, so subclasses do not need
31
to call super to call this method.
33
:param optparse.Values options:
34
User specified configuration parsed from both configuration files
35
and the command-line interface.
37
self.options = options
38
self.filename = options.output_file
44
"""Initialize the formatter further."""
48
"""Prepare the formatter to receive input.
50
This defaults to initializing :attr:`output_fd` if :attr:`filename`
53
self.output_fd = open(self.filename, 'a')
55
def handle(self, error):
56
"""Handle an error reported by Flake8.
58
This defaults to calling :meth:`format`, :meth:`show_source`, and
59
then :meth:`write`. To extend how errors are handled, override this
63
This will be an instance of :class:`~flake8.style_guide.Error`.
65
flake8.style_guide.Error
67
line = self.format(error)
68
source = self.show_source(error)
69
self.write(line, source)
71
def format(self, error):
72
"""Format an error reported by Flake8.
74
This method **must** be implemented by subclasses.
77
This will be an instance of :class:`~flake8.style_guide.Error`.
79
flake8.style_guide.Error
81
The formatted error string.
85
raise NotImplementedError('Subclass of BaseFormatter did not implement'
88
def show_statistics(self, statistics):
89
"""Format and print the statistics."""
90
for error_code in statistics.error_codes():
91
stats_for_error_code = statistics.statistics_for(error_code)
92
statistic = next(stats_for_error_code)
93
count = statistic.count
94
count += sum(stat.count for stat in stats_for_error_code)
95
self._write('{count:<5} {error_code} {message}'.format(
97
error_code=error_code,
98
message=statistic.message,
101
def show_benchmarks(self, benchmarks):
102
"""Format and print the benchmarks."""
103
# NOTE(sigmavirus24): The format strings are a little confusing, even
104
# to me, so here's a quick explanation:
105
# We specify the named value first followed by a ':' to indicate we're
106
# formatting the value.
107
# Next we use '<' to indicate we want the value left aligned.
108
# Then '10' is the width of the area.
109
# For floats, finally, we only want only want at most 3 digits after
110
# the decimal point to be displayed. This is the precision and it
111
# can not be specified for integers which is why we need two separate
113
float_format = '{value:<10.3} {statistic}'.format
114
int_format = '{value:<10} {statistic}'.format
115
for statistic, value in benchmarks:
116
if isinstance(value, int):
117
benchmark = int_format(statistic=statistic, value=value)
119
benchmark = float_format(statistic=statistic, value=value)
120
self._write(benchmark)
122
def show_source(self, error):
123
"""Show the physical line generating the error.
125
This also adds an indicator for the particular part of the line that
126
is reported as generating the problem.
129
This will be an instance of :class:`~flake8.style_guide.Error`.
131
flake8.style_guide.Error
133
The formatted error string if the user wants to show the source.
134
If the user does not want to show the source, this will return
139
if not self.options.show_source or error.physical_line is None:
142
# Because column numbers are 1-indexed, we need to remove one to get
143
# the proper number of space characters.
144
pointer = (' ' * (error.column_number - 1)) + '^'
145
# Physical lines have a newline at the end, no need to add an extra
147
return error.physical_line + pointer
149
def _write(self, output):
150
"""Handle logic of whether to use an output file or print()."""
151
if self.output_fd is not None:
152
self.output_fd.write(output + self.newline)
156
def write(self, line, source):
157
"""Write the line either to the output file or stdout.
159
This handles deciding whether to write to a file or print to standard
160
out for subclasses. Override this if you want behaviour that differs
164
The formatted string to print or write.
166
The source code that has been formatted and associated with the
175
"""Clean up after reporting is finished."""
176
if self.output_fd is not None:
177
self.output_fd.close()
178
self.output_fd = None