mirror of
https://github.com/PyCQA/flake8.git
synced 2026-04-09 14:24:17 +00:00
Add --benchmark and formatting for its values
This commit is contained in:
parent
1f7a8081ad
commit
adedd6c5cf
6 changed files with 112 additions and 6 deletions
|
|
@ -10,6 +10,12 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
multiprocessing = None
|
multiprocessing = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
import Queue as queue
|
||||||
|
except ImportError:
|
||||||
|
import queue
|
||||||
|
|
||||||
|
from flake8 import defaults
|
||||||
from flake8 import exceptions
|
from flake8 import exceptions
|
||||||
from flake8 import processor
|
from flake8 import processor
|
||||||
from flake8 import utils
|
from flake8 import utils
|
||||||
|
|
@ -72,14 +78,22 @@ class Manager(object):
|
||||||
self.jobs = self._job_count()
|
self.jobs = self._job_count()
|
||||||
self.process_queue = None
|
self.process_queue = None
|
||||||
self.results_queue = None
|
self.results_queue = None
|
||||||
|
self.statistics_queue = None
|
||||||
self.using_multiprocessing = self.jobs > 1
|
self.using_multiprocessing = self.jobs > 1
|
||||||
self.processes = []
|
self.processes = []
|
||||||
self.checkers = []
|
self.checkers = []
|
||||||
|
self.statistics = {
|
||||||
|
'files': 0,
|
||||||
|
'logical lines': 0,
|
||||||
|
'physical lines': 0,
|
||||||
|
'tokens': 0,
|
||||||
|
}
|
||||||
|
|
||||||
if self.using_multiprocessing:
|
if self.using_multiprocessing:
|
||||||
try:
|
try:
|
||||||
self.process_queue = multiprocessing.Queue()
|
self.process_queue = multiprocessing.Queue()
|
||||||
self.results_queue = multiprocessing.Queue()
|
self.results_queue = multiprocessing.Queue()
|
||||||
|
self.statistics_queue = multiprocessing.Queue()
|
||||||
except OSError as oserr:
|
except OSError as oserr:
|
||||||
if oserr.errno not in SERIAL_RETRY_ERRNOS:
|
if oserr.errno not in SERIAL_RETRY_ERRNOS:
|
||||||
raise
|
raise
|
||||||
|
|
@ -96,6 +110,29 @@ class Manager(object):
|
||||||
proc.join(0.2)
|
proc.join(0.2)
|
||||||
self._cleanup_queue(self.process_queue)
|
self._cleanup_queue(self.process_queue)
|
||||||
self._cleanup_queue(self.results_queue)
|
self._cleanup_queue(self.results_queue)
|
||||||
|
self._cleanup_queue(self.statistics_queue)
|
||||||
|
|
||||||
|
def _process_statistics(self):
|
||||||
|
all_statistics = self.statistics
|
||||||
|
if self.using_multiprocessing:
|
||||||
|
total_number_of_checkers = len(self.checkers)
|
||||||
|
statistics_gathered = 0
|
||||||
|
while statistics_gathered < total_number_of_checkers:
|
||||||
|
try:
|
||||||
|
statistics = self.statistics_queue.get(block=False)
|
||||||
|
statistics_gathered += 1
|
||||||
|
except queue.Empty:
|
||||||
|
break
|
||||||
|
|
||||||
|
for statistic in defaults.STATISTIC_NAMES:
|
||||||
|
all_statistics[statistic] += statistics[statistic]
|
||||||
|
else:
|
||||||
|
statistics_generator = (checker.statistics
|
||||||
|
for checker in self.checkers)
|
||||||
|
for statistics in statistics_generator:
|
||||||
|
for statistic in defaults.STATISTIC_NAMES:
|
||||||
|
all_statistics[statistic] += statistics[statistic]
|
||||||
|
all_statistics['files'] += len(self.checkers)
|
||||||
|
|
||||||
def _job_count(self):
|
def _job_count(self):
|
||||||
# type: () -> Union[int, NoneType]
|
# type: () -> Union[int, NoneType]
|
||||||
|
|
@ -182,7 +219,7 @@ class Manager(object):
|
||||||
LOG.info('Running checks in parallel')
|
LOG.info('Running checks in parallel')
|
||||||
for checker in iter(self.process_queue.get, 'DONE'):
|
for checker in iter(self.process_queue.get, 'DONE'):
|
||||||
LOG.debug('Running checker for file "%s"', checker.filename)
|
LOG.debug('Running checker for file "%s"', checker.filename)
|
||||||
checker.run_checks(self.results_queue)
|
checker.run_checks(self.results_queue, self.statistics_queue)
|
||||||
self.results_queue.put('DONE')
|
self.results_queue.put('DONE')
|
||||||
|
|
||||||
def is_path_excluded(self, path):
|
def is_path_excluded(self, path):
|
||||||
|
|
@ -280,7 +317,7 @@ class Manager(object):
|
||||||
def run_serial(self):
|
def run_serial(self):
|
||||||
"""Run the checkers in serial."""
|
"""Run the checkers in serial."""
|
||||||
for checker in self.checkers:
|
for checker in self.checkers:
|
||||||
checker.run_checks(self.results_queue)
|
checker.run_checks(self.results_queue, self.statistics_queue)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""Run all the checkers.
|
"""Run all the checkers.
|
||||||
|
|
@ -325,6 +362,7 @@ class Manager(object):
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""Stop checking files."""
|
"""Stop checking files."""
|
||||||
|
self._process_statistics()
|
||||||
for proc in self.processes:
|
for proc in self.processes:
|
||||||
LOG.info('Joining %s to the main process', proc.name)
|
LOG.info('Joining %s to the main process', proc.name)
|
||||||
proc.join()
|
proc.join()
|
||||||
|
|
@ -342,12 +380,21 @@ class FileChecker(object):
|
||||||
The plugins registered to check the file.
|
The plugins registered to check the file.
|
||||||
:type checks:
|
:type checks:
|
||||||
flake8.plugins.manager.Checkers
|
flake8.plugins.manager.Checkers
|
||||||
|
:param style_guide:
|
||||||
|
The initialized StyleGuide for this particular run.
|
||||||
|
:type style_guide:
|
||||||
|
flake8.style_guide.StyleGuide
|
||||||
"""
|
"""
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
self.checks = checks
|
self.checks = checks
|
||||||
self.style_guide = style_guide
|
self.style_guide = style_guide
|
||||||
self.results = []
|
self.results = []
|
||||||
self.processor = self._make_processor()
|
self.processor = self._make_processor()
|
||||||
|
self.statistics = {
|
||||||
|
'tokens': 0,
|
||||||
|
'logical lines': 0,
|
||||||
|
'physical lines': len(self.processor.lines),
|
||||||
|
}
|
||||||
|
|
||||||
def _make_processor(self):
|
def _make_processor(self):
|
||||||
try:
|
try:
|
||||||
|
|
@ -466,8 +513,10 @@ class FileChecker(object):
|
||||||
:meth:`flake8.checker.FileChecker.run_checks`.
|
:meth:`flake8.checker.FileChecker.run_checks`.
|
||||||
"""
|
"""
|
||||||
parens = 0
|
parens = 0
|
||||||
|
statistics = self.statistics
|
||||||
file_processor = self.processor
|
file_processor = self.processor
|
||||||
for token in file_processor.generate_tokens():
|
for token in file_processor.generate_tokens():
|
||||||
|
statistics['tokens'] += 1
|
||||||
self.check_physical_eol(token)
|
self.check_physical_eol(token)
|
||||||
token_type, text = token[0:2]
|
token_type, text = token[0:2]
|
||||||
processor.log_token(LOG, token)
|
processor.log_token(LOG, token)
|
||||||
|
|
@ -485,7 +534,7 @@ class FileChecker(object):
|
||||||
self.run_physical_checks(file_processor.lines[-1])
|
self.run_physical_checks(file_processor.lines[-1])
|
||||||
self.run_logical_checks()
|
self.run_logical_checks()
|
||||||
|
|
||||||
def run_checks(self, results_queue):
|
def run_checks(self, results_queue, statistics_queue):
|
||||||
"""Run checks against the file."""
|
"""Run checks against the file."""
|
||||||
if self.processor.should_ignore_file():
|
if self.processor.should_ignore_file():
|
||||||
return
|
return
|
||||||
|
|
@ -501,6 +550,11 @@ class FileChecker(object):
|
||||||
if results_queue is not None:
|
if results_queue is not None:
|
||||||
results_queue.put((self.filename, self.results))
|
results_queue.put((self.filename, self.results))
|
||||||
|
|
||||||
|
logical_lines = self.processor.statistics['logical lines']
|
||||||
|
self.statistics['logical lines'] = logical_lines
|
||||||
|
if statistics_queue is not None:
|
||||||
|
statistics_queue.put(self.statistics)
|
||||||
|
|
||||||
def handle_comment(self, token, token_text):
|
def handle_comment(self, token, token_text):
|
||||||
"""Handle the logic when encountering a comment token."""
|
"""Handle the logic when encountering a comment token."""
|
||||||
# The comment also ends a physical line
|
# The comment also ends a physical line
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,11 @@ MAX_LINE_LENGTH = 79
|
||||||
|
|
||||||
TRUTHY_VALUES = set(['true', '1', 't'])
|
TRUTHY_VALUES = set(['true', '1', 't'])
|
||||||
|
|
||||||
# Other consants
|
# Other constants
|
||||||
WHITESPACE = frozenset(' \t')
|
WHITESPACE = frozenset(' \t')
|
||||||
|
|
||||||
|
STATISTIC_NAMES = (
|
||||||
|
'logical lines',
|
||||||
|
'physical lines',
|
||||||
|
'tokens',
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,24 @@ class BaseFormatter(object):
|
||||||
|
|
||||||
def show_benchmarks(self, benchmarks):
|
def show_benchmarks(self, benchmarks):
|
||||||
"""Format and print the benchmarks."""
|
"""Format and print the benchmarks."""
|
||||||
pass
|
# 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)
|
||||||
|
|
||||||
def show_source(self, error):
|
def show_source(self, error):
|
||||||
"""Show the physical line generating the error.
|
"""Show the physical line generating the error.
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import time
|
||||||
|
|
||||||
import flake8
|
import flake8
|
||||||
from flake8 import checker
|
from flake8 import checker
|
||||||
|
from flake8 import defaults
|
||||||
from flake8 import style_guide
|
from flake8 import style_guide
|
||||||
from flake8 import utils
|
from flake8 import utils
|
||||||
from flake8.main import options
|
from flake8.main import options
|
||||||
|
|
@ -225,6 +226,22 @@ class Application(object):
|
||||||
self.file_checker_manager.run()
|
self.file_checker_manager.run()
|
||||||
LOG.info('Finished running')
|
LOG.info('Finished running')
|
||||||
self.file_checker_manager.stop()
|
self.file_checker_manager.stop()
|
||||||
|
self.end_time = time.time()
|
||||||
|
|
||||||
|
def report_benchmarks(self):
|
||||||
|
if not self.options.benchmark:
|
||||||
|
return
|
||||||
|
time_elapsed = self.end_time - self.start_time
|
||||||
|
statistics = [('seconds elapsed', time_elapsed)]
|
||||||
|
add_statistic = statistics.append
|
||||||
|
for statistic in (defaults.STATISTIC_NAMES + ('files',)):
|
||||||
|
value = self.file_checker_manager.statistics[statistic]
|
||||||
|
total_description = 'total ' + statistic + ' processed'
|
||||||
|
add_statistic((total_description, value))
|
||||||
|
per_second_description = statistic + ' processed per second'
|
||||||
|
add_statistic((per_second_description, int(value / time_elapsed)))
|
||||||
|
|
||||||
|
self.formatter.show_benchmarks(statistics)
|
||||||
|
|
||||||
def report_errors(self):
|
def report_errors(self):
|
||||||
# type: () -> NoneType
|
# type: () -> NoneType
|
||||||
|
|
@ -259,7 +276,7 @@ class Application(object):
|
||||||
self.initialize(argv)
|
self.initialize(argv)
|
||||||
self.run_checks()
|
self.run_checks()
|
||||||
self.report_errors()
|
self.report_errors()
|
||||||
self.end_time = time.time()
|
self.report_benchmarks()
|
||||||
|
|
||||||
def run(self, argv=None):
|
def run(self, argv=None):
|
||||||
# type: (Union[NoneType, List[str]]) -> NoneType
|
# type: (Union[NoneType, List[str]]) -> NoneType
|
||||||
|
|
|
||||||
|
|
@ -192,3 +192,10 @@ def register_default_options(option_manager):
|
||||||
'--isolated', default=False, action='store_true',
|
'--isolated', default=False, action='store_true',
|
||||||
help='Ignore all found configuration files.',
|
help='Ignore all found configuration files.',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Benchmarking
|
||||||
|
|
||||||
|
add_option(
|
||||||
|
'--benchmark', default=False, action='store_true',
|
||||||
|
help='Print benchmark information about this run of Flake8',
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,10 @@ class FileProcessor(object):
|
||||||
self.total_lines = len(self.lines)
|
self.total_lines = len(self.lines)
|
||||||
#: Verbosity level of Flake8
|
#: Verbosity level of Flake8
|
||||||
self.verbose = options.verbose
|
self.verbose = options.verbose
|
||||||
|
#: Statistics dictionary
|
||||||
|
self.statistics = {
|
||||||
|
'logical lines': 0,
|
||||||
|
}
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def inside_multiline(self, line_number):
|
def inside_multiline(self, line_number):
|
||||||
|
|
@ -186,6 +190,7 @@ class FileProcessor(object):
|
||||||
"""Build a logical line from the current tokens list."""
|
"""Build a logical line from the current tokens list."""
|
||||||
comments, logical, mapping_list = self.build_logical_line_tokens()
|
comments, logical, mapping_list = self.build_logical_line_tokens()
|
||||||
self.logical_line = ''.join(logical)
|
self.logical_line = ''.join(logical)
|
||||||
|
self.statistics['logical lines'] += 1
|
||||||
return ''.join(comments), self.logical_line, mapping_list
|
return ''.join(comments), self.logical_line, mapping_list
|
||||||
|
|
||||||
def split_line(self, token):
|
def split_line(self, token):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue