summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--flake8/checker.py33
-rw-r--r--flake8/main/cli.py3
-rw-r--r--flake8/processor.py9
-rw-r--r--flake8/style_guide.py5
4 files changed, 48 insertions, 2 deletions
diff --git a/flake8/checker.py b/flake8/checker.py
index c449a9b..905cade 100644
--- a/flake8/checker.py
+++ b/flake8/checker.py
@@ -326,6 +326,37 @@ class FileChecker(object):
self.processor.keyword_arguments_for(plugin.parameters, arguments)
return plugin.execute(**arguments)
+ def run_ast_checks(self):
+ """Run all checks expecting an abstract syntax tree."""
+ try:
+ ast = self.processor.build_ast()
+ except (ValueError, SyntaxError, TypeError):
+ (exc_type, exception) = sys.exc_info()[:2]
+ if len(exception.args) > 1:
+ offset = exception.args[1]
+ if len(offset) > 2:
+ offset = offset[1:3]
+ else:
+ offset = (1, 0)
+
+ self.report('E999', offset[0], offset[1], '%s: %s' %
+ (exc_type.__name__, exception.args[0]))
+ return
+
+ for plugin in self.checks.ast_plugins:
+ checker = self.run_check(plugin, tree=ast)
+ # NOTE(sigmavirus24): If we want to allow for AST plugins that are
+ # not classes exclusively, we can do the following:
+ # retrieve_results = getattr(checker, 'run', lambda: checker)
+ # Otherwise, we just call run on the checker
+ for (line_number, offset, text, check) in checker.run():
+ self.report(
+ error_code=None,
+ line_number=line_number,
+ column=offset,
+ text=text,
+ )
+
def run_logical_checks(self):
"""Run all checks expecting a logical line."""
comments, logical_line, mapping = self.processor.build_logical_line()
@@ -404,6 +435,8 @@ class FileChecker(object):
self.report(exc.error_code, exc.line_number, exc.column_number,
exc.error_message)
+ self.run_ast_checks()
+
if results_queue is not None:
results_queue.put((self.filename, self.results))
diff --git a/flake8/main/cli.py b/flake8/main/cli.py
index 5bf6144..dbd9603 100644
--- a/flake8/main/cli.py
+++ b/flake8/main/cli.py
@@ -227,6 +227,9 @@ class Application(object):
self.option_manager, argv
)
+ self.check_plugins.provide_options(self.option_manager, self.options,
+ self.args)
+
def make_formatter(self):
# type: () -> NoneType
"""Initialize a formatter based on the parsed options."""
diff --git a/flake8/processor.py b/flake8/processor.py
index d75256b..1dc27a1 100644
--- a/flake8/processor.py
+++ b/flake8/processor.py
@@ -10,6 +10,7 @@ from flake8 import defaults
from flake8 import exceptions
from flake8 import utils
+PyCF_ONLY_AST = 1024
NEWLINE = frozenset([tokenize.NL, tokenize.NEWLINE])
# Work around Python < 2.6 behaviour, which does not generate NL after
# a comment which is on a line by itself.
@@ -172,6 +173,10 @@ class FileProcessor(object):
(previous_row, previous_column) = end
return comments, logical, mapping
+ def build_ast(self):
+ """Build an abstract syntax tree from the list of lines."""
+ return compile(''.join(self.lines), '', 'exec', PyCF_ONLY_AST)
+
def build_logical_line(self):
"""Build a logical line from the current tokens list."""
comments, logical, mapping_list = self.build_logical_line_tokens()
@@ -223,6 +228,10 @@ class FileProcessor(object):
except tokenize.TokenError as exc:
raise exceptions.InvalidSyntax(exc.message, exception=exc)
+ def line_for(self, line_number):
+ """Retrieve the physical line at the specified line number."""
+ return self.lines[line_number - 1]
+
def next_line(self):
"""Get the next line from the list."""
if self.line_number >= self.total_lines:
diff --git a/flake8/style_guide.py b/flake8/style_guide.py
index 51c3a8e..2c18c9d 100644
--- a/flake8/style_guide.py
+++ b/flake8/style_guide.py
@@ -187,12 +187,13 @@ class StyleGuide(object):
error, codes_str)
return False
- def handle_error(self, code, filename, line_number, column_number, text):
+ def handle_error(self, code, filename, line_number, column_number, text,
+ physical_line=None):
# type: (str, str, int, int, str) -> NoneType
"""Handle an error reported by a check."""
error = Error(code, filename, line_number, column_number, text)
if (self.should_report_error(error.code) is Decision.Selected and
- self.is_inline_ignored(error) is False):
+ self.is_inline_ignored(error, physical_line) is False):
self.formatter.handle(error)
self.listener.notify(error.code, error)