mirror of
https://github.com/PyCQA/flake8.git
synced 2026-04-11 23:34:17 +00:00
Use black to reformat Flake8
Instead of just using Flake8 and pylint to keep Flake8 clean, let's also use black to make it less manual for clean-up.
This commit is contained in:
parent
a2b7a7e4c5
commit
c58a4662d8
27 changed files with 1052 additions and 812 deletions
|
|
@ -40,7 +40,7 @@ python37:
|
||||||
script: tox -e py37
|
script: tox -e py37
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
image: python:3.5
|
image: python:3.6
|
||||||
stage: test
|
stage: test
|
||||||
script: tox -e linters
|
script: tox -e linters
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,14 +15,16 @@ import sys
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
LOG.addHandler(logging.NullHandler())
|
LOG.addHandler(logging.NullHandler())
|
||||||
|
|
||||||
__version__ = '3.5.0'
|
__version__ = "3.5.0"
|
||||||
__version_info__ = tuple(int(i) for i in __version__.split('.') if i.isdigit())
|
__version_info__ = tuple(
|
||||||
|
int(i) for i in __version__.split(".") if i.isdigit()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# There is nothing lower than logging.DEBUG (10) in the logging library,
|
# There is nothing lower than logging.DEBUG (10) in the logging library,
|
||||||
# but we want an extra level to avoid being too verbose when using -vv.
|
# but we want an extra level to avoid being too verbose when using -vv.
|
||||||
_EXTRA_VERBOSE = 5
|
_EXTRA_VERBOSE = 5
|
||||||
logging.addLevelName(_EXTRA_VERBOSE, 'VERBOSE')
|
logging.addLevelName(_EXTRA_VERBOSE, "VERBOSE")
|
||||||
|
|
||||||
_VERBOSITY_TO_LOG_LEVEL = {
|
_VERBOSITY_TO_LOG_LEVEL = {
|
||||||
# output more than warnings but not debugging info
|
# output more than warnings but not debugging info
|
||||||
|
|
@ -33,8 +35,10 @@ _VERBOSITY_TO_LOG_LEVEL = {
|
||||||
3: _EXTRA_VERBOSE,
|
3: _EXTRA_VERBOSE,
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_FORMAT = ('%(name)-25s %(processName)-11s %(relativeCreated)6d '
|
LOG_FORMAT = (
|
||||||
'%(levelname)-8s %(message)s')
|
"%(name)-25s %(processName)-11s %(relativeCreated)6d "
|
||||||
|
"%(levelname)-8s %(message)s"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def configure_logging(verbosity, filename=None, logformat=LOG_FORMAT):
|
def configure_logging(verbosity, filename=None, logformat=LOG_FORMAT):
|
||||||
|
|
@ -55,8 +59,8 @@ def configure_logging(verbosity, filename=None, logformat=LOG_FORMAT):
|
||||||
|
|
||||||
log_level = _VERBOSITY_TO_LOG_LEVEL[verbosity]
|
log_level = _VERBOSITY_TO_LOG_LEVEL[verbosity]
|
||||||
|
|
||||||
if not filename or filename in ('stderr', 'stdout'):
|
if not filename or filename in ("stderr", "stdout"):
|
||||||
fileobj = getattr(sys, filename or 'stderr')
|
fileobj = getattr(sys, filename or "stderr")
|
||||||
handler_cls = logging.StreamHandler
|
handler_cls = logging.StreamHandler
|
||||||
else:
|
else:
|
||||||
fileobj = filename
|
fileobj = filename
|
||||||
|
|
@ -66,5 +70,6 @@ def configure_logging(verbosity, filename=None, logformat=LOG_FORMAT):
|
||||||
handler.setFormatter(logging.Formatter(logformat))
|
handler.setFormatter(logging.Formatter(logformat))
|
||||||
LOG.addHandler(handler)
|
LOG.addHandler(handler)
|
||||||
LOG.setLevel(log_level)
|
LOG.setLevel(log_level)
|
||||||
LOG.debug('Added a %s logging handler to logger root at %s',
|
LOG.debug(
|
||||||
filename, __name__)
|
"Added a %s logging handler to logger root at %s", filename, __name__
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ from flake8.main import application as app
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
__all__ = ('get_style_guide',)
|
__all__ = ("get_style_guide",)
|
||||||
|
|
||||||
|
|
||||||
def get_style_guide(**kwargs):
|
def get_style_guide(**kwargs):
|
||||||
|
|
@ -29,7 +29,8 @@ def get_style_guide(**kwargs):
|
||||||
application = app.Application()
|
application = app.Application()
|
||||||
application.parse_preliminary_options_and_args([])
|
application.parse_preliminary_options_and_args([])
|
||||||
flake8.configure_logging(
|
flake8.configure_logging(
|
||||||
application.prelim_opts.verbose, application.prelim_opts.output_file)
|
application.prelim_opts.verbose, application.prelim_opts.output_file
|
||||||
|
)
|
||||||
application.make_config_finder()
|
application.make_config_finder()
|
||||||
application.find_plugins()
|
application.find_plugins()
|
||||||
application.register_plugin_options()
|
application.register_plugin_options()
|
||||||
|
|
@ -113,18 +114,22 @@ class StyleGuide(object):
|
||||||
:rtype:
|
:rtype:
|
||||||
bool
|
bool
|
||||||
"""
|
"""
|
||||||
return (self._file_checker_manager.is_path_excluded(filename) or
|
return self._file_checker_manager.is_path_excluded(filename) or (
|
||||||
(parent and
|
parent
|
||||||
self._file_checker_manager.is_path_excluded(
|
and self._file_checker_manager.is_path_excluded(
|
||||||
os.path.join(parent, filename))))
|
os.path.join(parent, filename)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def init_report(self, reporter=None):
|
def init_report(self, reporter=None):
|
||||||
"""Set up a formatter for this run of Flake8."""
|
"""Set up a formatter for this run of Flake8."""
|
||||||
if reporter is None:
|
if reporter is None:
|
||||||
return
|
return
|
||||||
if not issubclass(reporter, formatter.BaseFormatter):
|
if not issubclass(reporter, formatter.BaseFormatter):
|
||||||
raise ValueError("Report should be subclass of "
|
raise ValueError(
|
||||||
"flake8.formatter.BaseFormatter.")
|
"Report should be subclass of "
|
||||||
|
"flake8.formatter.BaseFormatter."
|
||||||
|
)
|
||||||
self._application.formatter = None
|
self._application.formatter = None
|
||||||
self._application.make_formatter(reporter)
|
self._application.make_formatter(reporter)
|
||||||
self._application.guide = None
|
self._application.guide = None
|
||||||
|
|
@ -197,6 +202,6 @@ class Report(object):
|
||||||
list
|
list
|
||||||
"""
|
"""
|
||||||
return [
|
return [
|
||||||
'{} {} {}'.format(s.count, s.error_code, s.message)
|
"{} {} {}".format(s.count, s.error_code, s.message)
|
||||||
for s in self._stats.statistics_for(violation)
|
for s in self._stats.statistics_for(violation)
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -77,17 +77,17 @@ class Manager(object):
|
||||||
self.processes = []
|
self.processes = []
|
||||||
self.checkers = []
|
self.checkers = []
|
||||||
self.statistics = {
|
self.statistics = {
|
||||||
'files': 0,
|
"files": 0,
|
||||||
'logical lines': 0,
|
"logical lines": 0,
|
||||||
'physical lines': 0,
|
"physical lines": 0,
|
||||||
'tokens': 0,
|
"tokens": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
def _process_statistics(self):
|
def _process_statistics(self):
|
||||||
for checker in self.checkers:
|
for checker in self.checkers:
|
||||||
for statistic in defaults.STATISTIC_NAMES:
|
for statistic in defaults.STATISTIC_NAMES:
|
||||||
self.statistics[statistic] += checker.statistics[statistic]
|
self.statistics[statistic] += checker.statistics[statistic]
|
||||||
self.statistics['files'] += len(self.checkers)
|
self.statistics["files"] += len(self.checkers)
|
||||||
|
|
||||||
def _job_count(self):
|
def _job_count(self):
|
||||||
# type: () -> int
|
# type: () -> int
|
||||||
|
|
@ -101,40 +101,53 @@ class Manager(object):
|
||||||
# multiprocessing and which really shouldn't require multiprocessing
|
# multiprocessing and which really shouldn't require multiprocessing
|
||||||
# - the user provided some awful input
|
# - the user provided some awful input
|
||||||
if not multiprocessing:
|
if not multiprocessing:
|
||||||
LOG.warning('The multiprocessing module is not available. '
|
LOG.warning(
|
||||||
'Ignoring --jobs arguments.')
|
"The multiprocessing module is not available. "
|
||||||
|
"Ignoring --jobs arguments."
|
||||||
|
)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
if (utils.is_windows() and
|
if (
|
||||||
not utils.can_run_multiprocessing_on_windows()):
|
utils.is_windows()
|
||||||
LOG.warning('The --jobs option is not available on Windows due to'
|
and not utils.can_run_multiprocessing_on_windows()
|
||||||
' a bug (https://bugs.python.org/issue27649) in '
|
):
|
||||||
'Python 2.7.11+ and 3.3+. We have detected that you '
|
LOG.warning(
|
||||||
'are running an unsupported version of Python on '
|
"The --jobs option is not available on Windows due to"
|
||||||
'Windows. Ignoring --jobs arguments.')
|
" a bug (https://bugs.python.org/issue27649) in "
|
||||||
|
"Python 2.7.11+ and 3.3+. We have detected that you "
|
||||||
|
"are running an unsupported version of Python on "
|
||||||
|
"Windows. Ignoring --jobs arguments."
|
||||||
|
)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
if utils.is_using_stdin(self.arguments):
|
if utils.is_using_stdin(self.arguments):
|
||||||
LOG.warning('The --jobs option is not compatible with supplying '
|
LOG.warning(
|
||||||
'input using - . Ignoring --jobs arguments.')
|
"The --jobs option is not compatible with supplying "
|
||||||
|
"input using - . Ignoring --jobs arguments."
|
||||||
|
)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
if self.options.diff:
|
if self.options.diff:
|
||||||
LOG.warning('The --diff option was specified with --jobs but '
|
LOG.warning(
|
||||||
'they are not compatible. Ignoring --jobs arguments.')
|
"The --diff option was specified with --jobs but "
|
||||||
|
"they are not compatible. Ignoring --jobs arguments."
|
||||||
|
)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
jobs = self.options.jobs
|
jobs = self.options.jobs
|
||||||
if jobs != 'auto' and not jobs.isdigit():
|
if jobs != "auto" and not jobs.isdigit():
|
||||||
LOG.warning('"%s" is not a valid parameter to --jobs. Must be one '
|
LOG.warning(
|
||||||
'of "auto" or a numerical value, e.g., 4.', jobs)
|
'"%s" is not a valid parameter to --jobs. Must be one '
|
||||||
|
'of "auto" or a numerical value, e.g., 4.',
|
||||||
|
jobs,
|
||||||
|
)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# If the value is "auto", we want to let the multiprocessing library
|
# If the value is "auto", we want to let the multiprocessing library
|
||||||
# decide the number based on the number of CPUs. However, if that
|
# decide the number based on the number of CPUs. However, if that
|
||||||
# function is not implemented for this particular value of Python we
|
# function is not implemented for this particular value of Python we
|
||||||
# default to 1
|
# default to 1
|
||||||
if jobs == 'auto':
|
if jobs == "auto":
|
||||||
try:
|
try:
|
||||||
return multiprocessing.cpu_count()
|
return multiprocessing.cpu_count()
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
|
|
@ -170,8 +183,8 @@ class Manager(object):
|
||||||
:rtype:
|
:rtype:
|
||||||
bool
|
bool
|
||||||
"""
|
"""
|
||||||
if path == '-':
|
if path == "-":
|
||||||
if self.options.stdin_display_name == 'stdin':
|
if self.options.stdin_display_name == "stdin":
|
||||||
return False
|
return False
|
||||||
path = self.options.stdin_display_name
|
path = self.options.stdin_display_name
|
||||||
|
|
||||||
|
|
@ -185,8 +198,9 @@ class Manager(object):
|
||||||
|
|
||||||
absolute_path = os.path.abspath(path)
|
absolute_path = os.path.abspath(path)
|
||||||
match = utils.fnmatch(absolute_path, exclude)
|
match = utils.fnmatch(absolute_path, exclude)
|
||||||
LOG.debug('"%s" has %sbeen excluded', absolute_path,
|
LOG.debug(
|
||||||
'' if match else 'not ')
|
'"%s" has %sbeen excluded', absolute_path, "" if match else "not "
|
||||||
|
)
|
||||||
return match
|
return match
|
||||||
|
|
||||||
def make_checkers(self, paths=None):
|
def make_checkers(self, paths=None):
|
||||||
|
|
@ -196,7 +210,7 @@ class Manager(object):
|
||||||
paths = self.arguments
|
paths = self.arguments
|
||||||
|
|
||||||
if not paths:
|
if not paths:
|
||||||
paths = ['.']
|
paths = ["."]
|
||||||
|
|
||||||
filename_patterns = self.options.filename
|
filename_patterns = self.options.filename
|
||||||
running_from_vcs = self.options._running_from_vcs
|
running_from_vcs = self.options._running_from_vcs
|
||||||
|
|
@ -209,7 +223,7 @@ class Manager(object):
|
||||||
matches_filename_patterns = utils.fnmatch(
|
matches_filename_patterns = utils.fnmatch(
|
||||||
filename, filename_patterns
|
filename, filename_patterns
|
||||||
)
|
)
|
||||||
is_stdin = filename == '-'
|
is_stdin = filename == "-"
|
||||||
# NOTE(sigmavirus24): If a user explicitly specifies something,
|
# NOTE(sigmavirus24): If a user explicitly specifies something,
|
||||||
# e.g, ``flake8 bin/script`` then we should run Flake8 against
|
# e.g, ``flake8 bin/script`` then we should run Flake8 against
|
||||||
# that. Since should_create_file_checker looks to see if the
|
# that. Since should_create_file_checker looks to see if the
|
||||||
|
|
@ -217,24 +231,28 @@ class Manager(object):
|
||||||
# the event that the argument and the filename are identical.
|
# the event that the argument and the filename are identical.
|
||||||
# If it was specified explicitly, the user intended for it to be
|
# If it was specified explicitly, the user intended for it to be
|
||||||
# checked.
|
# checked.
|
||||||
explicitly_provided = (not running_from_vcs and
|
explicitly_provided = (
|
||||||
not running_from_diff and
|
not running_from_vcs
|
||||||
(argument == filename))
|
and not running_from_diff
|
||||||
return ((explicitly_provided or matches_filename_patterns) or
|
and (argument == filename)
|
||||||
is_stdin)
|
)
|
||||||
|
return (
|
||||||
|
explicitly_provided or matches_filename_patterns
|
||||||
|
) or is_stdin
|
||||||
|
|
||||||
checks = self.checks.to_dictionary()
|
checks = self.checks.to_dictionary()
|
||||||
checkers = (
|
checkers = (
|
||||||
FileChecker(filename, checks, self.options)
|
FileChecker(filename, checks, self.options)
|
||||||
for argument in paths
|
for argument in paths
|
||||||
for filename in utils.filenames_from(argument,
|
for filename in utils.filenames_from(
|
||||||
self.is_path_excluded)
|
argument, self.is_path_excluded
|
||||||
|
)
|
||||||
if should_create_file_checker(filename, argument)
|
if should_create_file_checker(filename, argument)
|
||||||
)
|
)
|
||||||
self.checkers = [
|
self.checkers = [
|
||||||
checker for checker in checkers if checker.should_process
|
checker for checker in checkers if checker.should_process
|
||||||
]
|
]
|
||||||
LOG.info('Checking %d files', len(self.checkers))
|
LOG.info("Checking %d files", len(self.checkers))
|
||||||
|
|
||||||
def report(self):
|
def report(self):
|
||||||
# type: () -> (int, int)
|
# type: () -> (int, int)
|
||||||
|
|
@ -250,7 +268,9 @@ class Manager(object):
|
||||||
"""
|
"""
|
||||||
results_reported = results_found = 0
|
results_reported = results_found = 0
|
||||||
for checker in self.checkers:
|
for checker in self.checkers:
|
||||||
results = sorted(checker.results, key=lambda tup: (tup[1], tup[2]))
|
results = sorted(
|
||||||
|
checker.results, key=lambda tup: (tup[1], tup[2])
|
||||||
|
)
|
||||||
filename = checker.display_name
|
filename = checker.display_name
|
||||||
with self.style_guide.processing_file(filename):
|
with self.style_guide.processing_file(filename):
|
||||||
results_reported += self._handle_results(filename, results)
|
results_reported += self._handle_results(filename, results)
|
||||||
|
|
@ -276,8 +296,7 @@ class Manager(object):
|
||||||
_run_checks,
|
_run_checks,
|
||||||
self.checkers,
|
self.checkers,
|
||||||
chunksize=calculate_pool_chunksize(
|
chunksize=calculate_pool_chunksize(
|
||||||
len(self.checkers),
|
len(self.checkers), self.jobs
|
||||||
self.jobs,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
for ret in pool_map:
|
for ret in pool_map:
|
||||||
|
|
@ -294,8 +313,9 @@ class Manager(object):
|
||||||
|
|
||||||
for checker in self.checkers:
|
for checker in self.checkers:
|
||||||
filename = checker.display_name
|
filename = checker.display_name
|
||||||
checker.results = sorted(final_results[filename],
|
checker.results = sorted(
|
||||||
key=lambda tup: (tup[2], tup[2]))
|
final_results[filename], key=lambda tup: (tup[2], tup[2])
|
||||||
|
)
|
||||||
checker.statistics = final_statistics[filename]
|
checker.statistics = final_statistics[filename]
|
||||||
|
|
||||||
def run_serial(self):
|
def run_serial(self):
|
||||||
|
|
@ -322,11 +342,11 @@ class Manager(object):
|
||||||
if oserr.errno not in SERIAL_RETRY_ERRNOS:
|
if oserr.errno not in SERIAL_RETRY_ERRNOS:
|
||||||
LOG.exception(oserr)
|
LOG.exception(oserr)
|
||||||
raise
|
raise
|
||||||
LOG.warning('Running in serial after OS exception, %r', oserr)
|
LOG.warning("Running in serial after OS exception, %r", oserr)
|
||||||
self.run_serial()
|
self.run_serial()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
LOG.warning('Flake8 was interrupted by the user')
|
LOG.warning("Flake8 was interrupted by the user")
|
||||||
raise exceptions.EarlyQuit('Early quit while running checks')
|
raise exceptions.EarlyQuit("Early quit while running checks")
|
||||||
|
|
||||||
def start(self, paths=None):
|
def start(self, paths=None):
|
||||||
"""Start checking files.
|
"""Start checking files.
|
||||||
|
|
@ -335,14 +355,14 @@ class Manager(object):
|
||||||
Path names to check. This is passed directly to
|
Path names to check. This is passed directly to
|
||||||
:meth:`~Manager.make_checkers`.
|
:meth:`~Manager.make_checkers`.
|
||||||
"""
|
"""
|
||||||
LOG.info('Making checkers')
|
LOG.info("Making checkers")
|
||||||
self.make_checkers(paths)
|
self.make_checkers(paths)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""Stop checking files."""
|
"""Stop checking files."""
|
||||||
self._process_statistics()
|
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()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -368,9 +388,9 @@ class FileChecker(object):
|
||||||
self.checks = checks
|
self.checks = checks
|
||||||
self.results = []
|
self.results = []
|
||||||
self.statistics = {
|
self.statistics = {
|
||||||
'tokens': 0,
|
"tokens": 0,
|
||||||
'logical lines': 0,
|
"logical lines": 0,
|
||||||
'physical lines': 0,
|
"physical lines": 0,
|
||||||
}
|
}
|
||||||
self.processor = self._make_processor()
|
self.processor = self._make_processor()
|
||||||
self.display_name = filename
|
self.display_name = filename
|
||||||
|
|
@ -378,11 +398,11 @@ class FileChecker(object):
|
||||||
if self.processor is not None:
|
if self.processor is not None:
|
||||||
self.display_name = self.processor.filename
|
self.display_name = self.processor.filename
|
||||||
self.should_process = not self.processor.should_ignore_file()
|
self.should_process = not self.processor.should_ignore_file()
|
||||||
self.statistics['physical lines'] = len(self.processor.lines)
|
self.statistics["physical lines"] = len(self.processor.lines)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""Provide helpful debugging representation."""
|
"""Provide helpful debugging representation."""
|
||||||
return 'FileChecker for {}'.format(self.filename)
|
return "FileChecker for {}".format(self.filename)
|
||||||
|
|
||||||
def _make_processor(self):
|
def _make_processor(self):
|
||||||
try:
|
try:
|
||||||
|
|
@ -395,20 +415,20 @@ class FileChecker(object):
|
||||||
# as an E902. We probably *want* a better error code for this
|
# as an E902. We probably *want* a better error code for this
|
||||||
# going forward.
|
# going forward.
|
||||||
(exc_type, exception) = sys.exc_info()[:2]
|
(exc_type, exception) = sys.exc_info()[:2]
|
||||||
message = '{0}: {1}'.format(exc_type.__name__, exception)
|
message = "{0}: {1}".format(exc_type.__name__, exception)
|
||||||
self.report('E902', 0, 0, message)
|
self.report("E902", 0, 0, message)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def report(self, error_code, line_number, column, text, line=None):
|
def report(self, error_code, line_number, column, text, line=None):
|
||||||
# type: (str, int, int, str) -> str
|
# type: (str, int, int, str) -> str
|
||||||
"""Report an error by storing it in the results list."""
|
"""Report an error by storing it in the results list."""
|
||||||
if error_code is None:
|
if error_code is None:
|
||||||
error_code, text = text.split(' ', 1)
|
error_code, text = text.split(" ", 1)
|
||||||
|
|
||||||
physical_line = line
|
physical_line = line
|
||||||
# If we're recovering from a problem in _make_processor, we will not
|
# If we're recovering from a problem in _make_processor, we will not
|
||||||
# have this attribute.
|
# have this attribute.
|
||||||
if not physical_line and getattr(self, 'processor', None):
|
if not physical_line and getattr(self, "processor", None):
|
||||||
physical_line = self.processor.line_for(line_number)
|
physical_line = self.processor.line_for(line_number)
|
||||||
|
|
||||||
error = (error_code, line_number, column, text, physical_line)
|
error = (error_code, line_number, column, text, physical_line)
|
||||||
|
|
@ -417,26 +437,24 @@ class FileChecker(object):
|
||||||
|
|
||||||
def run_check(self, plugin, **arguments):
|
def run_check(self, plugin, **arguments):
|
||||||
"""Run the check in a single plugin."""
|
"""Run the check in a single plugin."""
|
||||||
LOG.debug('Running %r with %r', plugin, arguments)
|
LOG.debug("Running %r with %r", plugin, arguments)
|
||||||
try:
|
try:
|
||||||
self.processor.keyword_arguments_for(
|
self.processor.keyword_arguments_for(
|
||||||
plugin['parameters'],
|
plugin["parameters"], arguments
|
||||||
arguments,
|
|
||||||
)
|
)
|
||||||
except AttributeError as ae:
|
except AttributeError as ae:
|
||||||
LOG.error('Plugin requested unknown parameters.')
|
LOG.error("Plugin requested unknown parameters.")
|
||||||
raise exceptions.PluginRequestedUnknownParameters(
|
raise exceptions.PluginRequestedUnknownParameters(
|
||||||
plugin=plugin,
|
plugin=plugin, exception=ae
|
||||||
exception=ae,
|
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
return plugin['plugin'](**arguments)
|
return plugin["plugin"](**arguments)
|
||||||
except Exception as all_exc:
|
except Exception as all_exc:
|
||||||
LOG.critical('Plugin %s raised an unexpected exception',
|
LOG.critical(
|
||||||
plugin['name'])
|
"Plugin %s raised an unexpected exception", plugin["name"]
|
||||||
|
)
|
||||||
raise exceptions.PluginExecutionFailed(
|
raise exceptions.PluginExecutionFailed(
|
||||||
plugin=plugin,
|
plugin=plugin, excetion=all_exc
|
||||||
excetion=all_exc,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -466,7 +484,7 @@ class FileChecker(object):
|
||||||
# "physical" line so much as what was accumulated by the point
|
# "physical" line so much as what was accumulated by the point
|
||||||
# tokenizing failed.
|
# tokenizing failed.
|
||||||
# See also: https://gitlab.com/pycqa/flake8/issues/237
|
# See also: https://gitlab.com/pycqa/flake8/issues/237
|
||||||
lines = physical_line.rstrip('\n').split('\n')
|
lines = physical_line.rstrip("\n").split("\n")
|
||||||
row_offset = len(lines) - 1
|
row_offset = len(lines) - 1
|
||||||
logical_line = lines[0]
|
logical_line = lines[0]
|
||||||
logical_line_length = len(logical_line)
|
logical_line_length = len(logical_line)
|
||||||
|
|
@ -483,11 +501,15 @@ class FileChecker(object):
|
||||||
except (ValueError, SyntaxError, TypeError):
|
except (ValueError, SyntaxError, TypeError):
|
||||||
(exc_type, exception) = sys.exc_info()[:2]
|
(exc_type, exception) = sys.exc_info()[:2]
|
||||||
row, column = self._extract_syntax_information(exception)
|
row, column = self._extract_syntax_information(exception)
|
||||||
self.report('E999', row, column, '%s: %s' %
|
self.report(
|
||||||
(exc_type.__name__, exception.args[0]))
|
"E999",
|
||||||
|
row,
|
||||||
|
column,
|
||||||
|
"%s: %s" % (exc_type.__name__, exception.args[0]),
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
for plugin in self.checks['ast_plugins']:
|
for plugin in self.checks["ast_plugins"]:
|
||||||
checker = self.run_check(plugin, tree=ast)
|
checker = self.run_check(plugin, tree=ast)
|
||||||
# If the plugin uses a class, call the run method of it, otherwise
|
# If the plugin uses a class, call the run method of it, otherwise
|
||||||
# the call should return something iterable itself
|
# the call should return something iterable itself
|
||||||
|
|
@ -512,7 +534,7 @@ class FileChecker(object):
|
||||||
|
|
||||||
LOG.debug('Logical line: "%s"', logical_line.rstrip())
|
LOG.debug('Logical line: "%s"', logical_line.rstrip())
|
||||||
|
|
||||||
for plugin in self.checks['logical_line_plugins']:
|
for plugin in self.checks["logical_line_plugins"]:
|
||||||
self.processor.update_checker_state_for(plugin)
|
self.processor.update_checker_state_for(plugin)
|
||||||
results = self.run_check(plugin, logical_line=logical_line) or ()
|
results = self.run_check(plugin, logical_line=logical_line) or ()
|
||||||
for offset, text in results:
|
for offset, text in results:
|
||||||
|
|
@ -529,7 +551,7 @@ class FileChecker(object):
|
||||||
|
|
||||||
def run_physical_checks(self, physical_line, override_error_line=None):
|
def run_physical_checks(self, physical_line, override_error_line=None):
|
||||||
"""Run all checks for a given physical line."""
|
"""Run all checks for a given physical line."""
|
||||||
for plugin in self.checks['physical_line_plugins']:
|
for plugin in self.checks["physical_line_plugins"]:
|
||||||
self.processor.update_checker_state_for(plugin)
|
self.processor.update_checker_state_for(plugin)
|
||||||
result = self.run_check(plugin, physical_line=physical_line)
|
result = self.run_check(plugin, physical_line=physical_line)
|
||||||
if result is not None:
|
if result is not None:
|
||||||
|
|
@ -555,7 +577,7 @@ class FileChecker(object):
|
||||||
statistics = self.statistics
|
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
|
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)
|
||||||
|
|
@ -564,8 +586,10 @@ class FileChecker(object):
|
||||||
elif parens == 0:
|
elif parens == 0:
|
||||||
if processor.token_is_newline(token):
|
if processor.token_is_newline(token):
|
||||||
self.handle_newline(token_type)
|
self.handle_newline(token_type)
|
||||||
elif (processor.token_is_comment(token) and
|
elif (
|
||||||
len(file_processor.tokens) == 1):
|
processor.token_is_comment(token)
|
||||||
|
and len(file_processor.tokens) == 1
|
||||||
|
):
|
||||||
self.handle_comment(token, text)
|
self.handle_comment(token, text)
|
||||||
|
|
||||||
if file_processor.tokens:
|
if file_processor.tokens:
|
||||||
|
|
@ -578,20 +602,24 @@ class FileChecker(object):
|
||||||
try:
|
try:
|
||||||
self.process_tokens()
|
self.process_tokens()
|
||||||
except exceptions.InvalidSyntax as exc:
|
except exceptions.InvalidSyntax as exc:
|
||||||
self.report(exc.error_code, exc.line_number, exc.column_number,
|
self.report(
|
||||||
exc.error_message)
|
exc.error_code,
|
||||||
|
exc.line_number,
|
||||||
|
exc.column_number,
|
||||||
|
exc.error_message,
|
||||||
|
)
|
||||||
|
|
||||||
self.run_ast_checks()
|
self.run_ast_checks()
|
||||||
|
|
||||||
logical_lines = self.processor.statistics['logical lines']
|
logical_lines = self.processor.statistics["logical lines"]
|
||||||
self.statistics['logical lines'] = logical_lines
|
self.statistics["logical lines"] = logical_lines
|
||||||
return self.filename, self.results, self.statistics
|
return self.filename, self.results, 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
|
||||||
token = list(token)
|
token = list(token)
|
||||||
token[1] = token_text.rstrip('\r\n')
|
token[1] = token_text.rstrip("\r\n")
|
||||||
token[3] = (token[2][0], token[2][1] + len(token[1]))
|
token[3] = (token[2][0], token[2][1] + len(token[1]))
|
||||||
self.processor.tokens = [tuple(token)]
|
self.processor.tokens = [tuple(token)]
|
||||||
self.run_logical_checks()
|
self.run_logical_checks()
|
||||||
|
|
@ -628,8 +656,9 @@ class FileChecker(object):
|
||||||
line_no = token[2][0]
|
line_no = token[2][0]
|
||||||
with self.processor.inside_multiline(line_number=line_no):
|
with self.processor.inside_multiline(line_number=line_no):
|
||||||
for line in self.processor.split_line(token):
|
for line in self.processor.split_line(token):
|
||||||
self.run_physical_checks(line + '\n',
|
self.run_physical_checks(
|
||||||
override_error_line=token[4])
|
line + "\n", override_error_line=token[4]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _pool_init():
|
def _pool_init():
|
||||||
|
|
|
||||||
|
|
@ -2,39 +2,26 @@
|
||||||
import re
|
import re
|
||||||
|
|
||||||
EXCLUDE = (
|
EXCLUDE = (
|
||||||
'.svn',
|
".svn",
|
||||||
'CVS',
|
"CVS",
|
||||||
'.bzr',
|
".bzr",
|
||||||
'.hg',
|
".hg",
|
||||||
'.git',
|
".git",
|
||||||
'__pycache__',
|
"__pycache__",
|
||||||
'.tox',
|
".tox",
|
||||||
'.eggs',
|
".eggs",
|
||||||
'*.egg',
|
"*.egg",
|
||||||
)
|
)
|
||||||
IGNORE = (
|
IGNORE = ("E121", "E123", "E126", "E226", "E24", "E704", "W503", "W504")
|
||||||
'E121',
|
SELECT = ("E", "F", "W", "C90")
|
||||||
'E123',
|
|
||||||
'E126',
|
|
||||||
'E226',
|
|
||||||
'E24',
|
|
||||||
'E704',
|
|
||||||
'W503',
|
|
||||||
'W504',
|
|
||||||
)
|
|
||||||
SELECT = ('E', 'F', 'W', 'C90')
|
|
||||||
MAX_LINE_LENGTH = 79
|
MAX_LINE_LENGTH = 79
|
||||||
|
|
||||||
TRUTHY_VALUES = {'true', '1', 't'}
|
TRUTHY_VALUES = {"true", "1", "t"}
|
||||||
|
|
||||||
# Other constants
|
# Other constants
|
||||||
WHITESPACE = frozenset(' \t')
|
WHITESPACE = frozenset(" \t")
|
||||||
|
|
||||||
STATISTIC_NAMES = (
|
STATISTIC_NAMES = ("logical lines", "physical lines", "tokens")
|
||||||
'logical lines',
|
|
||||||
'physical lines',
|
|
||||||
'tokens',
|
|
||||||
)
|
|
||||||
|
|
||||||
NOQA_INLINE_REGEXP = re.compile(
|
NOQA_INLINE_REGEXP = re.compile(
|
||||||
# We're looking for items that look like this:
|
# We're looking for items that look like this:
|
||||||
|
|
@ -46,8 +33,8 @@ NOQA_INLINE_REGEXP = re.compile(
|
||||||
# We do not care about the ``: `` that follows ``noqa``
|
# We do not care about the ``: `` that follows ``noqa``
|
||||||
# We do not care about the casing of ``noqa``
|
# We do not care about the casing of ``noqa``
|
||||||
# We want a comma-separated list of errors
|
# We want a comma-separated list of errors
|
||||||
r'# noqa(?:: (?P<codes>([A-Z][0-9]+(?:[,\s]+)?)+))?',
|
r"# noqa(?:: (?P<codes>([A-Z][0-9]+(?:[,\s]+)?)+))?",
|
||||||
re.IGNORECASE
|
re.IGNORECASE,
|
||||||
)
|
)
|
||||||
|
|
||||||
NOQA_FILE = re.compile(r'\s*# flake8[:=]\s*noqa', re.I)
|
NOQA_FILE = re.compile(r"\s*# flake8[:=]\s*noqa", re.I)
|
||||||
|
|
|
||||||
|
|
@ -24,15 +24,17 @@ class FailedToLoadPlugin(Flake8Exception):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Initialize our FailedToLoadPlugin exception."""
|
"""Initialize our FailedToLoadPlugin exception."""
|
||||||
self.plugin = kwargs.pop('plugin')
|
self.plugin = kwargs.pop("plugin")
|
||||||
self.ep_name = self.plugin.name
|
self.ep_name = self.plugin.name
|
||||||
self.original_exception = kwargs.pop('exception')
|
self.original_exception = kwargs.pop("exception")
|
||||||
super(FailedToLoadPlugin, self).__init__(*args, **kwargs)
|
super(FailedToLoadPlugin, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""Return a nice string for our exception."""
|
"""Return a nice string for our exception."""
|
||||||
return self.FORMAT % {'name': self.ep_name,
|
return self.FORMAT % {
|
||||||
'exc': self.original_exception}
|
"name": self.ep_name,
|
||||||
|
"exc": self.original_exception,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class InvalidSyntax(Flake8Exception):
|
class InvalidSyntax(Flake8Exception):
|
||||||
|
|
@ -40,19 +42,16 @@ class InvalidSyntax(Flake8Exception):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Initialize our InvalidSyntax exception."""
|
"""Initialize our InvalidSyntax exception."""
|
||||||
exception = kwargs.pop('exception', None)
|
exception = kwargs.pop("exception", None)
|
||||||
self.original_exception = exception
|
self.original_exception = exception
|
||||||
self.error_message = '{0}: {1}'.format(
|
self.error_message = "{0}: {1}".format(
|
||||||
exception.__class__.__name__,
|
exception.__class__.__name__, exception.args[0]
|
||||||
exception.args[0],
|
|
||||||
)
|
)
|
||||||
self.error_code = 'E902'
|
self.error_code = "E902"
|
||||||
self.line_number = 1
|
self.line_number = 1
|
||||||
self.column_number = 0
|
self.column_number = 0
|
||||||
super(InvalidSyntax, self).__init__(
|
super(InvalidSyntax, self).__init__(
|
||||||
self.error_message,
|
self.error_message, *args, **kwargs
|
||||||
*args,
|
|
||||||
**kwargs
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -63,17 +62,18 @@ class PluginRequestedUnknownParameters(Flake8Exception):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Pop certain keyword arguments for initialization."""
|
"""Pop certain keyword arguments for initialization."""
|
||||||
self.original_exception = kwargs.pop('exception')
|
self.original_exception = kwargs.pop("exception")
|
||||||
self.plugin = kwargs.pop('plugin')
|
self.plugin = kwargs.pop("plugin")
|
||||||
super(PluginRequestedUnknownParameters, self).__init__(
|
super(PluginRequestedUnknownParameters, self).__init__(
|
||||||
*args,
|
*args, **kwargs
|
||||||
**kwargs
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""Format our exception message."""
|
"""Format our exception message."""
|
||||||
return self.FORMAT % {'name': self.plugin['plugin_name'],
|
return self.FORMAT % {
|
||||||
'exc': self.original_exception}
|
"name": self.plugin["plugin_name"],
|
||||||
|
"exc": self.original_exception,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class PluginExecutionFailed(Flake8Exception):
|
class PluginExecutionFailed(Flake8Exception):
|
||||||
|
|
@ -83,16 +83,18 @@ class PluginExecutionFailed(Flake8Exception):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Utilize keyword arguments for message generation."""
|
"""Utilize keyword arguments for message generation."""
|
||||||
self.original_exception = kwargs.pop('exception')
|
self.original_exception = kwargs.pop("exception")
|
||||||
self.plugin = kwargs.pop('plugin')
|
self.plugin = kwargs.pop("plugin")
|
||||||
super(PluginExecutionFailed, self).__init__(
|
super(PluginExecutionFailed, self).__init__(
|
||||||
str(self), *args, **kwargs
|
str(self), *args, **kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""Format our exception message."""
|
"""Format our exception message."""
|
||||||
return self.FORMAT % {'name': self.plugin['plugin_name'],
|
return self.FORMAT % {
|
||||||
'exc': self.original_exception}
|
"name": self.plugin["plugin_name"],
|
||||||
|
"exc": self.original_exception,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class HookInstallationError(Flake8Exception):
|
class HookInstallationError(Flake8Exception):
|
||||||
|
|
@ -106,14 +108,16 @@ class GitHookAlreadyExists(HookInstallationError):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Initialize the path attribute."""
|
"""Initialize the path attribute."""
|
||||||
self.path = kwargs.pop('path')
|
self.path = kwargs.pop("path")
|
||||||
super(GitHookAlreadyExists, self).__init__(*args, **kwargs)
|
super(GitHookAlreadyExists, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""Provide a nice message regarding the exception."""
|
"""Provide a nice message regarding the exception."""
|
||||||
msg = ('The Git pre-commit hook ({0}) already exists. To convince '
|
msg = (
|
||||||
'Flake8 to install the hook, please remove the existing '
|
"The Git pre-commit hook ({0}) already exists. To convince "
|
||||||
'hook.')
|
"Flake8 to install the hook, please remove the existing "
|
||||||
|
"hook."
|
||||||
|
)
|
||||||
return msg.format(self.path)
|
return msg.format(self.path)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -124,25 +128,27 @@ class MercurialHookAlreadyExists(HookInstallationError):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Initialize the relevant attributes."""
|
"""Initialize the relevant attributes."""
|
||||||
self.path = kwargs.pop('path')
|
self.path = kwargs.pop("path")
|
||||||
self.value = kwargs.pop('value')
|
self.value = kwargs.pop("value")
|
||||||
super(MercurialHookAlreadyExists, self).__init__(*args, **kwargs)
|
super(MercurialHookAlreadyExists, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""Return a nicely formatted string for these errors."""
|
"""Return a nicely formatted string for these errors."""
|
||||||
msg = ('The Mercurial {0} hook already exists with "{1}" in {2}. '
|
msg = (
|
||||||
'To convince Flake8 to install the hook, please remove the '
|
'The Mercurial {0} hook already exists with "{1}" in {2}. '
|
||||||
'{0} configuration from the [hooks] section of your hgrc.')
|
"To convince Flake8 to install the hook, please remove the "
|
||||||
|
"{0} configuration from the [hooks] section of your hgrc."
|
||||||
|
)
|
||||||
return msg.format(self.hook_name, self.value, self.path)
|
return msg.format(self.hook_name, self.value, self.path)
|
||||||
|
|
||||||
|
|
||||||
class MercurialCommitHookAlreadyExists(MercurialHookAlreadyExists):
|
class MercurialCommitHookAlreadyExists(MercurialHookAlreadyExists):
|
||||||
"""Exception raised when the hg commit hook is already configured."""
|
"""Exception raised when the hg commit hook is already configured."""
|
||||||
|
|
||||||
hook_name = 'commit'
|
hook_name = "commit"
|
||||||
|
|
||||||
|
|
||||||
class MercurialQRefreshHookAlreadyExists(MercurialHookAlreadyExists):
|
class MercurialQRefreshHookAlreadyExists(MercurialHookAlreadyExists):
|
||||||
"""Exception raised when the hg commit hook is already configured."""
|
"""Exception raised when the hg commit hook is already configured."""
|
||||||
|
|
||||||
hook_name = 'qrefresh'
|
hook_name = "qrefresh"
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ class BaseFormatter(object):
|
||||||
self.options = options
|
self.options = options
|
||||||
self.filename = options.output_file
|
self.filename = options.output_file
|
||||||
self.output_fd = None
|
self.output_fd = None
|
||||||
self.newline = '\n'
|
self.newline = "\n"
|
||||||
self.after_init()
|
self.after_init()
|
||||||
|
|
||||||
def after_init(self):
|
def after_init(self):
|
||||||
|
|
@ -68,7 +68,7 @@ class BaseFormatter(object):
|
||||||
This defaults to initializing :attr:`output_fd` if :attr:`filename`
|
This defaults to initializing :attr:`output_fd` if :attr:`filename`
|
||||||
"""
|
"""
|
||||||
if self.filename:
|
if self.filename:
|
||||||
self.output_fd = open(self.filename, 'a')
|
self.output_fd = open(self.filename, "a")
|
||||||
|
|
||||||
def handle(self, error):
|
def handle(self, error):
|
||||||
"""Handle an error reported by Flake8.
|
"""Handle an error reported by Flake8.
|
||||||
|
|
@ -102,8 +102,9 @@ class BaseFormatter(object):
|
||||||
:rtype:
|
:rtype:
|
||||||
str
|
str
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Subclass of BaseFormatter did not implement'
|
raise NotImplementedError(
|
||||||
' format.')
|
"Subclass of BaseFormatter did not implement" " format."
|
||||||
|
)
|
||||||
|
|
||||||
def show_statistics(self, statistics):
|
def show_statistics(self, statistics):
|
||||||
"""Format and print the statistics."""
|
"""Format and print the statistics."""
|
||||||
|
|
@ -112,11 +113,13 @@ class BaseFormatter(object):
|
||||||
statistic = next(stats_for_error_code)
|
statistic = next(stats_for_error_code)
|
||||||
count = statistic.count
|
count = statistic.count
|
||||||
count += sum(stat.count for stat in stats_for_error_code)
|
count += sum(stat.count for stat in stats_for_error_code)
|
||||||
self._write('{count:<5} {error_code} {message}'.format(
|
self._write(
|
||||||
count=count,
|
"{count:<5} {error_code} {message}".format(
|
||||||
error_code=error_code,
|
count=count,
|
||||||
message=statistic.message,
|
error_code=error_code,
|
||||||
))
|
message=statistic.message,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def show_benchmarks(self, benchmarks):
|
def show_benchmarks(self, benchmarks):
|
||||||
"""Format and print the benchmarks."""
|
"""Format and print the benchmarks."""
|
||||||
|
|
@ -130,8 +133,8 @@ class BaseFormatter(object):
|
||||||
# the decimal point to be displayed. This is the precision and it
|
# 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
|
# can not be specified for integers which is why we need two separate
|
||||||
# format strings.
|
# format strings.
|
||||||
float_format = '{value:<10.3} {statistic}'.format
|
float_format = "{value:<10.3} {statistic}".format
|
||||||
int_format = '{value:<10} {statistic}'.format
|
int_format = "{value:<10} {statistic}".format
|
||||||
for statistic, value in benchmarks:
|
for statistic, value in benchmarks:
|
||||||
if isinstance(value, int):
|
if isinstance(value, int):
|
||||||
benchmark = int_format(statistic=statistic, value=value)
|
benchmark = int_format(statistic=statistic, value=value)
|
||||||
|
|
@ -158,11 +161,11 @@ class BaseFormatter(object):
|
||||||
str
|
str
|
||||||
"""
|
"""
|
||||||
if not self.options.show_source or error.physical_line is None:
|
if not self.options.show_source or error.physical_line is None:
|
||||||
return ''
|
return ""
|
||||||
|
|
||||||
# Because column numbers are 1-indexed, we need to remove one to get
|
# Because column numbers are 1-indexed, we need to remove one to get
|
||||||
# the proper number of space characters.
|
# the proper number of space characters.
|
||||||
pointer = (' ' * (error.column_number - 1)) + '^'
|
pointer = (" " * (error.column_number - 1)) + "^"
|
||||||
# Physical lines have a newline at the end, no need to add an extra
|
# Physical lines have a newline at the end, no need to add an extra
|
||||||
# one
|
# one
|
||||||
return error.physical_line + pointer
|
return error.physical_line + pointer
|
||||||
|
|
|
||||||
|
|
@ -42,24 +42,24 @@ class Default(SimpleFormatter):
|
||||||
format string.
|
format string.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
error_format = '%(path)s:%(row)d:%(col)d: %(code)s %(text)s'
|
error_format = "%(path)s:%(row)d:%(col)d: %(code)s %(text)s"
|
||||||
|
|
||||||
def after_init(self):
|
def after_init(self):
|
||||||
"""Check for a custom format string."""
|
"""Check for a custom format string."""
|
||||||
if self.options.format.lower() != 'default':
|
if self.options.format.lower() != "default":
|
||||||
self.error_format = self.options.format
|
self.error_format = self.options.format
|
||||||
|
|
||||||
|
|
||||||
class Pylint(SimpleFormatter):
|
class Pylint(SimpleFormatter):
|
||||||
"""Pylint formatter for Flake8."""
|
"""Pylint formatter for Flake8."""
|
||||||
|
|
||||||
error_format = '%(path)s:%(row)d: [%(code)s] %(text)s'
|
error_format = "%(path)s:%(row)d: [%(code)s] %(text)s"
|
||||||
|
|
||||||
|
|
||||||
class FilenameOnly(SimpleFormatter):
|
class FilenameOnly(SimpleFormatter):
|
||||||
"""Only print filenames, e.g., flake8 -q."""
|
"""Only print filenames, e.g., flake8 -q."""
|
||||||
|
|
||||||
error_format = '%(path)s'
|
error_format = "%(path)s"
|
||||||
|
|
||||||
def after_init(self):
|
def after_init(self):
|
||||||
"""Initialize our set of filenames."""
|
"""Initialize our set of filenames."""
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ LOG = logging.getLogger(__name__)
|
||||||
class Application(object):
|
class Application(object):
|
||||||
"""Abstract our application into a class."""
|
"""Abstract our application into a class."""
|
||||||
|
|
||||||
def __init__(self, program='flake8', version=flake8.__version__):
|
def __init__(self, program="flake8", version=flake8.__version__):
|
||||||
# type: (str, str) -> NoneType
|
# type: (str, str) -> NoneType
|
||||||
"""Initialize our application.
|
"""Initialize our application.
|
||||||
|
|
||||||
|
|
@ -42,7 +42,7 @@ class Application(object):
|
||||||
#: The instance of :class:`flake8.options.manager.OptionManager` used
|
#: The instance of :class:`flake8.options.manager.OptionManager` used
|
||||||
#: to parse and handle the options and arguments passed by the user
|
#: to parse and handle the options and arguments passed by the user
|
||||||
self.option_manager = manager.OptionManager(
|
self.option_manager = manager.OptionManager(
|
||||||
prog='flake8', version=flake8.__version__
|
prog="flake8", version=flake8.__version__
|
||||||
)
|
)
|
||||||
options.register_default_options(self.option_manager)
|
options.register_default_options(self.option_manager)
|
||||||
#: The preliminary options parsed from CLI before plugins are loaded,
|
#: The preliminary options parsed from CLI before plugins are loaded,
|
||||||
|
|
@ -118,21 +118,21 @@ class Application(object):
|
||||||
# Similarly we have to defer printing the help text until later.
|
# Similarly we have to defer printing the help text until later.
|
||||||
args = (argv or sys.argv)[:]
|
args = (argv or sys.argv)[:]
|
||||||
try:
|
try:
|
||||||
args.remove('--version')
|
args.remove("--version")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
args.remove('--help')
|
args.remove("--help")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
args.remove('-h')
|
args.remove("-h")
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
opts, args = self.option_manager.parse_known_args(args)
|
opts, args = self.option_manager.parse_known_args(args)
|
||||||
# parse_known_args includes program name and unknown options as args
|
# parse_known_args includes program name and unknown options as args
|
||||||
args = [a for a in args[1:] if not a.startswith('-')]
|
args = [a for a in args[1:] if not a.startswith("-")]
|
||||||
self.prelim_opts, self.prelim_args = opts, args
|
self.prelim_opts, self.prelim_args = opts, args
|
||||||
|
|
||||||
def exit(self):
|
def exit(self):
|
||||||
|
|
@ -146,14 +146,16 @@ class Application(object):
|
||||||
print(self.result_count)
|
print(self.result_count)
|
||||||
|
|
||||||
if not self.options.exit_zero:
|
if not self.options.exit_zero:
|
||||||
raise SystemExit((self.result_count > 0) or
|
raise SystemExit(
|
||||||
self.catastrophic_failure)
|
(self.result_count > 0) or self.catastrophic_failure
|
||||||
|
)
|
||||||
|
|
||||||
def make_config_finder(self):
|
def make_config_finder(self):
|
||||||
"""Make our ConfigFileFinder based on preliminary opts and args."""
|
"""Make our ConfigFileFinder based on preliminary opts and args."""
|
||||||
if self.config_finder is None:
|
if self.config_finder is None:
|
||||||
extra_config_files = utils.normalize_paths(
|
extra_config_files = utils.normalize_paths(
|
||||||
self.prelim_opts.append_config)
|
self.prelim_opts.append_config
|
||||||
|
)
|
||||||
self.config_finder = config.ConfigFileFinder(
|
self.config_finder = config.ConfigFileFinder(
|
||||||
self.option_manager.program_name,
|
self.option_manager.program_name,
|
||||||
self.prelim_args,
|
self.prelim_args,
|
||||||
|
|
@ -181,14 +183,16 @@ class Application(object):
|
||||||
|
|
||||||
if self.check_plugins is None:
|
if self.check_plugins is None:
|
||||||
self.check_plugins = plugin_manager.Checkers(
|
self.check_plugins = plugin_manager.Checkers(
|
||||||
self.local_plugins.extension)
|
self.local_plugins.extension
|
||||||
|
)
|
||||||
|
|
||||||
if self.listening_plugins is None:
|
if self.listening_plugins is None:
|
||||||
self.listening_plugins = plugin_manager.Listeners()
|
self.listening_plugins = plugin_manager.Listeners()
|
||||||
|
|
||||||
if self.formatting_plugins is None:
|
if self.formatting_plugins is None:
|
||||||
self.formatting_plugins = plugin_manager.ReportFormatters(
|
self.formatting_plugins = plugin_manager.ReportFormatters(
|
||||||
self.local_plugins.report)
|
self.local_plugins.report
|
||||||
|
)
|
||||||
|
|
||||||
self.check_plugins.load_plugins()
|
self.check_plugins.load_plugins()
|
||||||
self.listening_plugins.load_plugins()
|
self.listening_plugins.load_plugins()
|
||||||
|
|
@ -222,19 +226,20 @@ class Application(object):
|
||||||
|
|
||||||
self.options._running_from_vcs = False
|
self.options._running_from_vcs = False
|
||||||
|
|
||||||
self.check_plugins.provide_options(self.option_manager, self.options,
|
self.check_plugins.provide_options(
|
||||||
self.args)
|
self.option_manager, self.options, self.args
|
||||||
self.listening_plugins.provide_options(self.option_manager,
|
)
|
||||||
self.options,
|
self.listening_plugins.provide_options(
|
||||||
self.args)
|
self.option_manager, self.options, self.args
|
||||||
self.formatting_plugins.provide_options(self.option_manager,
|
)
|
||||||
self.options,
|
self.formatting_plugins.provide_options(
|
||||||
self.args)
|
self.option_manager, self.options, self.args
|
||||||
|
)
|
||||||
|
|
||||||
def formatter_for(self, formatter_plugin_name):
|
def formatter_for(self, formatter_plugin_name):
|
||||||
"""Retrieve the formatter class by plugin name."""
|
"""Retrieve the formatter class by plugin name."""
|
||||||
try:
|
try:
|
||||||
default_formatter = self.formatting_plugins['default']
|
default_formatter = self.formatting_plugins["default"]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise exceptions.ExecutionError(
|
raise exceptions.ExecutionError(
|
||||||
"The 'default' Flake8 formatting plugin is unavailable. "
|
"The 'default' Flake8 formatting plugin is unavailable. "
|
||||||
|
|
@ -259,9 +264,9 @@ class Application(object):
|
||||||
if self.formatter is None:
|
if self.formatter is None:
|
||||||
format_plugin = self.options.format
|
format_plugin = self.options.format
|
||||||
if 1 <= self.options.quiet < 2:
|
if 1 <= self.options.quiet < 2:
|
||||||
format_plugin = 'quiet-filename'
|
format_plugin = "quiet-filename"
|
||||||
elif 2 <= self.options.quiet:
|
elif 2 <= self.options.quiet:
|
||||||
format_plugin = 'quiet-nothing'
|
format_plugin = "quiet-nothing"
|
||||||
|
|
||||||
if formatter_class is None:
|
if formatter_class is None:
|
||||||
formatter_class = self.formatter_for(format_plugin)
|
formatter_class = self.formatter_for(format_plugin)
|
||||||
|
|
@ -313,9 +318,9 @@ class Application(object):
|
||||||
self.file_checker_manager.run()
|
self.file_checker_manager.run()
|
||||||
except exceptions.PluginExecutionFailed as plugin_failed:
|
except exceptions.PluginExecutionFailed as plugin_failed:
|
||||||
print(str(plugin_failed))
|
print(str(plugin_failed))
|
||||||
print('Run flake8 with greater verbosity to see more details')
|
print("Run flake8 with greater verbosity to see more details")
|
||||||
self.catastrophic_failure = True
|
self.catastrophic_failure = True
|
||||||
LOG.info('Finished running')
|
LOG.info("Finished running")
|
||||||
self.file_checker_manager.stop()
|
self.file_checker_manager.stop()
|
||||||
self.end_time = time.time()
|
self.end_time = time.time()
|
||||||
|
|
||||||
|
|
@ -325,13 +330,13 @@ class Application(object):
|
||||||
return
|
return
|
||||||
|
|
||||||
time_elapsed = self.end_time - self.start_time
|
time_elapsed = self.end_time - self.start_time
|
||||||
statistics = [('seconds elapsed', time_elapsed)]
|
statistics = [("seconds elapsed", time_elapsed)]
|
||||||
add_statistic = statistics.append
|
add_statistic = statistics.append
|
||||||
for statistic in (defaults.STATISTIC_NAMES + ('files',)):
|
for statistic in defaults.STATISTIC_NAMES + ("files",):
|
||||||
value = self.file_checker_manager.statistics[statistic]
|
value = self.file_checker_manager.statistics[statistic]
|
||||||
total_description = 'total ' + statistic + ' processed'
|
total_description = "total " + statistic + " processed"
|
||||||
add_statistic((total_description, value))
|
add_statistic((total_description, value))
|
||||||
per_second_description = statistic + ' processed per second'
|
per_second_description = statistic + " processed per second"
|
||||||
add_statistic((per_second_description, int(value / time_elapsed)))
|
add_statistic((per_second_description, int(value / time_elapsed)))
|
||||||
|
|
||||||
self.formatter.show_benchmarks(statistics)
|
self.formatter.show_benchmarks(statistics)
|
||||||
|
|
@ -343,11 +348,14 @@ class Application(object):
|
||||||
This also updates the :attr:`result_count` attribute with the total
|
This also updates the :attr:`result_count` attribute with the total
|
||||||
number of errors, warnings, and other messages found.
|
number of errors, warnings, and other messages found.
|
||||||
"""
|
"""
|
||||||
LOG.info('Reporting errors')
|
LOG.info("Reporting errors")
|
||||||
results = self.file_checker_manager.report()
|
results = self.file_checker_manager.report()
|
||||||
self.total_result_count, self.result_count = results
|
self.total_result_count, self.result_count = results
|
||||||
LOG.info('Found a total of %d violations and reported %d',
|
LOG.info(
|
||||||
self.total_result_count, self.result_count)
|
"Found a total of %d violations and reported %d",
|
||||||
|
self.total_result_count,
|
||||||
|
self.result_count,
|
||||||
|
)
|
||||||
|
|
||||||
def report_statistics(self):
|
def report_statistics(self):
|
||||||
"""Aggregate and report statistics from this run."""
|
"""Aggregate and report statistics from this run."""
|
||||||
|
|
@ -367,7 +375,8 @@ class Application(object):
|
||||||
# our legacy API calls to these same methods.
|
# our legacy API calls to these same methods.
|
||||||
self.parse_preliminary_options_and_args(argv)
|
self.parse_preliminary_options_and_args(argv)
|
||||||
flake8.configure_logging(
|
flake8.configure_logging(
|
||||||
self.prelim_opts.verbose, self.prelim_opts.output_file)
|
self.prelim_opts.verbose, self.prelim_opts.output_file
|
||||||
|
)
|
||||||
self.make_config_finder()
|
self.make_config_finder()
|
||||||
self.find_plugins()
|
self.find_plugins()
|
||||||
self.register_plugin_options()
|
self.register_plugin_options()
|
||||||
|
|
@ -402,15 +411,15 @@ class Application(object):
|
||||||
try:
|
try:
|
||||||
self._run(argv)
|
self._run(argv)
|
||||||
except KeyboardInterrupt as exc:
|
except KeyboardInterrupt as exc:
|
||||||
print('... stopped')
|
print("... stopped")
|
||||||
LOG.critical('Caught keyboard interrupt from user')
|
LOG.critical("Caught keyboard interrupt from user")
|
||||||
LOG.exception(exc)
|
LOG.exception(exc)
|
||||||
self.catastrophic_failure = True
|
self.catastrophic_failure = True
|
||||||
except exceptions.ExecutionError as exc:
|
except exceptions.ExecutionError as exc:
|
||||||
print('There was a critical error during execution of Flake8:')
|
print("There was a critical error during execution of Flake8:")
|
||||||
print(exc.message)
|
print(exc.message)
|
||||||
LOG.exception(exc)
|
LOG.exception(exc)
|
||||||
self.catastrophic_failure = True
|
self.catastrophic_failure = True
|
||||||
except exceptions.EarlyQuit:
|
except exceptions.EarlyQuit:
|
||||||
self.catastrophic_failure = True
|
self.catastrophic_failure = True
|
||||||
print('... stopped while processing files')
|
print("... stopped while processing files")
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,9 @@ import json
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
|
|
||||||
def print_information(option, option_string, value, parser,
|
def print_information(
|
||||||
option_manager=None):
|
option, option_string, value, parser, option_manager=None
|
||||||
|
):
|
||||||
"""Print debugging information used in bug reports.
|
"""Print debugging information used in bug reports.
|
||||||
|
|
||||||
:param option:
|
:param option:
|
||||||
|
|
@ -38,13 +39,13 @@ def print_information(option, option_string, value, parser,
|
||||||
def information(option_manager):
|
def information(option_manager):
|
||||||
"""Generate the information to be printed for the bug report."""
|
"""Generate the information to be printed for the bug report."""
|
||||||
return {
|
return {
|
||||||
'version': option_manager.version,
|
"version": option_manager.version,
|
||||||
'plugins': plugins_from(option_manager),
|
"plugins": plugins_from(option_manager),
|
||||||
'dependencies': dependencies(),
|
"dependencies": dependencies(),
|
||||||
'platform': {
|
"platform": {
|
||||||
'python_implementation': platform.python_implementation(),
|
"python_implementation": platform.python_implementation(),
|
||||||
'python_version': platform.python_version(),
|
"python_version": platform.python_version(),
|
||||||
'system': platform.system(),
|
"system": platform.system(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,9 +54,9 @@ def plugins_from(option_manager):
|
||||||
"""Generate the list of plugins installed."""
|
"""Generate the list of plugins installed."""
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
'plugin': plugin.name,
|
"plugin": plugin.name,
|
||||||
'version': plugin.version,
|
"version": plugin.version,
|
||||||
'is_local': plugin.local,
|
"is_local": plugin.local,
|
||||||
}
|
}
|
||||||
for plugin in sorted(option_manager.registered_plugins)
|
for plugin in sorted(option_manager.registered_plugins)
|
||||||
]
|
]
|
||||||
|
|
@ -66,4 +67,4 @@ def dependencies():
|
||||||
# defer this expensive import, not used outside --bug-report
|
# defer this expensive import, not used outside --bug-report
|
||||||
import setuptools
|
import setuptools
|
||||||
|
|
||||||
return [{'dependency': 'setuptools', 'version': setuptools.__version__}]
|
return [{"dependency": "setuptools", "version": setuptools.__version__}]
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import tempfile
|
||||||
from flake8 import defaults
|
from flake8 import defaults
|
||||||
from flake8 import exceptions
|
from flake8 import exceptions
|
||||||
|
|
||||||
__all__ = ('hook', 'install')
|
__all__ = ("hook", "install")
|
||||||
|
|
||||||
|
|
||||||
def hook(lazy=False, strict=False):
|
def hook(lazy=False, strict=False):
|
||||||
|
|
@ -39,10 +39,11 @@ def hook(lazy=False, strict=False):
|
||||||
"""
|
"""
|
||||||
# NOTE(sigmavirus24): Delay import of application until we need it.
|
# NOTE(sigmavirus24): Delay import of application until we need it.
|
||||||
from flake8.main import application
|
from flake8.main import application
|
||||||
|
|
||||||
app = application.Application()
|
app = application.Application()
|
||||||
with make_temporary_directory() as tempdir:
|
with make_temporary_directory() as tempdir:
|
||||||
filepaths = list(copy_indexed_files_to(tempdir, lazy))
|
filepaths = list(copy_indexed_files_to(tempdir, lazy))
|
||||||
app.initialize(['.'])
|
app.initialize(["."])
|
||||||
app.options.exclude = update_excludes(app.options.exclude, tempdir)
|
app.options.exclude = update_excludes(app.options.exclude, tempdir)
|
||||||
app.options._running_from_vcs = True
|
app.options._running_from_vcs = True
|
||||||
# Apparently there are times when there are no files to check (e.g.,
|
# Apparently there are times when there are no files to check (e.g.,
|
||||||
|
|
@ -81,22 +82,21 @@ def install():
|
||||||
if git_directory is None or not os.path.exists(git_directory):
|
if git_directory is None or not os.path.exists(git_directory):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
hooks_directory = os.path.join(git_directory, 'hooks')
|
hooks_directory = os.path.join(git_directory, "hooks")
|
||||||
if not os.path.exists(hooks_directory):
|
if not os.path.exists(hooks_directory):
|
||||||
os.mkdir(hooks_directory)
|
os.mkdir(hooks_directory)
|
||||||
|
|
||||||
pre_commit_file = os.path.abspath(
|
pre_commit_file = os.path.abspath(
|
||||||
os.path.join(hooks_directory, 'pre-commit')
|
os.path.join(hooks_directory, "pre-commit")
|
||||||
)
|
)
|
||||||
if os.path.exists(pre_commit_file):
|
if os.path.exists(pre_commit_file):
|
||||||
raise exceptions.GitHookAlreadyExists(
|
raise exceptions.GitHookAlreadyExists(
|
||||||
'File already exists',
|
"File already exists", path=pre_commit_file
|
||||||
path=pre_commit_file,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
executable = get_executable()
|
executable = get_executable()
|
||||||
|
|
||||||
with open(pre_commit_file, 'w') as fd:
|
with open(pre_commit_file, "w") as fd:
|
||||||
fd.write(_HOOK_TEMPLATE.format(executable=executable))
|
fd.write(_HOOK_TEMPLATE.format(executable=executable))
|
||||||
|
|
||||||
# NOTE(sigmavirus24): The following sets:
|
# NOTE(sigmavirus24): The following sets:
|
||||||
|
|
@ -108,8 +108,8 @@ def install():
|
||||||
pre_commit_permissions = stat.S_IRWXU | stat.S_IRGRP | stat.S_IROTH
|
pre_commit_permissions = stat.S_IRWXU | stat.S_IRGRP | stat.S_IROTH
|
||||||
os.chmod(pre_commit_file, pre_commit_permissions)
|
os.chmod(pre_commit_file, pre_commit_permissions)
|
||||||
|
|
||||||
print('git pre-commit hook installed, for configuration options see')
|
print("git pre-commit hook installed, for configuration options see")
|
||||||
print('http://flake8.pycqa.org/en/latest/user/using-hooks.html')
|
print("http://flake8.pycqa.org/en/latest/user/using-hooks.html")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
@ -117,11 +117,11 @@ def install():
|
||||||
def get_executable():
|
def get_executable():
|
||||||
if sys.executable is not None:
|
if sys.executable is not None:
|
||||||
return sys.executable
|
return sys.executable
|
||||||
return '/usr/bin/env python'
|
return "/usr/bin/env python"
|
||||||
|
|
||||||
|
|
||||||
def find_git_directory():
|
def find_git_directory():
|
||||||
rev_parse = piped_process(['git', 'rev-parse', '--git-dir'])
|
rev_parse = piped_process(["git", "rev-parse", "--git-dir"])
|
||||||
|
|
||||||
(stdout, _) = rev_parse.communicate()
|
(stdout, _) = rev_parse.communicate()
|
||||||
stdout = to_text(stdout)
|
stdout = to_text(stdout)
|
||||||
|
|
@ -146,12 +146,13 @@ def copy_indexed_files_to(temporary_directory, lazy):
|
||||||
|
|
||||||
def copy_file_to(destination_directory, filepath, contents):
|
def copy_file_to(destination_directory, filepath, contents):
|
||||||
directory, filename = os.path.split(os.path.abspath(filepath))
|
directory, filename = os.path.split(os.path.abspath(filepath))
|
||||||
temporary_directory = make_temporary_directory_from(destination_directory,
|
temporary_directory = make_temporary_directory_from(
|
||||||
directory)
|
destination_directory, directory
|
||||||
|
)
|
||||||
if not os.path.exists(temporary_directory):
|
if not os.path.exists(temporary_directory):
|
||||||
os.makedirs(temporary_directory)
|
os.makedirs(temporary_directory)
|
||||||
temporary_filepath = os.path.join(temporary_directory, filename)
|
temporary_filepath = os.path.join(temporary_directory, filename)
|
||||||
with open(temporary_filepath, 'wb') as fd:
|
with open(temporary_filepath, "wb") as fd:
|
||||||
fd.write(contents)
|
fd.write(contents)
|
||||||
return temporary_filepath
|
return temporary_filepath
|
||||||
|
|
||||||
|
|
@ -164,11 +165,15 @@ def make_temporary_directory_from(destination, directory):
|
||||||
|
|
||||||
def find_modified_files(lazy):
|
def find_modified_files(lazy):
|
||||||
diff_index_cmd = [
|
diff_index_cmd = [
|
||||||
'git', 'diff-index', '--cached', '--name-only',
|
"git",
|
||||||
'--diff-filter=ACMRTUXB', 'HEAD'
|
"diff-index",
|
||||||
|
"--cached",
|
||||||
|
"--name-only",
|
||||||
|
"--diff-filter=ACMRTUXB",
|
||||||
|
"HEAD",
|
||||||
]
|
]
|
||||||
if lazy:
|
if lazy:
|
||||||
diff_index_cmd.remove('--cached')
|
diff_index_cmd.remove("--cached")
|
||||||
|
|
||||||
diff_index = piped_process(diff_index_cmd)
|
diff_index = piped_process(diff_index_cmd)
|
||||||
(stdout, _) = diff_index.communicate()
|
(stdout, _) = diff_index.communicate()
|
||||||
|
|
@ -177,11 +182,9 @@ def find_modified_files(lazy):
|
||||||
|
|
||||||
|
|
||||||
def find_setup_cfgs(lazy):
|
def find_setup_cfgs(lazy):
|
||||||
setup_cfg_cmd = [
|
setup_cfg_cmd = ["git", "ls-files", "--cached", "*setup.cfg"]
|
||||||
'git', 'ls-files', '--cached', '*setup.cfg'
|
|
||||||
]
|
|
||||||
if lazy:
|
if lazy:
|
||||||
setup_cfg_cmd.remove('--cached')
|
setup_cfg_cmd.remove("--cached")
|
||||||
extra_files = piped_process(setup_cfg_cmd)
|
extra_files = piped_process(setup_cfg_cmd)
|
||||||
(stdout, _) = extra_files.communicate()
|
(stdout, _) = extra_files.communicate()
|
||||||
stdout = to_text(stdout)
|
stdout = to_text(stdout)
|
||||||
|
|
@ -189,7 +192,7 @@ def find_setup_cfgs(lazy):
|
||||||
|
|
||||||
|
|
||||||
def get_staged_contents_from(filename):
|
def get_staged_contents_from(filename):
|
||||||
git_show = piped_process(['git', 'show', ':{0}'.format(filename)])
|
git_show = piped_process(["git", "show", ":{0}".format(filename)])
|
||||||
(stdout, _) = git_show.communicate()
|
(stdout, _) = git_show.communicate()
|
||||||
return stdout
|
return stdout
|
||||||
|
|
||||||
|
|
@ -203,28 +206,26 @@ def make_temporary_directory():
|
||||||
|
|
||||||
def to_text(string):
|
def to_text(string):
|
||||||
"""Ensure that the string is text."""
|
"""Ensure that the string is text."""
|
||||||
if callable(getattr(string, 'decode', None)):
|
if callable(getattr(string, "decode", None)):
|
||||||
return string.decode('utf-8')
|
return string.decode("utf-8")
|
||||||
return string
|
return string
|
||||||
|
|
||||||
|
|
||||||
def piped_process(command):
|
def piped_process(command):
|
||||||
return subprocess.Popen(
|
return subprocess.Popen(
|
||||||
command,
|
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def git_config_for(parameter):
|
def git_config_for(parameter):
|
||||||
config = piped_process(['git', 'config', '--get', '--bool', parameter])
|
config = piped_process(["git", "config", "--get", "--bool", parameter])
|
||||||
(stdout, _) = config.communicate()
|
(stdout, _) = config.communicate()
|
||||||
return to_text(stdout).strip()
|
return to_text(stdout).strip()
|
||||||
|
|
||||||
|
|
||||||
def config_for(parameter):
|
def config_for(parameter):
|
||||||
environment_variable = 'flake8_{0}'.format(parameter).upper()
|
environment_variable = "flake8_{0}".format(parameter).upper()
|
||||||
git_variable = 'flake8.{0}'.format(parameter)
|
git_variable = "flake8.{0}".format(parameter)
|
||||||
value = os.environ.get(environment_variable, git_config_for(git_variable))
|
value = os.environ.get(environment_variable, git_config_for(git_variable))
|
||||||
return value.lower() in defaults.TRUTHY_VALUES
|
return value.lower() in defaults.TRUTHY_VALUES
|
||||||
|
|
||||||
|
|
@ -232,7 +233,8 @@ def config_for(parameter):
|
||||||
def update_excludes(exclude_list, temporary_directory_path):
|
def update_excludes(exclude_list, temporary_directory_path):
|
||||||
return [
|
return [
|
||||||
(temporary_directory_path + pattern)
|
(temporary_directory_path + pattern)
|
||||||
if os.path.isabs(pattern) else pattern
|
if os.path.isabs(pattern)
|
||||||
|
else pattern
|
||||||
for pattern in exclude_list
|
for pattern in exclude_list
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import subprocess
|
||||||
|
|
||||||
from flake8 import exceptions as exc
|
from flake8 import exceptions as exc
|
||||||
|
|
||||||
__all__ = ('hook', 'install')
|
__all__ = ("hook", "install")
|
||||||
|
|
||||||
|
|
||||||
def hook(ui, repo, **kwargs):
|
def hook(ui, repo, **kwargs):
|
||||||
|
|
@ -24,13 +24,14 @@ def hook(ui, repo, **kwargs):
|
||||||
avoid using it all the same.
|
avoid using it all the same.
|
||||||
"""
|
"""
|
||||||
from flake8.main import application
|
from flake8.main import application
|
||||||
|
|
||||||
hgrc = find_hgrc(create_if_missing=False)
|
hgrc = find_hgrc(create_if_missing=False)
|
||||||
if hgrc is None:
|
if hgrc is None:
|
||||||
print('Cannot locate your root mercurial repository.')
|
print("Cannot locate your root mercurial repository.")
|
||||||
raise SystemExit(True)
|
raise SystemExit(True)
|
||||||
|
|
||||||
hgconfig = configparser_for(hgrc)
|
hgconfig = configparser_for(hgrc)
|
||||||
strict = hgconfig.get('flake8', 'strict', fallback=True)
|
strict = hgconfig.get("flake8", "strict", fallback=True)
|
||||||
|
|
||||||
filenames = list(get_filenames_from(repo, kwargs))
|
filenames = list(get_filenames_from(repo, kwargs))
|
||||||
|
|
||||||
|
|
@ -68,42 +69,40 @@ def install():
|
||||||
|
|
||||||
hgconfig = configparser_for(hgrc)
|
hgconfig = configparser_for(hgrc)
|
||||||
|
|
||||||
if not hgconfig.has_section('hooks'):
|
if not hgconfig.has_section("hooks"):
|
||||||
hgconfig.add_section('hooks')
|
hgconfig.add_section("hooks")
|
||||||
|
|
||||||
if hgconfig.has_option('hooks', 'commit'):
|
if hgconfig.has_option("hooks", "commit"):
|
||||||
raise exc.MercurialCommitHookAlreadyExists(
|
raise exc.MercurialCommitHookAlreadyExists(
|
||||||
path=hgrc,
|
path=hgrc, value=hgconfig.get("hooks", "commit")
|
||||||
value=hgconfig.get('hooks', 'commit'),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if hgconfig.has_option('hooks', 'qrefresh'):
|
if hgconfig.has_option("hooks", "qrefresh"):
|
||||||
raise exc.MercurialQRefreshHookAlreadyExists(
|
raise exc.MercurialQRefreshHookAlreadyExists(
|
||||||
path=hgrc,
|
path=hgrc, value=hgconfig.get("hooks", "qrefresh")
|
||||||
value=hgconfig.get('hooks', 'qrefresh'),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
hgconfig.set('hooks', 'commit', 'python:flake8.main.mercurial.hook')
|
hgconfig.set("hooks", "commit", "python:flake8.main.mercurial.hook")
|
||||||
hgconfig.set('hooks', 'qrefresh', 'python:flake8.main.mercurial.hook')
|
hgconfig.set("hooks", "qrefresh", "python:flake8.main.mercurial.hook")
|
||||||
|
|
||||||
if not hgconfig.has_section('flake8'):
|
if not hgconfig.has_section("flake8"):
|
||||||
hgconfig.add_section('flake8')
|
hgconfig.add_section("flake8")
|
||||||
|
|
||||||
if not hgconfig.has_option('flake8', 'strict'):
|
if not hgconfig.has_option("flake8", "strict"):
|
||||||
hgconfig.set('flake8', 'strict', False)
|
hgconfig.set("flake8", "strict", False)
|
||||||
|
|
||||||
with open(hgrc, 'w') as fd:
|
with open(hgrc, "w") as fd:
|
||||||
hgconfig.write(fd)
|
hgconfig.write(fd)
|
||||||
|
|
||||||
print('mercurial hooks installed, for configuration options see')
|
print("mercurial hooks installed, for configuration options see")
|
||||||
print('http://flake8.pycqa.org/en/latest/user/using-hooks.html')
|
print("http://flake8.pycqa.org/en/latest/user/using-hooks.html")
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def get_filenames_from(repository, kwargs):
|
def get_filenames_from(repository, kwargs):
|
||||||
seen_filenames = set()
|
seen_filenames = set()
|
||||||
node = kwargs['node']
|
node = kwargs["node"]
|
||||||
for revision in range(repository[node], len(repository)):
|
for revision in range(repository[node], len(repository)):
|
||||||
for filename in repository[revision].files():
|
for filename in repository[revision].files():
|
||||||
full_filename = os.path.join(repository.root, filename)
|
full_filename = os.path.join(repository.root, filename)
|
||||||
|
|
@ -113,30 +112,26 @@ def get_filenames_from(repository, kwargs):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
seen_filenames.add(full_filename)
|
seen_filenames.add(full_filename)
|
||||||
if full_filename.endswith('.py'):
|
if full_filename.endswith(".py"):
|
||||||
yield full_filename
|
yield full_filename
|
||||||
|
|
||||||
|
|
||||||
def find_hgrc(create_if_missing=False):
|
def find_hgrc(create_if_missing=False):
|
||||||
root = subprocess.Popen(
|
root = subprocess.Popen(
|
||||||
['hg', 'root'],
|
["hg", "root"], stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
(hg_directory, _) = root.communicate()
|
(hg_directory, _) = root.communicate()
|
||||||
if callable(getattr(hg_directory, 'decode', None)):
|
if callable(getattr(hg_directory, "decode", None)):
|
||||||
hg_directory = hg_directory.decode('utf-8')
|
hg_directory = hg_directory.decode("utf-8")
|
||||||
|
|
||||||
if not os.path.isdir(hg_directory):
|
if not os.path.isdir(hg_directory):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
hgrc = os.path.abspath(
|
hgrc = os.path.abspath(os.path.join(hg_directory, ".hg", "hgrc"))
|
||||||
os.path.join(hg_directory, '.hg', 'hgrc')
|
|
||||||
)
|
|
||||||
if not os.path.exists(hgrc):
|
if not os.path.exists(hgrc):
|
||||||
if create_if_missing:
|
if create_if_missing:
|
||||||
open(hgrc, 'w').close()
|
open(hgrc, "w").close()
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,50 +39,66 @@ def register_default_options(option_manager):
|
||||||
|
|
||||||
# pep8 options
|
# pep8 options
|
||||||
add_option(
|
add_option(
|
||||||
'-v', '--verbose', default=0, action='count',
|
"-v",
|
||||||
|
"--verbose",
|
||||||
|
default=0,
|
||||||
|
action="count",
|
||||||
parse_from_config=True,
|
parse_from_config=True,
|
||||||
help='Print more information about what is happening in flake8.'
|
help="Print more information about what is happening in flake8."
|
||||||
' This option is repeatable and will increase verbosity each '
|
" This option is repeatable and will increase verbosity each "
|
||||||
'time it is repeated.',
|
"time it is repeated.",
|
||||||
)
|
)
|
||||||
add_option(
|
add_option(
|
||||||
'-q', '--quiet', default=0, action='count',
|
"-q",
|
||||||
|
"--quiet",
|
||||||
|
default=0,
|
||||||
|
action="count",
|
||||||
parse_from_config=True,
|
parse_from_config=True,
|
||||||
help='Report only file names, or nothing. This option is repeatable.',
|
help="Report only file names, or nothing. This option is repeatable.",
|
||||||
)
|
)
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--count', action='store_true', parse_from_config=True,
|
"--count",
|
||||||
help='Print total number of errors and warnings to standard error and'
|
action="store_true",
|
||||||
' set the exit code to 1 if total is not empty.',
|
parse_from_config=True,
|
||||||
|
help="Print total number of errors and warnings to standard error and"
|
||||||
|
" set the exit code to 1 if total is not empty.",
|
||||||
)
|
)
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--diff', action='store_true',
|
"--diff",
|
||||||
help='Report changes only within line number ranges in the unified '
|
action="store_true",
|
||||||
'diff provided on standard in by the user.',
|
help="Report changes only within line number ranges in the unified "
|
||||||
|
"diff provided on standard in by the user.",
|
||||||
)
|
)
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--exclude', metavar='patterns', default=','.join(defaults.EXCLUDE),
|
"--exclude",
|
||||||
comma_separated_list=True, parse_from_config=True,
|
metavar="patterns",
|
||||||
|
default=",".join(defaults.EXCLUDE),
|
||||||
|
comma_separated_list=True,
|
||||||
|
parse_from_config=True,
|
||||||
normalize_paths=True,
|
normalize_paths=True,
|
||||||
help='Comma-separated list of files or directories to exclude.'
|
help="Comma-separated list of files or directories to exclude."
|
||||||
' (Default: %default)',
|
" (Default: %default)",
|
||||||
)
|
)
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--filename', metavar='patterns', default='*.py',
|
"--filename",
|
||||||
parse_from_config=True, comma_separated_list=True,
|
metavar="patterns",
|
||||||
help='Only check for filenames matching the patterns in this comma-'
|
default="*.py",
|
||||||
'separated list. (Default: %default)',
|
parse_from_config=True,
|
||||||
|
comma_separated_list=True,
|
||||||
|
help="Only check for filenames matching the patterns in this comma-"
|
||||||
|
"separated list. (Default: %default)",
|
||||||
)
|
)
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--stdin-display-name', default='stdin',
|
"--stdin-display-name",
|
||||||
help='The name used when reporting errors from code passed via stdin.'
|
default="stdin",
|
||||||
' This is useful for editors piping the file contents to flake8.'
|
help="The name used when reporting errors from code passed via stdin."
|
||||||
' (Default: %default)',
|
" This is useful for editors piping the file contents to flake8."
|
||||||
|
" (Default: %default)",
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO(sigmavirus24): Figure out --first/--repeat
|
# TODO(sigmavirus24): Figure out --first/--repeat
|
||||||
|
|
@ -91,136 +107,183 @@ def register_default_options(option_manager):
|
||||||
# freely provide a format string and that will break if we restrict their
|
# freely provide a format string and that will break if we restrict their
|
||||||
# choices.
|
# choices.
|
||||||
add_option(
|
add_option(
|
||||||
'--format', metavar='format', default='default',
|
"--format",
|
||||||
|
metavar="format",
|
||||||
|
default="default",
|
||||||
parse_from_config=True,
|
parse_from_config=True,
|
||||||
help='Format errors according to the chosen formatter.',
|
help="Format errors according to the chosen formatter.",
|
||||||
)
|
)
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--hang-closing', action='store_true', parse_from_config=True,
|
"--hang-closing",
|
||||||
help='Hang closing bracket instead of matching indentation of opening'
|
action="store_true",
|
||||||
" bracket's line.",
|
parse_from_config=True,
|
||||||
|
help="Hang closing bracket instead of matching indentation of opening"
|
||||||
|
" bracket's line.",
|
||||||
)
|
)
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--ignore', metavar='errors', default=','.join(defaults.IGNORE),
|
"--ignore",
|
||||||
parse_from_config=True, comma_separated_list=True,
|
metavar="errors",
|
||||||
help='Comma-separated list of errors and warnings to ignore (or skip).'
|
default=",".join(defaults.IGNORE),
|
||||||
' For example, ``--ignore=E4,E51,W234``. (Default: %default)',
|
parse_from_config=True,
|
||||||
|
comma_separated_list=True,
|
||||||
|
help="Comma-separated list of errors and warnings to ignore (or skip)."
|
||||||
|
" For example, ``--ignore=E4,E51,W234``. (Default: %default)",
|
||||||
)
|
)
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--extend-ignore', metavar='errors', default='',
|
"--extend-ignore",
|
||||||
parse_from_config=True, comma_separated_list=True,
|
metavar="errors",
|
||||||
help='Comma-separated list of errors and warnings to add to the list'
|
default="",
|
||||||
' of ignored ones. For example, ``--extend-ignore=E4,E51,W234``.',
|
parse_from_config=True,
|
||||||
|
comma_separated_list=True,
|
||||||
|
help="Comma-separated list of errors and warnings to add to the list"
|
||||||
|
" of ignored ones. For example, ``--extend-ignore=E4,E51,W234``.",
|
||||||
)
|
)
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--max-line-length', type='int', metavar='n',
|
"--max-line-length",
|
||||||
default=defaults.MAX_LINE_LENGTH, parse_from_config=True,
|
type="int",
|
||||||
help='Maximum allowed line length for the entirety of this run. '
|
metavar="n",
|
||||||
'(Default: %default)',
|
default=defaults.MAX_LINE_LENGTH,
|
||||||
|
parse_from_config=True,
|
||||||
|
help="Maximum allowed line length for the entirety of this run. "
|
||||||
|
"(Default: %default)",
|
||||||
)
|
)
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--select', metavar='errors', default=','.join(defaults.SELECT),
|
"--select",
|
||||||
parse_from_config=True, comma_separated_list=True,
|
metavar="errors",
|
||||||
help='Comma-separated list of errors and warnings to enable.'
|
default=",".join(defaults.SELECT),
|
||||||
' For example, ``--select=E4,E51,W234``. (Default: %default)',
|
parse_from_config=True,
|
||||||
|
comma_separated_list=True,
|
||||||
|
help="Comma-separated list of errors and warnings to enable."
|
||||||
|
" For example, ``--select=E4,E51,W234``. (Default: %default)",
|
||||||
)
|
)
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--disable-noqa', default=False, parse_from_config=True,
|
"--disable-noqa",
|
||||||
action='store_true',
|
default=False,
|
||||||
|
parse_from_config=True,
|
||||||
|
action="store_true",
|
||||||
help='Disable the effect of "# noqa". This will report errors on '
|
help='Disable the effect of "# noqa". This will report errors on '
|
||||||
'lines with "# noqa" at the end.'
|
'lines with "# noqa" at the end.',
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO(sigmavirus24): Decide what to do about --show-pep8
|
# TODO(sigmavirus24): Decide what to do about --show-pep8
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--show-source', action='store_true', parse_from_config=True,
|
"--show-source",
|
||||||
help='Show the source generate each error or warning.',
|
action="store_true",
|
||||||
|
parse_from_config=True,
|
||||||
|
help="Show the source generate each error or warning.",
|
||||||
)
|
)
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--statistics', action='store_true', parse_from_config=True,
|
"--statistics",
|
||||||
help='Count errors and warnings.',
|
action="store_true",
|
||||||
|
parse_from_config=True,
|
||||||
|
help="Count errors and warnings.",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Flake8 options
|
# Flake8 options
|
||||||
add_option(
|
add_option(
|
||||||
'--enable-extensions', default='', parse_from_config=True,
|
"--enable-extensions",
|
||||||
comma_separated_list=True, type='string',
|
default="",
|
||||||
help='Enable plugins and extensions that are otherwise disabled '
|
parse_from_config=True,
|
||||||
'by default',
|
comma_separated_list=True,
|
||||||
|
type="string",
|
||||||
|
help="Enable plugins and extensions that are otherwise disabled "
|
||||||
|
"by default",
|
||||||
)
|
)
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--exit-zero', action='store_true',
|
"--exit-zero",
|
||||||
|
action="store_true",
|
||||||
help='Exit with status code "0" even if there are errors.',
|
help='Exit with status code "0" even if there are errors.',
|
||||||
)
|
)
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--install-hook', action='callback', type='choice',
|
"--install-hook",
|
||||||
choices=vcs.choices(), callback=vcs.install,
|
action="callback",
|
||||||
help='Install a hook that is run prior to a commit for the supported '
|
type="choice",
|
||||||
'version control system.'
|
choices=vcs.choices(),
|
||||||
|
callback=vcs.install,
|
||||||
|
help="Install a hook that is run prior to a commit for the supported "
|
||||||
|
"version control system.",
|
||||||
)
|
)
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'-j', '--jobs', type='string', default='auto', parse_from_config=True,
|
"-j",
|
||||||
help='Number of subprocesses to use to run checks in parallel. '
|
"--jobs",
|
||||||
'This is ignored on Windows. The default, "auto", will '
|
type="string",
|
||||||
'auto-detect the number of processors available to use.'
|
default="auto",
|
||||||
' (Default: %default)',
|
parse_from_config=True,
|
||||||
|
help="Number of subprocesses to use to run checks in parallel. "
|
||||||
|
'This is ignored on Windows. The default, "auto", will '
|
||||||
|
"auto-detect the number of processors available to use."
|
||||||
|
" (Default: %default)",
|
||||||
)
|
)
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--output-file', default=None, type='string', parse_from_config=True,
|
"--output-file",
|
||||||
|
default=None,
|
||||||
|
type="string",
|
||||||
|
parse_from_config=True,
|
||||||
# callback=callbacks.redirect_stdout,
|
# callback=callbacks.redirect_stdout,
|
||||||
help='Redirect report to a file.',
|
help="Redirect report to a file.",
|
||||||
)
|
)
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--tee', default=False, parse_from_config=True, action='store_true',
|
"--tee",
|
||||||
help='Write to stdout and output-file.',
|
default=False,
|
||||||
|
parse_from_config=True,
|
||||||
|
action="store_true",
|
||||||
|
help="Write to stdout and output-file.",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Config file options
|
# Config file options
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--append-config', action='append',
|
"--append-config",
|
||||||
help='Provide extra config files to parse in addition to the files '
|
action="append",
|
||||||
'found by Flake8 by default. These files are the last ones read '
|
help="Provide extra config files to parse in addition to the files "
|
||||||
'and so they take the highest precedence when multiple files '
|
"found by Flake8 by default. These files are the last ones read "
|
||||||
'provide the same option.',
|
"and so they take the highest precedence when multiple files "
|
||||||
|
"provide the same option.",
|
||||||
)
|
)
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--config', default=None,
|
"--config",
|
||||||
help='Path to the config file that will be the authoritative config '
|
default=None,
|
||||||
'source. This will cause Flake8 to ignore all other '
|
help="Path to the config file that will be the authoritative config "
|
||||||
'configuration files.'
|
"source. This will cause Flake8 to ignore all other "
|
||||||
|
"configuration files.",
|
||||||
)
|
)
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--isolated', default=False, action='store_true',
|
"--isolated",
|
||||||
help='Ignore all configuration files.',
|
default=False,
|
||||||
|
action="store_true",
|
||||||
|
help="Ignore all configuration files.",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Benchmarking
|
# Benchmarking
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--benchmark', default=False, action='store_true',
|
"--benchmark",
|
||||||
help='Print benchmark information about this run of Flake8',
|
default=False,
|
||||||
|
action="store_true",
|
||||||
|
help="Print benchmark information about this run of Flake8",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Debugging
|
# Debugging
|
||||||
|
|
||||||
add_option(
|
add_option(
|
||||||
'--bug-report', action='callback', callback=debug.print_information,
|
"--bug-report",
|
||||||
callback_kwargs={'option_manager': option_manager},
|
action="callback",
|
||||||
help='Print information necessary when preparing a bug report',
|
callback=debug.print_information,
|
||||||
|
callback_kwargs={"option_manager": option_manager},
|
||||||
|
help="Print information necessary when preparing a bug report",
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ UNSET = object()
|
||||||
class Flake8(setuptools.Command):
|
class Flake8(setuptools.Command):
|
||||||
"""Run Flake8 via setuptools/distutils for registered modules."""
|
"""Run Flake8 via setuptools/distutils for registered modules."""
|
||||||
|
|
||||||
description = 'Run Flake8 on modules registered in setup.py'
|
description = "Run Flake8 on modules registered in setup.py"
|
||||||
# NOTE(sigmavirus24): If we populated this with a list of tuples, users
|
# NOTE(sigmavirus24): If we populated this with a list of tuples, users
|
||||||
# could do something like ``python setup.py flake8 --ignore=E123,E234``
|
# could do something like ``python setup.py flake8 --ignore=E123,E234``
|
||||||
# but we would have to redefine it and we can't define it dynamically.
|
# but we would have to redefine it and we can't define it dynamically.
|
||||||
|
|
@ -39,23 +39,26 @@ class Flake8(setuptools.Command):
|
||||||
value = getattr(self, name, UNSET)
|
value = getattr(self, name, UNSET)
|
||||||
if value is UNSET:
|
if value is UNSET:
|
||||||
continue
|
continue
|
||||||
setattr(self.flake8.options,
|
setattr(
|
||||||
name,
|
self.flake8.options,
|
||||||
option.normalize_from_setuptools(value))
|
name,
|
||||||
|
option.normalize_from_setuptools(value),
|
||||||
|
)
|
||||||
|
|
||||||
def package_files(self):
|
def package_files(self):
|
||||||
"""Collect the files/dirs included in the registered modules."""
|
"""Collect the files/dirs included in the registered modules."""
|
||||||
seen_package_directories = ()
|
seen_package_directories = ()
|
||||||
directories = self.distribution.package_dir or {}
|
directories = self.distribution.package_dir or {}
|
||||||
empty_directory_exists = '' in directories
|
empty_directory_exists = "" in directories
|
||||||
packages = self.distribution.packages or []
|
packages = self.distribution.packages or []
|
||||||
for package in packages:
|
for package in packages:
|
||||||
package_directory = package
|
package_directory = package
|
||||||
if package in directories:
|
if package in directories:
|
||||||
package_directory = directories[package]
|
package_directory = directories[package]
|
||||||
elif empty_directory_exists:
|
elif empty_directory_exists:
|
||||||
package_directory = os.path.join(directories[''],
|
package_directory = os.path.join(
|
||||||
package_directory)
|
directories[""], package_directory
|
||||||
|
)
|
||||||
|
|
||||||
# NOTE(sigmavirus24): Do not collect submodules, e.g.,
|
# NOTE(sigmavirus24): Do not collect submodules, e.g.,
|
||||||
# if we have:
|
# if we have:
|
||||||
|
|
@ -66,13 +69,13 @@ class Flake8(setuptools.Command):
|
||||||
if package_directory.startswith(seen_package_directories):
|
if package_directory.startswith(seen_package_directories):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
seen_package_directories += (package_directory + '.',)
|
seen_package_directories += (package_directory + ".",)
|
||||||
yield package_directory
|
yield package_directory
|
||||||
|
|
||||||
def module_files(self):
|
def module_files(self):
|
||||||
"""Collect the files listed as py_modules."""
|
"""Collect the files listed as py_modules."""
|
||||||
modules = self.distribution.py_modules or []
|
modules = self.distribution.py_modules or []
|
||||||
filename_from = '{0}.py'.format
|
filename_from = "{0}.py".format
|
||||||
for module in modules:
|
for module in modules:
|
||||||
yield filename_from(module)
|
yield filename_from(module)
|
||||||
|
|
||||||
|
|
@ -84,7 +87,7 @@ class Flake8(setuptools.Command):
|
||||||
for module in self.module_files():
|
for module in self.module_files():
|
||||||
yield module
|
yield module
|
||||||
|
|
||||||
yield 'setup.py'
|
yield "setup.py"
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""Run the Flake8 application."""
|
"""Run the Flake8 application."""
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,7 @@ from flake8.main import mercurial
|
||||||
# as plugins, e.g., adding a flake8.vcs entry-point. In that case, this
|
# as plugins, e.g., adding a flake8.vcs entry-point. In that case, this
|
||||||
# dictionary should disappear, and this module might contain more code for
|
# dictionary should disappear, and this module might contain more code for
|
||||||
# managing those bits (in conjuntion with flake8.plugins.manager).
|
# managing those bits (in conjuntion with flake8.plugins.manager).
|
||||||
_INSTALLERS = {
|
_INSTALLERS = {"git": git.install, "mercurial": mercurial.install}
|
||||||
'git': git.install,
|
|
||||||
'mercurial': mercurial.install,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def install(option, option_string, value, parser):
|
def install(option, option_string, value, parser):
|
||||||
|
|
@ -30,7 +27,7 @@ def install(option, option_string, value, parser):
|
||||||
errored = True
|
errored = True
|
||||||
|
|
||||||
if not successful:
|
if not successful:
|
||||||
print('Could not find the {0} directory'.format(value))
|
print("Could not find the {0} directory".format(value))
|
||||||
raise SystemExit(not successful and errored)
|
raise SystemExit(not successful and errored)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,26 +37,28 @@ def aggregate_options(manager, config_finder, arglist=None, values=None):
|
||||||
|
|
||||||
# Make our new configuration file mergerator
|
# Make our new configuration file mergerator
|
||||||
config_parser = config.MergedConfigParser(
|
config_parser = config.MergedConfigParser(
|
||||||
option_manager=manager,
|
option_manager=manager, config_finder=config_finder
|
||||||
config_finder=config_finder,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Get the parsed config
|
# Get the parsed config
|
||||||
parsed_config = config_parser.parse(original_values.config,
|
parsed_config = config_parser.parse(
|
||||||
original_values.isolated)
|
original_values.config, original_values.isolated
|
||||||
|
)
|
||||||
|
|
||||||
# Extend the default ignore value with the extended default ignore list,
|
# Extend the default ignore value with the extended default ignore list,
|
||||||
# registered by plugins.
|
# registered by plugins.
|
||||||
extended_default_ignore = manager.extended_default_ignore.copy()
|
extended_default_ignore = manager.extended_default_ignore.copy()
|
||||||
LOG.debug('Extended default ignore list: %s',
|
LOG.debug(
|
||||||
list(extended_default_ignore))
|
"Extended default ignore list: %s", list(extended_default_ignore)
|
||||||
|
)
|
||||||
extended_default_ignore.update(default_values.ignore)
|
extended_default_ignore.update(default_values.ignore)
|
||||||
default_values.ignore = list(extended_default_ignore)
|
default_values.ignore = list(extended_default_ignore)
|
||||||
LOG.debug('Merged default ignore list: %s', default_values.ignore)
|
LOG.debug("Merged default ignore list: %s", default_values.ignore)
|
||||||
|
|
||||||
extended_default_select = manager.extended_default_select.copy()
|
extended_default_select = manager.extended_default_select.copy()
|
||||||
LOG.debug('Extended default select list: %s',
|
LOG.debug(
|
||||||
list(extended_default_select))
|
"Extended default select list: %s", list(extended_default_select)
|
||||||
|
)
|
||||||
default_values.extended_default_select = extended_default_select
|
default_values.extended_default_select = extended_default_select
|
||||||
|
|
||||||
# Merge values parsed from config onto the default values returned
|
# Merge values parsed from config onto the default values returned
|
||||||
|
|
@ -67,10 +69,12 @@ def aggregate_options(manager, config_finder, arglist=None, values=None):
|
||||||
if not hasattr(default_values, config_name):
|
if not hasattr(default_values, config_name):
|
||||||
dest_name = config_parser.config_options[config_name].dest
|
dest_name = config_parser.config_options[config_name].dest
|
||||||
|
|
||||||
LOG.debug('Overriding default value of (%s) for "%s" with (%s)',
|
LOG.debug(
|
||||||
getattr(default_values, dest_name, None),
|
'Overriding default value of (%s) for "%s" with (%s)',
|
||||||
dest_name,
|
getattr(default_values, dest_name, None),
|
||||||
value)
|
dest_name,
|
||||||
|
value,
|
||||||
|
)
|
||||||
# Override the default values with the config values
|
# Override the default values with the config values
|
||||||
setattr(default_values, dest_name, value)
|
setattr(default_values, dest_name, value)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,13 @@ from flake8 import utils
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
__all__ = ('ConfigFileFinder', 'MergedConfigParser')
|
__all__ = ("ConfigFileFinder", "MergedConfigParser")
|
||||||
|
|
||||||
|
|
||||||
class ConfigFileFinder(object):
|
class ConfigFileFinder(object):
|
||||||
"""Encapsulate the logic for finding and reading config files."""
|
"""Encapsulate the logic for finding and reading config files."""
|
||||||
|
|
||||||
PROJECT_FILENAMES = ('setup.cfg', 'tox.ini')
|
PROJECT_FILENAMES = ("setup.cfg", "tox.ini")
|
||||||
|
|
||||||
def __init__(self, program_name, args, extra_config_files):
|
def __init__(self, program_name, args, extra_config_files):
|
||||||
"""Initialize object to find config files.
|
"""Initialize object to find config files.
|
||||||
|
|
@ -31,25 +31,27 @@ class ConfigFileFinder(object):
|
||||||
extra_config_files = extra_config_files or []
|
extra_config_files = extra_config_files or []
|
||||||
self.extra_config_files = [
|
self.extra_config_files = [
|
||||||
# Ensure the paths are absolute paths for local_config_files
|
# Ensure the paths are absolute paths for local_config_files
|
||||||
os.path.abspath(f) for f in extra_config_files
|
os.path.abspath(f)
|
||||||
|
for f in extra_config_files
|
||||||
]
|
]
|
||||||
|
|
||||||
# Platform specific settings
|
# Platform specific settings
|
||||||
self.is_windows = sys.platform == 'win32'
|
self.is_windows = sys.platform == "win32"
|
||||||
self.xdg_home = os.environ.get('XDG_CONFIG_HOME',
|
self.xdg_home = os.environ.get(
|
||||||
os.path.expanduser('~/.config'))
|
"XDG_CONFIG_HOME", os.path.expanduser("~/.config")
|
||||||
|
)
|
||||||
|
|
||||||
# Look for '.<program_name>' files
|
# Look for '.<program_name>' files
|
||||||
self.program_config = '.' + program_name
|
self.program_config = "." + program_name
|
||||||
self.program_name = program_name
|
self.program_name = program_name
|
||||||
|
|
||||||
# List of filenames to find in the local/project directory
|
# List of filenames to find in the local/project directory
|
||||||
self.project_filenames = ('setup.cfg', 'tox.ini', self.program_config)
|
self.project_filenames = ("setup.cfg", "tox.ini", self.program_config)
|
||||||
|
|
||||||
self.local_directory = os.path.abspath(os.curdir)
|
self.local_directory = os.path.abspath(os.curdir)
|
||||||
|
|
||||||
if not args:
|
if not args:
|
||||||
args = ['.']
|
args = ["."]
|
||||||
self.parent = self.tail = os.path.abspath(os.path.commonprefix(args))
|
self.parent = self.tail = os.path.abspath(os.path.commonprefix(args))
|
||||||
|
|
||||||
# caches to avoid double-reading config files
|
# caches to avoid double-reading config files
|
||||||
|
|
@ -61,7 +63,7 @@ class ConfigFileFinder(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _read_config(files):
|
def _read_config(files):
|
||||||
config = configparser.RawConfigParser()
|
config = configparser.RawConfigParser()
|
||||||
if isinstance(files, (str, type(u''))):
|
if isinstance(files, (str, type(u""))):
|
||||||
files = [files]
|
files = [files]
|
||||||
|
|
||||||
found_files = []
|
found_files = []
|
||||||
|
|
@ -69,13 +71,17 @@ class ConfigFileFinder(object):
|
||||||
try:
|
try:
|
||||||
found_files.extend(config.read(filename))
|
found_files.extend(config.read(filename))
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
LOG.exception("There was an error decoding a config file."
|
LOG.exception(
|
||||||
"The file with a problem was %s.",
|
"There was an error decoding a config file."
|
||||||
filename)
|
"The file with a problem was %s.",
|
||||||
|
filename,
|
||||||
|
)
|
||||||
except configparser.ParsingError:
|
except configparser.ParsingError:
|
||||||
LOG.exception("There was an error trying to parse a config "
|
LOG.exception(
|
||||||
"file. The file with a problem was %s.",
|
"There was an error trying to parse a config "
|
||||||
filename)
|
"file. The file with a problem was %s.",
|
||||||
|
filename,
|
||||||
|
)
|
||||||
return (config, found_files)
|
return (config, found_files)
|
||||||
|
|
||||||
def cli_config(self, files):
|
def cli_config(self, files):
|
||||||
|
|
@ -83,7 +89,7 @@ class ConfigFileFinder(object):
|
||||||
if files not in self._cli_configs:
|
if files not in self._cli_configs:
|
||||||
config, found_files = self._read_config(files)
|
config, found_files = self._read_config(files)
|
||||||
if found_files:
|
if found_files:
|
||||||
LOG.debug('Found cli configuration files: %s', found_files)
|
LOG.debug("Found cli configuration files: %s", found_files)
|
||||||
self._cli_configs[files] = config
|
self._cli_configs[files] = config
|
||||||
return self._cli_configs[files]
|
return self._cli_configs[files]
|
||||||
|
|
||||||
|
|
@ -94,8 +100,9 @@ class ConfigFileFinder(object):
|
||||||
found_config_files = False
|
found_config_files = False
|
||||||
while tail and not found_config_files:
|
while tail and not found_config_files:
|
||||||
for project_filename in self.project_filenames:
|
for project_filename in self.project_filenames:
|
||||||
filename = os.path.abspath(os.path.join(parent,
|
filename = os.path.abspath(
|
||||||
project_filename))
|
os.path.join(parent, project_filename)
|
||||||
|
)
|
||||||
if os.path.exists(filename):
|
if os.path.exists(filename):
|
||||||
yield filename
|
yield filename
|
||||||
found_config_files = True
|
found_config_files = True
|
||||||
|
|
@ -117,8 +124,7 @@ class ConfigFileFinder(object):
|
||||||
"""
|
"""
|
||||||
exists = os.path.exists
|
exists = os.path.exists
|
||||||
return [
|
return [
|
||||||
filename
|
filename for filename in self.generate_possible_local_files()
|
||||||
for filename in self.generate_possible_local_files()
|
|
||||||
] + [f for f in self.extra_config_files if exists(f)]
|
] + [f for f in self.extra_config_files if exists(f)]
|
||||||
|
|
||||||
def local_configs_with_files(self):
|
def local_configs_with_files(self):
|
||||||
|
|
@ -129,7 +135,7 @@ class ConfigFileFinder(object):
|
||||||
if self._local_configs is None:
|
if self._local_configs is None:
|
||||||
config, found_files = self._read_config(self.local_config_files())
|
config, found_files = self._read_config(self.local_config_files())
|
||||||
if found_files:
|
if found_files:
|
||||||
LOG.debug('Found local configuration files: %s', found_files)
|
LOG.debug("Found local configuration files: %s", found_files)
|
||||||
self._local_configs = config
|
self._local_configs = config
|
||||||
self._local_found_files = found_files
|
self._local_found_files = found_files
|
||||||
return (self._local_configs, self._local_found_files)
|
return (self._local_configs, self._local_found_files)
|
||||||
|
|
@ -141,7 +147,7 @@ class ConfigFileFinder(object):
|
||||||
def user_config_file(self):
|
def user_config_file(self):
|
||||||
"""Find the user-level config file."""
|
"""Find the user-level config file."""
|
||||||
if self.is_windows:
|
if self.is_windows:
|
||||||
return os.path.expanduser('~\\' + self.program_config)
|
return os.path.expanduser("~\\" + self.program_config)
|
||||||
return os.path.join(self.xdg_home, self.program_name)
|
return os.path.join(self.xdg_home, self.program_name)
|
||||||
|
|
||||||
def user_config(self):
|
def user_config(self):
|
||||||
|
|
@ -149,7 +155,7 @@ class ConfigFileFinder(object):
|
||||||
if self._user_config is None:
|
if self._user_config is None:
|
||||||
config, found_files = self._read_config(self.user_config_file())
|
config, found_files = self._read_config(self.user_config_file())
|
||||||
if found_files:
|
if found_files:
|
||||||
LOG.debug('Found user configuration files: %s', found_files)
|
LOG.debug("Found user configuration files: %s", found_files)
|
||||||
self._user_config = config
|
self._user_config = config
|
||||||
return self._user_config
|
return self._user_config
|
||||||
|
|
||||||
|
|
@ -164,10 +170,10 @@ class MergedConfigParser(object):
|
||||||
|
|
||||||
#: Set of types that should use the
|
#: Set of types that should use the
|
||||||
#: :meth:`~configparser.RawConfigParser.getint` method.
|
#: :meth:`~configparser.RawConfigParser.getint` method.
|
||||||
GETINT_TYPES = {'int', 'count'}
|
GETINT_TYPES = {"int", "count"}
|
||||||
#: Set of actions that should use the
|
#: Set of actions that should use the
|
||||||
#: :meth:`~configparser.RawConfigParser.getbool` method.
|
#: :meth:`~configparser.RawConfigParser.getbool` method.
|
||||||
GETBOOL_ACTIONS = {'store_true', 'store_false'}
|
GETBOOL_ACTIONS = {"store_true", "store_false"}
|
||||||
|
|
||||||
def __init__(self, option_manager, config_finder):
|
def __init__(self, option_manager, config_finder):
|
||||||
"""Initialize the MergedConfigParser instance.
|
"""Initialize the MergedConfigParser instance.
|
||||||
|
|
@ -189,26 +195,32 @@ class MergedConfigParser(object):
|
||||||
|
|
||||||
def _normalize_value(self, option, value):
|
def _normalize_value(self, option, value):
|
||||||
final_value = option.normalize(
|
final_value = option.normalize(
|
||||||
value,
|
value, self.config_finder.local_directory
|
||||||
self.config_finder.local_directory,
|
)
|
||||||
|
LOG.debug(
|
||||||
|
'%r has been normalized to %r for option "%s"',
|
||||||
|
value,
|
||||||
|
final_value,
|
||||||
|
option.config_name,
|
||||||
)
|
)
|
||||||
LOG.debug('%r has been normalized to %r for option "%s"',
|
|
||||||
value, final_value, option.config_name)
|
|
||||||
return final_value
|
return final_value
|
||||||
|
|
||||||
def _parse_config(self, config_parser):
|
def _parse_config(self, config_parser):
|
||||||
config_dict = {}
|
config_dict = {}
|
||||||
for option_name in config_parser.options(self.program_name):
|
for option_name in config_parser.options(self.program_name):
|
||||||
if option_name not in self.config_options:
|
if option_name not in self.config_options:
|
||||||
LOG.debug('Option "%s" is not registered. Ignoring.',
|
LOG.debug(
|
||||||
option_name)
|
'Option "%s" is not registered. Ignoring.', option_name
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
option = self.config_options[option_name]
|
option = self.config_options[option_name]
|
||||||
|
|
||||||
# Use the appropriate method to parse the config value
|
# Use the appropriate method to parse the config value
|
||||||
method = config_parser.get
|
method = config_parser.get
|
||||||
if (option.type in self.GETINT_TYPES or
|
if (
|
||||||
option.action in self.GETINT_TYPES):
|
option.type in self.GETINT_TYPES
|
||||||
|
or option.action in self.GETINT_TYPES
|
||||||
|
):
|
||||||
method = config_parser.getint
|
method = config_parser.getint
|
||||||
elif option.action in self.GETBOOL_ACTIONS:
|
elif option.action in self.GETBOOL_ACTIONS:
|
||||||
method = config_parser.getboolean
|
method = config_parser.getboolean
|
||||||
|
|
@ -229,33 +241,39 @@ class MergedConfigParser(object):
|
||||||
"""Parse and return the local configuration files."""
|
"""Parse and return the local configuration files."""
|
||||||
config = self.config_finder.local_configs()
|
config = self.config_finder.local_configs()
|
||||||
if not self.is_configured_by(config):
|
if not self.is_configured_by(config):
|
||||||
LOG.debug('Local configuration files have no %s section',
|
LOG.debug(
|
||||||
self.program_name)
|
"Local configuration files have no %s section",
|
||||||
|
self.program_name,
|
||||||
|
)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
LOG.debug('Parsing local configuration files.')
|
LOG.debug("Parsing local configuration files.")
|
||||||
return self._parse_config(config)
|
return self._parse_config(config)
|
||||||
|
|
||||||
def parse_user_config(self):
|
def parse_user_config(self):
|
||||||
"""Parse and return the user configuration files."""
|
"""Parse and return the user configuration files."""
|
||||||
config = self.config_finder.user_config()
|
config = self.config_finder.user_config()
|
||||||
if not self.is_configured_by(config):
|
if not self.is_configured_by(config):
|
||||||
LOG.debug('User configuration files have no %s section',
|
LOG.debug(
|
||||||
self.program_name)
|
"User configuration files have no %s section",
|
||||||
|
self.program_name,
|
||||||
|
)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
LOG.debug('Parsing user configuration files.')
|
LOG.debug("Parsing user configuration files.")
|
||||||
return self._parse_config(config)
|
return self._parse_config(config)
|
||||||
|
|
||||||
def parse_cli_config(self, config_path):
|
def parse_cli_config(self, config_path):
|
||||||
"""Parse and return the file specified by --config."""
|
"""Parse and return the file specified by --config."""
|
||||||
config = self.config_finder.cli_config(config_path)
|
config = self.config_finder.cli_config(config_path)
|
||||||
if not self.is_configured_by(config):
|
if not self.is_configured_by(config):
|
||||||
LOG.debug('CLI configuration files have no %s section',
|
LOG.debug(
|
||||||
self.program_name)
|
"CLI configuration files have no %s section",
|
||||||
|
self.program_name,
|
||||||
|
)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
LOG.debug('Parsing CLI configuration files.')
|
LOG.debug("Parsing CLI configuration files.")
|
||||||
return self._parse_config(config)
|
return self._parse_config(config)
|
||||||
|
|
||||||
def merge_user_and_local_config(self):
|
def merge_user_and_local_config(self):
|
||||||
|
|
@ -293,14 +311,19 @@ class MergedConfigParser(object):
|
||||||
dict
|
dict
|
||||||
"""
|
"""
|
||||||
if isolated:
|
if isolated:
|
||||||
LOG.debug('Refusing to parse configuration files due to user-'
|
LOG.debug(
|
||||||
'requested isolation')
|
"Refusing to parse configuration files due to user-"
|
||||||
|
"requested isolation"
|
||||||
|
)
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
if cli_config:
|
if cli_config:
|
||||||
LOG.debug('Ignoring user and locally found configuration files. '
|
LOG.debug(
|
||||||
'Reading only configuration from "%s" specified via '
|
"Ignoring user and locally found configuration files. "
|
||||||
'--config by the user', cli_config)
|
'Reading only configuration from "%s" specified via '
|
||||||
|
"--config by the user",
|
||||||
|
cli_config,
|
||||||
|
)
|
||||||
return self.parse_cli_config(cli_config)
|
return self.parse_cli_config(cli_config)
|
||||||
|
|
||||||
return self.merge_user_and_local_config()
|
return self.merge_user_and_local_config()
|
||||||
|
|
@ -325,13 +348,18 @@ def get_local_plugins(config_finder, cli_config=None, isolated=False):
|
||||||
"""
|
"""
|
||||||
local_plugins = LocalPlugins(extension=[], report=[], paths=[])
|
local_plugins = LocalPlugins(extension=[], report=[], paths=[])
|
||||||
if isolated:
|
if isolated:
|
||||||
LOG.debug('Refusing to look for local plugins in configuration'
|
LOG.debug(
|
||||||
'files due to user-requested isolation')
|
"Refusing to look for local plugins in configuration"
|
||||||
|
"files due to user-requested isolation"
|
||||||
|
)
|
||||||
return local_plugins
|
return local_plugins
|
||||||
|
|
||||||
if cli_config:
|
if cli_config:
|
||||||
LOG.debug('Reading local plugins only from "%s" specified via '
|
LOG.debug(
|
||||||
'--config by the user', cli_config)
|
'Reading local plugins only from "%s" specified via '
|
||||||
|
"--config by the user",
|
||||||
|
cli_config,
|
||||||
|
)
|
||||||
config = config_finder.cli_config(cli_config)
|
config = config_finder.cli_config(cli_config)
|
||||||
config_files = [cli_config]
|
config_files = [cli_config]
|
||||||
else:
|
else:
|
||||||
|
|
@ -339,28 +367,31 @@ def get_local_plugins(config_finder, cli_config=None, isolated=False):
|
||||||
|
|
||||||
base_dirs = {os.path.dirname(cf) for cf in config_files}
|
base_dirs = {os.path.dirname(cf) for cf in config_files}
|
||||||
|
|
||||||
section = '%s:local-plugins' % config_finder.program_name
|
section = "%s:local-plugins" % config_finder.program_name
|
||||||
for plugin_type in ['extension', 'report']:
|
for plugin_type in ["extension", "report"]:
|
||||||
if config.has_option(section, plugin_type):
|
if config.has_option(section, plugin_type):
|
||||||
local_plugins_string = config.get(section, plugin_type).strip()
|
local_plugins_string = config.get(section, plugin_type).strip()
|
||||||
plugin_type_list = getattr(local_plugins, plugin_type)
|
plugin_type_list = getattr(local_plugins, plugin_type)
|
||||||
plugin_type_list.extend(utils.parse_comma_separated_list(
|
plugin_type_list.extend(
|
||||||
local_plugins_string,
|
utils.parse_comma_separated_list(
|
||||||
regexp=utils.LOCAL_PLUGIN_LIST_RE,
|
local_plugins_string, regexp=utils.LOCAL_PLUGIN_LIST_RE
|
||||||
))
|
)
|
||||||
if config.has_option(section, 'paths'):
|
)
|
||||||
|
if config.has_option(section, "paths"):
|
||||||
raw_paths = utils.parse_comma_separated_list(
|
raw_paths = utils.parse_comma_separated_list(
|
||||||
config.get(section, 'paths').strip()
|
config.get(section, "paths").strip()
|
||||||
)
|
)
|
||||||
norm_paths = []
|
norm_paths = []
|
||||||
for base_dir in base_dirs:
|
for base_dir in base_dirs:
|
||||||
norm_paths.extend(
|
norm_paths.extend(
|
||||||
path for path in
|
path
|
||||||
utils.normalize_paths(raw_paths, parent=base_dir)
|
for path in utils.normalize_paths(raw_paths, parent=base_dir)
|
||||||
if os.path.exists(path)
|
if os.path.exists(path)
|
||||||
)
|
)
|
||||||
local_plugins.paths.extend(norm_paths)
|
local_plugins.paths.extend(norm_paths)
|
||||||
return local_plugins
|
return local_plugins
|
||||||
|
|
||||||
|
|
||||||
LocalPlugins = collections.namedtuple('LocalPlugins', 'extension report paths')
|
LocalPlugins = collections.namedtuple(
|
||||||
|
"LocalPlugins", "extension report paths"
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,28 @@ LOG = logging.getLogger(__name__)
|
||||||
class Option(object):
|
class Option(object):
|
||||||
"""Our wrapper around an optparse.Option object to add features."""
|
"""Our wrapper around an optparse.Option object to add features."""
|
||||||
|
|
||||||
def __init__(self, short_option_name=None, long_option_name=None,
|
def __init__(
|
||||||
# Options below here are taken from the optparse.Option class
|
self,
|
||||||
action=None, default=None, type=None, dest=None,
|
short_option_name=None,
|
||||||
nargs=None, const=None, choices=None, callback=None,
|
long_option_name=None,
|
||||||
callback_args=None, callback_kwargs=None, help=None,
|
# Options below here are taken from the optparse.Option class
|
||||||
metavar=None,
|
action=None,
|
||||||
# Options below here are specific to Flake8
|
default=None,
|
||||||
parse_from_config=False, comma_separated_list=False,
|
type=None,
|
||||||
normalize_paths=False):
|
dest=None,
|
||||||
|
nargs=None,
|
||||||
|
const=None,
|
||||||
|
choices=None,
|
||||||
|
callback=None,
|
||||||
|
callback_args=None,
|
||||||
|
callback_kwargs=None,
|
||||||
|
help=None,
|
||||||
|
metavar=None,
|
||||||
|
# Options below here are specific to Flake8
|
||||||
|
parse_from_config=False,
|
||||||
|
comma_separated_list=False,
|
||||||
|
normalize_paths=False,
|
||||||
|
):
|
||||||
"""Initialize an Option instance wrapping optparse.Option.
|
"""Initialize an Option instance wrapping optparse.Option.
|
||||||
|
|
||||||
The following are all passed directly through to optparse.
|
The following are all passed directly through to optparse.
|
||||||
|
|
@ -73,18 +86,18 @@ class Option(object):
|
||||||
x for x in (short_option_name, long_option_name) if x is not None
|
x for x in (short_option_name, long_option_name) if x is not None
|
||||||
]
|
]
|
||||||
self.option_kwargs = {
|
self.option_kwargs = {
|
||||||
'action': action,
|
"action": action,
|
||||||
'default': default,
|
"default": default,
|
||||||
'type': type,
|
"type": type,
|
||||||
'dest': self._make_dest(dest),
|
"dest": self._make_dest(dest),
|
||||||
'nargs': nargs,
|
"nargs": nargs,
|
||||||
'const': const,
|
"const": const,
|
||||||
'choices': choices,
|
"choices": choices,
|
||||||
'callback': callback,
|
"callback": callback,
|
||||||
'callback_args': callback_args,
|
"callback_args": callback_args,
|
||||||
'callback_kwargs': callback_kwargs,
|
"callback_kwargs": callback_kwargs,
|
||||||
'help': help,
|
"help": help,
|
||||||
'metavar': metavar,
|
"metavar": metavar,
|
||||||
}
|
}
|
||||||
# Set attributes for our option arguments
|
# Set attributes for our option arguments
|
||||||
for key, value in self.option_kwargs.items():
|
for key, value in self.option_kwargs.items():
|
||||||
|
|
@ -98,27 +111,32 @@ class Option(object):
|
||||||
self.config_name = None
|
self.config_name = None
|
||||||
if parse_from_config:
|
if parse_from_config:
|
||||||
if not long_option_name:
|
if not long_option_name:
|
||||||
raise ValueError('When specifying parse_from_config=True, '
|
raise ValueError(
|
||||||
'a long_option_name must also be specified.')
|
"When specifying parse_from_config=True, "
|
||||||
self.config_name = long_option_name[2:].replace('-', '_')
|
"a long_option_name must also be specified."
|
||||||
|
)
|
||||||
|
self.config_name = long_option_name[2:].replace("-", "_")
|
||||||
|
|
||||||
self._opt = None
|
self._opt = None
|
||||||
|
|
||||||
def __repr__(self): # noqa: D105
|
def __repr__(self): # noqa: D105
|
||||||
return (
|
return (
|
||||||
'Option({0}, {1}, action={action}, default={default}, '
|
"Option({0}, {1}, action={action}, default={default}, "
|
||||||
'dest={dest}, type={type}, callback={callback}, help={help},'
|
"dest={dest}, type={type}, callback={callback}, help={help},"
|
||||||
' callback={callback}, callback_args={callback_args}, '
|
" callback={callback}, callback_args={callback_args}, "
|
||||||
'callback_kwargs={callback_kwargs}, metavar={metavar})'
|
"callback_kwargs={callback_kwargs}, metavar={metavar})"
|
||||||
).format(self.short_option_name, self.long_option_name,
|
).format(
|
||||||
**self.option_kwargs)
|
self.short_option_name,
|
||||||
|
self.long_option_name,
|
||||||
|
**self.option_kwargs
|
||||||
|
)
|
||||||
|
|
||||||
def _make_dest(self, dest):
|
def _make_dest(self, dest):
|
||||||
if dest:
|
if dest:
|
||||||
return dest
|
return dest
|
||||||
|
|
||||||
if self.long_option_name:
|
if self.long_option_name:
|
||||||
return self.long_option_name[2:].replace('-', '_')
|
return self.long_option_name[2:].replace("-", "_")
|
||||||
return self.short_option_name[1]
|
return self.short_option_name[1]
|
||||||
|
|
||||||
def normalize(self, value, *normalize_args):
|
def normalize(self, value, *normalize_args):
|
||||||
|
|
@ -136,33 +154,36 @@ class Option(object):
|
||||||
def normalize_from_setuptools(self, value):
|
def normalize_from_setuptools(self, value):
|
||||||
"""Normalize the value received from setuptools."""
|
"""Normalize the value received from setuptools."""
|
||||||
value = self.normalize(value)
|
value = self.normalize(value)
|
||||||
if self.type == 'int' or self.action == 'count':
|
if self.type == "int" or self.action == "count":
|
||||||
return int(value)
|
return int(value)
|
||||||
if self.action in ('store_true', 'store_false'):
|
if self.action in ("store_true", "store_false"):
|
||||||
value = str(value).upper()
|
value = str(value).upper()
|
||||||
if value in ('1', 'T', 'TRUE', 'ON'):
|
if value in ("1", "T", "TRUE", "ON"):
|
||||||
return True
|
return True
|
||||||
if value in ('0', 'F', 'FALSE', 'OFF'):
|
if value in ("0", "F", "FALSE", "OFF"):
|
||||||
return False
|
return False
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def to_optparse(self):
|
def to_optparse(self):
|
||||||
"""Convert a Flake8 Option to an optparse Option."""
|
"""Convert a Flake8 Option to an optparse Option."""
|
||||||
if self._opt is None:
|
if self._opt is None:
|
||||||
self._opt = optparse.Option(*self.option_args,
|
self._opt = optparse.Option(
|
||||||
**self.option_kwargs)
|
*self.option_args, **self.option_kwargs
|
||||||
|
)
|
||||||
return self._opt
|
return self._opt
|
||||||
|
|
||||||
|
|
||||||
PluginVersion = collections.namedtuple("PluginVersion",
|
PluginVersion = collections.namedtuple(
|
||||||
["name", "version", "local"])
|
"PluginVersion", ["name", "version", "local"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class OptionManager(object):
|
class OptionManager(object):
|
||||||
"""Manage Options and OptionParser while adding post-processing."""
|
"""Manage Options and OptionParser while adding post-processing."""
|
||||||
|
|
||||||
def __init__(self, prog=None, version=None,
|
def __init__(
|
||||||
usage='%prog [options] file file ...'):
|
self, prog=None, version=None, usage="%prog [options] file file ..."
|
||||||
|
):
|
||||||
"""Initialize an instance of an OptionManager.
|
"""Initialize an instance of an OptionManager.
|
||||||
|
|
||||||
:param str prog:
|
:param str prog:
|
||||||
|
|
@ -172,8 +193,9 @@ class OptionManager(object):
|
||||||
:param str usage:
|
:param str usage:
|
||||||
Basic usage string used by the OptionParser.
|
Basic usage string used by the OptionParser.
|
||||||
"""
|
"""
|
||||||
self.parser = optparse.OptionParser(prog=prog, version=version,
|
self.parser = optparse.OptionParser(
|
||||||
usage=usage)
|
prog=prog, version=version, usage=usage
|
||||||
|
)
|
||||||
self.config_options_dict = {}
|
self.config_options_dict = {}
|
||||||
self.options = []
|
self.options = []
|
||||||
self.program_name = prog
|
self.program_name = prog
|
||||||
|
|
@ -198,7 +220,7 @@ class OptionManager(object):
|
||||||
``short_option_name`` and ``long_option_name`` may be specified
|
``short_option_name`` and ``long_option_name`` may be specified
|
||||||
positionally as they are with optparse normally.
|
positionally as they are with optparse normally.
|
||||||
"""
|
"""
|
||||||
if len(args) == 1 and args[0].startswith('--'):
|
if len(args) == 1 and args[0].startswith("--"):
|
||||||
args = (None, args[0])
|
args = (None, args[0])
|
||||||
option = Option(*args, **kwargs)
|
option = Option(*args, **kwargs)
|
||||||
self.parser.add_option(option.to_optparse())
|
self.parser.add_option(option.to_optparse())
|
||||||
|
|
@ -206,7 +228,7 @@ class OptionManager(object):
|
||||||
if option.parse_from_config:
|
if option.parse_from_config:
|
||||||
name = option.config_name
|
name = option.config_name
|
||||||
self.config_options_dict[name] = option
|
self.config_options_dict[name] = option
|
||||||
self.config_options_dict[name.replace('_', '-')] = option
|
self.config_options_dict[name.replace("_", "-")] = option
|
||||||
LOG.debug('Registered option "%s".', option)
|
LOG.debug('Registered option "%s".', option)
|
||||||
|
|
||||||
def remove_from_default_ignore(self, error_codes):
|
def remove_from_default_ignore(self, error_codes):
|
||||||
|
|
@ -216,13 +238,16 @@ class OptionManager(object):
|
||||||
List of strings that are the error/warning codes to attempt to
|
List of strings that are the error/warning codes to attempt to
|
||||||
remove from the extended default ignore list.
|
remove from the extended default ignore list.
|
||||||
"""
|
"""
|
||||||
LOG.debug('Removing %r from the default ignore list', error_codes)
|
LOG.debug("Removing %r from the default ignore list", error_codes)
|
||||||
for error_code in error_codes:
|
for error_code in error_codes:
|
||||||
try:
|
try:
|
||||||
self.extended_default_ignore.remove(error_code)
|
self.extended_default_ignore.remove(error_code)
|
||||||
except (ValueError, KeyError):
|
except (ValueError, KeyError):
|
||||||
LOG.debug('Attempted to remove %s from default ignore'
|
LOG.debug(
|
||||||
' but it was not a member of the list.', error_code)
|
"Attempted to remove %s from default ignore"
|
||||||
|
" but it was not a member of the list.",
|
||||||
|
error_code,
|
||||||
|
)
|
||||||
|
|
||||||
def extend_default_ignore(self, error_codes):
|
def extend_default_ignore(self, error_codes):
|
||||||
"""Extend the default ignore list with the error codes provided.
|
"""Extend the default ignore list with the error codes provided.
|
||||||
|
|
@ -231,7 +256,7 @@ class OptionManager(object):
|
||||||
List of strings that are the error/warning codes with which to
|
List of strings that are the error/warning codes with which to
|
||||||
extend the default ignore list.
|
extend the default ignore list.
|
||||||
"""
|
"""
|
||||||
LOG.debug('Extending default ignore list with %r', error_codes)
|
LOG.debug("Extending default ignore list with %r", error_codes)
|
||||||
self.extended_default_ignore.update(error_codes)
|
self.extended_default_ignore.update(error_codes)
|
||||||
|
|
||||||
def extend_default_select(self, error_codes):
|
def extend_default_select(self, error_codes):
|
||||||
|
|
@ -241,11 +266,12 @@ class OptionManager(object):
|
||||||
List of strings that are the error/warning codes with which
|
List of strings that are the error/warning codes with which
|
||||||
to extend the default select list.
|
to extend the default select list.
|
||||||
"""
|
"""
|
||||||
LOG.debug('Extending default select list with %r', error_codes)
|
LOG.debug("Extending default select list with %r", error_codes)
|
||||||
self.extended_default_select.update(error_codes)
|
self.extended_default_select.update(error_codes)
|
||||||
|
|
||||||
def generate_versions(self, format_str='%(name)s: %(version)s',
|
def generate_versions(
|
||||||
join_on=', '):
|
self, format_str="%(name)s: %(version)s", join_on=", "
|
||||||
|
):
|
||||||
"""Generate a comma-separated list of versions of plugins."""
|
"""Generate a comma-separated list of versions of plugins."""
|
||||||
return join_on.join(
|
return join_on.join(
|
||||||
format_str % self.format_plugin(plugin)
|
format_str % self.format_plugin(plugin)
|
||||||
|
|
@ -255,14 +281,17 @@ class OptionManager(object):
|
||||||
def update_version_string(self):
|
def update_version_string(self):
|
||||||
"""Update the flake8 version string."""
|
"""Update the flake8 version string."""
|
||||||
self.parser.version = (
|
self.parser.version = (
|
||||||
self.version + ' (' + self.generate_versions() + ') ' +
|
self.version
|
||||||
utils.get_python_version()
|
+ " ("
|
||||||
|
+ self.generate_versions()
|
||||||
|
+ ") "
|
||||||
|
+ utils.get_python_version()
|
||||||
)
|
)
|
||||||
|
|
||||||
def generate_epilog(self):
|
def generate_epilog(self):
|
||||||
"""Create an epilog with the version and name of each of plugin."""
|
"""Create an epilog with the version and name of each of plugin."""
|
||||||
plugin_version_format = '%(name)s: %(version)s'
|
plugin_version_format = "%(name)s: %(version)s"
|
||||||
self.parser.epilog = 'Installed plugins: ' + self.generate_versions(
|
self.parser.epilog = "Installed plugins: " + self.generate_versions(
|
||||||
plugin_version_format
|
plugin_version_format
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -303,7 +332,10 @@ class OptionManager(object):
|
||||||
# Unfortunately, we need to rely on a private method here.
|
# Unfortunately, we need to rely on a private method here.
|
||||||
try:
|
try:
|
||||||
self.parser._process_args(largs, rargs, values)
|
self.parser._process_args(largs, rargs, values)
|
||||||
except (optparse.BadOptionError, optparse.OptionValueError) as err:
|
except (
|
||||||
|
optparse.BadOptionError,
|
||||||
|
optparse.OptionValueError,
|
||||||
|
) as err:
|
||||||
self.parser.largs.append(err.opt_str)
|
self.parser.largs.append(err.opt_str)
|
||||||
|
|
||||||
args = largs + rargs
|
args = largs + rargs
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
"""Independent implementation of a Trie tree."""
|
"""Independent implementation of a Trie tree."""
|
||||||
|
|
||||||
__all__ = ('Trie', 'TrieNode')
|
__all__ = ("Trie", "TrieNode")
|
||||||
|
|
||||||
|
|
||||||
def _iterate_stringlike_objects(string):
|
def _iterate_stringlike_objects(string):
|
||||||
for i in range(len(string)):
|
for i in range(len(string)):
|
||||||
yield string[i:i + 1]
|
yield string[i : i + 1]
|
||||||
|
|
||||||
|
|
||||||
class Trie(object):
|
class Trie(object):
|
||||||
|
|
@ -57,9 +57,7 @@ class TrieNode(object):
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""Generate an easy to read representation of the node."""
|
"""Generate an easy to read representation of the node."""
|
||||||
return 'TrieNode(prefix={0}, data={1})'.format(
|
return "TrieNode(prefix={0}, data={1})".format(self.prefix, self.data)
|
||||||
self.prefix, self.data
|
|
||||||
)
|
|
||||||
|
|
||||||
def find_prefix(self, prefix):
|
def find_prefix(self, prefix):
|
||||||
"""Find the prefix in the children of this node.
|
"""Find the prefix in the children of this node.
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,11 @@ from flake8.plugins import notifier
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'Checkers',
|
"Checkers",
|
||||||
'Listeners',
|
"Listeners",
|
||||||
'Plugin',
|
"Plugin",
|
||||||
'PluginManager',
|
"PluginManager",
|
||||||
'ReportFormatters',
|
"ReportFormatters",
|
||||||
)
|
)
|
||||||
|
|
||||||
NO_GROUP_FOUND = object()
|
NO_GROUP_FOUND = object()
|
||||||
|
|
@ -55,11 +55,11 @@ class Plugin(object):
|
||||||
def to_dictionary(self):
|
def to_dictionary(self):
|
||||||
"""Convert this plugin to a dictionary."""
|
"""Convert this plugin to a dictionary."""
|
||||||
return {
|
return {
|
||||||
'name': self.name,
|
"name": self.name,
|
||||||
'parameters': self.parameters,
|
"parameters": self.parameters,
|
||||||
'parameter_names': self.parameter_names,
|
"parameter_names": self.parameter_names,
|
||||||
'plugin': self.plugin,
|
"plugin": self.plugin,
|
||||||
'plugin_name': self.plugin_name,
|
"plugin_name": self.plugin_name,
|
||||||
}
|
}
|
||||||
|
|
||||||
def is_in_a_group(self):
|
def is_in_a_group(self):
|
||||||
|
|
@ -75,7 +75,7 @@ class Plugin(object):
|
||||||
def group(self):
|
def group(self):
|
||||||
"""Find and parse the group the plugin is in."""
|
"""Find and parse the group the plugin is in."""
|
||||||
if self._group is None:
|
if self._group is None:
|
||||||
name = self.name.split('.', 1)
|
name = self.name.split(".", 1)
|
||||||
if len(name) > 1:
|
if len(name) > 1:
|
||||||
self._group = name[0]
|
self._group = name[0]
|
||||||
else:
|
else:
|
||||||
|
|
@ -132,7 +132,7 @@ class Plugin(object):
|
||||||
@property
|
@property
|
||||||
def off_by_default(self):
|
def off_by_default(self):
|
||||||
"""Return whether the plugin is ignored by default."""
|
"""Return whether the plugin is ignored by default."""
|
||||||
return getattr(self.plugin, 'off_by_default', False)
|
return getattr(self.plugin, "off_by_default", False)
|
||||||
|
|
||||||
def execute(self, *args, **kwargs):
|
def execute(self, *args, **kwargs):
|
||||||
r"""Call the plugin with \*args and \*\*kwargs."""
|
r"""Call the plugin with \*args and \*\*kwargs."""
|
||||||
|
|
@ -140,22 +140,21 @@ class Plugin(object):
|
||||||
|
|
||||||
def _load(self, verify_requirements):
|
def _load(self, verify_requirements):
|
||||||
# Avoid relying on hasattr() here.
|
# Avoid relying on hasattr() here.
|
||||||
resolve = getattr(self.entry_point, 'resolve', None)
|
resolve = getattr(self.entry_point, "resolve", None)
|
||||||
require = getattr(self.entry_point, 'require', None)
|
require = getattr(self.entry_point, "require", None)
|
||||||
if resolve and require:
|
if resolve and require:
|
||||||
if verify_requirements:
|
if verify_requirements:
|
||||||
LOG.debug('Verifying plugin "%s"\'s requirements.',
|
LOG.debug('Verifying plugin "%s"\'s requirements.', self.name)
|
||||||
self.name)
|
|
||||||
require()
|
require()
|
||||||
self._plugin = resolve()
|
self._plugin = resolve()
|
||||||
else:
|
else:
|
||||||
self._plugin = self.entry_point.load(
|
self._plugin = self.entry_point.load(require=verify_requirements)
|
||||||
require=verify_requirements
|
|
||||||
)
|
|
||||||
if not callable(self._plugin):
|
if not callable(self._plugin):
|
||||||
msg = ('Plugin %r is not a callable. It might be written for an'
|
msg = (
|
||||||
' older version of flake8 and might not work with this'
|
"Plugin %r is not a callable. It might be written for an"
|
||||||
' version' % self._plugin)
|
" older version of flake8 and might not work with this"
|
||||||
|
" version" % self._plugin
|
||||||
|
)
|
||||||
LOG.critical(msg)
|
LOG.critical(msg)
|
||||||
raise TypeError(msg)
|
raise TypeError(msg)
|
||||||
|
|
||||||
|
|
@ -179,8 +178,7 @@ class Plugin(object):
|
||||||
except Exception as load_exception:
|
except Exception as load_exception:
|
||||||
LOG.exception(load_exception)
|
LOG.exception(load_exception)
|
||||||
failed_to_load = exceptions.FailedToLoadPlugin(
|
failed_to_load = exceptions.FailedToLoadPlugin(
|
||||||
plugin=self,
|
plugin=self, exception=load_exception
|
||||||
exception=load_exception,
|
|
||||||
)
|
)
|
||||||
LOG.critical(str(failed_to_load))
|
LOG.critical(str(failed_to_load))
|
||||||
raise failed_to_load
|
raise failed_to_load
|
||||||
|
|
@ -194,8 +192,11 @@ class Plugin(object):
|
||||||
try:
|
try:
|
||||||
options.ignore.remove(self.name)
|
options.ignore.remove(self.name)
|
||||||
except (ValueError, KeyError):
|
except (ValueError, KeyError):
|
||||||
LOG.debug('Attempted to remove %s from the ignore list but it was '
|
LOG.debug(
|
||||||
'not a member of the list.', self.name)
|
"Attempted to remove %s from the ignore list but it was "
|
||||||
|
"not a member of the list.",
|
||||||
|
self.name,
|
||||||
|
)
|
||||||
|
|
||||||
def disable(self, optmanager):
|
def disable(self, optmanager):
|
||||||
"""Add the plugin name to the default ignore list."""
|
"""Add the plugin name to the default ignore list."""
|
||||||
|
|
@ -203,7 +204,7 @@ class Plugin(object):
|
||||||
|
|
||||||
def provide_options(self, optmanager, options, extra_args):
|
def provide_options(self, optmanager, options, extra_args):
|
||||||
"""Pass the parsed options and extra arguments to the plugin."""
|
"""Pass the parsed options and extra arguments to the plugin."""
|
||||||
parse_options = getattr(self.plugin, 'parse_options', None)
|
parse_options = getattr(self.plugin, "parse_options", None)
|
||||||
if parse_options is not None:
|
if parse_options is not None:
|
||||||
LOG.debug('Providing options to plugin "%s".', self.name)
|
LOG.debug('Providing options to plugin "%s".', self.name)
|
||||||
try:
|
try:
|
||||||
|
|
@ -224,11 +225,12 @@ class Plugin(object):
|
||||||
:returns:
|
:returns:
|
||||||
Nothing
|
Nothing
|
||||||
"""
|
"""
|
||||||
add_options = getattr(self.plugin, 'add_options', None)
|
add_options = getattr(self.plugin, "add_options", None)
|
||||||
if add_options is not None:
|
if add_options is not None:
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
'Registering options from plugin "%s" on OptionManager %r',
|
'Registering options from plugin "%s" on OptionManager %r',
|
||||||
self.name, optmanager
|
self.name,
|
||||||
|
optmanager,
|
||||||
)
|
)
|
||||||
add_options(optmanager)
|
add_options(optmanager)
|
||||||
|
|
||||||
|
|
@ -239,8 +241,9 @@ class Plugin(object):
|
||||||
class PluginManager(object): # pylint: disable=too-few-public-methods
|
class PluginManager(object): # pylint: disable=too-few-public-methods
|
||||||
"""Find and manage plugins consistently."""
|
"""Find and manage plugins consistently."""
|
||||||
|
|
||||||
def __init__(self, namespace,
|
def __init__(
|
||||||
verify_requirements=False, local_plugins=None):
|
self, namespace, verify_requirements=False, local_plugins=None
|
||||||
|
):
|
||||||
"""Initialize the manager.
|
"""Initialize the manager.
|
||||||
|
|
||||||
:param str namespace:
|
:param str namespace:
|
||||||
|
|
@ -348,7 +351,7 @@ def version_for(plugin):
|
||||||
except ImportError:
|
except ImportError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return getattr(module, '__version__', None)
|
return getattr(module, "__version__", None)
|
||||||
|
|
||||||
|
|
||||||
class PluginTypeManager(object):
|
class PluginTypeManager(object):
|
||||||
|
|
@ -363,7 +366,8 @@ class PluginTypeManager(object):
|
||||||
Plugins from config file instead of entry-points
|
Plugins from config file instead of entry-points
|
||||||
"""
|
"""
|
||||||
self.manager = PluginManager(
|
self.manager = PluginManager(
|
||||||
self.namespace, local_plugins=local_plugins)
|
self.namespace, local_plugins=local_plugins
|
||||||
|
)
|
||||||
self.plugins_loaded = False
|
self.plugins_loaded = False
|
||||||
|
|
||||||
def __contains__(self, name):
|
def __contains__(self, name):
|
||||||
|
|
@ -406,9 +410,11 @@ class PluginTypeManager(object):
|
||||||
def _generate_call_function(method_name, optmanager, *args, **kwargs):
|
def _generate_call_function(method_name, optmanager, *args, **kwargs):
|
||||||
def generated_function(plugin): # noqa: D105
|
def generated_function(plugin): # noqa: D105
|
||||||
method = getattr(plugin, method_name, None)
|
method = getattr(plugin, method_name, None)
|
||||||
if (method is not None and
|
if method is not None and isinstance(
|
||||||
isinstance(method, collections.Callable)):
|
method, collections.Callable
|
||||||
|
):
|
||||||
return method(optmanager, *args, **kwargs)
|
return method(optmanager, *args, **kwargs)
|
||||||
|
|
||||||
return generated_function
|
return generated_function
|
||||||
|
|
||||||
def load_plugins(self):
|
def load_plugins(self):
|
||||||
|
|
@ -435,7 +441,7 @@ class PluginTypeManager(object):
|
||||||
"""Register all of the checkers' options to the OptionManager."""
|
"""Register all of the checkers' options to the OptionManager."""
|
||||||
self.load_plugins()
|
self.load_plugins()
|
||||||
call_register_options = self._generate_call_function(
|
call_register_options = self._generate_call_function(
|
||||||
'register_options', optmanager,
|
"register_options", optmanager
|
||||||
)
|
)
|
||||||
|
|
||||||
list(self.manager.map(call_register_options))
|
list(self.manager.map(call_register_options))
|
||||||
|
|
@ -443,7 +449,7 @@ class PluginTypeManager(object):
|
||||||
def provide_options(self, optmanager, options, extra_args):
|
def provide_options(self, optmanager, options, extra_args):
|
||||||
"""Provide parsed options and extra arguments to the plugins."""
|
"""Provide parsed options and extra arguments to the plugins."""
|
||||||
call_provide_options = self._generate_call_function(
|
call_provide_options = self._generate_call_function(
|
||||||
'provide_options', optmanager, options, extra_args,
|
"provide_options", optmanager, options, extra_args
|
||||||
)
|
)
|
||||||
|
|
||||||
list(self.manager.map(call_provide_options))
|
list(self.manager.map(call_provide_options))
|
||||||
|
|
@ -470,7 +476,7 @@ class NotifierBuilderMixin(object): # pylint: disable=too-few-public-methods
|
||||||
class Checkers(PluginTypeManager):
|
class Checkers(PluginTypeManager):
|
||||||
"""All of the checkers registered through entry-points or config."""
|
"""All of the checkers registered through entry-points or config."""
|
||||||
|
|
||||||
namespace = 'flake8.extension'
|
namespace = "flake8.extension"
|
||||||
|
|
||||||
def checks_expecting(self, argument_name):
|
def checks_expecting(self, argument_name):
|
||||||
"""Retrieve checks that expect an argument with the specified name.
|
"""Retrieve checks that expect an argument with the specified name.
|
||||||
|
|
@ -484,14 +490,15 @@ class Checkers(PluginTypeManager):
|
||||||
def to_dictionary(self):
|
def to_dictionary(self):
|
||||||
"""Return a dictionary of AST and line-based plugins."""
|
"""Return a dictionary of AST and line-based plugins."""
|
||||||
return {
|
return {
|
||||||
'ast_plugins': [
|
"ast_plugins": [
|
||||||
plugin.to_dictionary() for plugin in self.ast_plugins
|
plugin.to_dictionary() for plugin in self.ast_plugins
|
||||||
],
|
],
|
||||||
'logical_line_plugins': [
|
"logical_line_plugins": [
|
||||||
plugin.to_dictionary() for plugin in self.logical_line_plugins
|
plugin.to_dictionary() for plugin in self.logical_line_plugins
|
||||||
],
|
],
|
||||||
'physical_line_plugins': [
|
"physical_line_plugins": [
|
||||||
plugin.to_dictionary() for plugin in self.physical_line_plugins
|
plugin.to_dictionary()
|
||||||
|
for plugin in self.physical_line_plugins
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -508,7 +515,7 @@ class Checkers(PluginTypeManager):
|
||||||
# function to map over the plugins.
|
# function to map over the plugins.
|
||||||
self.load_plugins()
|
self.load_plugins()
|
||||||
call_register_options = self._generate_call_function(
|
call_register_options = self._generate_call_function(
|
||||||
'register_options', optmanager,
|
"register_options", optmanager
|
||||||
)
|
)
|
||||||
|
|
||||||
def register_and_enable(plugin):
|
def register_and_enable(plugin):
|
||||||
|
|
@ -521,27 +528,27 @@ class Checkers(PluginTypeManager):
|
||||||
@property
|
@property
|
||||||
def ast_plugins(self):
|
def ast_plugins(self):
|
||||||
"""List of plugins that expect the AST tree."""
|
"""List of plugins that expect the AST tree."""
|
||||||
plugins = getattr(self, '_ast_plugins', [])
|
plugins = getattr(self, "_ast_plugins", [])
|
||||||
if not plugins:
|
if not plugins:
|
||||||
plugins = list(self.checks_expecting('tree'))
|
plugins = list(self.checks_expecting("tree"))
|
||||||
self._ast_plugins = plugins
|
self._ast_plugins = plugins
|
||||||
return plugins
|
return plugins
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def logical_line_plugins(self):
|
def logical_line_plugins(self):
|
||||||
"""List of plugins that expect the logical lines."""
|
"""List of plugins that expect the logical lines."""
|
||||||
plugins = getattr(self, '_logical_line_plugins', [])
|
plugins = getattr(self, "_logical_line_plugins", [])
|
||||||
if not plugins:
|
if not plugins:
|
||||||
plugins = list(self.checks_expecting('logical_line'))
|
plugins = list(self.checks_expecting("logical_line"))
|
||||||
self._logical_line_plugins = plugins
|
self._logical_line_plugins = plugins
|
||||||
return plugins
|
return plugins
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def physical_line_plugins(self):
|
def physical_line_plugins(self):
|
||||||
"""List of plugins that expect the physical lines."""
|
"""List of plugins that expect the physical lines."""
|
||||||
plugins = getattr(self, '_physical_line_plugins', [])
|
plugins = getattr(self, "_physical_line_plugins", [])
|
||||||
if not plugins:
|
if not plugins:
|
||||||
plugins = list(self.checks_expecting('physical_line'))
|
plugins = list(self.checks_expecting("physical_line"))
|
||||||
self._physical_line_plugins = plugins
|
self._physical_line_plugins = plugins
|
||||||
return plugins
|
return plugins
|
||||||
|
|
||||||
|
|
@ -549,10 +556,10 @@ class Checkers(PluginTypeManager):
|
||||||
class Listeners(PluginTypeManager, NotifierBuilderMixin):
|
class Listeners(PluginTypeManager, NotifierBuilderMixin):
|
||||||
"""All of the listeners registered through entry-points or config."""
|
"""All of the listeners registered through entry-points or config."""
|
||||||
|
|
||||||
namespace = 'flake8.listen'
|
namespace = "flake8.listen"
|
||||||
|
|
||||||
|
|
||||||
class ReportFormatters(PluginTypeManager):
|
class ReportFormatters(PluginTypeManager):
|
||||||
"""All of the report formatters registered through entry-points/config."""
|
"""All of the report formatters registered through entry-points/config."""
|
||||||
|
|
||||||
namespace = 'flake8.report'
|
namespace = "flake8.report"
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ class Notifier(object):
|
||||||
path = error_code
|
path = error_code
|
||||||
while path:
|
while path:
|
||||||
node = self.listeners.find(path)
|
node = self.listeners.find(path)
|
||||||
listeners = getattr(node, 'data', [])
|
listeners = getattr(node, "data", [])
|
||||||
for listener in listeners:
|
for listener in listeners:
|
||||||
yield listener
|
yield listener
|
||||||
path = path[:-1]
|
path = path[:-1]
|
||||||
|
|
|
||||||
|
|
@ -18,35 +18,35 @@ from flake8 import utils
|
||||||
|
|
||||||
|
|
||||||
FLAKE8_PYFLAKES_CODES = {
|
FLAKE8_PYFLAKES_CODES = {
|
||||||
'UnusedImport': 'F401',
|
"UnusedImport": "F401",
|
||||||
'ImportShadowedByLoopVar': 'F402',
|
"ImportShadowedByLoopVar": "F402",
|
||||||
'ImportStarUsed': 'F403',
|
"ImportStarUsed": "F403",
|
||||||
'LateFutureImport': 'F404',
|
"LateFutureImport": "F404",
|
||||||
'ImportStarUsage': 'F405',
|
"ImportStarUsage": "F405",
|
||||||
'ImportStarNotPermitted': 'F406',
|
"ImportStarNotPermitted": "F406",
|
||||||
'FutureFeatureNotDefined': 'F407',
|
"FutureFeatureNotDefined": "F407",
|
||||||
'MultiValueRepeatedKeyLiteral': 'F601',
|
"MultiValueRepeatedKeyLiteral": "F601",
|
||||||
'MultiValueRepeatedKeyVariable': 'F602',
|
"MultiValueRepeatedKeyVariable": "F602",
|
||||||
'TooManyExpressionsInStarredAssignment': 'F621',
|
"TooManyExpressionsInStarredAssignment": "F621",
|
||||||
'TwoStarredExpressions': 'F622',
|
"TwoStarredExpressions": "F622",
|
||||||
'AssertTuple': 'F631',
|
"AssertTuple": "F631",
|
||||||
'BreakOutsideLoop': 'F701',
|
"BreakOutsideLoop": "F701",
|
||||||
'ContinueOutsideLoop': 'F702',
|
"ContinueOutsideLoop": "F702",
|
||||||
'ContinueInFinally': 'F703',
|
"ContinueInFinally": "F703",
|
||||||
'YieldOutsideFunction': 'F704',
|
"YieldOutsideFunction": "F704",
|
||||||
'ReturnWithArgsInsideGenerator': 'F705',
|
"ReturnWithArgsInsideGenerator": "F705",
|
||||||
'ReturnOutsideFunction': 'F706',
|
"ReturnOutsideFunction": "F706",
|
||||||
'DefaultExceptNotLast': 'F707',
|
"DefaultExceptNotLast": "F707",
|
||||||
'DoctestSyntaxError': 'F721',
|
"DoctestSyntaxError": "F721",
|
||||||
'ForwardAnnotationSyntaxError': 'F722',
|
"ForwardAnnotationSyntaxError": "F722",
|
||||||
'RedefinedWhileUnused': 'F811',
|
"RedefinedWhileUnused": "F811",
|
||||||
'RedefinedInListComp': 'F812',
|
"RedefinedInListComp": "F812",
|
||||||
'UndefinedName': 'F821',
|
"UndefinedName": "F821",
|
||||||
'UndefinedExport': 'F822',
|
"UndefinedExport": "F822",
|
||||||
'UndefinedLocal': 'F823',
|
"UndefinedLocal": "F823",
|
||||||
'DuplicateArgument': 'F831',
|
"DuplicateArgument": "F831",
|
||||||
'UnusedVariable': 'F841',
|
"UnusedVariable": "F841",
|
||||||
'RaiseNotImplemented': 'F901',
|
"RaiseNotImplemented": "F901",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -54,8 +54,9 @@ def patch_pyflakes():
|
||||||
"""Add error codes to Pyflakes messages."""
|
"""Add error codes to Pyflakes messages."""
|
||||||
for name, obj in vars(pyflakes.messages).items():
|
for name, obj in vars(pyflakes.messages).items():
|
||||||
if name[0].isupper() and obj.message:
|
if name[0].isupper() and obj.message:
|
||||||
obj.flake8_msg = '%s %s' % (
|
obj.flake8_msg = "%s %s" % (
|
||||||
FLAKE8_PYFLAKES_CODES.get(name, 'F999'), obj.message
|
FLAKE8_PYFLAKES_CODES.get(name, "F999"),
|
||||||
|
obj.message,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -65,7 +66,7 @@ patch_pyflakes()
|
||||||
class FlakesChecker(pyflakes.checker.Checker):
|
class FlakesChecker(pyflakes.checker.Checker):
|
||||||
"""Subclass the Pyflakes checker to conform with the flake8 API."""
|
"""Subclass the Pyflakes checker to conform with the flake8 API."""
|
||||||
|
|
||||||
name = 'pyflakes'
|
name = "pyflakes"
|
||||||
version = pyflakes.__version__
|
version = pyflakes.__version__
|
||||||
with_doctest = False
|
with_doctest = False
|
||||||
include_in_doctest = []
|
include_in_doctest = []
|
||||||
|
|
@ -75,48 +76,65 @@ class FlakesChecker(pyflakes.checker.Checker):
|
||||||
"""Initialize the PyFlakes plugin with an AST tree and filename."""
|
"""Initialize the PyFlakes plugin with an AST tree and filename."""
|
||||||
filename = utils.normalize_paths(filename)[0]
|
filename = utils.normalize_paths(filename)[0]
|
||||||
with_doctest = self.with_doctest
|
with_doctest = self.with_doctest
|
||||||
included_by = [include for include in self.include_in_doctest
|
included_by = [
|
||||||
if include != '' and filename.startswith(include)]
|
include
|
||||||
|
for include in self.include_in_doctest
|
||||||
|
if include != "" and filename.startswith(include)
|
||||||
|
]
|
||||||
if included_by:
|
if included_by:
|
||||||
with_doctest = True
|
with_doctest = True
|
||||||
|
|
||||||
for exclude in self.exclude_from_doctest:
|
for exclude in self.exclude_from_doctest:
|
||||||
if exclude != '' and filename.startswith(exclude):
|
if exclude != "" and filename.startswith(exclude):
|
||||||
with_doctest = False
|
with_doctest = False
|
||||||
overlaped_by = [include for include in included_by
|
overlaped_by = [
|
||||||
if include.startswith(exclude)]
|
include
|
||||||
|
for include in included_by
|
||||||
|
if include.startswith(exclude)
|
||||||
|
]
|
||||||
|
|
||||||
if overlaped_by:
|
if overlaped_by:
|
||||||
with_doctest = True
|
with_doctest = True
|
||||||
|
|
||||||
super(FlakesChecker, self).__init__(tree, filename,
|
super(FlakesChecker, self).__init__(
|
||||||
withDoctest=with_doctest)
|
tree, filename, withDoctest=with_doctest
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_options(cls, parser):
|
def add_options(cls, parser):
|
||||||
"""Register options for PyFlakes on the Flake8 OptionManager."""
|
"""Register options for PyFlakes on the Flake8 OptionManager."""
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
'--builtins', parse_from_config=True, comma_separated_list=True,
|
"--builtins",
|
||||||
|
parse_from_config=True,
|
||||||
|
comma_separated_list=True,
|
||||||
help="define more built-ins, comma separated",
|
help="define more built-ins, comma separated",
|
||||||
)
|
)
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
'--doctests', default=False, action='store_true',
|
"--doctests",
|
||||||
|
default=False,
|
||||||
|
action="store_true",
|
||||||
parse_from_config=True,
|
parse_from_config=True,
|
||||||
help="check syntax of the doctests",
|
help="check syntax of the doctests",
|
||||||
)
|
)
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
'--include-in-doctest', default='',
|
"--include-in-doctest",
|
||||||
dest='include_in_doctest', parse_from_config=True,
|
default="",
|
||||||
comma_separated_list=True, normalize_paths=True,
|
dest="include_in_doctest",
|
||||||
help='Run doctests only on these files',
|
parse_from_config=True,
|
||||||
type='string',
|
comma_separated_list=True,
|
||||||
|
normalize_paths=True,
|
||||||
|
help="Run doctests only on these files",
|
||||||
|
type="string",
|
||||||
)
|
)
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
'--exclude-from-doctest', default='',
|
"--exclude-from-doctest",
|
||||||
dest='exclude_from_doctest', parse_from_config=True,
|
default="",
|
||||||
comma_separated_list=True, normalize_paths=True,
|
dest="exclude_from_doctest",
|
||||||
help='Skip these files when running doctests',
|
parse_from_config=True,
|
||||||
type='string',
|
comma_separated_list=True,
|
||||||
|
normalize_paths=True,
|
||||||
|
help="Skip these files when running doctests",
|
||||||
|
type="string",
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -128,20 +146,20 @@ class FlakesChecker(pyflakes.checker.Checker):
|
||||||
|
|
||||||
included_files = []
|
included_files = []
|
||||||
for included_file in options.include_in_doctest:
|
for included_file in options.include_in_doctest:
|
||||||
if included_file == '':
|
if included_file == "":
|
||||||
continue
|
continue
|
||||||
if not included_file.startswith((os.sep, './', '~/')):
|
if not included_file.startswith((os.sep, "./", "~/")):
|
||||||
included_files.append('./' + included_file)
|
included_files.append("./" + included_file)
|
||||||
else:
|
else:
|
||||||
included_files.append(included_file)
|
included_files.append(included_file)
|
||||||
cls.include_in_doctest = utils.normalize_paths(included_files)
|
cls.include_in_doctest = utils.normalize_paths(included_files)
|
||||||
|
|
||||||
excluded_files = []
|
excluded_files = []
|
||||||
for excluded_file in options.exclude_from_doctest:
|
for excluded_file in options.exclude_from_doctest:
|
||||||
if excluded_file == '':
|
if excluded_file == "":
|
||||||
continue
|
continue
|
||||||
if not excluded_file.startswith((os.sep, './', '~/')):
|
if not excluded_file.startswith((os.sep, "./", "~/")):
|
||||||
excluded_files.append('./' + excluded_file)
|
excluded_files.append("./" + excluded_file)
|
||||||
else:
|
else:
|
||||||
excluded_files.append(excluded_file)
|
excluded_files.append(excluded_file)
|
||||||
cls.exclude_from_doctest = utils.normalize_paths(excluded_files)
|
cls.exclude_from_doctest = utils.normalize_paths(excluded_files)
|
||||||
|
|
@ -150,16 +168,20 @@ class FlakesChecker(pyflakes.checker.Checker):
|
||||||
cls.exclude_from_doctest
|
cls.exclude_from_doctest
|
||||||
)
|
)
|
||||||
if inc_exc:
|
if inc_exc:
|
||||||
raise ValueError('"%s" was specified in both the '
|
raise ValueError(
|
||||||
'include-in-doctest and exclude-from-doctest '
|
'"%s" was specified in both the '
|
||||||
'options. You are not allowed to specify it in '
|
"include-in-doctest and exclude-from-doctest "
|
||||||
'both for doctesting.' % inc_exc)
|
"options. You are not allowed to specify it in "
|
||||||
|
"both for doctesting." % inc_exc
|
||||||
|
)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""Run the plugin."""
|
"""Run the plugin."""
|
||||||
for message in self.messages:
|
for message in self.messages:
|
||||||
col = getattr(message, 'col', 0)
|
col = getattr(message, "col", 0)
|
||||||
yield (message.lineno,
|
yield (
|
||||||
col,
|
message.lineno,
|
||||||
(message.flake8_msg % message.message_args),
|
col,
|
||||||
message.__class__)
|
(message.flake8_msg % message.message_args),
|
||||||
|
message.__class__,
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,11 @@ PyCF_ONLY_AST = 1024
|
||||||
NEWLINE = frozenset([tokenize.NL, tokenize.NEWLINE])
|
NEWLINE = frozenset([tokenize.NL, tokenize.NEWLINE])
|
||||||
# Work around Python < 2.6 behaviour, which does not generate NL after
|
# Work around Python < 2.6 behaviour, which does not generate NL after
|
||||||
# a comment which is on a line by itself.
|
# a comment which is on a line by itself.
|
||||||
COMMENT_WITH_NL = tokenize.generate_tokens(['#\n'].pop).send(None)[1] == '#\n'
|
COMMENT_WITH_NL = tokenize.generate_tokens(["#\n"].pop).send(None)[1] == "#\n"
|
||||||
|
|
||||||
SKIP_TOKENS = frozenset([tokenize.NL, tokenize.NEWLINE, tokenize.INDENT,
|
SKIP_TOKENS = frozenset(
|
||||||
tokenize.DEDENT])
|
[tokenize.NL, tokenize.NEWLINE, tokenize.INDENT, tokenize.DEDENT]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class FileProcessor(object):
|
class FileProcessor(object):
|
||||||
|
|
@ -79,7 +80,7 @@ class FileProcessor(object):
|
||||||
#: Line number in the file
|
#: Line number in the file
|
||||||
self.line_number = 0
|
self.line_number = 0
|
||||||
#: Current logical line
|
#: Current logical line
|
||||||
self.logical_line = ''
|
self.logical_line = ""
|
||||||
#: Maximum line length as configured by the user
|
#: Maximum line length as configured by the user
|
||||||
self.max_line_length = options.max_line_length
|
self.max_line_length = options.max_line_length
|
||||||
#: Whether the current physical line is multiline
|
#: Whether the current physical line is multiline
|
||||||
|
|
@ -89,9 +90,9 @@ class FileProcessor(object):
|
||||||
#: Previous level of indentation
|
#: Previous level of indentation
|
||||||
self.previous_indent_level = 0
|
self.previous_indent_level = 0
|
||||||
#: Previous logical line
|
#: Previous logical line
|
||||||
self.previous_logical = ''
|
self.previous_logical = ""
|
||||||
#: Previous unindented (i.e. top-level) logical line
|
#: Previous unindented (i.e. top-level) logical line
|
||||||
self.previous_unindented_logical_line = ''
|
self.previous_unindented_logical_line = ""
|
||||||
#: Current set of tokens
|
#: Current set of tokens
|
||||||
self.tokens = []
|
self.tokens = []
|
||||||
#: Total number of lines in the file
|
#: Total number of lines in the file
|
||||||
|
|
@ -99,9 +100,7 @@ class FileProcessor(object):
|
||||||
#: Verbosity level of Flake8
|
#: Verbosity level of Flake8
|
||||||
self.verbose = options.verbose
|
self.verbose = options.verbose
|
||||||
#: Statistics dictionary
|
#: Statistics dictionary
|
||||||
self.statistics = {
|
self.statistics = {"logical lines": 0}
|
||||||
'logical lines': 0,
|
|
||||||
}
|
|
||||||
self._file_tokens = None
|
self._file_tokens = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
@ -115,9 +114,9 @@ class FileProcessor(object):
|
||||||
if self._file_tokens is None:
|
if self._file_tokens is None:
|
||||||
line_iter = iter(self.lines)
|
line_iter = iter(self.lines)
|
||||||
try:
|
try:
|
||||||
self._file_tokens = list(tokenize.generate_tokens(
|
self._file_tokens = list(
|
||||||
lambda: next(line_iter)
|
tokenize.generate_tokens(lambda: next(line_iter))
|
||||||
))
|
)
|
||||||
except tokenize.TokenError as exc:
|
except tokenize.TokenError as exc:
|
||||||
raise exceptions.InvalidSyntax(exc.message, exception=exc)
|
raise exceptions.InvalidSyntax(exc.message, exception=exc)
|
||||||
|
|
||||||
|
|
@ -153,9 +152,9 @@ class FileProcessor(object):
|
||||||
|
|
||||||
def update_checker_state_for(self, plugin):
|
def update_checker_state_for(self, plugin):
|
||||||
"""Update the checker_state attribute for the plugin."""
|
"""Update the checker_state attribute for the plugin."""
|
||||||
if 'checker_state' in plugin['parameters']:
|
if "checker_state" in plugin["parameters"]:
|
||||||
self.checker_state = self._checker_states.setdefault(
|
self.checker_state = self._checker_states.setdefault(
|
||||||
plugin['name'], {}
|
plugin["name"], {}
|
||||||
)
|
)
|
||||||
|
|
||||||
def next_logical_line(self):
|
def next_logical_line(self):
|
||||||
|
|
@ -194,10 +193,10 @@ class FileProcessor(object):
|
||||||
row_index = previous_row - 1
|
row_index = previous_row - 1
|
||||||
column_index = previous_column - 1
|
column_index = previous_column - 1
|
||||||
previous_text = self.lines[row_index][column_index]
|
previous_text = self.lines[row_index][column_index]
|
||||||
if (previous_text == ',' or
|
if previous_text == "," or (
|
||||||
(previous_text not in '{[(' and
|
previous_text not in "{[(" and text not in "}])"
|
||||||
text not in '}])')):
|
):
|
||||||
text = ' ' + text
|
text = " " + text
|
||||||
elif previous_column != start_column:
|
elif previous_column != start_column:
|
||||||
text = line[previous_column:start_column] + text
|
text = line[previous_column:start_column] + text
|
||||||
logical.append(text)
|
logical.append(text)
|
||||||
|
|
@ -208,16 +207,16 @@ class FileProcessor(object):
|
||||||
|
|
||||||
def build_ast(self):
|
def build_ast(self):
|
||||||
"""Build an abstract syntax tree from the list of lines."""
|
"""Build an abstract syntax tree from the list of lines."""
|
||||||
return compile(''.join(self.lines), '', 'exec', PyCF_ONLY_AST)
|
return compile("".join(self.lines), "", "exec", PyCF_ONLY_AST)
|
||||||
|
|
||||||
def build_logical_line(self):
|
def build_logical_line(self):
|
||||||
"""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()
|
||||||
joined_comments = ''.join(comments)
|
joined_comments = "".join(comments)
|
||||||
self.logical_line = ''.join(logical)
|
self.logical_line = "".join(logical)
|
||||||
if defaults.NOQA_INLINE_REGEXP.search(joined_comments):
|
if defaults.NOQA_INLINE_REGEXP.search(joined_comments):
|
||||||
self.noqa = True
|
self.noqa = True
|
||||||
self.statistics['logical lines'] += 1
|
self.statistics["logical lines"] += 1
|
||||||
return joined_comments, self.logical_line, mapping_list
|
return joined_comments, self.logical_line, mapping_list
|
||||||
|
|
||||||
def split_line(self, token):
|
def split_line(self, token):
|
||||||
|
|
@ -225,7 +224,7 @@ class FileProcessor(object):
|
||||||
|
|
||||||
This also auto-increments the line number for the caller.
|
This also auto-increments the line number for the caller.
|
||||||
"""
|
"""
|
||||||
for line in token[1].split('\n')[:-1]:
|
for line in token[1].split("\n")[:-1]:
|
||||||
yield line
|
yield line
|
||||||
self.line_number += 1
|
self.line_number += 1
|
||||||
|
|
||||||
|
|
@ -243,14 +242,16 @@ class FileProcessor(object):
|
||||||
LOG.exception(exc)
|
LOG.exception(exc)
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
LOG.warning('Plugin requested optional parameter "%s" '
|
LOG.warning(
|
||||||
'but this is not an available parameter.',
|
'Plugin requested optional parameter "%s" '
|
||||||
param)
|
"but this is not an available parameter.",
|
||||||
|
param,
|
||||||
|
)
|
||||||
return arguments
|
return arguments
|
||||||
|
|
||||||
def check_physical_error(self, error_code, line):
|
def check_physical_error(self, error_code, line):
|
||||||
"""Update attributes based on error code and line."""
|
"""Update attributes based on error code and line."""
|
||||||
if error_code == 'E101':
|
if error_code == "E101":
|
||||||
self.indent_char = line[0]
|
self.indent_char = line[0]
|
||||||
|
|
||||||
def generate_tokens(self):
|
def generate_tokens(self):
|
||||||
|
|
@ -282,7 +283,7 @@ class FileProcessor(object):
|
||||||
def next_line(self):
|
def next_line(self):
|
||||||
"""Get the next line from the list."""
|
"""Get the next line from the list."""
|
||||||
if self.line_number >= self.total_lines:
|
if self.line_number >= self.total_lines:
|
||||||
return ''
|
return ""
|
||||||
line = self.lines[self.line_number]
|
line = self.lines[self.line_number]
|
||||||
self.line_number += 1
|
self.line_number += 1
|
||||||
if self.indent_char is None and line[:1] in defaults.WHITESPACE:
|
if self.indent_char is None and line[:1] in defaults.WHITESPACE:
|
||||||
|
|
@ -292,8 +293,8 @@ class FileProcessor(object):
|
||||||
def read_lines(self):
|
def read_lines(self):
|
||||||
# type: () -> List[str]
|
# type: () -> List[str]
|
||||||
"""Read the lines for this file checker."""
|
"""Read the lines for this file checker."""
|
||||||
if self.filename is None or self.filename == '-':
|
if self.filename is None or self.filename == "-":
|
||||||
self.filename = self.options.stdin_display_name or 'stdin'
|
self.filename = self.options.stdin_display_name or "stdin"
|
||||||
lines = self.read_lines_from_stdin()
|
lines = self.read_lines_from_stdin()
|
||||||
else:
|
else:
|
||||||
lines = self.read_lines_from_filename()
|
lines = self.read_lines_from_filename()
|
||||||
|
|
@ -301,21 +302,20 @@ class FileProcessor(object):
|
||||||
|
|
||||||
def _readlines_py2(self):
|
def _readlines_py2(self):
|
||||||
# type: () -> List[str]
|
# type: () -> List[str]
|
||||||
with open(self.filename, 'rU') as fd:
|
with open(self.filename, "rU") as fd:
|
||||||
return fd.readlines()
|
return fd.readlines()
|
||||||
|
|
||||||
def _readlines_py3(self):
|
def _readlines_py3(self):
|
||||||
# type: () -> List[str]
|
# type: () -> List[str]
|
||||||
try:
|
try:
|
||||||
with open(self.filename, 'rb') as fd:
|
with open(self.filename, "rb") as fd:
|
||||||
(coding, lines) = tokenize.detect_encoding(fd.readline)
|
(coding, lines) = tokenize.detect_encoding(fd.readline)
|
||||||
textfd = io.TextIOWrapper(fd, coding, line_buffering=True)
|
textfd = io.TextIOWrapper(fd, coding, line_buffering=True)
|
||||||
return ([l.decode(coding) for l in lines] +
|
return [l.decode(coding) for l in lines] + textfd.readlines()
|
||||||
textfd.readlines())
|
|
||||||
except (LookupError, SyntaxError, UnicodeError):
|
except (LookupError, SyntaxError, UnicodeError):
|
||||||
# If we can't detect the codec with tokenize.detect_encoding, or
|
# If we can't detect the codec with tokenize.detect_encoding, or
|
||||||
# the detected encoding is incorrect, just fallback to latin-1.
|
# the detected encoding is incorrect, just fallback to latin-1.
|
||||||
with open(self.filename, encoding='latin-1') as fd:
|
with open(self.filename, encoding="latin-1") as fd:
|
||||||
return fd.readlines()
|
return fd.readlines()
|
||||||
|
|
||||||
def read_lines_from_filename(self):
|
def read_lines_from_filename(self):
|
||||||
|
|
@ -346,8 +346,8 @@ class FileProcessor(object):
|
||||||
return True
|
return True
|
||||||
elif any(defaults.NOQA_FILE.search(line) for line in self.lines):
|
elif any(defaults.NOQA_FILE.search(line) for line in self.lines):
|
||||||
LOG.warning(
|
LOG.warning(
|
||||||
'Detected `flake8: noqa` on line with code. To ignore an '
|
"Detected `flake8: noqa` on line with code. To ignore an "
|
||||||
'error on a line use `noqa` instead.',
|
"error on a line use `noqa` instead."
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
|
|
@ -367,25 +367,27 @@ class FileProcessor(object):
|
||||||
# If the first byte of the file is a UTF-8 BOM, strip it
|
# If the first byte of the file is a UTF-8 BOM, strip it
|
||||||
if first_byte == 0xFEFF:
|
if first_byte == 0xFEFF:
|
||||||
self.lines[0] = self.lines[0][1:]
|
self.lines[0] = self.lines[0][1:]
|
||||||
elif self.lines[0][:3] == '\xEF\xBB\xBF':
|
elif self.lines[0][:3] == "\xEF\xBB\xBF":
|
||||||
self.lines[0] = self.lines[0][3:]
|
self.lines[0] = self.lines[0][3:]
|
||||||
|
|
||||||
|
|
||||||
def is_eol_token(token):
|
def is_eol_token(token):
|
||||||
"""Check if the token is an end-of-line token."""
|
"""Check if the token is an end-of-line token."""
|
||||||
return token[0] in NEWLINE or token[4][token[3][1]:].lstrip() == '\\\n'
|
return token[0] in NEWLINE or token[4][token[3][1] :].lstrip() == "\\\n"
|
||||||
|
|
||||||
|
|
||||||
if COMMENT_WITH_NL: # If on Python 2.6
|
if COMMENT_WITH_NL: # If on Python 2.6
|
||||||
|
|
||||||
def is_eol_token(token, _is_eol_token=is_eol_token):
|
def is_eol_token(token, _is_eol_token=is_eol_token):
|
||||||
"""Check if the token is an end-of-line token."""
|
"""Check if the token is an end-of-line token."""
|
||||||
return (_is_eol_token(token) or
|
return _is_eol_token(token) or (
|
||||||
(token[0] == tokenize.COMMENT and token[1] == token[4]))
|
token[0] == tokenize.COMMENT and token[1] == token[4]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def is_multiline_string(token):
|
def is_multiline_string(token):
|
||||||
"""Check if this is a multiline string."""
|
"""Check if this is a multiline string."""
|
||||||
return token[0] == tokenize.STRING and '\n' in token[1]
|
return token[0] == tokenize.STRING and "\n" in token[1]
|
||||||
|
|
||||||
|
|
||||||
def token_is_newline(token):
|
def token_is_newline(token):
|
||||||
|
|
@ -401,9 +403,9 @@ def token_is_comment(token):
|
||||||
def count_parentheses(current_parentheses_count, token_text):
|
def count_parentheses(current_parentheses_count, token_text):
|
||||||
"""Count the number of parentheses."""
|
"""Count the number of parentheses."""
|
||||||
current_parentheses_count = current_parentheses_count or 0
|
current_parentheses_count = current_parentheses_count or 0
|
||||||
if token_text in '([{':
|
if token_text in "([{":
|
||||||
return current_parentheses_count + 1
|
return current_parentheses_count + 1
|
||||||
elif token_text in '}])':
|
elif token_text in "}])":
|
||||||
return current_parentheses_count - 1
|
return current_parentheses_count - 1
|
||||||
return current_parentheses_count
|
return current_parentheses_count
|
||||||
|
|
||||||
|
|
@ -411,12 +413,14 @@ def count_parentheses(current_parentheses_count, token_text):
|
||||||
def log_token(log, token):
|
def log_token(log, token):
|
||||||
"""Log a token to a provided logging object."""
|
"""Log a token to a provided logging object."""
|
||||||
if token[2][0] == token[3][0]:
|
if token[2][0] == token[3][0]:
|
||||||
pos = '[%s:%s]' % (token[2][1] or '', token[3][1])
|
pos = "[%s:%s]" % (token[2][1] or "", token[3][1])
|
||||||
else:
|
else:
|
||||||
pos = 'l.%s' % token[3][0]
|
pos = "l.%s" % token[3][0]
|
||||||
log.log(flake8._EXTRA_VERBOSE, 'l.%s\t%s\t%s\t%r' %
|
log.log(
|
||||||
(token[2][0], pos, tokenize.tok_name[token[0]],
|
flake8._EXTRA_VERBOSE,
|
||||||
token[1]))
|
"l.%s\t%s\t%s\t%r"
|
||||||
|
% (token[2][0], pos, tokenize.tok_name[token[0]], token[1]),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# NOTE(sigmavirus24): This was taken wholesale from
|
# NOTE(sigmavirus24): This was taken wholesale from
|
||||||
|
|
@ -435,13 +439,13 @@ def expand_indent(line):
|
||||||
>>> expand_indent(' \t')
|
>>> expand_indent(' \t')
|
||||||
16
|
16
|
||||||
"""
|
"""
|
||||||
if '\t' not in line:
|
if "\t" not in line:
|
||||||
return len(line) - len(line.lstrip())
|
return len(line) - len(line.lstrip())
|
||||||
result = 0
|
result = 0
|
||||||
for char in line:
|
for char in line:
|
||||||
if char == '\t':
|
if char == "\t":
|
||||||
result = result // 8 * 8 + 8
|
result = result // 8 * 8 + 8
|
||||||
elif char == ' ':
|
elif char == " ":
|
||||||
result += 1
|
result += 1
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
@ -470,4 +474,4 @@ def mutate_string(text):
|
||||||
if text[-3:] in ('"""', "'''"):
|
if text[-3:] in ('"""', "'''"):
|
||||||
start += 2
|
start += 2
|
||||||
end -= 2
|
end -= 2
|
||||||
return text[:start] + 'x' * (end - start) + text[end:]
|
return text[:start] + "x" * (end - start) + text[end:]
|
||||||
|
|
|
||||||
|
|
@ -56,13 +56,14 @@ class Statistics(object):
|
||||||
:returns:
|
:returns:
|
||||||
Generator of instances of :class:`Statistic`
|
Generator of instances of :class:`Statistic`
|
||||||
"""
|
"""
|
||||||
matching_errors = sorted(key for key in self._store
|
matching_errors = sorted(
|
||||||
if key.matches(prefix, filename))
|
key for key in self._store if key.matches(prefix, filename)
|
||||||
|
)
|
||||||
for error_code in matching_errors:
|
for error_code in matching_errors:
|
||||||
yield self._store[error_code]
|
yield self._store[error_code]
|
||||||
|
|
||||||
|
|
||||||
class Key(collections.namedtuple('Key', ['filename', 'code'])):
|
class Key(collections.namedtuple("Key", ["filename", "code"])):
|
||||||
"""Simple key structure for the Statistics dictionary.
|
"""Simple key structure for the Statistics dictionary.
|
||||||
|
|
||||||
To make things clearer, easier to read, and more understandable, we use a
|
To make things clearer, easier to read, and more understandable, we use a
|
||||||
|
|
@ -75,10 +76,7 @@ class Key(collections.namedtuple('Key', ['filename', 'code'])):
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_from(cls, error):
|
def create_from(cls, error):
|
||||||
"""Create a Key from :class:`flake8.style_guide.Violation`."""
|
"""Create a Key from :class:`flake8.style_guide.Violation`."""
|
||||||
return cls(
|
return cls(filename=error.filename, code=error.code)
|
||||||
filename=error.filename,
|
|
||||||
code=error.code,
|
|
||||||
)
|
|
||||||
|
|
||||||
def matches(self, prefix, filename):
|
def matches(self, prefix, filename):
|
||||||
"""Determine if this key matches some constraints.
|
"""Determine if this key matches some constraints.
|
||||||
|
|
@ -94,9 +92,9 @@ class Key(collections.namedtuple('Key', ['filename', 'code'])):
|
||||||
:rtype:
|
:rtype:
|
||||||
bool
|
bool
|
||||||
"""
|
"""
|
||||||
return (self.code.startswith(prefix) and
|
return self.code.startswith(prefix) and (
|
||||||
(filename is None or
|
filename is None or self.filename == filename
|
||||||
self.filename == filename))
|
)
|
||||||
|
|
||||||
|
|
||||||
class Statistic(object):
|
class Statistic(object):
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,7 @@ from flake8 import defaults
|
||||||
from flake8 import statistics
|
from flake8 import statistics
|
||||||
from flake8 import utils
|
from flake8 import utils
|
||||||
|
|
||||||
__all__ = (
|
__all__ = ("StyleGuide",)
|
||||||
'StyleGuide',
|
|
||||||
)
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -21,34 +19,32 @@ LOG = logging.getLogger(__name__)
|
||||||
try:
|
try:
|
||||||
lru_cache = functools.lru_cache
|
lru_cache = functools.lru_cache
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
|
||||||
def lru_cache(maxsize=128, typed=False):
|
def lru_cache(maxsize=128, typed=False):
|
||||||
"""Stub for missing lru_cache."""
|
"""Stub for missing lru_cache."""
|
||||||
def fake_decorator(func):
|
return lambda func: func
|
||||||
return func
|
|
||||||
|
|
||||||
return fake_decorator
|
|
||||||
|
|
||||||
|
|
||||||
# TODO(sigmavirus24): Determine if we need to use enum/enum34
|
# TODO(sigmavirus24): Determine if we need to use enum/enum34
|
||||||
class Selected(enum.Enum):
|
class Selected(enum.Enum):
|
||||||
"""Enum representing an explicitly or implicitly selected code."""
|
"""Enum representing an explicitly or implicitly selected code."""
|
||||||
|
|
||||||
Explicitly = 'explicitly selected'
|
Explicitly = "explicitly selected"
|
||||||
Implicitly = 'implicitly selected'
|
Implicitly = "implicitly selected"
|
||||||
|
|
||||||
|
|
||||||
class Ignored(enum.Enum):
|
class Ignored(enum.Enum):
|
||||||
"""Enum representing an explicitly or implicitly ignored code."""
|
"""Enum representing an explicitly or implicitly ignored code."""
|
||||||
|
|
||||||
Explicitly = 'explicitly ignored'
|
Explicitly = "explicitly ignored"
|
||||||
Implicitly = 'implicitly ignored'
|
Implicitly = "implicitly ignored"
|
||||||
|
|
||||||
|
|
||||||
class Decision(enum.Enum):
|
class Decision(enum.Enum):
|
||||||
"""Enum representing whether a code should be ignored or selected."""
|
"""Enum representing whether a code should be ignored or selected."""
|
||||||
|
|
||||||
Ignored = 'ignored error'
|
Ignored = "ignored error"
|
||||||
Selected = 'selected error'
|
Selected = "selected error"
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=512)
|
@lru_cache(maxsize=512)
|
||||||
|
|
@ -57,14 +53,14 @@ def find_noqa(physical_line):
|
||||||
|
|
||||||
|
|
||||||
_Violation = collections.namedtuple(
|
_Violation = collections.namedtuple(
|
||||||
'Violation',
|
"Violation",
|
||||||
[
|
[
|
||||||
'code',
|
"code",
|
||||||
'filename',
|
"filename",
|
||||||
'line_number',
|
"line_number",
|
||||||
'column_number',
|
"column_number",
|
||||||
'text',
|
"text",
|
||||||
'physical_line',
|
"physical_line",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -89,26 +85,29 @@ class Violation(_Violation):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if physical_line is None:
|
if physical_line is None:
|
||||||
physical_line = linecache.getline(self.filename,
|
physical_line = linecache.getline(self.filename, self.line_number)
|
||||||
self.line_number)
|
|
||||||
noqa_match = find_noqa(physical_line)
|
noqa_match = find_noqa(physical_line)
|
||||||
if noqa_match is None:
|
if noqa_match is None:
|
||||||
LOG.debug('%r is not inline ignored', self)
|
LOG.debug("%r is not inline ignored", self)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
codes_str = noqa_match.groupdict()['codes']
|
codes_str = noqa_match.groupdict()["codes"]
|
||||||
if codes_str is None:
|
if codes_str is None:
|
||||||
LOG.debug('%r is ignored by a blanket ``# noqa``', self)
|
LOG.debug("%r is ignored by a blanket ``# noqa``", self)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
codes = set(utils.parse_comma_separated_list(codes_str))
|
codes = set(utils.parse_comma_separated_list(codes_str))
|
||||||
if self.code in codes or self.code.startswith(tuple(codes)):
|
if self.code in codes or self.code.startswith(tuple(codes)):
|
||||||
LOG.debug('%r is ignored specifically inline with ``# noqa: %s``',
|
LOG.debug(
|
||||||
self, codes_str)
|
"%r is ignored specifically inline with ``# noqa: %s``",
|
||||||
|
self,
|
||||||
|
codes_str,
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
LOG.debug('%r is not ignored inline with ``# noqa: %s``',
|
LOG.debug(
|
||||||
self, codes_str)
|
"%r is not ignored inline with ``# noqa: %s``", self, codes_str
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_in(self, diff):
|
def is_in(self, diff):
|
||||||
|
|
@ -158,30 +157,29 @@ class DecisionEngine(object):
|
||||||
"""Initialize the engine."""
|
"""Initialize the engine."""
|
||||||
self.cache = {}
|
self.cache = {}
|
||||||
self.selected = tuple(options.select)
|
self.selected = tuple(options.select)
|
||||||
self.extended_selected = tuple(sorted(
|
self.extended_selected = tuple(
|
||||||
options.extended_default_select,
|
sorted(options.extended_default_select, reverse=True)
|
||||||
reverse=True,
|
|
||||||
))
|
|
||||||
self.enabled_extensions = tuple(options.enable_extensions)
|
|
||||||
self.all_selected = tuple(sorted(
|
|
||||||
self.selected + self.enabled_extensions,
|
|
||||||
reverse=True,
|
|
||||||
))
|
|
||||||
self.ignored = tuple(sorted(
|
|
||||||
itertools.chain(options.ignore, options.extend_ignore),
|
|
||||||
reverse=True,
|
|
||||||
))
|
|
||||||
self.using_default_ignore = set(self.ignored) == set(defaults.IGNORE)
|
|
||||||
self.using_default_select = (
|
|
||||||
set(self.selected) == set(defaults.SELECT)
|
|
||||||
)
|
)
|
||||||
|
self.enabled_extensions = tuple(options.enable_extensions)
|
||||||
|
self.all_selected = tuple(
|
||||||
|
sorted(self.selected + self.enabled_extensions, reverse=True)
|
||||||
|
)
|
||||||
|
self.ignored = tuple(
|
||||||
|
sorted(
|
||||||
|
itertools.chain(options.ignore, options.extend_ignore),
|
||||||
|
reverse=True,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.using_default_ignore = set(self.ignored) == set(defaults.IGNORE)
|
||||||
|
self.using_default_select = set(self.selected) == set(defaults.SELECT)
|
||||||
|
|
||||||
def _in_all_selected(self, code):
|
def _in_all_selected(self, code):
|
||||||
return self.all_selected and code.startswith(self.all_selected)
|
return self.all_selected and code.startswith(self.all_selected)
|
||||||
|
|
||||||
def _in_extended_selected(self, code):
|
def _in_extended_selected(self, code):
|
||||||
return (self.extended_selected and
|
return self.extended_selected and code.startswith(
|
||||||
code.startswith(self.extended_selected))
|
self.extended_selected
|
||||||
|
)
|
||||||
|
|
||||||
def was_selected(self, code):
|
def was_selected(self, code):
|
||||||
# type: (str) -> Union[Selected, Ignored]
|
# type: (str) -> Union[Selected, Ignored]
|
||||||
|
|
@ -264,11 +262,13 @@ class DecisionEngine(object):
|
||||||
# default select list. In either case, we want the violation to be
|
# default select list. In either case, we want the violation to be
|
||||||
# selected.
|
# selected.
|
||||||
return Decision.Selected
|
return Decision.Selected
|
||||||
if (select is None and
|
if select is None and (
|
||||||
(extra_select is None or not self.using_default_ignore)):
|
extra_select is None or not self.using_default_ignore
|
||||||
|
):
|
||||||
return Decision.Ignored
|
return Decision.Ignored
|
||||||
if ((select is None and not self.using_default_select) and
|
if (select is None and not self.using_default_select) and (
|
||||||
(ignore is None and self.using_default_ignore)):
|
ignore is None and self.using_default_ignore
|
||||||
|
):
|
||||||
return Decision.Ignored
|
return Decision.Ignored
|
||||||
return Decision.Selected
|
return Decision.Selected
|
||||||
|
|
||||||
|
|
@ -277,20 +277,24 @@ class DecisionEngine(object):
|
||||||
LOG.debug('Deciding if "%s" should be reported', code)
|
LOG.debug('Deciding if "%s" should be reported', code)
|
||||||
selected = self.was_selected(code)
|
selected = self.was_selected(code)
|
||||||
ignored = self.was_ignored(code)
|
ignored = self.was_ignored(code)
|
||||||
LOG.debug('The user configured "%s" to be "%s", "%s"',
|
LOG.debug(
|
||||||
code, selected, ignored)
|
'The user configured "%s" to be "%s", "%s"',
|
||||||
|
code,
|
||||||
|
selected,
|
||||||
|
ignored,
|
||||||
|
)
|
||||||
|
|
||||||
if ((selected is Selected.Explicitly or
|
if (
|
||||||
selected is Selected.Implicitly) and
|
selected is Selected.Explicitly or selected is Selected.Implicitly
|
||||||
ignored is Selected.Implicitly):
|
) and ignored is Selected.Implicitly:
|
||||||
decision = Decision.Selected
|
decision = Decision.Selected
|
||||||
elif ((selected is Selected.Explicitly and
|
elif (
|
||||||
ignored is Ignored.Explicitly) or
|
selected is Selected.Explicitly and ignored is Ignored.Explicitly
|
||||||
(selected is Ignored.Implicitly and
|
) or (
|
||||||
ignored is Selected.Implicitly)):
|
selected is Ignored.Implicitly and ignored is Selected.Implicitly
|
||||||
|
):
|
||||||
decision = self.more_specific_decision_for(code)
|
decision = self.more_specific_decision_for(code)
|
||||||
elif (selected is Ignored.Implicitly or
|
elif selected is Ignored.Implicitly or ignored is Ignored.Explicitly:
|
||||||
ignored is Ignored.Explicitly):
|
|
||||||
decision = Decision.Ignored # pylint: disable=R0204
|
decision = Decision.Ignored # pylint: disable=R0204
|
||||||
return decision
|
return decision
|
||||||
|
|
||||||
|
|
@ -354,8 +358,15 @@ class StyleGuide(object):
|
||||||
"""
|
"""
|
||||||
return self.decider.decision_for(code)
|
return self.decider.decision_for(code)
|
||||||
|
|
||||||
def handle_error(self, code, filename, line_number, column_number, text,
|
def handle_error(
|
||||||
physical_line=None):
|
self,
|
||||||
|
code,
|
||||||
|
filename,
|
||||||
|
line_number,
|
||||||
|
column_number,
|
||||||
|
text,
|
||||||
|
physical_line=None,
|
||||||
|
):
|
||||||
# type: (str, str, int, int, str) -> int
|
# type: (str, str, int, int, str) -> int
|
||||||
"""Handle an error reported by a check.
|
"""Handle an error reported by a check.
|
||||||
|
|
||||||
|
|
@ -385,14 +396,24 @@ class StyleGuide(object):
|
||||||
# caught, column_number may be None.
|
# caught, column_number may be None.
|
||||||
if not column_number:
|
if not column_number:
|
||||||
column_number = 0
|
column_number = 0
|
||||||
error = Violation(code, filename, line_number, column_number + 1,
|
error = Violation(
|
||||||
text, physical_line)
|
code,
|
||||||
error_is_selected = (self.should_report_error(error.code) is
|
filename,
|
||||||
Decision.Selected)
|
line_number,
|
||||||
|
column_number + 1,
|
||||||
|
text,
|
||||||
|
physical_line,
|
||||||
|
)
|
||||||
|
error_is_selected = (
|
||||||
|
self.should_report_error(error.code) is Decision.Selected
|
||||||
|
)
|
||||||
is_not_inline_ignored = error.is_inline_ignored(disable_noqa) is False
|
is_not_inline_ignored = error.is_inline_ignored(disable_noqa) is False
|
||||||
is_included_in_diff = error.is_in(self._parsed_diff)
|
is_included_in_diff = error.is_in(self._parsed_diff)
|
||||||
if (error_is_selected and is_not_inline_ignored and
|
if (
|
||||||
is_included_in_diff):
|
error_is_selected
|
||||||
|
and is_not_inline_ignored
|
||||||
|
and is_included_in_diff
|
||||||
|
):
|
||||||
self.formatter.handle(error)
|
self.formatter.handle(error)
|
||||||
self.stats.record(error)
|
self.stats.record(error)
|
||||||
self.listener.notify(error.code, error)
|
self.listener.notify(error.code, error)
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@ import re
|
||||||
import sys
|
import sys
|
||||||
import tokenize
|
import tokenize
|
||||||
|
|
||||||
DIFF_HUNK_REGEXP = re.compile(r'^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@.*$')
|
DIFF_HUNK_REGEXP = re.compile(r"^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@.*$")
|
||||||
COMMA_SEPARATED_LIST_RE = re.compile(r'[,\s]')
|
COMMA_SEPARATED_LIST_RE = re.compile(r"[,\s]")
|
||||||
LOCAL_PLUGIN_LIST_RE = re.compile(r'[,\t\n\r\f\v]')
|
LOCAL_PLUGIN_LIST_RE = re.compile(r"[,\t\n\r\f\v]")
|
||||||
|
|
||||||
|
|
||||||
def parse_comma_separated_list(value, regexp=COMMA_SEPARATED_LIST_RE):
|
def parse_comma_separated_list(value, regexp=COMMA_SEPARATED_LIST_RE):
|
||||||
|
|
@ -49,8 +49,9 @@ def normalize_paths(paths, parent=os.curdir):
|
||||||
:rtype:
|
:rtype:
|
||||||
[str]
|
[str]
|
||||||
"""
|
"""
|
||||||
return [normalize_path(p, parent)
|
return [
|
||||||
for p in parse_comma_separated_list(paths)]
|
normalize_path(p, parent) for p in parse_comma_separated_list(paths)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def normalize_path(path, parent=os.curdir):
|
def normalize_path(path, parent=os.curdir):
|
||||||
|
|
@ -67,9 +68,10 @@ def normalize_path(path, parent=os.curdir):
|
||||||
# Unix style paths (/foo/bar).
|
# Unix style paths (/foo/bar).
|
||||||
separator = os.path.sep
|
separator = os.path.sep
|
||||||
# NOTE(sigmavirus24): os.path.altsep may be None
|
# NOTE(sigmavirus24): os.path.altsep may be None
|
||||||
alternate_separator = os.path.altsep or ''
|
alternate_separator = os.path.altsep or ""
|
||||||
if separator in path or (alternate_separator and
|
if separator in path or (
|
||||||
alternate_separator in path):
|
alternate_separator and alternate_separator in path
|
||||||
|
):
|
||||||
path = os.path.abspath(os.path.join(parent, path))
|
path = os.path.abspath(os.path.join(parent, path))
|
||||||
return path.rstrip(separator + alternate_separator)
|
return path.rstrip(separator + alternate_separator)
|
||||||
|
|
||||||
|
|
@ -81,13 +83,13 @@ def _stdin_get_value_py3():
|
||||||
(coding, lines) = tokenize.detect_encoding(fd.readline)
|
(coding, lines) = tokenize.detect_encoding(fd.readline)
|
||||||
return io.StringIO(stdin_value.decode(coding))
|
return io.StringIO(stdin_value.decode(coding))
|
||||||
except (LookupError, SyntaxError, UnicodeError):
|
except (LookupError, SyntaxError, UnicodeError):
|
||||||
return io.StringIO(stdin_value.decode('utf-8'))
|
return io.StringIO(stdin_value.decode("utf-8"))
|
||||||
|
|
||||||
|
|
||||||
def stdin_get_value():
|
def stdin_get_value():
|
||||||
# type: () -> str
|
# type: () -> str
|
||||||
"""Get and cache it so plugins can use it."""
|
"""Get and cache it so plugins can use it."""
|
||||||
cached_value = getattr(stdin_get_value, 'cached_stdin', None)
|
cached_value = getattr(stdin_get_value, "cached_stdin", None)
|
||||||
if cached_value is None:
|
if cached_value is None:
|
||||||
if sys.version_info < (3, 0):
|
if sys.version_info < (3, 0):
|
||||||
stdin_value = io.BytesIO(sys.stdin.read())
|
stdin_value = io.BytesIO(sys.stdin.read())
|
||||||
|
|
@ -118,7 +120,7 @@ def parse_unified_diff(diff=None):
|
||||||
if number_of_rows:
|
if number_of_rows:
|
||||||
# NOTE(sigmavirus24): Below we use a slice because stdin may be
|
# NOTE(sigmavirus24): Below we use a slice because stdin may be
|
||||||
# bytes instead of text on Python 3.
|
# bytes instead of text on Python 3.
|
||||||
if line[:1] != '-':
|
if line[:1] != "-":
|
||||||
number_of_rows -= 1
|
number_of_rows -= 1
|
||||||
# We're in the part of the diff that has lines starting with +, -,
|
# We're in the part of the diff that has lines starting with +, -,
|
||||||
# and ' ' to show context and the changes made. We skip these
|
# and ' ' to show context and the changes made. We skip these
|
||||||
|
|
@ -139,10 +141,10 @@ def parse_unified_diff(diff=None):
|
||||||
# +++ b/file.py\t100644
|
# +++ b/file.py\t100644
|
||||||
# Which is an example that has the new file permissions/mode.
|
# Which is an example that has the new file permissions/mode.
|
||||||
# In this case we only care about the file name.
|
# In this case we only care about the file name.
|
||||||
if line[:3] == '+++':
|
if line[:3] == "+++":
|
||||||
current_path = line[4:].split('\t', 1)[0]
|
current_path = line[4:].split("\t", 1)[0]
|
||||||
# NOTE(sigmavirus24): This check is for diff output from git.
|
# NOTE(sigmavirus24): This check is for diff output from git.
|
||||||
if current_path[:2] == 'b/':
|
if current_path[:2] == "b/":
|
||||||
current_path = current_path[2:]
|
current_path = current_path[2:]
|
||||||
# We don't need to do anything else. We have set up our local
|
# We don't need to do anything else. We have set up our local
|
||||||
# ``current_path`` variable. We can skip the rest of this loop.
|
# ``current_path`` variable. We can skip the rest of this loop.
|
||||||
|
|
@ -179,7 +181,7 @@ def is_windows():
|
||||||
:rtype:
|
:rtype:
|
||||||
bool
|
bool
|
||||||
"""
|
"""
|
||||||
return os.name == 'nt'
|
return os.name == "nt"
|
||||||
|
|
||||||
|
|
||||||
# NOTE(sigmavirus24): If and when https://bugs.python.org/issue27649 is fixed,
|
# NOTE(sigmavirus24): If and when https://bugs.python.org/issue27649 is fixed,
|
||||||
|
|
@ -217,7 +219,7 @@ def is_using_stdin(paths):
|
||||||
:rtype:
|
:rtype:
|
||||||
bool
|
bool
|
||||||
"""
|
"""
|
||||||
return '-' in paths
|
return "-" in paths
|
||||||
|
|
||||||
|
|
||||||
def _default_predicate(*args):
|
def _default_predicate(*args):
|
||||||
|
|
@ -312,19 +314,23 @@ def parameters_for(plugin):
|
||||||
argspec = inspect.getargspec(func)
|
argspec = inspect.getargspec(func)
|
||||||
start_of_optional_args = len(argspec[0]) - len(argspec[-1] or [])
|
start_of_optional_args = len(argspec[0]) - len(argspec[-1] or [])
|
||||||
parameter_names = argspec[0]
|
parameter_names = argspec[0]
|
||||||
parameters = collections.OrderedDict([
|
parameters = collections.OrderedDict(
|
||||||
(name, position < start_of_optional_args)
|
[
|
||||||
for position, name in enumerate(parameter_names)
|
(name, position < start_of_optional_args)
|
||||||
])
|
for position, name in enumerate(parameter_names)
|
||||||
|
]
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
parameters = collections.OrderedDict([
|
parameters = collections.OrderedDict(
|
||||||
(parameter.name, parameter.default is parameter.empty)
|
[
|
||||||
for parameter in inspect.signature(func).parameters.values()
|
(parameter.name, parameter.default is parameter.empty)
|
||||||
if parameter.kind == parameter.POSITIONAL_OR_KEYWORD
|
for parameter in inspect.signature(func).parameters.values()
|
||||||
])
|
if parameter.kind == parameter.POSITIONAL_OR_KEYWORD
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
if is_class:
|
if is_class:
|
||||||
parameters.pop('self', None)
|
parameters.pop("self", None)
|
||||||
|
|
||||||
return parameters
|
return parameters
|
||||||
|
|
||||||
|
|
@ -341,5 +347,5 @@ def get_python_version():
|
||||||
try:
|
try:
|
||||||
impl = platform.python_implementation() + " "
|
impl = platform.python_implementation() + " "
|
||||||
except AttributeError: # Python 2.5
|
except AttributeError: # Python 2.5
|
||||||
impl = ''
|
impl = ""
|
||||||
return '%s%s on %s' % (impl, platform.python_version(), platform.system())
|
return "%s%s on %s" % (impl, platform.python_version(), platform.system())
|
||||||
|
|
|
||||||
16
tox.ini
16
tox.ini
|
|
@ -23,12 +23,22 @@ basepython = python2.7
|
||||||
skip_install = true
|
skip_install = true
|
||||||
deps =
|
deps =
|
||||||
wheel
|
wheel
|
||||||
|
flake8-colors
|
||||||
commands =
|
commands =
|
||||||
python setup.py -qq bdist_wheel
|
python setup.py -qq bdist_wheel
|
||||||
pip install -U --pre --find-links ./dist/ flake8
|
pip install --force-reinstall -U --pre --find-links ./dist/ flake8
|
||||||
flake8 --version
|
flake8 --version
|
||||||
flake8 src/flake8/ tests/ setup.py
|
flake8 src/flake8/ tests/ setup.py
|
||||||
|
|
||||||
|
# Autoformatter
|
||||||
|
[testenv:black]
|
||||||
|
basepython = python3
|
||||||
|
skip_install = true
|
||||||
|
deps =
|
||||||
|
black
|
||||||
|
commands =
|
||||||
|
black -l 78 -N src/
|
||||||
|
|
||||||
# Linters
|
# Linters
|
||||||
[testenv:flake8]
|
[testenv:flake8]
|
||||||
basepython = python3
|
basepython = python3
|
||||||
|
|
@ -80,12 +90,14 @@ commands =
|
||||||
basepython = python3
|
basepython = python3
|
||||||
skip_install = true
|
skip_install = true
|
||||||
deps =
|
deps =
|
||||||
|
{[testenv:black]deps}
|
||||||
{[testenv:flake8]deps}
|
{[testenv:flake8]deps}
|
||||||
{[testenv:pylint]deps}
|
{[testenv:pylint]deps}
|
||||||
{[testenv:doc8]deps}
|
{[testenv:doc8]deps}
|
||||||
{[testenv:readme]deps}
|
{[testenv:readme]deps}
|
||||||
{[testenv:bandit]deps}
|
{[testenv:bandit]deps}
|
||||||
commands =
|
commands =
|
||||||
|
{[testenv:black]commands}
|
||||||
{[testenv:flake8]commands}
|
{[testenv:flake8]commands}
|
||||||
{[testenv:pylint]commands}
|
{[testenv:pylint]commands}
|
||||||
{[testenv:doc8]commands}
|
{[testenv:doc8]commands}
|
||||||
|
|
@ -143,7 +155,7 @@ commands =
|
||||||
# defaults to selecting all other errors so we do not need select=E,F,W,I,D
|
# defaults to selecting all other errors so we do not need select=E,F,W,I,D
|
||||||
# Once Flake8 3.0 is released and in a good state, we can use both and it will
|
# Once Flake8 3.0 is released and in a good state, we can use both and it will
|
||||||
# work well \o/
|
# work well \o/
|
||||||
ignore = D203, W504
|
ignore = D203, W503, E203
|
||||||
exclude =
|
exclude =
|
||||||
.tox,
|
.tox,
|
||||||
.git,
|
.git,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue